吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1707|回复: 3
收起左侧

[其他转载] OGRE资源管理器源代码

[复制链接]
simakuangbiao 发表于 2020-12-22 09:26
        大家好,我是表哥。
        最近组里需要汉化金发美女(So Blonde),通过分析资源包格式,写了一个OGRE资源管理器程序(控制台程序),实现了解包、打包、汉化补丁打包等功能,其源代码分享如下,供感兴趣的坛友学习交流:
[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.



免费评分

参与人数 2吾爱币 +8 热心值 +2 收起 理由
苏紫方璇 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
笙若 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

chz268 发表于 2020-12-22 09:35
厉害哦。
夏天爸爸 发表于 2020-12-22 09:46
asong 发表于 2021-1-21 10:46
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 20:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表