Formati di file di Blood Omen
Contents |
Questa pagina è dedicata alle specifiche per i formati dei file usati in Blood Omen. Include il lavoro di esplorazione fatto dalla community da quando Blood Omen è uscito.
La maggior parte dei formati semplici (come la struttura del file BIG, le specifiche del TIM) sono stati resi esplorabili anni fa, ma altri (il formato della mappa, gli sprite) soltanto di recente, durante lo sviluppo di Blood Pill.
File BIG
Questo è un file generale che contiene tutte le risorse di gioco. La sua struttura è molto semplice:
uint32 - numero di entries Array delle entries: uint32 - Nome hash del file uint32 - Dimensione del file uint32 - Offset del file Array dei dati della entry: bytes - Contenuti del file
Immagine TIM
Immagini per PlayStation. Usate per contenere gli elementi grafici e i componenti della mappa. Le specifiche dei TIM sono ben conosciute, quindi non verranno elencate qui. Riferimenti per decodificare e codificare sono disponibili nel codice sorgente di Blood Pill.
Consigli:
- Alcune entries del pill.big possono contenere più file TIM fusi insieme.
- Alcuni file TIM di Blood Omen possono terminare con dei null.
Suoni
VAG
I file "Very-Audio-Good" per PlayStation (variazione ADPCM di 4-bit) usati nella versione PlayStation di Blood Omen e dal preview di gioco. Non sono usati nella versione PC. Riferimenti per decodificare e codificare sono disponibili nel codice sorgente di Blood Pill.
Consigli:
- Molti file VAG sono senza header (il loro header è confuso, non c'è neppure il fourCC "VAGb", dunque possono essere individuati solo con un'analisi sintattica).
- Normalmente andrebbero eseguiti a 11025hz, ma alcuni (le musiche e alcuni effetti sonori) a 22050hz.
IMA ADPCM grezzi
Dei file senza header che contengono flussi grezzi IMA ADPCM che servono a sostituire i file VAG nella versione PC. Usano esattamente gli stessi nomi dei file VAG per la PlayStation.
File audio WAV
Blood Omen per PC ha alcuni effetti sonori in formato WAV (La PlayStation li ha in VAG). Questo formato è largamente conosciuto e non c'è bisogno di spiegarlo.
Video
STR della PSX
Usato nella versione PlayStation di Blood Omen. L'STR è un flusso video MJPEG con l'audio aggiunto come interleave. Si può usare il tool jPsxDec per estrarre e decodificare questo formato dall'immagine del cd per PlayStation del gioco.
Video JAM
JAM è un formato di compressione video scadente sviluppato da Semilogic Entertainment e usato nella versione PC di Blood Omen. Il formato usa una tavolozza in comune con più frame e un tipo di compressione dinamica. La specifica e la decodifica dei JAM sono state scoperte nel 2011 da MisterGrim. Il JAM può solo contenere video, l'audio era contenuto in un file VAG separato (che era, in effetti, un IMA ADPCM grezzo).
I video JAM hanno molte più alterazioni dei STR, e la traccia audio è in mono (gli STR hanno una stereo).
Il codice per decomprimere i JAM è disponibile nel codice sorgente di Blood Pill.
Gli sprite
Gli sprite sono immagini di personaggi o altre cose animate. Ci sono tre estensioni e altrettanti nomi dei file: SHA, SDR, SHD. Tuttavia quando Blood Pill è stato sviluppato non erano noti i veri nomi dei file, quindi questa specifica viene definita "Tipi di sprite" e copre la decompressione di tutti gli sprite, inclusi quelli non utilizzati.
Sprite di tipo 1 - Carte degli oggetti
Ha sempre 1 frame e nessuna compressione.
uint32 - sempre 0x01 0x00 0x00 0x00 uint32 - dimensione del file 768 bytes - dati della mappa dei colori a 24 bit (256 colori) int16 - posizione su x int16 - posizione su y byte - larghezza byte - altezza byte - x byte - y larghezza*altezza bytes - contenuto dell'immagine (gli indici nella mappa dei colori)
Sprite di tipo 2 - Insieme di immagini
Sprite a più frame con tavolozza dedicata ad ogni frame. Usato per le raccolte di immagini dell'interfaccia (come le immagini degli incantesimi e degli oggetti).
uint32 - numero di frame uint32 - dimensione del file 768 bytes - dati della mappa dei colori a 24 bit (inutilizzato) Array degli header del frame: 768 bytes - dati della mappa dei colri a 24 bit uint32 - offset dei dati dell'immagine dalla fine degli header ubyte - larghezza ubyte - altezza byte - x byte - y Array dei dati dell'immagine del frame: larghezza*altezza bytes - contenuto dell'immagine (gli indici nella mappa dei colori)
Sprite di tipo 3 - Sprite degli oggetti
Sprite a più frame con tavolozza condivisa.
uint32 - numero di frame uint32 - dimensione del file 768 bytes - dati della mappa dei colori a 24 bit int16 - offset in x per tutti i frame int16 - offset in y per tutti i frame Array degli header del frame: uint32 - offset dei dati dell'immagine dalla fine degli header ubyte - larghezza ubyte - altezza byte - x byte - y Array dei dati dell'immagine del frame: bytes - contenuti dell'immagine compressa (gli indici nella mappa dei colori) usando un run-length encoding; alcuni file usando colori a 4 bit (vedi #Metodi di compressione)
Consigli:
- Non si sa come scoprire quale compressione del file sia usata (RLE a 0x00, o 0xFF o entrambe), sembra che l'engine dia questa informazione quando il file viene caricato
- Alcuni file usano colori a 4 bit invece che RLE
Sprite di tipo 4 - Sprite dei personaggi
Sprite a più frame con tavolozza condivisa, con header dei frame aggiuntivi. Usato dai personaggi. Molto simile al tipo 3.
uint32 - numero di frame uint32 - dimensione del file Array degli header aggiuntivi del frame (arrotondati per eccesso al multiplo di 4 più vicino): byte - informazioni ignote 768 bytes - dati della mappa dei colori a 24 bit int16 - offset in x per tutti i frame int16 - offset in y per tutti i frame Array dehli header del frame: uint32 - offset dei dati dell'immagine dalla fine degli header ubyte - larghezza ubyte - altezza byte - x byte - y Array dei dati dell'immagine del frame: bytes - contenuti dell'immagine compressa (gli indici nella mappa dei colori) usando un run-length encoding; alcuni file usando colori a 4 bit (vedi #Metodi di compressione)
Consigli:
- Non si sa come scoprire quale compressione del file sia usata (RLE a 0x00, o 0xFF o entrambe), sembra che l'engine dia questa informazione quando il file viene caricato
- Alcuni file usano colori a 4 bit invece che RLE
Sprite di tipo 5
Un'altra variante del tipo 3 senza informazioni sull'offset e (a volte) con larghezza e altezza non valide, da mettere a posto con un particolare trucco.
uint32 - numero di frame uint32 - dimensione del file 768 bytes - dati della mappa dei colori a 24 bit Array degli header del frame: uint32 - offset dei dati dell'immagine dalla fine degli header ubyte - larghezza ubyte - altezza byte - x byte - y Array dei dati dell'immagine del frame: bytes - contenuti dell'immagine compressa (gli indici nella mappa dei colori) che usano RLE (vedi #Metodi di compressione)
Consigli:
- Alcuni frame hanno larghezza e altezza non valide perché la loro larghezza è maggiore di 255 e il formato usa 1 bit per codificarle. Per correggere il problema va usato 255 + [larghezza]
- Non si sa come scoprire quale compressione del file sia usata (RLE a 0x00, o 0xFF o entrambe), sembra che l'engine dia questa informazione quando il file viene caricato
Mappe
Le mappe sono un elemento centrale che mette insieme tutte le risorse (immagini, suoni, mattonelle, sprite). Circa l'80% della struttura della mappa è stata scoperta e questo permette di estrarre una serie di cose, incluse: le texture usate, l'immagine della mappa, posizione di mostri/effetti/oggetti, attivatori, informazioni scritte, porte e bottoni...
Ogni mappa ha un mapnum (numero della mappa) e una sezione inclusa nel nome del file. Quell'informazione viene usata per piazzare la mappa nella mappa globale di un'avventura (vedi TheLost Worlds - May survey per le informazioni complete sui nomi dei livelli).
Mattonelle
Le mattonelle sono immagini TIM a 256 colori compresse con LZ77 (vedi #Metodi di compressione).
La texture di ogni mattonella ha 8x8=64 mattonelle. L'engine renderizza ogni mattonella separatmaente, spostando la texture della mattonella del momento come necessario.
uint32 - dimensione dei dati compressi bytes - dati compressi
Consigli:
- Dato che tutte le mattonelle sono dei TIM a 8 bit 256x256, la dimensione dei dati decompressi è sempre la stessa
File della mappa
Il file della mappa è una struttura di lunghezza fissa che contiene la posizione delle mattonelle (80x80), i nemici, le luci, i botoni e altri oggetti che compongono il livellod i Blood Omen. Per via della sua natura statica ha molti limiti, e ogni mappa ha esattamente la stessa dimensione (quindi basta copiarla in memoria).
// map trigger typedef struct { unsigned short parm1; // primo parm, se non definito lo script è 0xFFFF, 0xFFFE è il valore null // - numero di mappa di destinazione/origine (TRIGGER_EXIT, TRIGGER_ENTRANCE, TRIGGER_TELEPORT) // - numero del discorso (TRIGGER_INFOMARK, TRIGGER_IMAGEMARK) unsigned short parm2; // secondo parm: // - numero del pulsante (TRIGGER_BUTTON) // - numero dell'immagine (TRIGGER_IMAGEMARK) byte parm3; // sezione della mappa di destinazione/origine (TRIGGER_EXIT, TRIGGER_ENTRANCE, TRIGGER_TELEPORT) byte srcx; // posizione byte srcy; // posizione byte type; // tipo di script, uno dei TRIGGER_ byte u2[3]; // ? byte x; // posizione byte y; // posizione byte u3[3]; // ? }bo_trigger_t; // oggetto della mappa typedef struct { unsigned short savenum; // id unico di salvataggio byte itemcode; // uno dei codici MAPITEM_ byte x; // posizione byte y; // posizione byte hidden; // vale 1 se l'oggetto viene spawnato da un attivatore (come la distruzione di un barile) unsigned short target; // gruppo di attivatori bersaglio byte u[4]; // ? }bo_item_t; // mattonella animata typedef struct { unsigned short targetnum;// un gruppo di attivatori byte x; // posizione byte u1; // ? byte y; // posizione byte u2; // ? unsigned short tile1; // numero della mattonella unsigned short tile2; // numero della mattonella allo stato alternativo byte contents1; // contenuti, vedi le definizioni di CONTENTS_ per i possibili valori dei contenuti byte contents2; // contenuti allo stato alternativo byte u3[12]; // ? }bo_atile_t; // controller del pulsante typedef struct { unsigned short target; // un gruppo di attivatori da chiamare unsigned short tile1; // numero della mattonella unsigned short tile2; // numero della mattonella allo stato alternativo byte flags; // vedi le definizioni dei BUTTONFLAG_* byte u1; // ? unsigned short savenum; // id unico di salvataggio byte u2; // ? byte u3; // ? }bo_button_t; // effetto typedef struct { byte x; // posizione byte y; // posizione byte lightposx; // posizione della fonte di luce byte lightposy; // posizione della fonte di luce byte sprite; // indice dello sprite dall'array degli sprite byte lightsizex; // dimensione dei poligoni di luce byte lightsizey; // dimensione dei poligoni di luce byte u1; // ? byte lightform; // flag della forma della luce byte r; // quantità di rosso byte g; // quantità di verde byte b; // quantità di blu unsigned short targetnum;// gruppo di attivatori byte start_on; // 1 se l'effetto parte attivo, 0 se parte disattivo byte u3[5]; // ? }bo_effect_t; typedef struct { byte data[164]; }bo_object_t; // definizione dell'oggetto sulla mattonella (barili, forzieri) typedef struct { byte x; // posizione iniziale byte y; // posizione finale byte w; // larghezza byte h; // altezza byte ofsx; // moltiplica per 16 per il vero offset byte ofsy; // moltiplica per 16 per il vero offset byte u1; byte u2; }bo_grpobject_t; // entità dello scenario typedef struct { byte u1; // ? byte u2; // ? byte u3; // ? byte u4; // ? unsigned short tile1; // mattonella base (in realtà l'oggetto gruppo, non la mattonella 32x32 standard unsigned short tile2; // mattonella alternativa (oggetto gruppo) unsigned short tile3; // mattonella distrutta (oggetto gruppo) byte u5; // ? byte u6; // ? byte pushable; // una forza necessaria per spingere byte toggled; // l'attivatore che cambia lo stato (significa che c'è la mattonella 2) byte u7; // ? byte spawnitems[3]; // codici dell'oggetto da spawnare quando attivato/distrutto byte u8; // ? byte destructible; // i flag distruggibili unsigned short savenum; // id unico di salvataggio byte u10; // ? byte x; // posizione byte y; // posizione byte u11; // ? byte active; // 1 è pieno, altrimenti 0 byte u13; // ? }bo_scenery_t; // tracciati dei nemici typedef struct { byte x; // posizione byte y; // posizione byte u1; // ? byte u2; // ? }bo_path_t; // entità nemica typedef struct { bo_path_t paths[4]; // tracciati di pattugliamento byte u1[12]; // ? unsigned short savenum; // id unico di salvataggio byte u2[52]; // ? byte charnum; // classe del mostro (come per lo sprite del personaggio) byte u3[10]; // ? unsigned short target; // gruppo di attivatori da chiamare quando viene ucciso byte u4[20]; // ? byte lastpath; // ? byte u6[18]; // ? byte u7[15]; // ? byte x; // posizione byte y; // posizione byte u8[4]; // ? unsigned short speechnum;// un numero di parlato per i discorsi del travestimento byte u9[6]; // ? }bo_monster_t; // struttura generale del file della mappa typedef struct { // una catena di numeri della mappa delle mattonelle usati (per creare i nomi dei file) // ogni mappa delle mattonelle contiene 64 mattonelle, i cui numeri sono lineari // per esempio, una mattonella #131 è la mattonella #3 dalla mappa delle mattonelle #2 // con flag da 2-byte (abbreviazione senza firma) contenuti nel numero della mattonella (i flag cominciano dal byte 10) // vedi le definizioni dei TILEFLAG_ per i possibili flag delle mattonelle unsigned short tilemaps[40]; // informazione ancora ignota byte u1[12]; bo_object_t objects[10]; bo_monster_t monsters[32]; bo_grpobject_t grpobjects[8][32]; // mattonelle animate - una mattonella che può essere in 2 stati e che ha contenuti diversi ad ogni stato // usato per le porte, le punte che escono dal terreno ecc. // le mattonelle animate hanno un gruppo di attivatori da eseguire con degli script bo_atile_t atiles[100]; // l'array di un pulsante speciale controlla tutti i pulsanti usati nel livello // molto simile alle mattonelle animate // i pulsanti hanno gruppi di attivatori da eseguire con degli script bo_button_t buttons[20]; // informazione #2 ancora ignota byte u2[8]; // oggetti spingibili nella scena - tavoli, sedie, pietre ecc. bo_scenery_t scenery[256]; // prima mappa delle mattonelle unsigned short backtiles[80][80]; // seconda mappa delle mattonelle unsigned short foretiles[80][80]; // mappa dei contenuti - un valore di contenuto per ogni mattonella statica (in gioco vengono sostituite dalle mattonelle modificabili, se presenti) // vedi le definizioni dei CONTENTS_ per i possibili valori dei contenuti byte contents[80][80]; // linear triggers array - all triggers used on map bo_trigger_t triggers[255]; // triggers are non-solid interaction points, activated when player stands in them // this array golds pointers to triggers byte triggertiles[80][80]; // oggetti piazzati nel livello // i codici degli oggetti 0-9 sono potenziamenti, i codici 10, 11, 12, 13 sono riservati per i codici di oggetti unici // quindi non possono esserci più di 4 tipi di oggetti unici in ogni livello //sono oggetti unici: incantesimi, armi, forme, artefatti, oggetti delle missioni byte uniqueitems[4]; bo_item_t items[50]; // dati ancora sconosciuti byte u4[8]; byte u5[8][40]; byte u6[24]; // effetti unsigned short sprites[8]; bo_effect_t effects[64]; // dati ancora sconosciuti byte u7[936]; }bo_map_t; // cose ancora non capite: // - come funziona l'illuminazione (luci di vario tipo, luce di notte e giorno, luce ambientale) // - gli interruttori dei tracciati dei mostri (alcuni tracciati sono incasinati) // - counter e attivatori dei puzzle? // flag delle mattonelle #define TILEFLAG_UNKNOWN1 1024 #define TILEFLAG_NODRAW 2048 #define TILEFLAG_UNKNOWN3 4096 #define TILEFLAG_UNKNOWN4 8192 #define TILEFLAG_ALWAYSONTOP 16384 #define TILEFLAG_UNKNOWN6 32768 // contenuti #define CONTENTS_SOLID 1 #define CONTENTS_WATER 2 #define CONTENTS_LAVA 3 #define CONTENTS_FIRE 4 #define CONTENTS_SPIKES 5 #define CONTENTS_TRAPTELEPORT 6 #define CONTENTS_JUMPWALL 8 #define CONTENTS_SWAMP 11 #define CONTENTS_JUMPFENCE 10 #define CONTENTS_MISTWALK 12 #define CONTENTS_SACRIFICE 13 #define CONTENTS_ICE 19 #define CONTENTS_SAVEGAM 44 #define CONTENTS_BATMARK 45 // attivatori #define TRIGGER_EXIT 1 #define TRIGGER_ENTRANCE 2 #define TRIGGER_SPEECHMARK 3 #define TRIGGER_TOUCH 4 #define TRIGGER_TELEPORT 6 #define TRIGGER_IMAGEMARK 7 // flag dei pulsanti #define BUTTONFLAG_TOGGLE 1 #define BUTTONFLAG_SECRET 2 // codici degli oggetti #define MAPITEM_SMALLBLOODFLASK 1 #define MAPITEM_BLOODFLASK 2 #define MAPITEM_RUNEPYRAMID 3 #define MAPITEM_PURPLESPHERE 4 #define MAPITEM_BLUESPHERE 5 #define MAPITEM_GREENSPHERE 6 #define MAPITEM_GOLDSPHERE 7 #define MAPITEM_REDSPHERE 8 #define MAPITEM_ANCIENTVIAL 9 #define MAPITEM_UNIQUE1 10 // codici di oggetti unici #define MAPITEM_UNIQUE2 11 // codici di oggetti unici #define MAPITEM_UNIQUE3 12 // codici di oggetti unici #define MAPITEM_UNIQUE4 13 // codici di oggetti unici // codici degli oggetti unici #define ITEM_SPELL_INSPIREHATE 10 #define ITEM_SPELL_STUN 11 #define ITEM_SPELL_SPIRITWRACK 12 #define ITEM_SPELL_BLOODGOUT 13 #define ITEM_SPELL_BLOODSHOWER 14 #define ITEM_SPELL_SPIRITDEATH 15 #define ITEM_SPELL_LIGHT 16 #define ITEM_SPELL_LIGHTNING 17 #define ITEM_SPELL_REPEL 18 #define ITEM_SPELL_ENERGYBOLT 19 #define ITEM_SPELL_INCAPACITATE 20 #define ITEM_SPELL_CONTROLMIND 21 #define ITEM_SPELL_SANCTUARY 22 #define ITEM_ARTIFACT_FLAY 23 #define ITEM_ARTIFACT_PENTALICH 24 #define ITEM_ARTIFACT_IMPLODE 25 #define ITEM_ARTIFACT_FONT 26 #define ITEM_ARTIFACT_BANK 27 #define ITEM_ARTIFACT_HEART 28 #define ITEM_ARTIFACT_ANTITOXIN 29 #define ITEM_ARTIFACT_SLOWTIME 30 #define ITEM_TOKEN_MOEBIUS 31 #define ITEM_TOKEN_DEJOULE 32 #define ITEM_TOKEN_DOLL 33 #define ITEM_TOKEN_MALEK 34 #define ITEM_TOKEN_AZIMUTH 35 #define ITEM_TOKEN_MORTANIUS 36 #define ITEM_TOKEN_NUPRAPTOR 37 #define ITEM_TOKEN_BANE 38 #define ITEM_TOKEN_SIGNETRING 39 #define ITEM_TOKEN_TIMEDEVICE 40 #define ITEM_TOKEN_ANACROTHE 41 #define ITEM_WEAPON_MACE 43 #define ITEM_WEAPON_AXES 44 #define ITEM_WEAPON_FLAMESWORD 45 #define ITEM_WEAPON_SOULREAVER 46 #define ITEM_ARMOR_BONE 48 #define ITEM_ARMOR_CHAOS 49 #define ITEM_ARMOR_FLESH 50 #define ITEM_ARMOR_WRAITH 51 #define ITEM_FORM_BAT 53 #define ITEM_FORM_WOLF 54 #define ITEM_FORM_DISGUISE 55 #define ITEM_FORM_MIST 56 #define ITEM_ENDING_1 69 #define ITEM_ENDING_2 70
Metodi di compressione
Run Length Encoding (RLE) basato sull'indice
Usato sui dati degli sprite per comprimere le zone nere che normalmente sono trasparenti. Questa tecnica altera il processo di lettura dei byte 0x0 e 0xFF. Se durante la lettura dei dati dell'immagine si ottiene questo byte, si dovrà leggere un byte in più che conterrà il numero di volte che quel byte va ripetuto. Blood Omen RLE è usato solo per valori di 0x00 o 0xFF.
Esempio:
// output di 32 byte col valore 0xFF
0xFF 0x20
Immagini a 4 bit
Codifica 2 pixel per ogni byte con una tavolozza da 256 colori ma con solo 16 colori usati (gli altri pixel sono neri).
Per ogni byte produce 2 byte, in ordine:
- byte 1: <byte> - (int)(<byte>/16)*16; // <byte> mod 16
- byte 2: (int)(<byte>/16) // <byte> div 16
Esempio:
231 manderà in output 2 pixel coi valori: byte 1: 7 byte 2: 14
Variazione del LZ77
Una variazione degli algoritmi di compressione dei dati del LZ77 (LZ1).
Un decodificatore campione è disponibile a The Lost Worlds e nel codice sorgente di Blood Pill.
Ringraziamenti
- Andrey [Rackot] Grachev - specifiche del file BIG
- Pavel [VorteX] Timofeyev - file della mappa, sprite
- Ben Lincoln - decompressore LZ77
- Specifiche di decodifica JAM di MisterGrim
- Balder e Zench dai forum di XentaX (www.xentax.com) - IMA ADPCM
Vedere anche
- Blood Pill
- jPsxDec homepage
- TIM specifications at QHimmWiki
- Blood Omen Decompressor homepage
- Blood Omen Map Survey (TheLostWorlds)