|
Registriert seit: 12. Apr 2004 92 Beiträge |
#1
Hallo!
Das hier ist ein Crosspost, da ich den Thread am Sonntag schon im Delphi-Forum erstellt habe. Leider bekam ich dort bis dato keine Antwort die mir weiterhelfen konnte, deswegen versuche ich es hier nochmal. Ich versuche das Problem so gut wie möglich zu erklären: Es geht um die Replaydateien eines Computerspiels (Warcraft III). Die Replaydateien kann man mit dem Hauptprogramm abspielen und sich so Spiele von anderen Leuten ansehen. Ich möchte ein Programm schreiben, dass ein paar interessante Daten aus den Replaydateien ausliest. Z.B. die Spieler, die Karte auf der gespielt wurde etc. Es gibt zu diesen Replayfiles von Warcraft eine herrvoragende Dokumentation. In dieser steht, dass der Header der Dateien so aufgebaut ist:
Code:
Diese Daten aus dem Header und Subheader lese ich mit folgendem Code aus:
offset | size/type | Description
-------+-----------+----------------------------------------------------------- 0x0000 | 28 chars | zero terminated string "Warcraft III recorded game\0x1A\0" 0x001c | 1 dword | fileoffset of first compressed data block (header size) | | 0x40 for WarCraft III with patch <= v1.06 | | 0x44 for WarCraft III patch >= 1.07 and TFT replays 0x0020 | 1 dword | overall size of compressed file 0x0024 | 1 dword | replay header version: | | 0x00 for WarCraft III with patch <= 1.06 | | 0x01 for WarCraft III patch >= 1.07 and TFT replays 0x0028 | 1 dword | overall size of decompressed data (excluding header) 0x002c | 1 dword | number of compressed data blocks in file 0x0030 | n bytes | SubHeader (see section 2.1 and 2.2)
Delphi-Quellcode:
Das funktioniert soweit, mit diesem Code würde ich z.B. den dword-Wert an Offset 0x0020 auslesen.
var DATA : dword;
hFile : Thandle; begin hFile := FileOpen('c:\test\replay.w3g', $0000); FileSeek(hFile, integer($20), 0); FileRead(hFile, DATA, sizeof(dword)); memo1.lines.add(inttostr(Data)); end; Der Subheader beginnt ja dann ab 0x0030 und endet bei 0x044. Wenn ich z.B.: den Wert an Offset 0x002 des Subheaders auslesen möchte, dann komm ich an die Adresse mittels: FileSeek(hFile, integer($30+$2), 0); Soweit so gut, diese Dinge funktionieren. Jetzt komme ich in der Dokumentation aber an einen Punkt an dem ich absolut nichts mehr verstehe. Zur Erläuterung eine kurze Zusammenfassung, was die Dokumenation aussagt (Ihr könnt sie euch auch gerne selbst anschauen wenn ihr wollt, sie ist unten angehängt). Also, die Dokumentation sagt folgendes über Rohbau der Datei: 1.) Header geht bist 0x030 2.) Subheader geht bis 0x044 3.) Data Blocks Über die Data Blocks an 3.) steht in der Doku wortwörtlich folgendes:
Code:
Also, ab 0x044 (nach dem 2. Header) folgen x-viele compressed Data Blocks.
===============================================================================
3.0 [Data block header] =============================================================================== Each compressed data block consists of a header followed by compressed data. The first data block starts at the address denoted in the replay file header. All following addresses are relative to the start of the data block header. The decompressed data blocks append to a single continueous data stream (disregarding the block headers). The content of this stream (see section 4) is completely independent of the original block boundaries. offset | size/type | Description -------+-----------+----------------------------------------------------------- 0x0000 | 1 word | size n of compressed data block (excluding header) 0x0002 | 1 word | size of decompressed data block (currently 8k) 0x0004 | 1 dword | unknown (probably checksum) 0x0008 | n bytes | compressed data (decompress using zlib) Jeder Data Block hat einen Header, der aufgebaut ist wie oben angegeben. So, damit ihr versteht was einen erwarten soll, wenn man die compressed Data dekomprimiert hat (oder wie auch immer man da ran kommt), ist hier der nächste Teil aus der Doku, der beschreibt wie man mit dem dekomprimierten Data Block arbeiten soll:
Code:
=============================================================================== 4.0 [Decompressed data] =============================================================================== Decompressed data is a collection of data items that appear back to back in the stream. The offsets for these items vary depending on the size of every single item. This section describes the records that always appear at the beginning of a replay data stream. They hold information about settings and players right before the start of the game. Data about the game in progress is described in section 5. The order of the start up items is as follows: # | Size | Name ---+----------+-------------------------- 1 | 4 byte | Unknown (0x00000110 - another record id?) 2 | variable | PlayerRecord (see 4.1) 3 | variable | GameName (null terminated string) (see 4.2) 4 | 1 byte | Nullbyte 5 | variable | Encoded String (null terminated) (see 4.3) | | - GameSettings (see 4.4) | | - Map&CreatorName (see 4.5) 6 | 4 byte | PlayerCount (see 4.6) 7 | 4 byte | GameType (see 4.7) 8 | 4 byte | LanguageID (see 4.8) 9 | variable | PlayerList (see 4.9) 10 | variable | GameStartRecord (see 4.11) The following sections describe these items in detail. After the static items (as described above) there follow variable information organized in blocks that are described in section 5. So, was mich jetzt zuerst mal verblüfft ist, dass hier nicht mehr wie bei den Headern die Offsets angegeben werden, ich also selbst wenn ich den Data Block dekomprimieren könnte, nicht wüsste wie ich auf diese Daten dann zugreifen kann. Der User Sinspin auf dem Delphi-Forum hat mir dazu diesen Tipp gegeben: ![]() Stream oder String stellt hier keinen Unterschied dar.
Du nimmst einfach die Daten die du entschlüsseln willst und speicherst sie in einen String, dekomprimierst diesem mit zlib und arbeitest mit dem Ergebnis weiter. Dazu könntest du es ja auch wieder in eine datei speichern, so das du das gleiche vorgehen für die Zugriffe hast, das dir schon bekannt ist. 1.) Grösse der zu lesenden Bytes aus dem Header auslesen Da der Subheader an 0x044 endet, müsste ja der Header des ersten Datablocks an dieser Adresse beginnen. Zu diesem Header stand in der Dokumentation ja:
Code:
Also als allererste 1 word in dem die Grösse der compressed Data steht.
0x0000 | 1 word | size n of compressed data block (excluding header)
Mit diesem Code könnte ich die Data auslesen, wenn da nicht irgendwo ein Fehler steckt:
Delphi-Quellcode:
Ich nehme diesen Wert $44 an, da ich vorhin ja auch mit $30+$02 auf den Wert an offset $02 des Subheaders zugreifen konnte. Zur Erinnerung: Der Header endet bei $30, der Subheader endet bei $44.
var compresseddatasize : word;
begin FileSeek(hFile, integer($44), 0); FileRead(hFile, compresseddatasize, sizeof(word)); end; 2.) Compressed Data aus der Datei auslesen Ich müsste jetzt ja theoretisch wissen, wie gross die compressed Data in dem Data Block ist. Und ich weiss auch wo die compressed Data beginnt. Zur Erinnerung: Im Compressed-Data-Header stand:
Code:
Also weiss ich, dass ab ($44+$08) die Compressed Data beginnt.
0x0008 | n bytes | compressed data (decompress using zlib)
Wenn ich wieder genau denselben Code anwende wie ganz am Anfang, um den normalen Header auszulesen:
Delphi-Quellcode:
Aufgrund von Sinspins Hinweis, und der Tatsache, dass ich zLib-dekomprimierung nur in Verbindung mit Strings gefunden habe, bin ich davon ausgegangen, dass der Datentyp für die compressedData String sein sollte.
var compressedData : string;
begin fileseek(hfile, integer($44+$08), 0); fileread(hfile, compressedDATA, compresseddatasize); //compresseddatasize habe ich ja vorhin aus dem Header gelesen. end; Den string compressedData habe ich dann durch einen in eurer Code-Library gefundenen Quellcode gejagt:
Delphi-Quellcode:
Es tritt eine AccessViolation auf.
function DeCompressString(input:string):string;
var InpBuf, OutBuf: Pointer; OutBytes: Integer; begin InpBuf := nil; OutBuf := nil; try GetMem(InpBuf, Length(input)); Move(input[1], InpBuf^, Length(input)); DeCompressBuf(InpBuf, Length(input),0,OutBuf, OutBytes); SetLength(result,OutBytes); Move(OutBuf^, result[1], OutBytes); finally if InpBuf <> nil then FreeMem(InpBuf); if OutBuf <> nil then FreeMem(OutBuf); end; end; Das bringt folgende Rückschlüsse: Möglichkeit 1) Mein kompletter Ansatz ist falsch. Möglichkeit 2) Meine ausgelesene Grösse des compressed Data Blocks ist falsch, weil a) Die Adresse an der ich auslese falsch ist b) Die Grössenangabe zuerst umgerechnet werden muss o.ä. Möglichkeit 3) Die compressed Data wird falsch ausgelesen, weil a) Die Adresse an der ich auslese falsch ist b) die Grössenangabe die ausgelesen wurde falsch ist c) Der Datentyp in den ich einlese (String) falsch ist Möglichkeit 4) Die Funktion zum dekomprimieren mit zLib ist nicht auf diesen Fall anwendbar (geht davon aus, dass in 1-3 keine Fehler sind). Ad. Dekomprimierung mit zLib: In der Dokumenation ist zum Dekomprimieren dieser Hinweis gegeben:
Code:
Ich kann damit auch mit Googlen absolut nicht anfangen.
To decompress one block with zlib:
1. call 'inflate_init' 2. call 'inflate' with Z_SYNC_FLUSH for the block The last block is padded with 0 bytes up to the 8K border. These bytes can be disregarded. Okay, ich hoffe ich hab mich halbwegs deutlich ausgedrückt. Im Anhang noch die Dokumentation zu dem Dateiformat, allerdings sollte ich alles was mit meinem Problem zu tun hat hier gepostet haben. Vielen Dank für Hilfe. |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |