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.
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 Savegame-Versionen der beiden Programmversionen 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 Blocks 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.
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:
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 Blocks 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 "" ' Oder irgendein anderer 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 Verbesserung des Artikels wertvolle Tipps gegeben hat!
Letzte Änderung: 01.09.2024, 11:24