Entwickler-Ecke

Dateizugriff - Noob sucht Hilfe: Wie speichern Spiele oder Programme Datein


Nano-Ware - Sa 19.03.11 17:23
Titel: Noob sucht Hilfe: Wie speichern Spiele oder Programme Datein
Hey,

mir ist jetzt schon öfters aufgefallen, dass wenn ich ein gespeichertes Spiel oder die ein oder andere Programmdatei im Editor öffne, dass ich dort nur auf total sinnlose zeichen stoße statt auf gehoffte Ini oder Xml Struktur. Was ich mich jetzt frage ist, wie solche Dateien generiert werden und ob ich die Möglichkeit habe herauszufinden wie ich an die Dateien komme:

Z.B. bei dem Spiel Minecraft: Es wird in einem Ordner das Spiel in der Datei level.dat gespeichert. In der Datei befinden sich "nur" die Koordinaten aller Objekte und die Objekte + Anzahl die ich im Inventar habe. Trotzdem ist diese Datei sehr komisch.
Wie kann ich sowas auslesen. Geht mal wirklich davon aus, dass ich echt garkeine Ahnung hab, was binäres schreiben von Dateien betrifft auch total zutrifft.


Danke

Achja meistens bringen mir Erklärungen die direkt darauf bezogen sind, mehr als irgendwelche Links!


Moderiert von user profile iconChristian S.: Topic aus Off Topic verschoben am Sa 19.03.2011 um 17:39


Gausi - Sa 19.03.11 17:49

Naja, im grunde sind ja alle Dateien "binäre Dateien". Es gibt halt nur einige, da sehen die Nullen und Einsen wie Buchstaben aus. ;-)

Ob man nun einen String wie "x = 23" reinschreibt, oder direkt die 23 so, wie sie auch im Speicher steht, ist dem Computer ja egal.

Sowas regelt man am einfachsten mit FileStreams. Wenn man z.B. zwischen durch auch mal Strings reinschreiben will, sollte man vorher die Länge in den Stream schreiben. Kleines Beispiel:

Delphi-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:
// Speichern
procedure TForm1.Button1Click(Sender: TObject);
var fs: TFileStream;
    aString: AnsiString;
    aInteger: Integer;
begin
    fs := TFileStream.Create('test.tst', fmCreate or fmOpenReadWrite);
    try
        aString := 'Hallo Welt';
        aInteger := length(aString);
        fs.Write(aInteger, SizeOf(aInteger));
        fs.Write(aString[1], aInteger);
    finally
        fs.Free;
    end;
end;

// wieder laden
procedure TForm1.Button2Click(Sender: TObject);
var fs: TFileStream;
    aString: AnsiString;
    aInteger: Integer;
begin
    fs := TFileStream.Create('test.tst', fmOpenRead);
    try
        fs.Read(aInteger, SizeOf(aInteger));
        Setlength(aString, aInteger);
        fs.Read(aString[1], aInteger);
        showmessage(aString);
    finally
        fs.Free;
    end;
end;


ALF - Sa 19.03.11 17:49

Meist werden solche saves als Record gespeichert. Da gabs hier mal nen paar Threads dazu.
Vorteil, man kann nicht so schnell manipulieren :wink: weil halt binär.
Nachteil, wenn man es ebend will :dance2:
Meist macht man sich die Mühe mit einen HexEditor. Vergleicht Sav1 mit sav2, um zu sehen wo sich was ändert! zb Rucksack. Habe 1 element drin. Save, sichere Datei. Finde diese Element nochmal und nehem es auf. Save. Nun vergleiche beide Saves mit einen Exeditor.
So kenn ich es noch aus uralten Zeiten.

ALf


Nano-Ware - Sa 19.03.11 18:03

Ich hab das mim Hex Editor versucht. Wenn ich nur von einem Item im Inventar die Anzahl um 1 runterschraube ändert sich so ziemlich die GANZE Datei... Ich bin am verzweifeln -.-


Gausi - Sa 19.03.11 18:08

Dann sind die Daten da evtl. verschlüsselt gespeichert, damit pöhse Cheater da nicht so einfach drin rumhäcken können. ;-)


Nano-Ware - Sa 19.03.11 18:11

Es gibt aber schon 1. ein Programm, dass das Inventar füllen kann und 2. ist das Programm in Java geschrieben...


jaenicke - Sa 19.03.11 18:12

Dann schau dir doch davon einfach den Quelltext an. Wenn du Glück hast, ist der nicht obfuscated. ;-)


Nano-Ware - Sa 19.03.11 18:18

Ich hab im Internet gefunden, dass die Datei GZipped ist? Wie kann man die denn in Delphi GUnzippen?


jaenicke - Sa 19.03.11 18:28

Ich denke einmal am einfachsten mit den Indy-Komponenten, die sind ja eh mit Delphi gleich installiert.


Nano-Ware - Sa 19.03.11 18:30

Und mit welcher und wie?


jaenicke - Sa 19.03.11 18:45

So viele Möglichkeiten gibts da ja nicht, hab kurz geschaut, wie wäre es mit TIdCompressorZLib.DecompressGZipStream?


elundril - Sa 19.03.11 18:51

Wenn du nur bei dem spiel jetzt nachschauen willst wie das gespeichert ist, kannst du auch unter linux die datei entpacken, oder mit 7zip. Können soweit ich weiß beide die GZip-Kompression.

lg elundril


Gerd Kayser - Sa 19.03.11 18:51

Beim Lesen / Schreiben von binären Dateien kann es aber noch einige Fallstricke geben. Zum Beispiel Big oder Little Endian, Verwendung von MJD (modifiziertes Julianisches Datum) usw.


JungerIslaender - So 20.03.11 11:47

user profile iconNano-Ware hat folgendes geschrieben Zum zitierten Posting springen:
Ich hab das mim Hex Editor versucht. Wenn ich nur von einem Item im Inventar die Anzahl um 1 runterschraube ändert sich so ziemlich die GANZE Datei... Ich bin am verzweifeln -.-


Naja und ich möchte behaupten das es dir in Minecraft nicht gelingt zwei identische speicherstände anzulegen, die sich nur in der Anzahl eines Items unterscheiden. Es wird ja noch viel mehr gespeichert als nur die Itemzahl, nämlich Uhrzeit, position Blickrichtung usw...

Es ist allerdings möglich die Anzahl der Items per Cheatengine zu verändern. D.h. das müsste dann simultan ja auch mit Delphi machar sein. LG JungerIslaender


Nano-Ware - So 20.03.11 11:50

Ich wüsste nicht wie das per Cheatengine gehen sollte habs ja schon versucht. Nur mir gehts ja auch nicht darum.. das könnte ich ja ich will ja lernen was es mit den Dateien auf sich hat!


Martok - Mo 21.03.11 17:40

Wie wäre es dann mit Dokumentation lesen? NBT ist ein sehr gut dokumentiertes Format...

http://www.minecraft.net/docs/NBT.txt
http://www.minecraftwiki.net/wiki/Alpha_Level_Format#level.dat_Format

Allerdings ist das nicht so ganz einfach eine funktionierene ZLib dafür zu finden. Ich benutze dafür eine "ZLibEx" von "base2 technologies" und einen eigenen Stream, weil GZip keinen Dateinamen verwendet, aber der ZDecompressionStream eigentlich einen sehen will; deswegen über-seek-e ich den einfach:


Lizenz: WTFPL
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
unit GZip;

interface

uses Classes, ZLibEx;

type
  TGZDecompressionStream = class(TZDecompressionStream)
  public
    constructor Create(source: TStream);
  end;

implementation

constructor TGZDecompressionStream.Create(source: TStream);
begin
  Source.Position:= 10;
  inherited Create(Source, -15);
end;

end.


EDIT: Was natürlich quatsch ist hier: nur die Maps sind komprimiert, und in 1.3 ja dann auch anders als dieser Code lesen würde. Für die level.dat brauchst du nichts zum dekomprimieren.


Nano-Ware - Mo 21.03.11 23:40

Ehm ich hab das Spiel ja auf dem PC ich hkönnte bei bedarf eine level.dat hochladen.. Naja also wenn ich die Datei einfach mal frech mit WinRAR entpacke ist der Text bis auf 5-6 Buchstaben lesbar...


Martok - Mo 21.03.11 23:46

Könnte ich auch *g*

War da grade auf dem falschen Dampfer. Natürlich ist eine .dat immer komprimiert. Da drin ist dann das NBT, für das man den Auslese-Code mit etwas aufwand direkt runterschreiben kann - die Doku ist halt direkt für Entwickler geschrieben ;)

Aber stimmt, Winrar erkennt das verwendete GZip z.B. direkt. Nur halt keiner der üblichen DecompressionStream-Klassen, daher der Aufwand.

Woran scheiterts denn?


Nano-Ware - Di 22.03.11 19:55

weil es dekomprimiert so aussieht :


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  
 Data Time     ’ 
LastPlayed  .Þ3¼_
 Player 
SleepTimer     Motion   ? [€#äÞà¿´Â\)¿ô +7Ö OnGround HurtTime   Health    Dimension     Air,     Inventory
   
 id B Damage   Count Slot   id ¸ Damage   Count Slot  id Y Damage   Count, Slot  id\ Damage   Count Slot  id 5 Damage   Count; Slot  id | Damage   Count? Slot  id9 Damage   Count Slotd  id8 Damage   Count Slote  id7 Damage   Count Slotf  id6 Damage   Count Slotg    Pos   @Uà"~˜Ú@Z'®€  @BBæÄ‹p 
AttackTime   Sleeping  Fireÿì  FallDistance       Rotation   Å¿…A½™… Score       DeathTime    SpawnX     SpawnY   @   LevelName Marka SpawnZ     
SizeOnDisk     >0  
RandomSeed    i«Q version  J¼


FrEaKY - So 27.03.11 19:07

Und wo ist das Problem? Du hast doch nicht erwartet, dass es wie ein Word-Dokument aussieht?
Anscheinend werden die Werte als Record gespeichert... dann solltest du sie mit einem Record auch wieder lesen können, sofern du das Format richtig rausliest.


Martok - So 27.03.11 20:11

Nicht direkt ein Record, eher ein allgmeiner Baum von Key-Value Paaren. Quasi so eine Art binäres, typisiertes XML. Wie genau das funktioniert, hab ich oben verlinkt.
Diesen kann man dann z.B. auch wieder als solchen darstellen:
pick

Das ist z.B. eine level.dat aus MC 1.1 von mir.


Aber mal kurze Rückfrage: ist das jetzt ein allgemeiner Thread oder ein Minecraft-Thread? Eventuell wäre dann nämlich ein Teil abzutrennen.


Nano-Ware - So 27.03.11 22:52

Also ich bin bei diesem Thema leider total unterbelichtet. Martok, kannst du vielleicht dein Projekt hochladen? Ich möchte sowieso das ganze nur machen um langsam zu verstehen, wie das funktioniert, ich werde keinen einzigen Teil deines Codes verwenden nur mir fehlt der Ansatz.

Und nein es ist kein Minecraftspiel. Mir geht es hier generell ums Datenspeichern und ich wollte mich an das Beispiel von Minecraft ranwagen.

Danke schonmal Martok das sieht nämlich so aus wie ich mir das vorgestellt hab (:

Lg


jaenicke - So 27.03.11 23:08

user profile iconNano-Ware hat folgendes geschrieben Zum zitierten Posting springen:
Mir geht es hier generell ums Datenspeichern und ich wollte mich an das Beispiel von Minecraft ranwagen.
Nun ja, es hat niemand gesagt, dass das dort eine optimale Variante der Datenspeicherung ist. Das heißt, wenn es dir darum geht eigene Daten in einer Datei zu speichern und wie sowas funktioniert, würde ich jetzt nicht einfach irgendwelche Dateien anschauen. ;-)


Nano-Ware - So 27.03.11 23:47

Irgendwo muss man mal anfangen.. ich fang bei MC an und schau dann mal weiter :P