//::///////////////////////////////////////////////
//:: NeverPas - Encapsulated Resource File Format
//:: nw_erf.pas
//:: Copyright (c) 2003 Errant Logic.
//:://////////////////////////////////////////////
{
  Contains data types and classes for manipulating
  and reading .erf/hak/mod/sav (ERF) files.
}
//:://////////////////////////////////////////////
//:: Created By:
//:: Created On:   October 13, 2003
//:://////////////////////////////////////////////



unit nw_erf;

interface

uses
  nw_generic,
  nw_consts,
  Dialogs,
  SysUtils,
  Classes;

type

  { ERF Header }
  nw_erfHeader = packed record
    FileType:     array[0..3] of Char; // 'ERF ', 'MOD ', 'SAV ', 'HAK '
    FileVersion:  array[0..3] of Char; // 'V1.0' currently
    LanguageCount: DWORD;
    LocalizedStringSize: DWORD;
    EntryCount: DWORD;
    OffsetToLocalizedString: DWORD;
    OffsetToKeyList: DWORD;
    OffsetToResourceList: DWORD;
    BuildYear: DWORD;
    BuildDay: DWORD;
    DescriptionStrRef: DWORD;
    Reserved: array[0..115] of Byte;  // Reserved
  end;

  { ERF Localized String List }
  nw_erfStringListElement = record
    LanguageID: DWORD;
    StringSize: DWORD;
    StringElement: string;
  end;

  { Key List Element Structure }
  nw_erfKeyStruct = packed record
    ResRef: array[0..15] of Char;
    ResID:  DWORD;
    ResType: WORD;
    Unused: WORD;
  end;

  nw_erfResourceStruct = packed record
    OffsetToResource: DWORD;
    ResourceSize: DWORD;
  end;

  TERFFile = class(TObject)
  private
    Header: nw_erfHeader;
    LocalizedStringList: array of nw_erfStringListElement;
    KeyList: array of nw_erfKeyStruct;
    ResourceList: array of nw_erfResourceStruct;
    ERF: TMemoryStream;
    FLoaded: Boolean;
    FChanged: Boolean;
    FFilename: string;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Initialize;
    function Verify(Filetype,FileVersion: string): Boolean;

    function LoadERF(Filename: String): Boolean;

    function GetResourceCount(): DWORD;

    procedure GetResourceList(var Strings: TStrings);
    function GetResourceFileName(Index: DWORD): String;

    function GetResourceFileSize(Index: DWORD): DWORD;
    function GetResourceFileType(Index: DWORD): String;
    function GetVerboseResourceFileType(Index: DWORD): String;    
  end;

implementation

constructor TERFFile.Create;
begin
  ERF := TMemoryStream.Create;

  Initialize;

end;

destructor TERFFile.Destroy;
begin
  if ERF <> nil then ERF.Free;
  inherited Destroy;
end;

procedure TERFFile.Initialize;
begin
  SetLength(LocalizedStringList,0);
  SetLength(KeyList,0);
  SetLength(ResourceList,0);
end;

function TERFFile.GetResourceCount(): DWORD;
begin
  Result := Header.EntryCount;
end;

procedure TERFFile.GetResourceList(var Strings: TStrings);
var
  i: DWORD;
begin
  Strings.Clear;
  for i := 0 to Header.EntryCount - 1 do
  begin
    Strings.Add(KeyList[i].ResRef);
  end;
end;

function TERFFile.GetResourceFileName(Index: DWORD): String;
begin
  Result := '';
  if (Index > Length(KeyList)) Then Exit;
  Result := KeyList[Index].ResRef;
end;

function TERFFile.GetResourceFileSize(Index: DWORD): DWORD;
begin
  Result := 0;
  if (Index > Length(ResourceList)) then Exit;
  Result := ResourceList[Index].ResourceSize;
end;

function TERFFile.GetResourceFileType(Index:DWORD): String;
begin
  Result := '';
  if (Index > Length(ResourceList)) then Exit;
  Result := GetFileTypeByID(KeyList[Index].ResType);
end;

function TERFFile.GetVerboseResourceFileType(Index:DWORD): String;
begin
  Result := '';
  if (Index > Length(ResourceList)) then Exit;
  Result := GetVerboseFileTypeByID(KeyList[Index].ResType);
end;

function TERFFile.Verify(Filetype,FileVersion: string): Boolean;
begin
  Result := False;
  if (Header.FileType = Filetype) and (Header.FileVersion = FileVersion) then
    Result := True;
end;

function TERFFile.LoadERF(Filename: String): Boolean;
var
  i: DWORD;
begin
  Result := False;
  if Not(FileExists(Filename)) then Exit;
  Initialize;   // Clear out the ERF Class' data structures.
  ERF.LoadFromFile(Filename);

    if ERF.Size < SizeOf(nw_erfHeader) then Exit;
    ERF.Position := 0;
    ERF.Read(Header,SizeOf(nw_erfHeader));

    { Load Localized Strings}
    { TODO: Load Localized Strings }
{
    SetLength(LocalizedStringList,Header.LanguageCount);
    ERF.Seek(Header.OffsetToLocalizedString, soFromBeginning);
    for i := 0 to Header.LanguageCount -1 do
    begin
      ERF.Read(LocalizedStringList[i].LanguageID,SizeOf(DWORD));
      ERF.Read(LocalizedStringList[i].StringSize,SizeOf(DWORD));
      SetLength(LocalizedStringList[i].StringElement,LocalizedStringList[i].StringSize+1);
      FillChar(LocalizedStringList[i].StringElement,Length(LocalizedStringList[i].StringElement),$0);
      ERF.Read(LocalizedStringList[i].StringElement,LocalizedStringList[i].StringSize);
      ShowMessage(LocalizedStringList[i].StringElement);
    end;
}
    { Load Key List }
    SetLength(KeyList,Header.EntryCount);
    ERF.Seek(Header.OffsetToKeyList,soFromBeginning);
    for i := 0 to Header.EntryCount - 1 do
    begin
      ERF.Read(KeyList[i],SizeOf(nw_erfKeyStruct));
    end;

    { Load Resource List }
    SetLength(ResourceList,Header.EntryCount);
    ERF.Seek(Header.OffsetToResourceList,soFromBeginning);
    for i := 0 to Header.EntryCount - 1 do
    begin
      ERF.Read(ResourceList[i],SizeOf(nw_erfResourceStruct));
    end;

    FFileName := Filename;
    FLoaded := True;

end;

end.
