[Delphi] 纯文本查看 复制代码
program SMKB_1;
{$APPTYPE CONSOLE}
{$R *.res}
{$WeakLinkRTTI on}
{$RTTI Explicit Methods([]) Properties([]) Fields([])}
{$R *.dres}
uses
System.SysUtils,
System.Classes,
System.IOUtils,
System.IniFiles,
Vcl.Grids,
System.Types;
//显示帮助信息
procedure ShowHelp();
begin
Writeln('-----------------------------------------------------------------');
Writeln('【ORGE资源管理器 0.1.20201208】,程序开发:表哥');
Writeln('Object-Oriented Graphics Rendering Engine Resource Manager');
Writeln('');
Writeln('使用用法:SMKB_1.exe "*.config"');
Writeln('');
Writeln('(注意:【】内的文字仅用于此处的帮助说明,在实际配置文件中不存在)');
Writeln('解包用配置文件(unpackage.config)格式:');
Writeln('[SMKB]');
Writeln('types=unpackage【指明此配置文件用于解包】');
Writeln('sources=d:\xxx.toc【指定全路径资源包文件名称】');
Writeln('targets=d:\demo\【指定全路径解包存储文件夹名称】');
Writeln('');
Writeln('打包用配置文件(package.config)格式:');
Writeln('[SMKB]');
Writeln('types=package【指明此配置文件用于打包】');
Writeln('sources=d:\demo\【指定全路径包含等待打包的资源文件夹名称】');
Writeln('targets=d:\game\【指定全路径打包存储文件夹名称】');
Writeln('');
Writeln('汉化补丁打包用配置文件(patch.config)格式:');
Writeln('[SMKB]');
Writeln('types=patch【指明此配置文件用于汉化补丁打包】');
Writeln('sources=d:\demo\【指定全路径包含已汉化的资源文件夹名称】');
Writeln('targets=d:\game\【指定全路径汉化补丁存储文件夹名称】');
Writeln('-----------------------------------------------------------------');
end;
//加载参数(配置文件)信息
procedure LoadConfigParam(var Types:string;var Sources:string;var Targets:string);
begin
var configFilename:string:=ParamStr(1);//获取命令行参数(配置文件)
if not configFilename.Contains(':') then configFilename:=GetCurrentDir+'\'+configFilename;//补全路径信息
//加载配置文件
var configFile:Tinifile:=nil;
if TFile.Exists(configFilename) then configFile:=TIniFile.Create(configFilename);
//解析参数信息
if configFile<>nil then
begin
Types:=configFile.ReadString('SMKB','types','');
Sources:=configFile.ReadString('SMKB','sources','');
Targets:=configFile.ReadString('SMKB','targets','');
configFile.Free();
end;
end;
//获取定长字符串
function GetFixString(Value:UInt32):string;
begin
Result:=Value.ToString();
if (5-Length(Result))>0 then for var i:Integer:=1 to 5-Length(Result) do Result:='0'+Result;
end;
//获取crc32校验值
procedure GetCRC32(PathFilename:string;var CRC32:UInt32);
const
crc32CodeTable:array [0..255] of UInt32=//CRC32编码表
($00000000,$77073096,$EE0E612C,$990951BA,$076DC419,$706AF48F,$E963A535,$9E6495A3,$0EDB8832,$79DCB8A4,$E0D5E91E,$97D2D988,$09B64C2B,$7EB17CBD,$E7B82D07,$90BF1D91,
$1DB71064,$6AB020F2,$F3B97148,$84BE41DE,$1ADAD47D,$6DDDE4EB,$F4D4B551,$83D385C7,$136C9856,$646BA8C0,$FD62F97A,$8A65C9EC,$14015C4F,$63066CD9,$FA0F3D63,$8D080DF5,
$3B6E20C8,$4C69105E,$D56041E4,$A2677172,$3C03E4D1,$4B04D447,$D20D85FD,$A50AB56B,$35B5A8FA,$42B2986C,$DBBBC9D6,$ACBCF940,$32D86CE3,$45DF5C75,$DCD60DCF,$ABD13D59,
$26D930AC,$51DE003A,$C8D75180,$BFD06116,$21B4F4B5,$56B3C423,$CFBA9599,$B8BDA50F,$2802B89E,$5F058808,$C60CD9B2,$B10BE924,$2F6F7C87,$58684C11,$C1611DAB,$B6662D3D,
$76DC4190,$01DB7106,$98D220BC,$EFD5102A,$71B18589,$06B6B51F,$9FBFE4A5,$E8B8D433,$7807C9A2,$0F00F934,$9609A88E,$E10E9818,$7F6A0DBB,$086D3D2D,$91646C97,$E6635C01,
$6B6B51F4,$1C6C6162,$856530D8,$F262004E,$6C0695ED,$1B01A57B,$8208F4C1,$F50FC457,$65B0D9C6,$12B7E950,$8BBEB8EA,$FCB9887C,$62DD1DDF,$15DA2D49,$8CD37CF3,$FBD44C65,
$4DB26158,$3AB551CE,$A3BC0074,$D4BB30E2,$4ADFA541,$3DD895D7,$A4D1C46D,$D3D6F4FB,$4369E96A,$346ED9FC,$AD678846,$DA60B8D0,$44042D73,$33031DE5,$AA0A4C5F,$DD0D7CC9,
$5005713C,$270241AA,$BE0B1010,$C90C2086,$5768B525,$206F85B3,$B966D409,$CE61E49F,$5EDEF90E,$29D9C998,$B0D09822,$C7D7A8B4,$59B33D17,$2EB40D81,$B7BD5C3B,$C0BA6CAD,
$EDB88320,$9ABFB3B6,$03B6E20C,$74B1D29A,$EAD54739,$9DD277AF,$04DB2615,$73DC1683,$E3630B12,$94643B84,$0D6D6A3E,$7A6A5AA8,$E40ECF0B,$9309FF9D,$0A00AE27,$7D079EB1,
$F00F9344,$8708A3D2,$1E01F268,$6906C2FE,$F762575D,$806567CB,$196C3671,$6E6B06E7,$FED41B76,$89D32BE0,$10DA7A5A,$67DD4ACC,$F9B9DF6F,$8EBEEFF9,$17B7BE43,$60B08ED5,
$D6D6A3E8,$A1D1937E,$38D8C2C4,$4FDFF252,$D1BB67F1,$A6BC5767,$3FB506DD,$48B2364B,$D80D2BDA,$AF0A1B4C,$36034AF6,$41047A60,$DF60EFC3,$A867DF55,$316E8EEF,$4669BE79,
$CB61B38C,$BC66831A,$256FD2A0,$5268E236,$CC0C7795,$BB0B4703,$220216B9,$5505262F,$C5BA3BBE,$B2BD0B28,$2BB45A92,$5CB36A04,$C2D7FFA7,$B5D0CF31,$2CD99E8B,$5BDEAE1D,
$9B64C2B0,$EC63F226,$756AA39C,$026D930A,$9C0906A9,$EB0E363F,$72076785,$05005713,$95BF4A82,$E2B87A14,$7BB12BAE,$0CB61B38,$92D28E9B,$E5D5BE0D,$7CDCEFB7,$0BDBDF21,
$86D3D2D4,$F1D4E242,$68DDB3F8,$1FDA836E,$81BE16CD,$F6B9265B,$6FB077E1,$18B74777,$88085AE6,$FF0F6A70,$66063BCA,$11010B5C,$8F659EFF,$F862AE69,$616BFFD3,$166CCF45,
$A00AE278,$D70DD2EE,$4E048354,$3903B3C2,$A7672661,$D06016F7,$4969474D,$3E6E77DB,$AED16A4A,$D9D65ADC,$40DF0B66,$37D83BF0,$A9BCAE53,$DEBB9EC5,$47B2CF7F,$30B5FFE9,
$BDBDF21C,$CABAC28A,$53B39330,$24B4A3A6,$BAD03605,$CDD70693,$54DE5729,$23D967BF,$B3667A2E,$C4614AB8,$5D681B02,$2A6F2B94,$B40BBE37,$C30C8EA1,$5A05DF1B,$2D02EF8D);
begin
var buffer:TBytes:=BytesOf(PathFilename);
CRC32:=$FFFFFFFF;
for var i:Integer:=0 to Length(buffer)-1 do
begin
var PathNameChar:UInt32:=buffer[i];
//额外处理西欧字符集问题
if PathNameChar=$7E then PathNameChar:=$E9;
if PathNameChar=$60 then PathNameChar:=$E7;
//计算crc32
CRC32:=(CRC32 shr 8) xor crc32CodeTable[(CRC32 xor PathNameChar) and $FF];
end;
end;
//控制台程序入口
begin
try
//未提供命令行参数则显示帮助信息
if ParamCount=0 then
begin
ShowHelp();
Exit;
end;
//加载并解析参数配置文件
var types:string:='';
var sources:string:='';
var targets:string:='';
LoadConfigParam(types,sources,targets);
if (types='')or(sources='')or(targets='') then
begin
Writeln('参数配置文件中的内容不正确或缺失,请重新指定。');
Exit;
end;
//执行解包操作
if types='unpackage' then
begin
//加载资源包文件(.TOC)
var tocMemoryStream:TMemoryStream:=TMemoryStream.Create();
if TFile.Exists(sources) then tocMemoryStream.LoadFromFile(sources);
//资源包太小则不进行解包操作
if tocMemoryStream.Size<160 then
begin
Writeln('在参数配置文件中由sources指定的资源包文件太小了,无法执行格式判断,请重新指定。');
tocMemoryStream.Free();
Exit;
end;
//检测PACK第一魔法值
var pack:UInt32:=0;
tocMemoryStream.ReadData(pack);
if pack<>1262698832 then
begin
Writeln('在参数配置文件中由sources指定的文件不是资源包文件,请重新指定。');
tocMemoryStream.Free();
Exit;
end;
//检测REST第二魔法值
var rest:UInt32:=0;
tocMemoryStream.Position:=128;
tocMemoryStream.ReadData(rest);
if rest<>1414743378 then
begin
Writeln('在参数配置文件中由sources指定的文件不是资源包文件,请重新指定。');
tocMemoryStream.Free();
Exit;
end;
//检测文件表第一部分大小
var fileTableSize:UInt32:=0;
tocMemoryStream.Position:=136;
tocMemoryStream.ReadData(fileTableSize);
if (fileTableSize+160)>tocMemoryStream.Size then
begin
Writeln('资源包中的文件表第一部分大小不正确,无法进行解包操作。');
tocMemoryStream.Free();
Exit;
end;
//处理资源包关联文件
var dirFilename:string:=TPath.GetDirectoryName(sources)+'\'+TPath.GetFileNameWithoutExtension(sources)+'.DIR';//文件表第二部分
var datFilename:string:=TPath.GetDirectoryName(sources)+'\'+TPath.GetFileNameWithoutExtension(sources)+'.DAT';//文件数据
if not TFile.Exists(dirFilename) then
begin
Writeln('指定的资源包缺少.DIR关联文件,无法进行解包操作。');
tocMemoryStream.Free();
Exit;
end;
if not TFile.Exists(datFilename) then
begin
Writeln('指定的资源包缺少.DAT关联文件,无法进行解包操作。');
tocMemoryStream.Free();
Exit;
end;
//初始化解包网格:【CRC32】【数据长度】【数据偏移】【文件名偏移】【文件名长度】【文件名】【存盘文件名】
var fileCount:UInt32:=0;
tocMemoryStream.ReadData(fileCount);
var unpackageGrid:TStringGrid:=TStringGrid.Create(nil);
unpackageGrid.RowCount:=fileCount;
unpackageGrid.ColCount:=7;
//解析文件表第一部分
var buffer:UInt32:=0;
tocMemoryStream.Position:=160;
for var i:Integer:=0 to fileCount-1 do
begin
//获取crc32校验值
tocMemoryStream.ReadData(buffer);
unpackageGrid.Cells[0,i]:=buffer.ToString();
//获取文件数据长度(文件实际大小需要除以4)
tocMemoryStream.ReadData(buffer);
unpackageGrid.Cells[1,i]:=(buffer div 4).ToString();
//获取文件数据偏移
tocMemoryStream.ReadData(buffer);
unpackageGrid.Cells[2,i]:=buffer.ToString();
end;
//解析文件表第二部分
var dirMemoryStream:TMemoryStream:=TMemoryStream.Create();
dirMemoryStream.LoadFromFile(dirFilename);
var dirBuffer:TBytes;
SetLength(dirBuffer,dirMemoryStream.Size);
dirMemoryStream.ReadData(dirBuffer,dirMemoryStream.Size);
//处理西欧字符集问题
var filenameOffset:UInt32:=fileCount*4;
for var i:Integer:=filenameOffset to Length(dirBuffer) do
begin
if dirBuffer[i]=$E7 then dirBuffer[i]:=$60;
if dirBuffer[i]=$E9 then dirBuffer[i]:=$7E;
end;
dirMemoryStream.Clear();
dirMemoryStream.WriteData(dirBuffer,Length(dirBuffer));
dirMemoryStream.Position:=0;
//获取文件名称偏移
for var i:Integer:=0 to fileCount-1 do
begin
dirMemoryStream.ReadData(Buffer);
unpackageGrid.Cells[3,i]:=buffer.ToString();
end;
//计算文件名称长度
for var i:UInt32:=0 to fileCount-1 do
begin
if i=(fileCount-1) then
begin
unpackageGrid.Cells[4,i]:=(dirMemoryStream.Size-StrToUInt(unpackageGrid.Cells[3,i])).ToString();
end else unpackageGrid.Cells[4,i]:=(StrToUInt(unpackageGrid.Cells[3,i+1])-StrToUInt(unpackageGrid.Cells[3,i])).ToString();
end;
//获取文件名称
var filename:TBytes;
for var i:Integer:=0 to fileCount-1 do
begin
SetLength(filename,StrToUInt(unpackageGrid.Cells[4,i]));
dirMemoryStream.ReadData(filename,StrToUInt(unpackageGrid.Cells[4,i]));
unpackageGrid.Cells[5,i]:=StringOf(filename);
//获取存盘文件名称
unpackageGrid.Cells[6,i]:=TPath.GetFileNameWithoutExtension(sources)+'.smkb.'+GetFixString(i+1)+unpackageGrid.Cells[5,i].Replace('/','-smkb-');
end;
//解包存盘
var datFileStream:TFileStream:=TFileStream.Create(datFilename,fmOpenRead);
if targets[targets.Length-1]<>'\' then targets:=targets+'\';//挂载路径后缀
if not TDirectory.Exists(targets) then TDirectory.CreateDirectory(targets);//存盘文件夹不存在则创建
var fileSaveStream:TMemoryStream:=TMemoryStream.Create();
for var i:Integer:=0 to fileCount-1 do//循环解包存盘
begin
//解包资源文件
Writeln((i+1).ToString()+' '+targets+unpackageGrid.Cells[6,i]);
fileSaveStream.Clear();
datFileStream.Position:=unpackageGrid.Cells[2,i].ToInt64();
fileSaveStream.CopyFrom(datFileStream,StrToUInt(unpackageGrid.Cells[1,i]));
fileSaveStream.SaveToFile(targets+unpackageGrid.Cells[6,i]);
end;
Writeln('解包操作完成,请在文件夹:'+targets+'下查看解包结果。');
//完成解包操作后释放内存
tocMemoryStream.Free();
dirMemoryStream.Free();
datFileStream.Free();
unpackageGrid.Free();
fileSaveStream.Free();
end;
//执行打包操作
if types='package' then
begin
//挂载路径后缀
if sources[sources.Length-1]<>'\' then sources:=sources+'\';
if targets[targets.Length-1]<>'\' then targets:=targets+'\';
//获取资源包名称
var fileList:TStringDynArray:=TDirectory.GetFiles(sources,'*',TSearchOption.soTopDirectoryOnly);
var assetNames:TStringList:=TStringList.Create();
const splitString:string='.smkb.';
for var i:Integer:=0 to Length(fileList)-1 do
begin
if assetNames.IndexOf(fileList[i].Replace(sources,'').Trim().Split(splitString)[0].Trim())<0 then
begin
assetNames.Add(fileList[i].Replace(sources,'').Trim().Split(splitString)[0].Trim());
end;
end;
//初始化打包网格:【文件名】【crc32】【文件名长度】【文件名偏移】【数据长度】【数据偏移】
var packageGrid:TStringGrid:=TStringGrid.Create(nil);
packageGrid.ColCount:=6;
//针对资源包名称分别打包
var resourceMemoeyStream:TMemoryStream:=TMemoryStream.Create();
for var i:Integer:=0 to assetNames.Count-1 do
begin
//获取当前资源文件列表
fileList:=TDirectory.GetFiles(sources,assetNames.Strings[i]+splitString+'*',TSearchOption.soTopDirectoryOnly);
packageGrid.RowCount:=Length(fileList);
//计算资源打包所需各项信息
var filenameOffset:UInt32:=Length(fileList)*4;
var fileDataOffset:UInt32:=0;
for var j:Integer:=0 to Length(fileList)-1 do
begin
//加载资源文件
resourceMemoeyStream.Clear();
resourceMemoeyStream.LoadFromFile(fileList[j]);
//设置文件名
packageGrid.Cells[0,j]:=fileList[j].Replace(sources+assetNames.Strings[i]+splitString,'').Replace('-smkb-','/').Substring(5).Trim();
Writeln('正在计算打包资源信息,请耐心等待......'+packageGrid.Cells[0,j]);
//设置crc32
var crc32:UInt32:=0;
GetCRC32(packageGrid.Cells[0,j].Replace('/','|').Replace('\','|').ToLower(),crc32);
packageGrid.Cells[1,j]:=crc32.ToString();
//设置文件名长度
packageGrid.Cells[2,j]:=(packageGrid.Cells[0,j].Length+1).ToString();
//设置文件名偏移
packageGrid.Cells[3,j]:=filenameOffset.ToString();
filenameOffset:=filenameOffset+StrToUInt(packageGrid.Cells[2,j]);
//设置数据大小
packageGrid.Cells[4,j]:=(resourceMemoeyStream.Size*4).ToString();
//设置数据偏移
packageGrid.Cells[5,j]:=fileDataOffset.ToString();
fileDataOffset:=fileDataOffset+resourceMemoeyStream.Size;
var modValue:UInt32:=resourceMemoeyStream.Size mod 512;
if modValue<>0 then fileDataOffset:=fileDataOffset+(512-modValue);
end;
//打包.TOC文件
var tocMemoryStream:TMemoryStream:=TMemoryStream.Create();
//写入文件头信息(共160字节)
tocMemoryStream.WriteData(BytesOf('PACK'),4);
tocMemoryStream.WriteData(65536,4);
tocMemoryStream.WriteData(96,4);
tocMemoryStream.WriteData(3,4);
for var j:Integer:=1 to 16 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('MKRT'),4);
tocMemoryStream.WriteData(65536,4);
for var j:Integer:=1 to 24 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('OFFT'),4);
tocMemoryStream.WriteData(65536,4);
for var j:Integer:=1 to 24 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('DEPT'),4);
tocMemoryStream.WriteData(65536,4);
for var j:Integer:=1 to 24 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('REST'),4);
tocMemoryStream.WriteData(65536,4);
tocMemoryStream.WriteData(packageGrid.RowCount*12,4);
tocMemoryStream.WriteData(packageGrid.RowCount,4);
for var j:Integer:=1 to 16 do tocMemoryStream.WriteData($00,1);
//写入文件表第一部分信息(每个文件12字节)
for var j:Integer:=0 to Length(fileList)-1 do
begin
tocMemoryStream.WriteData(StrToUInt(packageGrid.Cells[1,j]),4);
tocMemoryStream.WriteData(StrToUInt(packageGrid.Cells[4,j]),4);
tocMemoryStream.WriteData(StrToUInt(packageGrid.Cells[5,j]),4);
end;
tocMemoryStream.SaveToFile(targets+assetNames.Strings[i]+'.TOC');
tocMemoryStream.Free();
//打包.DIR文件
var dirMemoryStream:TMemoryStream:=TMemoryStream.Create();
//写入文件表第二部分信息(文件名称偏移信息)
for var j:Integer:=0 to Length(fileList)-1 do dirMemoryStream.WriteData(StrToUInt(packageGrid.Cells[3,j]),4);
//写入文件表第二部分信息(文件名称信息)
var filenameBuffer:TBytes;
for var j:Integer:=0 to Length(fileList)-1 do
begin
SetLength(filenameBuffer,packageGrid.Cells[0,j].Length);
filenameBuffer:=BytesOf(packageGrid.Cells[0,j]);
//处理西欧字符集问题
for var k:Integer:=0 to Length(filenameBuffer)-1 do
begin
if filenameBuffer[k]=$7E then filenameBuffer[k]:=$E9;
if filenameBuffer[k]=$60 then filenameBuffer[k]:=$E7;
end;
dirMemoryStream.WriteData(filenameBuffer,Length(filenameBuffer));
dirMemoryStream.WriteData($00,1);
end;
dirMemoryStream.SaveToFile(targets+assetNames.Strings[i]+'.DIR');
dirMemoryStream.Free();
//打包.DAT文件
var datFileStream:TFileStream:=TFileStream.Create(targets+assetNames.Strings[i]+'.DAT',fmCreate);
var fileDataBuffer:TBytes;
for var j:Integer:=0 to Length(fileList)-1 do
begin
//加载资源文件
resourceMemoeyStream.Clear();
resourceMemoeyStream.LoadFromFile(fileList[j]);
Writeln((j+1).ToString()+' '+fileList[j]);
SetLength(fileDataBuffer,resourceMemoeyStream.Size);
resourceMemoeyStream.ReadData(fileDataBuffer,resourceMemoeyStream.Size);
//写入数据文件信息(需要512字节对齐)
datFileStream.WriteData(fileDataBuffer,resourceMemoeyStream.Size);
if (resourceMemoeyStream.Size mod 512)<>0 then for var k:Integer:=1 to 512-(resourceMemoeyStream.Size mod 512) do datFileStream.WriteData($00,1);
end;
datFileStream.Free();
end;
Writeln('打包操作完成,请在文件夹:'+targets+'下查看打包结果。');
//完成打包操作后释放内存
assetNames.Free();
packageGrid.Free();
resourceMemoeyStream.Free();
end;
//执行汉化补丁打包操作
if types='patch' then
begin
//挂载路径后缀
if sources[sources.Length-1]<>'\' then sources:=sources+'\';
if targets[targets.Length-1]<>'\' then targets:=targets+'\';
//获取资源包名称
var fileList:TStringDynArray:=TDirectory.GetFiles(sources,'*',TSearchOption.soTopDirectoryOnly);
var assetNames:TStringList:=TStringList.Create();
const splitString:string='.smkb.';
for var i:Integer:=0 to Length(fileList)-1 do
begin
if assetNames.IndexOf(fileList[i].Replace(sources,'').Trim().Split(splitString)[0].Trim())<0 then
begin
assetNames.Add(fileList[i].Replace(sources,'').Trim().Split(splitString)[0].Trim());
end;
end;
//初始化汉化补丁打包网格:【文件名】【crc32】【文件名长度】【文件名偏移】【数据长度】【数据偏移】
var patchGrid:TStringGrid:=TStringGrid.Create(nil);
patchGrid.ColCount:=6;
patchGrid.RowCount:=Length(fileList);
//计算资源打包所需各项信息
var filenameOffset:UInt32:=Length(fileList)*4;
var fileDataOffset:UInt32:=0;
var resourceMemoeyStream:TMemoryStream:=TMemoryStream.Create();
for var j:Integer:=0 to Length(fileList)-1 do
begin
//加载资源文件
resourceMemoeyStream.Clear();
resourceMemoeyStream.LoadFromFile(fileList[j]);
//设置文件名
patchGrid.Cells[0,j]:=fileList[j];
for var i:Integer:=0 to assetNames.Count-1 do patchGrid.Cells[0,j]:=patchGrid.Cells[0,j].Replace(sources+assetNames.Strings[i]+splitString,'');
patchGrid.Cells[0,j]:=patchGrid.Cells[0,j].Replace('-smkb-','/').Substring(5).Trim();
Writeln('正在计算打包资源信息,请耐心等待......'+patchGrid.Cells[0,j]);
//设置crc32
var crc32:UInt32:=0;
GetCRC32(patchGrid.Cells[0,j].Replace('/','|').Replace('\','|').ToLower(),crc32);
patchGrid.Cells[1,j]:=crc32.ToString();
//设置文件名长度
patchGrid.Cells[2,j]:=(patchGrid.Cells[0,j].Length+1).ToString();
//设置文件名偏移
patchGrid.Cells[3,j]:=filenameOffset.ToString();
filenameOffset:=filenameOffset+StrToUInt(patchGrid.Cells[2,j]);
//设置数据大小
patchGrid.Cells[4,j]:=(resourceMemoeyStream.Size*4).ToString();
//设置数据偏移
patchGrid.Cells[5,j]:=fileDataOffset.ToString();
fileDataOffset:=fileDataOffset+resourceMemoeyStream.Size;
var modValue:UInt32:=resourceMemoeyStream.Size mod 512;
if modValue<>0 then fileDataOffset:=fileDataOffset+(512-modValue);
end;
//打包.TOC文件
var tocMemoryStream:TMemoryStream:=TMemoryStream.Create();
//写入文件头信息(共160字节)
tocMemoryStream.WriteData(BytesOf('PACK'),4);
tocMemoryStream.WriteData(65536,4);
tocMemoryStream.WriteData(96,4);
tocMemoryStream.WriteData(3,4);
for var j:Integer:=1 to 16 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('MKRT'),4);
tocMemoryStream.WriteData(65536,4);
for var j:Integer:=1 to 24 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('OFFT'),4);
tocMemoryStream.WriteData(65536,4);
for var j:Integer:=1 to 24 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('DEPT'),4);
tocMemoryStream.WriteData(65536,4);
for var j:Integer:=1 to 24 do tocMemoryStream.WriteData($00,1);
tocMemoryStream.WriteData(BytesOf('REST'),4);
tocMemoryStream.WriteData(65536,4);
tocMemoryStream.WriteData(patchGrid.RowCount*12,4);
tocMemoryStream.WriteData(patchGrid.RowCount,4);
for var j:Integer:=1 to 16 do tocMemoryStream.WriteData($00,1);
//写入文件表第一部分信息(每个文件12字节)
for var j:Integer:=0 to Length(fileList)-1 do
begin
tocMemoryStream.WriteData(StrToUInt(patchGrid.Cells[1,j]),4);
tocMemoryStream.WriteData(StrToUInt(patchGrid.Cells[4,j]),4);
tocMemoryStream.WriteData(StrToUInt(patchGrid.Cells[5,j]),4);
end;
tocMemoryStream.SaveToFile(targets+'PATCH.TOC');
tocMemoryStream.Free();
//打包.DIR文件
var dirMemoryStream:TMemoryStream:=TMemoryStream.Create();
//写入文件表第二部分信息(文件名称偏移信息)
for var j:Integer:=0 to Length(fileList)-1 do dirMemoryStream.WriteData(StrToUInt(patchGrid.Cells[3,j]),4);
//写入文件表第二部分信息(文件名称信息)
var filenameBuffer:TBytes;
for var j:Integer:=0 to Length(fileList)-1 do
begin
SetLength(filenameBuffer,patchGrid.Cells[0,j].Length);
filenameBuffer:=BytesOf(patchGrid.Cells[0,j]);
//处理西欧字符集问题
for var k:Integer:=0 to Length(filenameBuffer)-1 do
begin
if filenameBuffer[k]=$7E then filenameBuffer[k]:=$E9;
if filenameBuffer[k]=$60 then filenameBuffer[k]:=$E7;
end;
dirMemoryStream.WriteData(filenameBuffer,Length(filenameBuffer));
dirMemoryStream.WriteData($00,1);
end;
dirMemoryStream.SaveToFile(targets+'PATCH.DIR');
dirMemoryStream.Free();
//打包.DAT文件
var datFileStream:TFileStream:=TFileStream.Create(targets+'PATCH.DAT',fmCreate);
var fileDataBuffer:TBytes;
for var j:Integer:=0 to Length(fileList)-1 do
begin
//加载资源文件
resourceMemoeyStream.Clear();
resourceMemoeyStream.LoadFromFile(fileList[j]);
Writeln((j+1).ToString()+' '+fileList[j]);
SetLength(fileDataBuffer,resourceMemoeyStream.Size);
resourceMemoeyStream.ReadData(fileDataBuffer,resourceMemoeyStream.Size);
//写入数据文件信息(需要512字节对齐)
datFileStream.WriteData(fileDataBuffer,resourceMemoeyStream.Size);
if (resourceMemoeyStream.Size mod 512)<>0 then for var k:Integer:=1 to 512-(resourceMemoeyStream.Size mod 512) do datFileStream.WriteData($00,1);
end;
datFileStream.Free();
//生成资源配置文件(需要放在游戏执行文件同一目录)
var resourceConfig:TResourceStream:=TResourceStream.Create(HInstance,'patch',RT_RCDATA);
resourceConfig.SaveToFile(targets+'resources.cfg');
resourceConfig.Free();
Writeln('汉化补丁打包操作完成,请在文件夹:'+targets+'下查看汉化补丁打包结果。');
//完成打包操作后释放内存
assetNames.Free();
patchGrid.Free();
resourceMemoeyStream.Free();
end;
except
on E: Exception do
begin
Writeln(E.ClassName, ': ', E.Message);
end;
end;
end.