DO/PRJ
From Dark Omen Wiki
Contents |
Conventions and definitions:
"Byte" signifies a 1-byte (8-bit) integer
"Long" signifies a Big-endian 4-byte (32-bit) integer (i.e. stored as "n¤¤¤")
"Array" denotes a literal sequence of characters
"String" denotes a sequence of characters ended with a NULL (0x00)
"¤" signifies a NULL (0x00)
"•" signifies a space (0x20)
The .PRJ Battle Project File Layout
The entry point of Dark Omen’s battle description is the .PRJ-file in each battle’s folder. It is a reasonable hypothesis that ".prj" stands for project, and just as reasonable that the name comes from an experimental phase or structure that happened to become the actual core, or that nobody could bother change the names to something more appropriate. Before delving into it, it is worth noting and keeping in mind that the PRJ format is really more or less a hack and is structurally and internally inconsistent, and contains several unconventional (not to say outright stupid) solutions.
Anyway, Dark Omen battles principally consist of a general description of the level (scene and battle) in the .prj-file, a .BTB-file ("BaTtle Boundaries"?) that specifies the deployment zone, a number of .M3D ("Model 3D"?) model files (the special format used in Dark Omen), and 8-bit 128x128 .BMP textures used by and specified in the M3D models.
The .PRJ-file is structured into a file format identification header and then into ten specialised blocks of binary data. The design of these blocks seems inspired by ignorance-friendly nested-block layouts (such as f.i. the .3DS file format) in that each block is systematically begun with a unique block ID header and block size value, thus apparently allowing blind/ignorant parsing, but since the implementation of the block size value is inconsistent the original Dark Omen code probably employs vanilla forward-parsing.
Project file general layout
Section | Notes | |
1 | "Dark•Omen•Battle•file•1.10••••••" | 32-byte file ID header |
2 | "BASE"-block | Map socket base and terrain block |
3 | "WATR"-block | Water mesh block |
4 | "FURN"-block | Meshes used ("furniture") block |
5 | "INST"-block | Furniture instances block |
6 | "TERR"-block | Terrain data entries block - this is the heightmap data, it means that the units don't fall through the ground mesh |
7 | "ATTR"-block | Block - Terrain attributes. This holds information about the type of terrain at each point. |
8 | "EXCL"-block | Block - Exclusions - unknown so far. Seems like it might have to do with Line of Sight |
9 | "MUSC"-block | Block - Music - the music file to play on this map |
10 | "TRAC"-block | Block - Camera track - this stores the camera sweep at the beginning of each match. |
11 | "EDIT"-block | Block - Assumed editor specific information. |
Data layout of subsections.
General block layout
Offset | Size | Type | Description |
0 | 4 | Array | Data block type identifier ("ID") |
4 | 4 | Long | Size of data block ("BSIZE", never less than 1) |
8 | BSIZE | … | Block data |
ID is one of these 4-character literals
- "BASE" – Map socket ("base", map cut-away edges)
- "WATR" – Waterline?
- "FURN" – 3D object on map ("Furniture")
- "INST" - ??
- "TERR" - ?? Terrain data ??
- ATTR
- EXCL
- MUSC
- TRAC
- EDIT
ID "BASE" block data layout
The BASE block layout is minimal with only a string as data. The "base" in a Dark Omen map is the terrain and the socket surrounding it. These are saved in two separate flies with identical names but different extensions.
Offset | Size | Type | Description |
0 | 4 | "BASE" | Base data block identifier |
4 | 4 | Long | Size of data block ("BSIZE", never less than 1) |
8 | BSIZE | String | Filename of socket mesh (generally "base.m3d") |
The socket has the filename as written in the data ("base.m3d"), while the terrain filename is obtained by replacing the extension with "m3x" ("base.m3x").
ID: "WATR" block data layout
Offset | Size | Type | Description |
0 | 4 | "WATR" | Water mesh data block identifier |
4 | 4 | Long | Size of data block ("BSIZE", never less than 1) |
8 | BSIZE | String | Filename of water mesh |
ID: "FURN" block data layout
The "FURN" (probably "FURNiture") block contains the file name of all 3D models used in the scene. These models are then referenced by number (1-based, where "0" indicated NO model is used) in INST entries (next section).
Offset | Size | Type | Description | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 4 | "FURN" | Water mesh data block identifier | |||||||||||||||
4 | 4 | Long | Size of data block less 4×F | |||||||||||||||
8 | 4 | Long | Number of furniture objects ("F") | |||||||||||||||
12 | BSIZE | Spec | List of filenames of furniture for map | |||||||||||||||
Repeat F times |
Notice that the BSIZE element of the FURN block is short by 4 × F bytes. The block size was calculated using only the length of the strings, and the size of the string length element was omitted.
ID: "INST" block data layout
The INST (probably "INSTances") block details the objects to be instantiated in the scene and their attributes. Instantiated scene entities seems to be called "furniture" in the Dark Omen source code.
Offset | Size | Type | Description |
0 | 4 | "INST" | Furniture instances data block identifier |
4 | 4 | Long | Size of data block less 8 ("BSIZE", never less than 1) |
8 | 4 | Long | Number of furniture object instances ("I") |
12 | 4 | Long | Size of each furniture instance ("FSIZE", always 152) |
I×FSIZE bytes: "Furniture" composite data records. See separate description below. |
Notice that the BSIZE element of the INST block is short by 8 bytes. The block size was calculated using only the data area of the furniture records, and number of objects and record size elements were omitted.
Furniture record layout and description
The furniture record seems to contain a mixture of data of relevance for game as well as of relevance only for a graphical level editor.
Notes:
- The position and extents vector items have been transformed into and stored as longs. To obtain the float values, divide each element in orientation by 1024.0f. Further note that the vector described in orientation has not been normalised, and can also be of 0 magnitude.
- The orientation vector item has been transformed into and stored as longs. To obtain the float values, divide each element in orientation by 4096.0f (divide again by 2∏ to obtain rotation in radians). Watch for
- The orientation vector is not normalised
- The orientation vector magnitude can be 0.
- The mesh_slot and dead_mesh_slot items refer to the filename specified in the FURN block, but "1" indicated the first model and "0" signifies that NO mesh is used.
# | Offset | Size | Type | Description |
0 | 0 | 4 | long | ID_prev; //void * |
1 | 4 | 4 | long | ID_next; //void * |
2 | 8 | 4 | long | selected; |
3 | 12 | 4 | long | exclude_from_terrain; |
4 6 | 16 | 12 | long[3] | Position (stored as longs: divide each element by 1024.0f) |
7 9 | 28 | 12 | long[3] | Orientation (stored as longs: divide each element by 4096.0f) |
10 12 | 40 | 12 | long[3] | Min extent (stored as longs: divide each element by 1024.0f) |
13 15 | 52 | 12 | long[3] | Max extent (stored as longs: divide each element by 1024.0f) |
16 | 64 | 4 | long | mesh_slot;// used to store a mesh slot number in the SAVED project |
17 | 68 | 4 | long | Mesh *pMesh; //long ID_mesh; //void * |
18 | 72 | 4 | long | make_attackable; |
19 | 76 | 4 | long | toughness;// used if the furniture is to be attackable |
20 | 80 | 4 | long | wounds; |
21 | 84 | 4 | long | Padding |
22 | 88 | 4 | long | owner_unit_index; |
23 | 92 | 4 | long | is_burning; |
24 | 96 | 4 | long | SFX_code; |
25 | 100 | 4 | long | GFX_code; |
26 | 104 | 4 | long | is_locked;// cannot be dragged while locked |
27 | 108 | 4 | long | exclude_from_terrain_shadow; |
28 | 112 | 4 | long | exclude_from_walk; |
29 | 116 | 4 | long | magic_item_code; |
30 | 120 | 4 | long | particle_effect_code; |
31 | 124 | 4 | long | dead_mesh_slot;// used to store a mesh slot number in the SAVED project |
32 | 128 | 4 | long | Mesh *pDeadMesh; //long dead_mesh; //void * |
33 | 132 | 4 | long | light; |
34 | 136 | 4 | long | light_radius; |
35 | 140 | 4 | long | light_ambient; |
36 | 144 | 4 | long | Padding |
37 | 148 | 4 | long | Padding |
ID: "TERR" data block layout
Offset | Size | Type | Description |
0 | 4 | "TERR" | TERR (terrain?) data block identifier |
4 | 4 | Long | Size of data block ("BSIZE", never less than 1) |
8 | 4 | Long | Number of TERR data entries ("T") |
12 | 4 | Long | Size of each TERR data entry ("TSIZE", always 200?) |
T×TSIZE bytes: composite data records. See separate description below. |
TERR Data record format not yet mapped!
ID: "ATTR" block data layout
from "Rob"
I figured out a bit of the ATTR block: ATTR { char[4] ’ATTR’ long BLOCK_SIZE long WIDTH_OF_MAP long HEIGHT_OF_MAP } Then follows (width*height/2) bytes. Each tile has 4 bits of attributes and these are packed 2 to a byte. Then there are 64 bytes, of which I can’t make out much. It looks like this is line-of-sight / accessibility information. attributes are 0 for ’ordinary’ tiles, 2 for blocked ( i.e. unit can’t move there ), 8 for passable water (such as fords), 10 (8|2) for impassible water. There are probably more attribute values, its just a matter of looking at all the maps. I made a program which spits out the data as a bitmap, so I can just compare to the minimaps. |
ID: "EXCL" data block layout
List of all the Maps EXCL items http://img692.imageshack.us/i/pyraexcldifference2.jpg/
B5_01 has 33 EXCL items and B5_01B has 30 maybe something to do with Jewel?
and even replaced the EXCL block of B5_01 into B5_01B but did nothing I could detect and didn't
crash game.
http://img98.imageshack.us/i/pyraexcldifference3.jpg/
B5_01 has a different Value of 04 where as B5_01B had 01 towards the end of its EXCL block.
ID: "MUSC" data block layout
ID: "TRAC" data block layout
ID: "EDIT" data block layout
Graphical effects
From filenames
Water movement, etc: animated texture coordinates Transparency Particle effects from settings in the INST block Water vapour (waterfalls etc) Dust Fire Explosions