Hallo!
Ich habe ein Problem bei einem Programm an dem ich gerade arbeite. Sicher habt ihr schon einmal von GTA (Grand Theft Auto) gehört und wenn ihr dort Erfahrung habt wisst ihr vielleicht, dass es rund um diese Spieleserie eine große internationale Fangemeinde gibt, die sich im speziellen mit dem Modden des Spiels beschäftigt. Da mich neue Autos und ähnliches weniger interessieren, bin ich meistens bei den Scriptern unterwegs. Die GTA-Engine nutzt eine binär-kompilierte Datei (main.scm). Diese hat einen recht simplen Aufbau, welcher sich hier nachlesen lässt:
Klick. Nun gibt es für das (noch) neuste GTA "San Andreas" im Grunde nur zwei Editoren zum dekompilieren/neu kompilieren dieser Datei, von denen einer nicht mehr gewartet wird und unter massiven Fehlern leidet und ein zweiter (aus Russland), welcher "nur" Freeware ist und dem (meiner Meinung nach) noch einige Features fehlen um perfekt zu sein. Jedenfalls habe ich mir überlegt meine Delphi Erfahrung (müssten nun 3 Jahre sein) zu nutzen um einen weiteren Open-Source Editor zu basteln. Bisher allerdings hatte ich noch keine größere Erfahrung mit binären Dateien, doch TFileStream machte im gepufferten Zustand kaum Probleme, ganz im Gegensatz zu meinem alten Freund TStrings der scheinbar mit meiner massiven Anzahl an Updates kaum klarkommt.
Die Standard main.scm ist ca. 3mb groß, getestet habe ich alles an einer 1kb main.scm und alles lief prima, doch schon bei rund 70kb macht mir TString massive Probleme. Wenn ich alle Ausgabe-Befehle auskommentiere läuft es innerhalb von zwei Sekunden durch, genau wie es sollte, mit TStrings wird es mit steigender kb Zahl immer langsamer (ersichtlich wenn man sich die abgearbeiteten kb ausgeben lässt), so dauert es von 2 auf 3 kb einige Sekunden und von 61 auf 62 ewige Jahre. Deshalb, nach dieser ewigen Einleitung, meine Frage was ich dagegen tun könnte.
Hier der Quellcode der entsprechenden Funktion (muss evt. noch an einigen Stellen geändert werden
).
Delphi-Quellcode:
procedure TfrmMain.btnOpenClick(Sender: TObject);
var
FS: TJclBufferedStream;
I, J, K: Integer;
Ch : Char;
EndOfHeader: Boolean;
High, Low: String;
IsHigh: Boolean;
Param: Integer;
ParamTyp, ParamSCMStr: String;
ini: TINIFile;
begin
if NOT OpenDialog.Execute then
Exit;
Code.Lines.Clear;
EndOfHeader := False;
IsHigh := False;
try
Code.Lines.BeginUpdate;
FS := TJclBufferedStream.Create(TFileStream.Create(OpenDialog.FileName, fmOpenRead),True);
ini := TINIFile.Create(ExtractFilePath(Application.ExeName)+'SASCM.ini');
I := 0;
While I < FS.Size do
begin
Caption := FloatToStr(I DIV 1024);
Application.ProcessMessages;
FS.Read(Ch, SizeOf(Ch));
if IntToHex(Ord(Ch),2) = 'A4' then
EndOfHeader := True;
if NOT EndOfHeader then
Code.Lines.Text := Code.Lines.Text + IntToHex(Ord(Ch),2)
else
begin
If IsHigh then
High := IntToHex(Ord(Ch),2)
else
Low := IntToHex(Ord(Ch),2);
if isHigh then
begin
ParamSCMStr := ini.ReadString('main',LowerCase(High+Low),'0,');
ParamSCMStr := Copy(ParamSCMStr, Pos(',',ParamSCMStr)+1, Length(ParamSCMStr));
Code.Lines.Add(GetOP(High, Low, ParamSCMStr));
ParamSCMStr := ini.ReadString('main',LowerCase(High+Low),'0,');
ParamSCMStr := Copy(ParamSCMStr,1,Pos(',',ParamSCMStr)-1);
for K := 1 to StrToInt(ParamSCMStr) do
begin
Application.ProcessMessages;
FS.Read(Ch, SizeOf(Ch));
I := I + 1;
Param := Ord(Ch);
case Param of
01: begin Param := 4; ParamTyp := 'INT32'; end;
02: begin Param := 2; ParamTyp := 'GVAR'; end;
03: begin Param := 2; ParamTyp := 'LVAR'; end;
04: begin Param := 1; ParamTyp := 'INT8'; end;
05: begin Param := 2; ParamTyp := 'INT16'; end;
06: begin Param := 4; ParamTyp := 'FLOAT32'; end;
07: begin Param := 6; ParamTyp := 'GARRAY'; end;
08: begin Param := 6; ParamTyp := 'LARRAY'; end;
09: begin Param := 8; ParamTyp := 'STR8'; end;
10: begin Param := 2; ParamTyp := 'GSTR8'; end;
11: begin Param := 2; ParamTyp := 'LSTR8'; end;
12: begin Param := 6; ParamTyp := 'GSTRA8'; end;
13: begin Param := 6; ParamTyp := 'LSTRA8'; end;
14: begin
FS.Read(Ch, SizeOf(Ch));
I := I + 1;
Param := Ord(Ch);
ParamTyp := 'USTR';
end;
15: begin Param := 16; ParamTyp := 'STR16'; end;
16: begin Param := 2; ParamTyp := 'GVSTR'; end;
17: begin Param := 2; ParamTyp := 'LVSTR'; end;
18: begin Param := 6; ParamTyp := 'GVSTRA'; end;
19: begin Param := 6; ParamTyp := 'LVSTRA'; end;
else
begin
Param := 0;
ParamTyp := '';
end;
end;
if ParamTyp <> '' then
begin
Code.Lines.Text := Code.Lines.Text + ParamTyp + ' ';
for j := Param downto 1 do
begin
FS.Read(Ch, SizeOf(Ch));
I := I + 1;
Code.Lines.Text := Code.Lines.Text + IntToHex(Ord(Ch),2)+' ';
end;
end;
end;
end;
IsHigh := NOT IsHigh;
end;
I := I + 1;
end;
finally
FS.Free;
ini.Free;
Code.Lines.EndUpdate;
end;
end;
Angehängt mal das komplette Projekt, falls jemand eine Idee hat (beide scms [70kb + mini 1kb] sind mal mit drin, zum testen [Beide übrigens inoffiziell und somit nicht vom Copyright des Spieleherstellers betroffen]).
Oh und ja, mein Programm entziffert den Header (noch) nicht.