diff --git a/dat_repack/rs_repack.lpi b/dat_repack/rs_repack.lpi index 9a6c640..c16adf6 100644 --- a/dat_repack/rs_repack.lpi +++ b/dat_repack/rs_repack.lpi @@ -1,7 +1,7 @@ - + @@ -17,9 +17,6 @@ - - - @@ -32,27 +29,19 @@ - + - - - - - - - - - - - - - + + + + + @@ -63,6 +52,7 @@ + @@ -73,12 +63,6 @@ - - - - - - diff --git a/dat_repack/rs_repack.lpr b/dat_repack/rs_repack.lpr index acac725..8dba9a0 100644 --- a/dat_repack/rs_repack.lpr +++ b/dat_repack/rs_repack.lpr @@ -1,23 +1,22 @@ program rs_repack; uses - sysutils, rsdat, rsdat_pack; + sysutils, rs_dat, rsdat_pack; procedure UnpackData(const basedir: string); var dat: TRSDatFile; fhdr, fdat: string; begin - fhdr := basedir + 'DATA.HDR'; - fdat := basedir + 'DATA.DAT'; + fhdr := basedir + RS_DATA_HDR; + fdat := basedir + RS_DATA_DAT; if not FileExists(fhdr) or not FileExists(fdat) then begin writeln('missing input files ', fhdr, ' or ', fdat); exit; end; - dat := TRSDatFile.Create; - dat.ReadHeader(fhdr); - dat.ReadSections(fdat); + dat := TRSDatFile.Create(fhdr, fdat); + dat.Parse(); dat.WriteFilesToDirectory(basedir); dat.Free; end; diff --git a/dat_repack/rsdat.pas b/dat_repack/rsdat.pas deleted file mode 100644 index d31ad0a..0000000 --- a/dat_repack/rsdat.pas +++ /dev/null @@ -1,221 +0,0 @@ -unit rsdat; - -{$mode objfpc}{$H+} - -interface - -uses - Classes, SysUtils, rsdat_common; - -type - { TRSDatFile } - - TRSDatFile = class - private - sections: array of TSection; - data: pbyte; - - procedure ReadDatFile(const fname: string); - function ReadEntry(const stream: TMemoryStream): TFileNode; - procedure ReadSectionEntries(var section: TSection); - procedure ParseSectionStructure(var section: TSection); - - public - procedure ReadHeader(const fname: string); - procedure ReadSections(const fname: string); - procedure WriteFilesToDirectory(const path: string); - constructor Create; - destructor Destroy; override; - end; - -//************************************************************************************************** -implementation - -procedure SaveFile(const name: string; const buffer: pbyte; const buf_size: integer); -var - f: file; - fname: string; -begin - fname := name; - AssignFile(f, fname); - Rewrite(f, 1); - BlockWrite(f, buffer^, buf_size); - CloseFile(f); -end; - -{ TRSDatFile } - -procedure TRSDatFile.ReadDatFile(const fname: string); -var - f: file; - fsize: integer; -begin - AssignFile(f, fname); - reset(f, 1); - fsize := FileSize(f); - data := getmem(fsize); - Blockread(f, data^, fsize); - closefile(f); -end; - -function TRSDatFile.ReadEntry(const stream: TMemoryStream): TFileNode; -var - entry: TFileEntry; -begin - stream.ReadBuffer(entry, 32); - result.name := Trim(entry.filename); - result.offset := entry.offset; - result.size := entry.length; - result.is_directory := (entry.type_flag and FEDirectoryFlag) <> 0; - result.subentries_count := entry.sub_entry_size div 32 - 1; - result.entry := entry; - result.data := nil; - //if (result.offset mod 32) <> 0 then writeln('unaligned offset'); - writeln(stderr, format('name: %s size: %d dir: %s subsize: %d flags: %s', - [result.Name, entry.length, BoolToStr(result.is_directory), - entry.sub_entry_size, binStr(entry.type_flag, 16)])); -end; - -procedure TRSDatFile.ReadSectionEntries(var section: TSection); -var - entries_offset: integer; - entries_length: integer; - entry_count: integer; - stream: TMemoryStream; - i: integer; -begin - entries_offset := (pinteger(section.data))^; //offset relative to section beginning - entries_length := (pinteger(section.data + 4))^; //length in bytes - section.size := entries_offset + entries_length; - entry_count := entries_length div 32; //actual count of entries - writeln('entries: ', entry_count); - - stream := TMemoryStream.Create; - stream.WriteBuffer(section.data^, section.size); - stream.Seek(entries_offset, soBeginning); - - SetLength(section.nodes, entry_count); - for i := 0 to entry_count - 1 do begin - section.nodes[i] := ReadEntry(stream); - end; - - stream.Free; -end; - - -procedure AddNode(const parent: PFileNode; var nodes: array of TFileNode; var node_index: integer); -var - i: integer; - node: PFileNode; -begin - if node_index > length(nodes) - 1 then - exit; - - //add current to parent - node := @nodes[node_index]; - i := length(parent^.nodes); - Setlength(parent^.nodes, i + 1); - parent^.nodes[i] := node; - //writeln('added node: ', node^.name, ' to parent ', parent^.name); - - //add all subentries if any - if node^.is_directory then begin - //writeln(' subentries: ', node^.subentries_count); - while CountSubNodes(node) < node^.subentries_count + 1 do begin - node_index += 1; - AddNode(node, nodes, node_index); - end; - end; -end; - -procedure TRSDatFile.ParseSectionStructure(var section: TSection); -var - node_idx: integer = 0; -begin - section.root.name := section.name; - section.root.is_directory := true; - section.root.data := nil; - while node_idx < length(section.nodes) do begin - AddNode(@section.root, section.nodes, node_idx); - node_idx += 1; - end; -end; - -procedure TRSDatFile.ReadHeader(const fname: string); -var - f: file; - section: TSection; - section_n: integer; - i: integer; - buffer: array[0..15] of char; -begin - assignfile(f, fname); - reset(f, 1); - section_n := FileSize(f) div 32; - SetLength(sections, section_n); - - for i := 0 to section_n - 1 do begin - blockread(f, buffer, 16); //name - section.name := trim(buffer); - blockread(f, buffer, 12); //empty - blockread(f, section.offset, 4); //offset in DATA.DAT - sections[i] := section; - end; - - closefile(f); -end; - -procedure TRSDatFile.ReadSections(const fname: string); -var - i: integer; -begin - ReadDatFile(fname); - for i := 0 to length(sections) - 1 do begin - Writeln('reading section ', sections[i].name); - sections[i].data := data + sections[i].offset; - ReadSectionEntries(sections[i]); - ParseSectionStructure(sections[i]); - end; -end; - -procedure WriteDirectory(const node: PFileNode; const path: string; const data: pbyte); -var - dir: string; - subnode: PFileNode; - i: Integer; -begin - dir := path + node^.name; - if not DirectoryExists(dir) then - MkDir(dir); - for i := 0 to length(node^.nodes) - 1 do begin - subnode := node^.nodes[i]; - if subnode^.is_directory then - WriteDirectory(subnode, dir + DirectorySeparator, data) - else - SaveFile(dir + DirectorySeparator + subnode^.name, data + subnode^.offset, subnode^.size); - end; -end; - -procedure TRSDatFile.WriteFilesToDirectory(const path: string); -var - section: TSection; -begin - for section in sections do begin - WriteDirectory(@section.root, path, section.data); - end; -end; - -constructor TRSDatFile.Create; -begin - -end; - -destructor TRSDatFile.Destroy; -begin - inherited Destroy; - sections := nil; - freemem(data); -end; - -end. - diff --git a/dat_repack/rsdat_common.pas b/dat_repack/rsdat_common.pas deleted file mode 100644 index ef12ea6..0000000 --- a/dat_repack/rsdat_common.pas +++ /dev/null @@ -1,83 +0,0 @@ -unit rsdat_common; - -{$mode objfpc}{$H+} - -interface - -type - (* - uint32 {4} - Offset - uint32 {4} - Length (entry count * 32) - uint32 {4} - Padding (all 255's) 0xFF FF FF FF - uint16 {2} - type flag: - %10000000 - folder - %00000010 - file - %10000010 - subfolder - uint16 {2} - directory subentries length (entry count * 32) - char {16} - Filename (null) (replace "_" with ".") - *) - TFileEntry = packed record - offset: longword; - length: longword; - padding: longword; - type_flag: word; - sub_entry_size: word; - filename: array[0..15] of char; - end; - PFileEntry = ^TFileEntry; - -const - FEDirectoryFlag = %10000000; - -type - //file or directory node - PFileNode = ^TFileNode; - - TFileNode = record - Name: string; - is_directory: boolean; - subentries_count: integer; - offset: longword; - size: longword; - entry: TFileEntry; - Data: pbyte; - nodes: array of PFileNode; //children if directory - end; - - //root - TSection = record - Name: string; //section name - offset: integer; //offset in dat file - size: integer; //section length in bytes - Data: pbyte; //data - nodes: array of TFileNode; //all file entries / nodes - root: TFileNode; //tree structure of nodes - end; - -function CountSubNodes(node: PFileNode): integer; -function CountSubNodeSizes(node: PFileNode): integer; - -//************************************************************************************************** -implementation - -function CountSubNodes(node: PFileNode): integer; -var - i: integer; -begin - Result := 1; - if node^.is_directory then - for i := 0 to Length(node^.nodes) - 1 do - Result += CountSubNodes(node^.nodes[i]); -end; - -function CountSubNodeSizes(node: PFileNode): integer; -var - i: integer; -begin - Result := node^.size; - if node^.is_directory then - for i := 0 to Length(node^.nodes) - 1 do - Result += CountSubNodeSizes(node^.nodes[i]); -end; - -end. diff --git a/dat_repack/rsdat_pack.pas b/dat_repack/rsdat_pack.pas index 7f73cc2..e77e0f6 100644 --- a/dat_repack/rsdat_pack.pas +++ b/dat_repack/rsdat_pack.pas @@ -5,7 +5,7 @@ unit rsdat_pack; interface uses - Classes, SysUtils, rsdat_common; + Classes, SysUtils, rs_dat; type @@ -13,15 +13,15 @@ type TRSDatPacker = class private - Sections: array of TSection; + Sections: array of TRsHdrSection; Data: TMemoryStream; procedure FreeSections; procedure ReadSectionFiles(const basepath: string); procedure WriteData(path: string); procedure WriteHeader(path: string); - procedure WriteNodeData(node: PFileNode); - procedure WriteFileEntries(node: PFileNode; const base_offset: integer); + procedure WriteNodeData(node: PRsDatFileNode); + procedure WriteFileEntries(node: PRsDatFileNode; const base_offset: integer); public procedure PackDirectory(const path: string); @@ -32,9 +32,9 @@ type //************************************************************************************************** implementation -procedure ReadFileNodes(parent: PFileNode; path: string); +procedure ReadFileNodes(parent: PRsDatFileNode; path: string); var - node: PFileNode; + node: PRsDatFileNode; info: TSearchRec; n: integer; f: file; @@ -80,7 +80,7 @@ begin end; end; -procedure FreeFileNodes(node: PFileNode; const no_disposing: boolean = false); +procedure FreeFileNodes(node: PRsDatFileNode; const no_disposing: boolean = false); var i: integer; begin @@ -99,8 +99,8 @@ procedure TRSDatPacker.ReadSectionFiles(const basepath: string); var n: integer; info: TSearchRec; - section: TSection; - node: TFileNode; + section: TRsHdrSection; + node: TRsDatFileNode; begin n := 0; if FindFirst(basepath + '*', faDirectory, Info) = 0 then begin @@ -136,7 +136,7 @@ begin Sections := nil; end; -procedure TRSDatPacker.WriteNodeData(node: PFileNode); +procedure TRSDatPacker.WriteNodeData(node: PRsDatFileNode); var i: integer; begin @@ -161,9 +161,9 @@ TFileEntry = packed record sub_entry_size: word; filename: array[0..15] of char; end; } -procedure TRSDatPacker.WriteFileEntries(node: PFileNode; const base_offset: integer); +procedure TRSDatPacker.WriteFileEntries(node: PRsDatFileNode; const base_offset: integer); var - entry: TFileEntry; + entry: TRsDatFileEntry; name: string; i: integer; begin @@ -175,9 +175,9 @@ begin entry.sub_entry_size := (node^.subentries_count + 1) * 32; if node^.is_directory then - entry.type_flag := FEDirectoryFlag + entry.type_flag := RS_DATA_FEDirectoryFlag else - entry.type_flag := %00000010; + entry.type_flag := RS_DATA_FEFileFlag; writeln(stderr, format('name: %s size: %d dir: %s subsize: %d', [node^.Name, entry.length, BoolToStr(node^.is_directory), entry.sub_entry_size])); @@ -200,7 +200,7 @@ var i, k: integer; head: pinteger; entries: integer; - section: TSection; + section: TRsHdrSection; begin Data := TMemoryStream.Create; Data.Size := 1 shl 20; @@ -208,7 +208,7 @@ begin section := Sections[i]; Writeln('writing section: ', section.name); - section.offset := Data.Position; + section.dat_offset := Data.Position; Data.WriteQWord(0); //offset + size placeholder Writeln('writing file data'); @@ -216,13 +216,13 @@ begin WriteNodeData(section.root.nodes[k]); entries := section.root.subentries_count; - head := pinteger (pbyte(Data.Memory) + section.offset); - head^ := Data.Position - section.offset; + head := pinteger (pbyte(Data.Memory) + section.dat_offset); + head^ := Data.Position - section.dat_offset; head += 1; head^ := entries * 32; Writeln('writing file entries: ', entries); for k := 0 to Length(section.root.nodes) - 1 do - WriteFileEntries(section.root.nodes[k], section.offset); + WriteFileEntries(section.root.nodes[k], section.dat_offset); //align? for k := 1 to 4 - (Data.Position mod 4) do @@ -249,7 +249,7 @@ begin name[k] := byte( section_name[k] ); Blockwrite(f, name, 28); - Blockwrite(f, Sections[i].offset, 4); + Blockwrite(f, Sections[i].dat_offset, 4); end; CloseFile(f); end; diff --git a/rs_units/rs_dat.pas b/rs_units/rs_dat.pas index c4c155c..1d813de 100644 --- a/rs_units/rs_dat.pas +++ b/rs_units/rs_dat.pas @@ -15,6 +15,7 @@ const RS_DATA_HDR = 'DATA.HDR'; RS_DATA_DAT = 'DATA.DAT'; RS_DATA_FEDirectoryFlag = %10000000; + RS_DATA_FEFileFlag = %00000010; type //data file entry