DO/PRJ
From Dark Omen Wiki
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 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 |
7 | “ATTR”-block | Block - Terrain attributes? |
8 | “EXCL”-block | Block - Exclusions??? |
9 | “MUSC”-block | Block - Music? |
10 | “TRAC”-block | Block -??? |
11 | “EDIT”-block | Block -??? |
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.
TODO: Add Wiki Syntax
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”). 2.2 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
2.3 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
Offset Size Type Description 0 4 Long Size of string data 4 String size String Filename of furniture object 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.
2.4 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 o The orientation vector is not normalised o 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
2.5 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!
2.6 ID: “ATTR” block data layout
“Rob” from http://darkomenworld.freeforums.org/looking-at-the-data-files-t33-15.html 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.