2
0
mirror of https://github.com/dpethes/rerogue.git synced 2025-06-07 18:58:32 +02:00

dat repack: use common units

This commit is contained in:
dpethes 2017-02-04 18:10:31 +01:00
parent 32d9abb3ba
commit 86ea3565a4
6 changed files with 34 additions and 354 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<CONFIG> <CONFIG>
<ProjectOptions> <ProjectOptions>
<Version Value="9"/> <Version Value="10"/>
<PathDelim Value="\"/> <PathDelim Value="\"/>
<General> <General>
<Flags> <Flags>
@ -17,9 +17,6 @@
<i18n> <i18n>
<EnableI18N LFM="False"/> <EnableI18N LFM="False"/>
</i18n> </i18n>
<VersionInfo>
<StringTable ProductVersion=""/>
</VersionInfo>
<BuildModes Count="1"> <BuildModes Count="1">
<Item1 Name="Default" Default="True"/> <Item1 Name="Default" Default="True"/>
</BuildModes> </BuildModes>
@ -32,27 +29,19 @@
<CommandLineParams Value="p r:\rogue_data\"/> <CommandLineParams Value="p r:\rogue_data\"/>
</local> </local>
</RunParams> </RunParams>
<Units Count="4"> <Units Count="3">
<Unit0> <Unit0>
<Filename Value="rs_repack.lpr"/> <Filename Value="rs_repack.lpr"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="rs_repack"/>
</Unit0> </Unit0>
<Unit1> <Unit1>
<Filename Value="rsdat.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="rsdat"/>
</Unit1>
<Unit2>
<Filename Value="rsdat_common.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="rsdat_common"/>
</Unit2>
<Unit3>
<Filename Value="rsdat_pack.pas"/> <Filename Value="rsdat_pack.pas"/>
<IsPartOfProject Value="True"/> <IsPartOfProject Value="True"/>
<UnitName Value="rsdat_pack"/> </Unit1>
</Unit3> <Unit2>
<Filename Value="..\rs_units\rs_dat.pas"/>
<IsPartOfProject Value="True"/>
</Unit2>
</Units> </Units>
</ProjectOptions> </ProjectOptions>
<CompilerOptions> <CompilerOptions>
@ -63,6 +52,7 @@
</Target> </Target>
<SearchPaths> <SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/> <IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="..\rs_units"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths> </SearchPaths>
<CodeGeneration> <CodeGeneration>
@ -73,12 +63,6 @@
<StackChecks Value="True"/> <StackChecks Value="True"/>
</Checks> </Checks>
</CodeGeneration> </CodeGeneration>
<Other>
<CompilerMessages>
<MsgFileName Value=""/>
</CompilerMessages>
<CompilerPath Value="$(CompPath)"/>
</Other>
</CompilerOptions> </CompilerOptions>
<Debugging> <Debugging>
<Exceptions Count="3"> <Exceptions Count="3">

View File

@ -1,23 +1,22 @@
program rs_repack; program rs_repack;
uses uses
sysutils, rsdat, rsdat_pack; sysutils, rs_dat, rsdat_pack;
procedure UnpackData(const basedir: string); procedure UnpackData(const basedir: string);
var var
dat: TRSDatFile; dat: TRSDatFile;
fhdr, fdat: string; fhdr, fdat: string;
begin begin
fhdr := basedir + 'DATA.HDR'; fhdr := basedir + RS_DATA_HDR;
fdat := basedir + 'DATA.DAT'; fdat := basedir + RS_DATA_DAT;
if not FileExists(fhdr) or not FileExists(fdat) then begin if not FileExists(fhdr) or not FileExists(fdat) then begin
writeln('missing input files ', fhdr, ' or ', fdat); writeln('missing input files ', fhdr, ' or ', fdat);
exit; exit;
end; end;
dat := TRSDatFile.Create; dat := TRSDatFile.Create(fhdr, fdat);
dat.ReadHeader(fhdr); dat.Parse();
dat.ReadSections(fdat);
dat.WriteFilesToDirectory(basedir); dat.WriteFilesToDirectory(basedir);
dat.Free; dat.Free;
end; end;

View File

@ -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.

View File

@ -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.

View File

@ -5,7 +5,7 @@ unit rsdat_pack;
interface interface
uses uses
Classes, SysUtils, rsdat_common; Classes, SysUtils, rs_dat;
type type
@ -13,15 +13,15 @@ type
TRSDatPacker = class TRSDatPacker = class
private private
Sections: array of TSection; Sections: array of TRsHdrSection;
Data: TMemoryStream; Data: TMemoryStream;
procedure FreeSections; procedure FreeSections;
procedure ReadSectionFiles(const basepath: string); procedure ReadSectionFiles(const basepath: string);
procedure WriteData(path: string); procedure WriteData(path: string);
procedure WriteHeader(path: string); procedure WriteHeader(path: string);
procedure WriteNodeData(node: PFileNode); procedure WriteNodeData(node: PRsDatFileNode);
procedure WriteFileEntries(node: PFileNode; const base_offset: integer); procedure WriteFileEntries(node: PRsDatFileNode; const base_offset: integer);
public public
procedure PackDirectory(const path: string); procedure PackDirectory(const path: string);
@ -32,9 +32,9 @@ type
//************************************************************************************************** //**************************************************************************************************
implementation implementation
procedure ReadFileNodes(parent: PFileNode; path: string); procedure ReadFileNodes(parent: PRsDatFileNode; path: string);
var var
node: PFileNode; node: PRsDatFileNode;
info: TSearchRec; info: TSearchRec;
n: integer; n: integer;
f: file; f: file;
@ -80,7 +80,7 @@ begin
end; end;
end; end;
procedure FreeFileNodes(node: PFileNode; const no_disposing: boolean = false); procedure FreeFileNodes(node: PRsDatFileNode; const no_disposing: boolean = false);
var var
i: integer; i: integer;
begin begin
@ -99,8 +99,8 @@ procedure TRSDatPacker.ReadSectionFiles(const basepath: string);
var var
n: integer; n: integer;
info: TSearchRec; info: TSearchRec;
section: TSection; section: TRsHdrSection;
node: TFileNode; node: TRsDatFileNode;
begin begin
n := 0; n := 0;
if FindFirst(basepath + '*', faDirectory, Info) = 0 then begin if FindFirst(basepath + '*', faDirectory, Info) = 0 then begin
@ -136,7 +136,7 @@ begin
Sections := nil; Sections := nil;
end; end;
procedure TRSDatPacker.WriteNodeData(node: PFileNode); procedure TRSDatPacker.WriteNodeData(node: PRsDatFileNode);
var var
i: integer; i: integer;
begin begin
@ -161,9 +161,9 @@ TFileEntry = packed record
sub_entry_size: word; sub_entry_size: word;
filename: array[0..15] of char; filename: array[0..15] of char;
end; } end; }
procedure TRSDatPacker.WriteFileEntries(node: PFileNode; const base_offset: integer); procedure TRSDatPacker.WriteFileEntries(node: PRsDatFileNode; const base_offset: integer);
var var
entry: TFileEntry; entry: TRsDatFileEntry;
name: string; name: string;
i: integer; i: integer;
begin begin
@ -175,9 +175,9 @@ begin
entry.sub_entry_size := (node^.subentries_count + 1) * 32; entry.sub_entry_size := (node^.subentries_count + 1) * 32;
if node^.is_directory then if node^.is_directory then
entry.type_flag := FEDirectoryFlag entry.type_flag := RS_DATA_FEDirectoryFlag
else else
entry.type_flag := %00000010; entry.type_flag := RS_DATA_FEFileFlag;
writeln(stderr, format('name: %s size: %d dir: %s subsize: %d', writeln(stderr, format('name: %s size: %d dir: %s subsize: %d',
[node^.Name, entry.length, BoolToStr(node^.is_directory), entry.sub_entry_size])); [node^.Name, entry.length, BoolToStr(node^.is_directory), entry.sub_entry_size]));
@ -200,7 +200,7 @@ var
i, k: integer; i, k: integer;
head: pinteger; head: pinteger;
entries: integer; entries: integer;
section: TSection; section: TRsHdrSection;
begin begin
Data := TMemoryStream.Create; Data := TMemoryStream.Create;
Data.Size := 1 shl 20; Data.Size := 1 shl 20;
@ -208,7 +208,7 @@ begin
section := Sections[i]; section := Sections[i];
Writeln('writing section: ', section.name); Writeln('writing section: ', section.name);
section.offset := Data.Position; section.dat_offset := Data.Position;
Data.WriteQWord(0); //offset + size placeholder Data.WriteQWord(0); //offset + size placeholder
Writeln('writing file data'); Writeln('writing file data');
@ -216,13 +216,13 @@ begin
WriteNodeData(section.root.nodes[k]); WriteNodeData(section.root.nodes[k]);
entries := section.root.subentries_count; entries := section.root.subentries_count;
head := pinteger (pbyte(Data.Memory) + section.offset); head := pinteger (pbyte(Data.Memory) + section.dat_offset);
head^ := Data.Position - section.offset; head^ := Data.Position - section.dat_offset;
head += 1; head += 1;
head^ := entries * 32; head^ := entries * 32;
Writeln('writing file entries: ', entries); Writeln('writing file entries: ', entries);
for k := 0 to Length(section.root.nodes) - 1 do 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? //align?
for k := 1 to 4 - (Data.Position mod 4) do for k := 1 to 4 - (Data.Position mod 4) do
@ -249,7 +249,7 @@ begin
name[k] := byte( section_name[k] ); name[k] := byte( section_name[k] );
Blockwrite(f, name, 28); Blockwrite(f, name, 28);
Blockwrite(f, Sections[i].offset, 4); Blockwrite(f, Sections[i].dat_offset, 4);
end; end;
CloseFile(f); CloseFile(f);
end; end;

View File

@ -15,6 +15,7 @@ const
RS_DATA_HDR = 'DATA.HDR'; RS_DATA_HDR = 'DATA.HDR';
RS_DATA_DAT = 'DATA.DAT'; RS_DATA_DAT = 'DATA.DAT';
RS_DATA_FEDirectoryFlag = %10000000; RS_DATA_FEDirectoryFlag = %10000000;
RS_DATA_FEFileFlag = %00000010;
type type
//data file entry //data file entry