Blood Omen file formats
Contents |
This page is dedicated to the specifications for file formats used in Blood Omen. It encompasses exploring work done by the community since Blood Omen was released.
Most simple formats (such as .BIG file structure, TIM specs) were unveiled long time ago, but some ones (map format, sprites) was unveiled lately during Blood Pill development.
BIG file
This is general file storing all game resources. It's structure is quite simple:
uint32 - number of entries Array of entries: uint32 - Hashed name of file uint32 - File size uint32 - File offset Array of entry data: bytes - File contents
TIM image
Playstation images. Used to store graphics and tiles. TIM specs are widely known, so it will be not listed here. Reference decoder/encoder can be found here.
Tips:
- Some pill.big entries can contain several TIM's glued together.
- Some blood omen TIM files can contain tails with nulls.
Sounds
VAG
Playstation's Very-Audio-Good files (4-bit ADPCM variation) used in blood omen playstation version and by blood omen sneak peek preview. Unused in PC version. Reference decoder/encoder can be found here.
Tips:
- Many VAG files are headerless (having mess in the header, even no 'VAGb' fourCC, hence could only be detected by direct parsing).
- Normally should be played at 11025hz, but some ones (music and some sounds) at 22050hz
RAW IMA ADPCM
Headerless files containing raw IMA ADPCM stream to replaces VAG files in PC version. They are using exactly same file names as VAG files in playstation version.
Waveform Audio File
Blood Omen PC version have some sounds in .WAV format (playstation have them in .VAG). This format is widely known and does not need to be explained.
Video
PSX STR
Used in Blood Omen PlayStation version. STR is MJPEG video stream with interleaved audio. jPsxDec tool could be used to extract and decode this format from playstation CD images.
JAM Videos
JAM is lossy video compression format developed by Semilogic entertainment and used in Blood Omen PC version. The format uses shared palette for several frames and a form of dynamic compression. JAM spec and decoder was unveiled in 2011 by MisterGrim. JAM can only store video, audio was stored in separate .VAG file (which was for real, raw IMA ADPCM).
JAM videos have much more artifacts than STR, sound track is mono (STR have stereo).
JAM decompressing code can be found here
Sprites
Sprites are images of characters and animated stuff. There are 3 extensions as of filenames: SHA, SDR, SHD. However, when Blood Pill was developed, there were no knowledge of real file names, so this spec will define "Sprite types" which covers unpacking of all sprites, including unused ones.
Sprite type 1 - Item Cards
Always have 1 frame and no compression used.
uint32 - always 0x01 0x00 0x00 0x00 uint32 - file size 768 bytes - 24-bit colormap data (256 colors) int16 - x position int16 - y position byte - width byte - height byte - x byte - y width*height bytes - picture contents (indexes into colormap)
Sprite type 2 - Images collection
Multiframe sprite with per-frame palette. Used by interface images collections (such as spell/item pics).
uint32 - number of frames uint32 - file size 768 bytes - 24-bit colormap data (unused) Array of frame headers: 768 bytes - 24-bit colormap data uint32 - offset to the picture data from the end of headers ubyte - width ubyte - height byte - x byte - y Array of frame picture data: width*height bytes - picture contents (indexes into colormap)
Sprite type 3 - Object sprites
Multiframe sprite with shared palette.
uint32 - number of frames uint32 - file size 768 bytes - 24-bit colormap data int16 - x offset for all frames int16 - y offset for all frames Array of frame headers: uint32 - offset to the picture data from the end of headers ubyte - width ubyte - height byte - x byte - y Array of frame picture data: bytes - compressed picture contents (indexes into colormap) using run-length encodingE, some files using 4-bit color (see #Compression methods)
Tips:
- It is unknown how to get what compression file do use (RLE on 0x00, or 0xFF or both), it seems engine gives that info when file get loaded
- Some files are using 4-bit color instead of RLE
Sprite type 4 - character sprites
Multiframe sprite with shared palette, with additional frames headers. Used by characters. Pretty same as type 3.
uint32 - number of frames uint32 - file size Array of additional frame headers (rounded to the greatest multiplier of 4): byte - damage mark. A value of 1 tell the game to deal damage in this frame. 768 bytes - 24-bit colormap data int16 - x offset for all frames int16 - y offset for all frames Array of frame headers: uint32 - offset to the picture data from the end of headers ubyte - width ubyte - height byte - x byte - y Array of frame picture data: bytes - compressed picture contents (indexes into colormap) using run-length encoding, some files using 4-bit color (see #Compression methods)
Tips:
- It is unknown how to get what compression file do use (RLE on 0x00, or 0xFF or both), it seems engine gives that info when file get loaded
- Some files are using 4-bit color instead of RLE
Sprite type 5
Another variation of type 3 with no offset info and (sometimes) broken width/height, which should be fixed with special trick.
uint32 - number of frames uint32 - file size 768 bytes - 24-bit colormap data Array of frame headers: uint32 - offset to the picture data from the end of headers ubyte - width ubyte - height byte - x byte - y Array of frame picture data: bytes - compressed picture contents (indexes into colormap) using RLE (see #Compression methods)
Tips:
- Some frames have broken width/height because they width is above 255 and format uses 1 byte to encode it, 255 + [width] should be used to correct this
- It is unknown how to get what compression file do use (RLE on 0x00, or 0xFF or both), it seems engine gives that info when file get loaded
Maps
Maps are central thing which brings all resources (images, sounds, tiles, sprites) together. There is about 80% of map structure revealed, allowing to extract general amount of things from it, including: textures used, a map image, monster/effect/item placement, triggers, info marks, doors and buttons etc.
Each map has a mapnum and section that is included in filename. That info is used to place map in a global adventure map (see TheLost Worlds - May survey for complete info about level naming).
Tiles
Tiles are 256-color TIM images compressed with LZ77 (see #Compression methods).
Each tile texture has 8x8 = 64 tiles. Engine renders all tile separately, switching current tile texture as needed.
uint32 - compressed data size bytes - compressed data
Tips:
- Since all tiles are 8-bit 256x256 TIM, unpacked data size is always same
Map file
Map file is fixed-length structure which holds tiles placement (80x80), enemies, lights, buttons and other objects to form Blood Omen level. Because of it's fixed nature, there are many limits, and each map file have exactly same size (so it can just be copied into memory).
// blood omen map structure typedef struct { unsigned short parm1; // first parm, if script is not defined it"s 0xFFFF, 0xFFFE is null value // - destination/source map number (TRIGGER_EXIT, TRIGGER_ENTRANCE, TRIGGER_TELEPORT) // - speech num (TRIGGER_INFOMARK, TRIGGER_IMAGEMARK) unsigned short parm2; // second parm: // - button num (TRIGGER_BUTTON) // - pic num (TRIGGER_IMAGEMARK) byte parm3; // destination/source map section (TRIGGER_EXIT, TRIGGER_ENTRANCE, TRIGGER_TELEPORT) byte srcx; // sposition byte srcy; // sposition byte type; // script type, one of TRIGGER_ byte u2[3]; // ? byte x; // position byte y; // position byte u3[3]; // ? }bo_trigger_t; typedef struct { unsigned short savenum; // unique savegame id byte itemcode; // one of MAPITEM_ code byte x; // position byte y; // position byte hidden; // set to 1 if item is spawned by trigger (like barrel destruction) unsigned short target; // targeted triggergroup byte u[4]; // ? }bo_item_t; typedef struct { unsigned short targetnum;// a triggergroup byte x; // position byte u1; // ? byte y; // position byte u2; // ? unsigned short tile1; // tilenum unsigned short tile2; // alternate state tilenum byte contents1; // contents, see CONTENTS_ defines for possible content values byte contents2; // alternate state contents byte u3[12]; // ? }bo_atile_t; typedef struct { unsigned short target; // a triggergroup to call unsigned short tile1; // tilenum unsigned short tile2; // alternate state tilenum byte flags; // see BUTTONFLAG_* defs byte u1; // ? unsigned short savenum; // unique savegame id byte u2; // ? byte u3; // ? }bo_button_t; typedef struct { byte x; // position byte y; // position byte lightposx; // position of lightsource byte lightposy; // position of lightsource byte sprite; // index of sprite from sprites array byte lightheight; // height (for lightsource) byte lightwidth; // width (for lightsource) byte flags; // effect flags byte lightflags; // light flags byte r; // red color component byte g; // green color component byte b; // blue color component unsigned short targetnum;// triggergroup byte start_on; // 1 if effect starts on, 0 if starts off byte u3[5]; // ? }bo_effect_t; typedef struct { byte data[164]; }bo_object_t; typedef struct { byte x; // start position byte y; // end position byte w; // width byte h; // height byte ofsx; // multiply by 16 to get real offset byte ofsy; // multiply by 16 to get real offset byte u1; byte u2; }bo_grpobject_t; typedef struct { byte u1; // ? byte u2; // ? byte u3; // ? byte u4; // ? unsigned short tile1; // base tile (grpobject really, not standart 32x32 tile) unsigned short tile2; // toggled tile (grpobject) unsigned short tile3; // destroyed tile (grpobject) byte u5; // ? byte u6; // ? byte pushable; // a strength required to push byte toggled; // trigger can toggle it"s state (means tile2 is there) byte u7; // ? byte spawnitems[3]; // item codes to spawn when toggled/destroyed byte u8; // ? byte destructible; // destructible flags unsigned short savenum; // unique savegame id byte u10; // ? byte x; // position byte y; // position byte u11; // ? byte active; // 1 is filled, otherwise 0 byte u13; // ? }bo_scenery_t; typedef struct { byte x; // position byte y; // position byte u1; // ? byte u2; // ? }bo_path_t; typedef struct { bo_path_t paths[4]; // patrole paths byte u1[12]; // ? unsigned short savenum; // unique savegame id byte u2[52]; // ? byte charnum; // monster class (same as char* sprite) byte u3[10]; // ? unsigned short target; // triggergroup to call once killed byte u4[20]; // ? byte lastpath; // ? byte u6[18]; // ? byte u7[15]; // ? byte x; // position byte y; // position byte u8[4]; // ? unsigned short speechnum;// a speech number for disguise talk byte u9[6]; // ? }bo_monster_t; typedef struct { // a chain of tilemap numbers used (to make a filenames) // each tilemap holds 64 tiles, tiles numbering is linear // like, a tile #131 is tile #3 from tilemap #2 // being 2-byte (unsigned short) tilenum contains some flags (flag are starting from byte 10) // see TILEFLAG_ defines for possible tileflags unsigned short tilemaps[40]; byte u1[12]; // yet unknown info bo_object_t objects[10]; bo_monster_t monsters[32]; bo_grpobject_t grpobjects[8][32]; // animated tiles - a tiles that can be toggled between 2 states and have different contents for each state // used on doors, spikes that pops up on the floor etc. // animated tiles have triggergroup to be executed by script bo_atile_t atiles[100]; // a special buttons array holds all buttons used on level // pretty similar to animated tiles // buttons have triggergroup to be executed by script bo_button_t buttons[20]; // yet unknown info #2 byte u2[8]; // scenery pushable objects - tables, chairs, stones etc. bo_scenery_t scenery[256]; // first tilemap unsigned short backtiles[80][80]; // second tilemap unsigned short foretiles[80][80]; // contents map - a contents value for each static tile (in game they get overriden by switchable tiles if presented) // see CONTENTS_ defines for possible content values 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]; // items placed on level // itemcodes 0-9 are powerups, codes 10, 11, 12, 13 reserved for unique item codes // so there will be not more than 4 unique item type per level // unique items are: spells, weapons, forms, artifacts, tokens byte uniqueitems[4]; bo_item_t items[50]; // yet unknown data byte u4[8]; byte u5[8][40]; byte u6[24]; // effects unsigned short sprites[8]; bo_effect_t effects[64]; // map general info unsigned char u61; unsigned char environments; // 0 is outdoor, 1 is indoor unsigned char ambientcolor[3]; // rgb format, 15 15 15 is white, 31 31 31 is double white unsigned char songnum; // 0 is oracle caves // 1 is mausoleum // 2 is awakening // 3 is road to vengeance // 4 is nupraptor // 5 is maleks bastion // 6 is voradors mansion // 7 is dark eden // 8 is solemn dirge // 9 is avernus // 10 is elzevir // yet unknown info #7 unsigned char u7[930]; }bo_map_t; // things that are not figured out yet: // - how lightning is done (variable form lights, day-night light, ambient light) // - monster"s paths"s switch (some paths are messed up) // - counters and puzzle triggers? // map environments #define ENVIRONMENTS_OUTDOOR 0 #define ENVIRONMENTS_INDOOR 1 // tileflags #define TILEFLAG_UNKNOWN1 1024 #define TILEFLAG_NODRAW 2048 #define TILEFLAG_ONTOPINFRONT 4096 #define TILEFLAG_ALWAYSBELOW 8192 #define TILEFLAG_ALWAYSONTOP 16384 #define TILEFLAG_UNKNOWN6 32768 #define TILEFLAG_MASK (1024 + 2048 + 4096 + 8192 + 16384 + 32768) #define TILEFLAG_IMASK (1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256 + 512) // contents #define CONTENTS_NONE 0 #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_SAVEGAME 44 #define CONTENTS_BATMARK 45 // triggers #define TRIGGER_EXIT 1 #define TRIGGER_ENTRANCE 2 #define TRIGGER_SPEECHMARK 3 #define TRIGGER_TOUCH 4 #define TRIGGER_TELEPORT 6 #define TRIGGER_IMAGEMARK 7 // button flags #define BUTTONFLAG_TOGGLE 1 #define BUTTONFLAG_SECRET 2 // effect flags #define LIGHTFLAG_FIRE 2 // fire damage #define LIGHTFLAG_THUNDER 4 // thunder light #define LIGHTFLAG_X 8 #define LIGHTFLAG_FORWARD 16 // spot light orientation, vertical lightsize sets number of steps, horizontal size sets starting step size #define LIGHTFLAG_BACK 32 // spot light orientation, vertical lightsize sets number of steps, horizontal size sets starting step size #define LIGHTFLAG_RIGHT 64 // spot light orientation, vertical lightsize sets number of steps, horizontal size sets starting step size #define LIGHTFLAG_LEFT 128 // spot light orientation, vertical lightsize sets number of steps, horizontal size sets starting step size // light flags (possible light flags - 4 or 6) #define EFFECTFLAG_DYNAMIC 1 // dynamic light adds to world light, not replacing it #define EFFECTFLAG_FLICKER 2 // flickering light #define EFFECTFLAG_LIGHT 4 // has light // item codes #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 // unique item codes #define MAPITEM_UNIQUE2 11 // unique item codes #define MAPITEM_UNIQUE3 12 // unique item codes #define MAPITEM_UNIQUE4 13 // unique item codes // real item codes #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
Compression methods
Index-based Run Length Encoding (RLE)
Used on sprite data to compress black areas which is normally transparent. This technique alters reading process of byte 0x0 and 0xFF. If got this byte when reading picture data, should read 1 byte more which will be times to repeat this byte. Blood Omen RLE only used for values 0x00 or 0xFF.
Example:
// output 32 bytes with value 0xFF
0xFF 0x20
4-bit images
Encode 2 pixels per byte with 256 color palette but with only 16 colors used (other pixels are black).
For each byte it produces 2 bytes in order:
- byte 1 : <byte> - (int)(<byte>/16)*16; // <byte> mod 16
- byte 2 : (int)(<byte>/16) // <byte> div 16
Example:
231 will output 2 pixels with values: byte 1 : 7 byte 2 : 14
Variation of LZ77
A variation of the LZ77 (LZ1) data compression algorithms.
Sample decoder can be found at The Lost Worlds and Blood Pill sourcecode.
Credits
- Andrey [Rackot] Grachev - Bigfile specs
- Pavel [VorteX] Timofeyev - Map file, sprites
- Ben Lincoln - LZ77 decompressor
- JAM decoding specs by MisterGrim
- Balder and Zench from XentaX forums (www.xentax.com) - IMA ADPCM