AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Crosspost: Compressed Data in Datei auslesen
Thema durchsuchen
Ansicht
Themen-Optionen

Crosspost: Compressed Data in Datei auslesen

Offene Frage von "Karlson"
Ein Thema von Karlson · begonnen am 23. Apr 2008 · letzter Beitrag vom 25. Apr 2008
 
Karlson

Registriert seit: 12. Apr 2004
92 Beiträge
 
#1

Crosspost: Compressed Data in Datei auslesen

  Alt 23. Apr 2008, 00:08
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:
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)
Diese Daten aus dem Header und Subheader lese ich mit folgendem Code aus:

Delphi-Quellcode:
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;
Das funktioniert soweit, mit diesem Code würde ich z.B. den dword-Wert an Offset 0x0020 auslesen.

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:
===============================================================================
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)
Also, ab 0x044 (nach dem 2. Header) folgen x-viele compressed Data Blocks.
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:

Zitat von Sinspin (Delphi-Forum.de):
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.
Anhand dieses Hinweis habe ich mir folgenden Ansatz zurechtgelegt:

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:
0x0000 |  1  word | size n of compressed data block (excluding header)
Also als allererste 1 word in dem die Grösse der compressed Data steht.

Mit diesem Code könnte ich die Data auslesen, wenn da nicht irgendwo ein Fehler steckt:

Delphi-Quellcode:
var compresseddatasize : word;
begin
  FileSeek(hFile, integer($44), 0);
  FileRead(hFile, compresseddatasize, sizeof(word));
end;
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.

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:
0x0008 |  n bytes | compressed data (decompress using zlib)
Also weiss ich, dass ab ($44+$08) die Compressed Data beginnt.

Wenn ich wieder genau denselben Code anwende wie ganz am Anfang, um den normalen Header auszulesen:

Delphi-Quellcode:
var compressedData : string;
begin
  fileseek(hfile, integer($44+$08), 0);
  fileread(hfile, compressedDATA, compresseddatasize); //compresseddatasize habe ich ja vorhin aus dem Header gelesen.
end;
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.

Den string compressedData habe ich dann durch einen in eurer Code-Library gefundenen Quellcode gejagt:

Delphi-Quellcode:
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;
Es tritt eine AccessViolation auf.

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:
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.
Ich kann damit auch mit Googlen absolut nicht anfangen.

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.
Angehängte Dateien
Dateityp: txt w3g_format_934.txt (57,2 KB, 4x aufgerufen)
  Mit Zitat antworten Zitat
 


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:24 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz