Nå, så her jeg en lille unit som kan læse det meste i en ITP fil.
Der mangler nogle enkelte typer (DATREF og CAPREF) men resten skulle være der.
Først kommer koden til unit'en og der efter en lille procedure som bruger den.
unit NWN;
interface
uses
Classes;
type
PCardinalArray = ^TCardinalArray;
TCardinalArray = array[0..1000] of Cardinal;
PFileHeader = ^TFileHeader;
TFileHeader = packed record
Signature : array[0..3] of Char;
Version : array[0..3] of Char;
FirstEntry : Cardinal; // Offset from start of the file to the first entry
NumOfEntries : Cardinal; // Number of entries in the file
FirstElement : Cardinal; // Offset from start of the file to the first element
NumOfElements : Cardinal; // Number of elements in the file
FirstVarName : Cardinal; // Offset from start of the file to the first variable name
VarNameSize : Cardinal; // Number of bytes in the variable name block
FirstVarData : Cardinal; // Offset from start of the file to the first variable data
VarDataSize : Cardinal; // Number of bytes in the variable data block
FirstMultiMap : Cardinal; // Offset from start of the file to the first multimap
MultiMapSize : Cardinal; // Number of bytes in the multimap table
FirstList : Cardinal; // Offset from start of the file to the first list
ListSize : Cardinal; // Number of bytes in the list table
end;
PEntryTable = ^TEntryTable;
TEntryTable = packed record
Code : Cardinal;
Index : Cardinal; // Element index or MultiMap offset
Count : Cardinal;
end;
TElementType = (
etUINT8, // Direct Unsigned byte
etINT8, // Direct Signed byte
etUINT16, // Direct Unsigned word
etINT16, // Direct Signed word
etUINT32, // Direct Unsigned longword
etINT32, // Direct Signed longword
etUINT64, // Indirect Unsigned quadword
etINT64, // Indirect Signed quadword
etFLOAT, // Indirect Four byte floating point value
etDOUBLE, // Indirect Eight byte floating point value
etSTRING, // Indirect Counted string
etRESREF, // Indirect Counted resource name
etSTRREF, // Complex Multilingual capable string
etDATREF, // Complex Counted binary data
etCAPREF, // Complex List of child elements
etLIST // Complex List of child entries
);
PElement = ^TElement;
TElement = packed record
_Type : Cardinal; // Must be mapped to TElementType.
NameIndex : Cardinal;
case Integer of
0: (ui8 : Byte);
1: (si8 : ShortInt);
2: (ui16 : Word);
3: (si16 : SmallInt);
4: (ui32 : Cardinal);
5: (si32 : Integer);
6: (flt : Single);
7: (Offset : Cardinal);
end;
PStrRef = ^TStrRef;
TStrRef = packed record
Size : Cardinal;
ID : Integer;
StrCount : Cardinal;
end;
PStrRefItem = ^TStrRefItem;
TStrRefItem = packed record
Language : Cardinal;
StrCount : Cardinal;
FirstChar : Char;
end;
TITFReader = class(TObject)
private
FBuffer : PByte;
FSize : Cardinal;
protected
procedure AllocBuffer(ASize : Cardinal);
procedure FreeBuffer;
public
constructor Create;
destructor Destroy; override;
function LoadFile(AFileName : String) : Boolean;
function Decode : TStringList;
end;
implementation
uses
SysUtils;
constructor TITFReader.Create;
begin
FBuffer := nil;
FSize := 0;
end;
destructor TITFReader.Destroy;
begin
FreeBuffer;
inherited Destroy;
end;
procedure TITFReader.AllocBuffer(ASize : Cardinal);
begin
if FBuffer <> nil then
FreeMem(FBuffer);
FBuffer := AllocMem(ASize);
FSize := ASize;
end;
procedure TITFReader.FreeBuffer;
begin
if FBuffer <> nil then
FreeMem(FBuffer);
FBuffer := nil;
FSize := 0;
end;
function TITFReader.LoadFile(AFileName : String) : Boolean;
var
Stream : TFileStream;
begin
try
Result := True;
Stream := TFileStream.Create(AFileName, fmOpenRead);
Stream.Position := 0;
AllocBuffer(Stream.Size);
Stream.Read(FBuffer^, Stream.Size);
Stream.Free;
except
Result := False;
end;
end;
function TITFReader.Decode : TStringList;
function ReadString(Header : PFileHeader; Element : PElement) : String;
var
Len : Cardinal;
Str : PChar;
begin
Len := PCardinal(Cardinal(Header)+Header^.FirstVarData+Element^.Offset)^;
Str := AllocMem(Len+1);
StrMove(Str, PChar(Cardinal(Header)+Header^.FirstVarData+Element^.Offset+SizeOf(Cardinal)), Len);
Result := Str;
FreeMem(Str);
end;
function ReadResRef(Header : PFileHeader; Element : PElement) : String;
var
Len : Cardinal;
Str : PChar;
begin
Len := PByte(Cardinal(Header)+Header^.FirstVarData+Element^.Offset)^;
Str := AllocMem(Len+1);
StrMove(Str, PChar(Cardinal(Header)+Header^.FirstVarData+Element^.Offset+SizeOf(Byte)), Len);
Result := Str;
FreeMem(Str);
end;
function ReadStrRef(Header : PFileHeader; Element : PElement) : String;
var
SR : PStrRef;
SRI : PStrRefItem;
I : Integer;
Str : PChar;
begin
SR := PStrRef(Cardinal(Header)+Header^.FirstVarData+Element^.Offset);
Result := 'Count: '+IntToStr(SR^.StrCount)+' (ID:'+IntToStr(SR^.ID);
if SR^.StrCount > 0 then
begin
SRI := PStrRefItem(Cardinal(SR)+SizeOf(TStrRef));
for I := 0 to Integer(SR^.StrCount)-1 do
begin
Str := AllocMem(SRI^.StrCount+1);
StrMove(Str, PChar(@SRI^.FirstChar), SRI^.StrCount);
Result := Result+#10#10+'('+IntToStr(SRI^.Language)+'): '+Str;
SRI := PStrRefItem(Cardinal(SRI)+SizeOf(TStrRefItem));
end;
end;
end;
function ReadDatRef(Header : PFileHeader; Element : PElement) : String;
begin
Result := 'Not implemented';
end;
function ReadCapRef(Header : PFileHeader; Element : PElement) : String;
begin
Result := 'Not implemented';
end;
function ReadList(Header : PFileHeader; Element : PElement) : String;
var
List : PCardinalArray;
Count : Cardinal;
I : Cardinal;
begin
Result := 'List(';
List := PCardinalArray(Cardinal(Header)+Header^.FirstList+Element^.Offset);
Count := List^[0];
Result := Result+IntToStr(Count)+'): ';
for I := 0 To Integer(Count)-1 do
Result := Result+IntToStr(List^[I+1])+' ';
end;
procedure ReadEntities(Header : PFileHeader; AList : TStringList);
const
ElemName : array[0..15] of String = (
'UINT8', // Direct Unsigned byte
'INT8', // Direct Signed byte
'UINT16', // Direct Unsigned word
'INT16', // Direct Signed word
'UINT32', // Direct Unsigned longword
'INT32', // Direct Signed longword
'UINT64', // Indirect Unsigned quadword
'INT64', // Indirect Signed quadword
'FLOAT', // Indirect Four byte floating point value
'DOUBLE', // Indirect Eight byte floating point value
'STRING', // Indirect Counted string
'RESREF', // Indirect Counted resource name
'STRREF', // Complex Multilingual capable string
'DATREF', // Complex Counted binary data
'CAPREF', // Complex List of child elements
'LIST' // Complex List of child entries
);
var
Entry : PEntryTable;
ElementTable : PElement;
Element : PElement;
MMap : PCardinalArray;
I : Integer;
begin
Entry := PEntryTable(Cardinal(Header)+Header^.FirstEntry);
ElementTable := PElement(Cardinal(Header)+Header^.FirstElement);
Result.Add('Entities:');
if (Entry^.Count = 1) then
MMap := PCardinalArray(@Entry^.Index)
else
MMap := PCardinalArray(Cardinal(Header)+Header^.FirstMultiMap+Entry^.Index);
for I := 0 to Entry^.Count-1 do
begin
Element := PElement(Cardinal(ElementTable)+(MMap^[I]*SizeOf(TElement)));
Result.Add(' Type: '+ElemName[Element^._Type]);
Result.Add(' NameIndex: '+IntToStr(Element^.NameIndex));
case TElementType(Element^._Type) of
etUINT8: Result.Add(' Value: '+IntToStr(Element^.ui8));
etINT8: Result.Add(' Value: '+IntToStr(Element^.si8));
etUINT16: Result.Add(' Value: '+IntToStr(Element^.ui16));
etINT16: Result.Add(' Value: '+IntToStr(Element^.si16));
etUINT32: Result.Add(' Value: '+IntToStr(Element^.ui32));
etINT32: Result.Add(' Value: '+IntToStr(Element^.si32));
etUINT64: Result.Add(' Value: '+IntToStr(Int64(Pointer(Cardinal(Header)+Header^.FirstVarData+Element^.NameIndex)^)));
etINT64: Result.Add(' Value: '+IntToStr(Int64(Pointer(Cardinal(Header)+Header^.FirstVarData+Element^.NameIndex)^)));
etFLOAT: Result.Add(' Value: '+Format('%f',[Element^.flt]));
etDOUBLE: Result.Add(' Value: '+Format('%f',[Double(Pointer(Cardinal(Header)+Header^.FirstVarData+Element^.NameIndex)^)]));
etSTRING: Result.Add(' Value: '+ReadString(Header, Element));
etRESREF: Result.Add(' Value: '+ReadResRef(Header, Element));
etSTRREF: Result.Add(' Value: '+ReadStrRef(Header, Element));
etDATREF: Result.Add(' Value: '+ReadDatRef(Header, Element));
etCAPREF: Result.Add(' Value: '+ReadCapRef(Header, Element));
etLIST: Result.Add(' Value: '+ReadList(Header, Element));
end;
end;
end;
var
Header : PFileHeader;
begin
Result := TStringList.Create;
if FBuffer <> nil then
begin
Header := PFileHeader(FBuffer);
Result.Add('FileHeader:');
Result.Add(' Signature : '+Header^.Signature);
Result.Add(' Version : '+Header^.Version);
Result.Add(' FirstEntry : '+IntToStr(Header^.FirstEntry));
Result.Add(' NumOfEntries : '+IntToStr(Header^.NumOfEntries));
Result.Add(' FirstElement : '+IntToStr(Header^.FirstElement));
Result.Add(' NumOfElements : '+IntToStr(Header^.NumOfElements));
Result.Add(' FirstVarName : '+IntToStr(Header^.FirstVarName));
Result.Add(' VarNameSize : '+IntToStr(Header^.VarNameSize));
Result.Add(' FirstVarData : '+IntToStr(Header^.FirstVarData));
Result.Add(' VarDataSize : '+IntToStr(Header^.VarDataSize));
Result.Add(' FirstMultiMap : '+IntToStr(Header^.FirstMultiMap));
Result.Add(' MultiMapSize : '+IntToStr(Header^.MultiMapSize));
Result.Add(' FirstList : '+IntToStr(Header^.FirstList));
Result.Add(' ListSize : '+IntToStr(Header^.ListSize));
ReadEntities(Header, Result);
end
else
begin
Result.Add('File not loaded!');
end;
end;
end.
Og her er hvordan du bruger den:
// Du skal ha' en Button (navn: Button1) og et Memo felt (navn: Memo1) sat på din form.
// Husk at tilføje NWN under Uses.
procedure TForm1.Button1Click(Sender: TObject);
var
R : TITFReader;
L : TStringList;
begin
R := TITFReader.Create;
R.LoadFile('G:\\Games\\NeverwinterNights\\NWN\\localvault\\drax.bic');
L := R.Decode;
Memo1.Lines.AddStrings(L);
L.Free;
R.Free;
end;
Hvis du har nogle spørgsmål til unit'en så fyr løs :0)
God fornøjelse
Michael.