2
0
mirror of https://github.com/dpethes/rerogue.git synced 2025-06-07 18:58:32 +02:00
rerogue/dat_repack/rsdat.pas
2015-03-07 19:49:46 +01:00

222 lines
5.5 KiB
ObjectPascal

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.