Der Weg zum zukunftssicheren Dateiformat


Natürlich, wer heute Daten wie Spielstände, Konfigurationen, etc. abspeichern will, benutzt oft XML. Jeder kann's lesen, jeder kann's verstehen - doch das ist nicht immer gut... außerdem ist das Verfahren nicht sonderlich gut für Binärdaten geeignet. Wie kann man also sein eigenes Dateiformat entwickeln, das auch noch zukunftssicher arbeitet?

Nun, ich habe mir ein Format für Binärdaten und lesbare Textformate ausgedacht (allerdings nicht, um diese lesbarer zu machen, wie sich später rausstellen wird), das ich an dieser Stelle erläutern möchte.

→ Die Beispiele wurden für FreeBasic geschrieben, doch es dürfte nicht allzu schwer sein, sie in einen anderen Basic-Dialekt oder gar in eine andere Programmiersprache zu übersetzen.

Schritt 1: Der Weg zum neuen Format...


Zuerst ein kleines Beispiel: Du hast gerade das neue Doom 7 programmiert, hast auch schon einen Publisher gefunden... nein, kleiner Scherz

Fangen wir mit den kleinen Dingen an: Du hast dein erstes Spiel im Internet veröffentlicht, inzwischen gibt es sogar schon eine neue Version. Doch die Benutzer haben dir gemeldet, dass ein schwerer Bug beim Speichern auftritt, seit sie die neue Version benutzen. Nach langer Suche hast du rausgefunden, dass es da einige Inkompatibilitäten in den beiden Savegame-Versionen gibt.

Und hier beginnt das Tutorial: Ich habe nämlich vor der Programmierung des Sampleformats für mein Programm vChip überlegt, wie ich die Samples von zukünftigen Versionen noch sicher in der allerersten Version anzeigen kann. Das geht sogar recht einfach, wenn man so ähnlich wie bei XML IDs - also eindeutige Namen - an Datenblöcke vergibt, mit denen man auch direkt auf die gewünschten Daten zugreifen kann und diese eindeutig auslesbar sind. Vereinfachtes Beispiel:

ID-1: Das ist der Inhalt von ID-1.
ID-2: Hier folgt der Inhalt von ID-2.

Dein Programm kann nun einfach nach dem Datenblock "ID-2" suchen und dann den Inhalt des Blockes ausgeben... Genau das war mein Ziel: Neue Programmversionen können Datenblöcke in den gespeicherten Dateien hinzufügen, ohne dass dies ältere Versionen beeinflussen würde. Außerdem können andersrum auch noch neuere Versionen natürlich auf die alten Blöcke zugreifen und eventuell fehlende Daten beim Laden ergänzen.

Schritt 2: Wie geht's weiter?

Nun könnte aber in einem Datenblock doch plötzlich die Zeichenfolge "ID-2" auftauchen... was nun?

Die Antwort ist leicht: Nach der ID steht noch die Länge des jeweiligen Datenblocks. Entspricht die ID nicht der Gesuchten, so wird der Datenblock nun einfach bei der Suche übersprungen:

ID-1 (xy Zeichen lang): Das ist der Inhalt von ID-1.
ID-2 (xy Zeichen lang): Hier folgt der Inhalt von ID-2.

Und genau so funktioniert's auch im Binärformat:

Schritt 3: Der Code

Es folgt ein kleines Code-Beispiel:

Dim ID As String * 6
Dim BlockLaenge As Integer
Dim Block As String

'Fiktive Spielvariablen
Dim Gold As Integer = 44690
Dim KartenName As String = "Aventurien.map"

Open "test.bin" For Binary As #1

  ID = "GoldSt"
  Block = Trim(Str(Gold))
  BlockLaenge = Len(Block)
  Put #1, , ID
  Put #1, , BlockLaenge
  Put #1, , Block

  ID = "Karte "
  Block = KartenName
  BlockLaenge = Len(Block)
  Put #1, , ID
  Put #1, , BlockLaenge
  Put #1, , Block

Close #1

Natürlich kannst du dir für das Schreiben eines Blockes auch eine Sub schreiben. Diese könnte z.B. so aussehen:

Sub SpeichereWert (Dateinummer As Integer, BlockID As String, Block As String)

  Dim ID As String * 6
  Dim BlockLaenge As Integer

  ID = BlockID & Space(6 - Len(BlockID))
  BlockLaenge = Len(Block)

  Put #Dateinummer, , ID
  Put #Dateinummer, , BlockLaenge
  Put #Dateinummer, , Block

End Sub

Sobald du die mit dieser Funktion bearbeiteten Datei öffnest, schnell feststellen, dass diese nicht unbedingt lesbar ist: Die Längenangaben der Blöcke werden im Binärformat abgelegt, welches Normalsterbliche nicht unbedingt aus dem Stehgreif in Dezimalzahlen übersetzen können.

Aber das ist auch gar nicht mein Ziel. Für so etwas gibt es schließlich das aufgeblähte XML oder das schlankere YAML.

Nun zeige ich, wie man mit einer Function die Werte ganz einfach wieder aus der Datei holen kann...

Function HoleWert (Dateinummer As Integer, BlockID As String) As String

  Dim ID As String * 6
  Dim BlockLaenge As Integer
  Dim Block As String

  Do

    Get #Dateinummer, , ID
    Get #Dateinummer, , BlockLaenge
    Block = String(BlockLaenge, 0)
    Get #Dateinummer, , Block

    If Trim(UCase(ID)) = Trim(UCase(BlockID)) Then
      Return Block
    End If

  Loop Until Eof(DateiNummer)

  Return Standardwert

End Function

Wenn der gewünschte Block nicht zur Verfügung steht, wird eine leere Variable zurückgeliefert, was du natürlich nach Belieben ändern kannst - ändere dazu einfach die Zeile Return "". Es wäre zum Beispiel vorstellbar, einen Standardwert als zusätzlichen Parameter an die Funktion zu übergeben.

Wenn du Zahlen in der Datei gespeichert hast, könntest du die Function z.B. so aufrufen:

Open "test.bin" For Binary As #1
  Gold = Val(HoleWert(1, "GoldSt"))
Close #1

So, das war's auch schon! Das ist eigentlich alles, was du für dein zukunftssicheren Binär- und Textformat brauchst! Solltest du trotzdem noch Fragen oder Verbesserungsvorschläge haben, dann schreib' mir einfach eine E-Mail!

Dank geht an dieser Stelle noch an Helium, der mir bei der Verberssung des Artikels wertvolle Tipps gegeben hat!



Letzte Änderung: 21.01.2012, 20:32