Autor Beitrag
Karlson
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Mo 21.04.08 04:40 
Hallo,

Das ganze hat mit diesem Thread angefangen. Es geht darum bestimmte Daten aus einer Replay-Datei eines Computerspiels auszulesen.

Es gibt eine sehr gute Dokumentation zu diesen Replay-Dateien. Im oben genannten Thread ging es nun erstmal darum wie ich prinzipiell Daten deren Offset, Typ und Grösse mir bekannt ist aus einer Datei auslesen kann. Das wurde in dem Thread gelöst.

Jetzt bin ich aber an eine Stelle gekommen an der ich nicht mehr weiter weiss und hoffe dass jemand evt. in dem Bereich etwas Erfahrung hat und mir erklären kann wie ich da vorgehen muss.

Zunächsteinmal hier der Originaltext aus der Dokumentation:

ausblenden volle Höhe Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
===============================================================================
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)

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.

//TODO: add decompression details and move explanation to seperate file


===============================================================================
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.


Der Subheader endet je nach Version an der Stelle 0x0040 oder 0x0042, also müsste dort dann der Data Block Header anfangen. Das Problem ist, dass die darauffolgenden Daten (4.0) irgendwie compressed sind. Der Autor der Dokumentation weisst darauf hin mal solle sie mit zlib dekomprimieren und könne sie dann erst auslesen...Wie soll dass den gehen? :lol: Kann mir da wirklich nichts drunter vorstellen gerade...
Hinzu kommt, dass der Autor jetzt von einem Stream redet.

Also mit zLib kann ich String komprimieren / dekomprimieren, das weiss ich bereits, aber mir fehlt ganz klar der Ansatz.
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1335
Erhaltene Danke: 118

Win 10
RIO, CE, Lazarus
BeitragVerfasst: Mo 21.04.08 10:01 
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.

_________________
Wir zerstören die Natur und Wälder der Erde. Wir töten wilde Tiere für Trophäen. Wir produzieren Lebewesen als Massenware um sie nach wenigen Monaten zu töten. Warum sollte unser aller Mutter, die Natur, nicht die gleichen Rechte haben?
Karlson Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Mo 21.04.08 15:37 
Okay, was ich jetzt mal ausprobiert habe folgendes:

Ich weiss dass der Dataheader an 0x44 beginnt. An Stelle 0x44 steht dann auch die Grösse des Dataheaders.
Diese hab ich ausgelesen (3050, kann das sein?).
An Stelle 0x44+0x08 beginnt der Dataheader.
Dann hab ich mit readfile ausgelesen:

ausblenden Delphi-Quelltext
1:
readfile(hfile, integer($44+$8), StringDATA, DATASize)					


Diesen String müsste ich dann dekomprimieren, oder hab ich jetzt was falsch gemacht?
Ich hab nämlich schon mehrere String dekomprimier-codes ausprobiert und erhalte immer eine Zugriffsverletzung.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
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); <--------------- AV 
    Move(OutBuf^, result[1], OutBytes); 
  finally 
    if InpBuf <> nil then FreeMem(InpBuf); 
    if OutBuf <> nil then FreeMem(OutBuf); 
  end
end;


Bei diesem Code aus der DP z.B.
Ich fürchte meine "Ausgangsdaten" sind falsch.

lg.
Karlson Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Di 22.04.08 18:44 
Hallo!

Ich werd hier nochmal pushen und die vorhandenen Fakten nochmal zusammentragen:

Die Dokumentation des Dateiformats sagt folgendes über den groben Aufbau:

ausblenden Quelltext
1:
2:
Header     Endet bei: 0x30 bytes
Subheader  Endet bei: 0x44 bytes


Darauf folgen die "Compressed Data Blocks".
Jeder "Compressed Data Block" besteht aus einem Header, gefolgt von den komprimierten Daten.

Der compressed Data Block (einschließlich Header) ist so aufgebaut:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
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)


Wie die Dokumentation schon sagt, soll für das Dekomprimieren der compressed data zlib verwendet werden, darüberhinaus werden noch folgende zwei Hinweise gemacht:

ausblenden Quelltext
1:
2:
3:
To decompress one block with zlib:
 1. call 'inflate_init'
 2. call 'inflate' with Z_SYNC_FLUSH for the block



Meine Frage, kurz und bündig: Wie komm ich an die Decompressed Data, über die später in der Dokumentation folgendes gesagt wird:

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
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:
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 23.04.08 12:01 
Hm, den Puffer als String zu machen halte ich für potentiell Fehler-Erzeugend. Direkt mit Zeigern arbeiten ist meistens einfacher. Was du da hast, halte ich außerdem für problematisch insofern, dass alles 100mal hin und her gemoved wird ;)

Wenn du mir (ggf per PM) mal eine Solche Datei zukommen lässt (oder sagst welches Spiel, vllt kann ich ja selbst eine aufnehmen^^), dann würde ich mir das mal ansehen.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Karlson Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Mi 23.04.08 12:19 
Hallo! Ich habe mittlerweile einen Crosspost in der DP eröffnet. Wenn dieser zu der Lösung des Problems führt poste ich das hier. Ich hab hier trotzdem mal so eine Datei per PM geschickt.
Danke für die Hilfe, lg.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19313
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 23.04.08 12:25 
Im Crosspost in der DP hat er geschrieben, dass es sich um Warcraft III handelt ;-), den Crosspost hättest du ruhig auch andersherum kundtun können^^ (EDIT: das hat sich ja erledigt :D).

Ja, ich werd mal schauen, ob ich eine solche Datei von jemandem bekomme, dann kann ich es mal testen. Ohne ist es etwas schwierig, aber ich schau auch mal über deinen geposteten Quelltext.
Karlson Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Mi 23.04.08 13:46 
Servus!

Ich häng dir auch mal so eine Datei an: Die Erweiterung von zip in .w3g umbenennen.

lg.
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19313
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 23.04.08 15:11 
0 Bytes? :lol:
Karlson Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Mi 23.04.08 15:26 
Hä?! Ist ja seltsam...Ach weisste was, für was soll ich so ne Datei hochladen, wenns auf www.replayers.com zigtausende davon gibt :lol:

Hier ein x-belibiges: roc.replayers.com/?a...on=view&id=27388 Zum herunterladen auf den blauen "Save Replay" Button klicken.

Lg.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mi 23.04.08 17:07 
So. Interessant, manchmal hilft Manual Lesen doch.

Also, abgesehen von deinem String-gebastel wäre, hätte das funktioniert, noch ein anderes Problem gekommen:
Die allgemeine zlib-Kapselung ruft die inflate-Funktion so auf:
ausblenden Delphi-Quelltext
1:
inflate(strm, Z_FINISH)					

Wie soll man das nochmal richtig machen?
Zitat:
call 'inflate' with Z_SYNC_FLUSH for the block

Ahja, also selber Kapselung schreiben. Schon gehts ;)

Im Anhang mal eine kleine Lösungsmöglichkeit. Bitte nicht mich für den Programmierstil schlagen, das hab ich nur mal schnell hingeschraubt. Bitte auch die Kommentare beachten.

Du solltest übrigens sattelfest in Pointerarithmetik sein, das was als nächstes kommt wird wildes Record-und-^Record-rumgewerfe ;)
Einloggen, um Attachments anzusehen!
_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Karlson Threadstarter
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 2088



BeitragVerfasst: Fr 25.04.08 03:32 
Hi Martok!

Vielen Dank für das Demoprojekt! Genial, dass du dir soviel Mühe damit gemacht hast. Das hilft mir wirklich weiter, obgleich ich jetzt erstmal versuchen muss, das was ich jetzt habe einzusetzen.

Ich habe ja besagten Crosspost in der DP eröffnet. Blöderweise wird jetzt mehr oder weniger parallel diskutiert. Das soll natürlich nicht sein, sorry dafür. Deswegen "mach ich hier mal zu".

Vielen Dank auch nochmal den anderen die geantwortet haben.

Lg.