31.01.2024, 22:48
(Dieser Beitrag wurde zuletzt bearbeitet: 02.02.2024, 21:04 von cmfrydos.
Bearbeitungsgrund: Formatierung / Ergänzung
)
Ich hatte mich kürzlich mal mit dem BOB-Dateiformat auseinander gesetzt, die offenen Fragen des Wikis klären können, und den Dateiaufbau zusammen gefasst. Zu Dokuzwecken poste ich es mal hier:
[BOB-Dateiformat in RIVA]
Grundsätzlich alles Unsigned / Little Endian.
BOBs sind gif-ähnliche Bilddateien, die aus einem Hauptbild und vielen kleinen Ausschnitts-Animationen bestehen. Das sind hauptsächlich die zahlreichen Animationen, wenn Bewohner ihre Tür öffnen und die Helden ansprechen. Hier bewegen sich Mund, Augen, Stirnrunzeln usw. unabhängig voneinander und oft auch mit unterschiedlichen Animationslängen, sodass der Gesamteindruck nicht repetitiv wirkt.
Datenaufbau:
Header: 16 Bytes + ein 0x00-Trennbyte
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 char[3] Signatur 3 Bytes Signatur "BOB"
3 char Trennbyte Ein 0x00-Trennbyte
4 short Version 2 Bytes Version 0x0101
6 byte AnzAnimation 1 Byte Anzahl der Animationen
7 short Konstante 2 Bytes immer 0x010A
9 char[8] Leer8Bytes 8 Bytes immer leer
17 byte LeerByte 1 Byte immer leer, Trennung zum folgenden Abschnitt
---------------------------------------------------------------------
Gibt es Animationen, folgt nun eine Beschreibung, welche (Achtung, einsbasierte) Bildindizes für wie lange gezeigt werden. Dieser Teil der Daten ist doppelt vorhanden! Die einzelnen Teile kommen auch in der jeweiligen Beschreibung der Animation (im Animationsheader) erneut vor.
Miniheader, fasst Längen der nachfolgenden Datenpakete zusammen.
---------------------------------------------------------------------
Datenlängen - Miniheader
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 char DatenLaenge_1 1 Byte Datenlänge für Animation 1 (Größe in Bytes / 4)
1 char DatenLaenge_2 1 Byte Datenlänge für Animation 2 (Größe in Bytes / 4)
2 char DatenLaenge_3 1 Byte Datenlänge für Animation 3 (Größe in Bytes / 4)
... ... ... ...
n-1 char DatenLaenge_n 1 Byte Datenlänge für Animation n (Größe in Bytes / 4)
---------------------------------------------------------------------
Nun folgen dem Datenlängen - Miniheader die eigentlichen Datenpakete, von denen es für jede Animation eine gibt:
---------------------------------------------------------------------
Animationen Datenpakete
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 byte StartByte Immer 0x00
1 byte DatenLaenge Datenlänge für die folgende Animationsbeschreibung (entspricht Miniheader)
2 word Bildindex_1 1 Word Bildindex für Animation 1 (einsbasiert)
4 word Anzeigedauer_1 1 Word Anzeigedauer für Animation 1
6 word Bildindex_2 1 Word Bildindex für Animation 2 (einsbasiert)
8 word Anzeigedauer_2 1 Word Anzeigedauer für Animation 2
... ... ... ...
n-4 word Bildindex_n 1 Word Bildindex für Animation n (einsbasiert)
n-2 word Anzeigedauer_n 1 Word Anzeigedauer für Animation n
---------------------------------------------------------------------
n = 2 + DatenLaenge * 4
Die Anzeigedauer ist in einer Einheit von ~45 ms, das heißt ein Bild, das für 10 Anzeigeeinheiten gezeigt wird, wird für etwa 450 ms angezeigt. Sowohl Bildindex als auch Anzeigedauer sind immer < 256, außer bei Adran. Adrans BOB Animation wird jedoch nie im Spiel gezeigt, sodass sich der Effekt nicht beobachten lässt. Was etwas verwunderlich ist, dass die Alternative "Kein Bild", d.h., den Ausschnitt des Hauptbildes zu verwenden, scheinbar nie benutzt wird.
---------------------------------------------------------------------
Header (0 ist auch 'Headerbeginn')
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 dword Headerlaenge 1 DWord Headerlänge (einschließlich dieser 4 Bytes)
4 dword OffsetDatenEnde 1 DWord Offset zum Datenende vom Headerbeginn aus
8 word BreiteHauptbild 1 Word Breite Hauptbild
10 byte HoeheHauptbild 1 Byte Höhe Hauptbild
11 byte AnzOffsets 1 Byte Anzahl der Offsets (entspricht Anzahl der Animationen)
12 udword OffsetAnimation1 1 UDWord Offset zur Startposition des Animationsheader 1 vom Headerbeginn aus
16 udword OffsetAnimation2 1 UDWord Offset zur Startposition des Animationsheader 2 vom Headerbeginn aus
... ... ... ...
n-4 udword OffsetAnimation_AnzOffsets 1 UDWord Offset zur Startposition der Animation n
---------------------------------------------------------------------
n = 12 + AnzAnimationen * 4
Nun folgt für jede Animation ein Animationsheader
---------------------------------------------------------------------
Animationsheader
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 char[4] Name 4 Bytes Name/Abkürzung der Animation
4 word XOffset 1 Word X-Offset
6 byte YOffset 1 Byte Y-Offset
7 byte Hoehe 1 Byte Höhe
8 word Breite 1 Word Breite
10 byte ImmerNull 1 Byte Immer 0
11 byte AnzTeilbilder 1 Byte Anzahl der Teilbilder
12 udword StartOffset_1 1 UDWord Startoffset der Bilddaten vom Start des Hauptheaders aus für Teilbild 1
16 udword StartOffset_2 1 UDWord Startoffset der Bilddaten vom Start des Hauptheaders aus für Teilbild 2
... ... ... ...
n-4 udword StartOffset_n 1 UDWord Startoffset der Bilddaten vom Start des Hauptheaders aus für Teilbild n
n byte AnzFramezeiten 1 Byte Anzahl der Framezeiten (entspricht der Anzahl aus dem Miniheader)
n+1 dword Framezeit_1 1 DWord Framezeit für Teilbild 1 (entspricht den Daten aus dem Miniheader: 2 Byte Bildindex + 2 Byte Anzeigedauer)
n+5 dword Framezeit_2 1 DWord Framezeit für Teilbild 2
... ... ... ...
m-4 dword Framezeit_o 1 DWord Framezeit für Teilbild o
---------------------------------------------------------------------
m = n + 1 + 4 * AnzFramezeiten
n = 12 + 4 * AnzTeilbilder
Nun folgen die eigentlichen Bilddaten. Es lohnt sich an dieser Stelle zu bestimmen, ob die Daten komprimiert wurden. Dies steht am Datenende.
Wenn die Daten komprimiert sind, sind diese PP20 komprimiert, wobei anstatt der Signatur "PP20" die komprimierte Größe, die 4 Bytes eingeschlossen, am Anfang steht.
Die Daten des Hauptbildes und jede der Animationen sind dabei einzeln komprimiert, wobei die Teilbilder einer Animation zusammen komprimiert vorliegen. D.h. nach dem Entpacken haben wir:
- Hauptbild-Breite mal Höhe Daten für das Hauptbild und
- Anzahl der Teilbilder * dessen Höhe mal dessen Breite an Bytes für jede Animation. Diese Daten muss man dann einfach in gleich große Pakete für die Einzelbilder zerteilen.
---------------------------------------------------------------------
Datenende
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 word Unbekannt_1 2 unbekannte Words < 60
2 word Unbekannt_2
4 byte FFByte 1 Byte immer 0xFF
5 byte Komprimiert 1 Byte 1 bei komprimierten Daten, 0 bei unkomprimierten Daten
6 byte[768] Palette_RGB 256 * 3 Bytes Rot-Grün-Blau-Werte der Palette
---------------------------------------------------------------------
Wie Shihan hier entdeckte, muss man jeden Farbwert leicht verändern: c = (c & 0x3f) * 4. Was in den höchsten 2 Bits kodiert ist, ist noch unbekannt.
Zum Anzeigen muss man nun das Hauptbild zeichnen und jede Animation starten. Diese laufen dabei durch ihre (Index, Dauer) - Liste und rendern das dem Index entsprechende Teilbild. Die Zeichenreihenfolge ist dabei noch unklar. Die Kräuterhändlerin beispielsweise sieht immer falsch aus. Entweder bewegt sich nicht der Mund, weil die Tasse diesen überdeckt, oder beim Pusten sehen die Backen falsch aus. Was bei ihr funktioniert, ist statt Index-1 Bilder (die Ersten) zu zeichnen, die Animation auszulassen, d.h., den Ausschnitt vom Hauptbild zu verwenden. Dieses Vorgehen wiederum funktioniert bei anderen .BOBs gar nicht. Dies bleibt also noch ein letztes Rätsel. Ich habe mal ein Screenshot des ungewöhnlichen .BOB von Adran, das ich im Spiel nicht triggern konnte, beigefügt:
[BOB-Dateiformat in RIVA]
Grundsätzlich alles Unsigned / Little Endian.
BOBs sind gif-ähnliche Bilddateien, die aus einem Hauptbild und vielen kleinen Ausschnitts-Animationen bestehen. Das sind hauptsächlich die zahlreichen Animationen, wenn Bewohner ihre Tür öffnen und die Helden ansprechen. Hier bewegen sich Mund, Augen, Stirnrunzeln usw. unabhängig voneinander und oft auch mit unterschiedlichen Animationslängen, sodass der Gesamteindruck nicht repetitiv wirkt.
Datenaufbau:
Header: 16 Bytes + ein 0x00-Trennbyte
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 char[3] Signatur 3 Bytes Signatur "BOB"
3 char Trennbyte Ein 0x00-Trennbyte
4 short Version 2 Bytes Version 0x0101
6 byte AnzAnimation 1 Byte Anzahl der Animationen
7 short Konstante 2 Bytes immer 0x010A
9 char[8] Leer8Bytes 8 Bytes immer leer
17 byte LeerByte 1 Byte immer leer, Trennung zum folgenden Abschnitt
---------------------------------------------------------------------
Gibt es Animationen, folgt nun eine Beschreibung, welche (Achtung, einsbasierte) Bildindizes für wie lange gezeigt werden. Dieser Teil der Daten ist doppelt vorhanden! Die einzelnen Teile kommen auch in der jeweiligen Beschreibung der Animation (im Animationsheader) erneut vor.
Miniheader, fasst Längen der nachfolgenden Datenpakete zusammen.
---------------------------------------------------------------------
Datenlängen - Miniheader
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 char DatenLaenge_1 1 Byte Datenlänge für Animation 1 (Größe in Bytes / 4)
1 char DatenLaenge_2 1 Byte Datenlänge für Animation 2 (Größe in Bytes / 4)
2 char DatenLaenge_3 1 Byte Datenlänge für Animation 3 (Größe in Bytes / 4)
... ... ... ...
n-1 char DatenLaenge_n 1 Byte Datenlänge für Animation n (Größe in Bytes / 4)
---------------------------------------------------------------------
Nun folgen dem Datenlängen - Miniheader die eigentlichen Datenpakete, von denen es für jede Animation eine gibt:
---------------------------------------------------------------------
Animationen Datenpakete
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 byte StartByte Immer 0x00
1 byte DatenLaenge Datenlänge für die folgende Animationsbeschreibung (entspricht Miniheader)
2 word Bildindex_1 1 Word Bildindex für Animation 1 (einsbasiert)
4 word Anzeigedauer_1 1 Word Anzeigedauer für Animation 1
6 word Bildindex_2 1 Word Bildindex für Animation 2 (einsbasiert)
8 word Anzeigedauer_2 1 Word Anzeigedauer für Animation 2
... ... ... ...
n-4 word Bildindex_n 1 Word Bildindex für Animation n (einsbasiert)
n-2 word Anzeigedauer_n 1 Word Anzeigedauer für Animation n
---------------------------------------------------------------------
n = 2 + DatenLaenge * 4
Die Anzeigedauer ist in einer Einheit von ~45 ms, das heißt ein Bild, das für 10 Anzeigeeinheiten gezeigt wird, wird für etwa 450 ms angezeigt. Sowohl Bildindex als auch Anzeigedauer sind immer < 256, außer bei Adran. Adrans BOB Animation wird jedoch nie im Spiel gezeigt, sodass sich der Effekt nicht beobachten lässt. Was etwas verwunderlich ist, dass die Alternative "Kein Bild", d.h., den Ausschnitt des Hauptbildes zu verwenden, scheinbar nie benutzt wird.
---------------------------------------------------------------------
Header (0 ist auch 'Headerbeginn')
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 dword Headerlaenge 1 DWord Headerlänge (einschließlich dieser 4 Bytes)
4 dword OffsetDatenEnde 1 DWord Offset zum Datenende vom Headerbeginn aus
8 word BreiteHauptbild 1 Word Breite Hauptbild
10 byte HoeheHauptbild 1 Byte Höhe Hauptbild
11 byte AnzOffsets 1 Byte Anzahl der Offsets (entspricht Anzahl der Animationen)
12 udword OffsetAnimation1 1 UDWord Offset zur Startposition des Animationsheader 1 vom Headerbeginn aus
16 udword OffsetAnimation2 1 UDWord Offset zur Startposition des Animationsheader 2 vom Headerbeginn aus
... ... ... ...
n-4 udword OffsetAnimation_AnzOffsets 1 UDWord Offset zur Startposition der Animation n
---------------------------------------------------------------------
n = 12 + AnzAnimationen * 4
Nun folgt für jede Animation ein Animationsheader
---------------------------------------------------------------------
Animationsheader
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 char[4] Name 4 Bytes Name/Abkürzung der Animation
4 word XOffset 1 Word X-Offset
6 byte YOffset 1 Byte Y-Offset
7 byte Hoehe 1 Byte Höhe
8 word Breite 1 Word Breite
10 byte ImmerNull 1 Byte Immer 0
11 byte AnzTeilbilder 1 Byte Anzahl der Teilbilder
12 udword StartOffset_1 1 UDWord Startoffset der Bilddaten vom Start des Hauptheaders aus für Teilbild 1
16 udword StartOffset_2 1 UDWord Startoffset der Bilddaten vom Start des Hauptheaders aus für Teilbild 2
... ... ... ...
n-4 udword StartOffset_n 1 UDWord Startoffset der Bilddaten vom Start des Hauptheaders aus für Teilbild n
n byte AnzFramezeiten 1 Byte Anzahl der Framezeiten (entspricht der Anzahl aus dem Miniheader)
n+1 dword Framezeit_1 1 DWord Framezeit für Teilbild 1 (entspricht den Daten aus dem Miniheader: 2 Byte Bildindex + 2 Byte Anzeigedauer)
n+5 dword Framezeit_2 1 DWord Framezeit für Teilbild 2
... ... ... ...
m-4 dword Framezeit_o 1 DWord Framezeit für Teilbild o
---------------------------------------------------------------------
m = n + 1 + 4 * AnzFramezeiten
n = 12 + 4 * AnzTeilbilder
Nun folgen die eigentlichen Bilddaten. Es lohnt sich an dieser Stelle zu bestimmen, ob die Daten komprimiert wurden. Dies steht am Datenende.
Wenn die Daten komprimiert sind, sind diese PP20 komprimiert, wobei anstatt der Signatur "PP20" die komprimierte Größe, die 4 Bytes eingeschlossen, am Anfang steht.
Die Daten des Hauptbildes und jede der Animationen sind dabei einzeln komprimiert, wobei die Teilbilder einer Animation zusammen komprimiert vorliegen. D.h. nach dem Entpacken haben wir:
- Hauptbild-Breite mal Höhe Daten für das Hauptbild und
- Anzahl der Teilbilder * dessen Höhe mal dessen Breite an Bytes für jede Animation. Diese Daten muss man dann einfach in gleich große Pakete für die Einzelbilder zerteilen.
---------------------------------------------------------------------
Datenende
---------------------------------------------------------------------
Off Typ Name Anmerkung
---------------------------------------------------------------------
0 word Unbekannt_1 2 unbekannte Words < 60
2 word Unbekannt_2
4 byte FFByte 1 Byte immer 0xFF
5 byte Komprimiert 1 Byte 1 bei komprimierten Daten, 0 bei unkomprimierten Daten
6 byte[768] Palette_RGB 256 * 3 Bytes Rot-Grün-Blau-Werte der Palette
---------------------------------------------------------------------
Wie Shihan hier entdeckte, muss man jeden Farbwert leicht verändern: c = (c & 0x3f) * 4. Was in den höchsten 2 Bits kodiert ist, ist noch unbekannt.
Zum Anzeigen muss man nun das Hauptbild zeichnen und jede Animation starten. Diese laufen dabei durch ihre (Index, Dauer) - Liste und rendern das dem Index entsprechende Teilbild. Die Zeichenreihenfolge ist dabei noch unklar. Die Kräuterhändlerin beispielsweise sieht immer falsch aus. Entweder bewegt sich nicht der Mund, weil die Tasse diesen überdeckt, oder beim Pusten sehen die Backen falsch aus. Was bei ihr funktioniert, ist statt Index-1 Bilder (die Ersten) zu zeichnen, die Animation auszulassen, d.h., den Ausschnitt vom Hauptbild zu verwenden. Dieses Vorgehen wiederum funktioniert bei anderen .BOBs gar nicht. Dies bleibt also noch ein letztes Rätsel.