06 May 2022, 00:52 | #21 |
research
Join Date: May 2018
Location: Germany
Posts: 21
|
Finished the structural analysis of the secondary blocks of the mission track data.
No member names and not that many comments yet - but it might already help you to get started. Here is some pseudo-code: Code:
#ifndef DOGSOFWAR_TRACKDATA_DOWMIS_H #define DOGSOFWAR_TRACKDATA_DOWMIS_H #if !defined(__GHIDRA__) #include <stdint.h> #define byte uint8_t #define sbyte int8_t #define word uint16_t #define sword int16_t #define dword uint32_t #define sdword int32_t #endif /* org #$06AAF8 */ /* * Many Dogs of War track data blocks start with this header. * This is not specific to the misson data and is completely * ignored by the game (included here for documentation only). * The misson track data starts with a struct DowMisDataInfo. * All later offsets are relative to the data (offset 0x001C). */ struct DowMisDataTrackHead { byte bras_data[2]; /* bra.s data */ byte data_size[4]; /* big-endian */ byte reserved[22]; /* dcb.b 22,0 */ #if defined(__GHIDRA__) || \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) byte data[]; #endif }; struct DowMisDataInfo { word data1[4]; /* relative offset to struct DowMisData1Misc */ word data2[7]; /* relative offset to struct DowMisData2Head */ }; /******************************************************************************/ /* * The data1 blocks start with this (unused?) block (the game * just skips it and starts reading data at offset + 0x0040). */ struct DowMisData1Misc { dword reserved[16]; /* dcb.l 16,$00000024 */ }; /*TODO: data1 analysis */ /******************************************************************************/ struct DowMisData2End { byte end_tag; /* $FF */ }; enum DowMisData2_OpCode #if defined(__GHIDRA__) || (defined(__cplusplus) && (__cplusplus >= 201103L)) : byte #endif { /* struct DowMisData2_EAEF */ DowMisData2_OpCode_EAEFMin = 0xEA, DowMisData2_OpCode_EAEFMax = 0xEF, /* struct DowMisData2_F0F5 */ DowMisData2_OpCode_F0F5Min = 0xF0, DowMisData2_OpCode_F0F5Max = 0xF5, /* struct DowMisData2_FE */ DowMisData2_OpCode_FE = 0xFE, /* struct DowMisData2_FD */ DowMisData2_OpCode_FD = 0xFD, /* struct DowMisData2_F6FC */ DowMisData2_OpCode_F6FCMin = 0xF6, DowMisData2_OpCode_F6FCMax = 0xFC, /* struct DowMisData2 (default case) */ DowMisData2_OpCode_Min = 0x00, DowMisData2_OpCode_Max = 0x6F /* everything else is undefined behaviour */ }; struct DowMisData2Head { byte seq_num; /* game starts with $B8, stops with $FF */ byte op_code; /* enum DowMisData2_OpCode */ }; struct DowMisData2Data { byte field0_0x0; byte field1_0x1; }; struct DowMisData2 { struct DowMisData2Head head; /* op_code = index into a game offset table */ struct DowMisData2Data data; }; struct DowMisData2_EAEFData { byte field0_0x0; byte field1_0x1; }; struct DowMisData2_EAEF { struct DowMisData2Head head; struct DowMisData2_EAEFData data; }; struct DowMisData2_F0F5Data { byte field0_0x0; byte field1_0x1; byte field2_0x2; byte field3_0x3; byte field4_0x4; byte field5_0x5; }; struct DowMisData2_F0F5 { struct DowMisData2Head head; struct DowMisData2_F0F5Data data; }; struct DowMisData2_F6FCData { byte field0_0x0; byte field1_0x1; byte field2_0x2; byte field3_0x3; byte field4_0x4; }; struct DowMisData2_F6FCItemData { byte field0_0x0; byte field1_0x1; byte field2_0x2; byte field3_0x3; }; union DowMisData2_F6FCItem { struct DowMisData2End end; struct DowMisData2_F6FCItemData data; }; struct DowMisData2_F6FC { struct DowMisData2Head head; struct DowMisData2_F6FCData data; #if defined(__GHIDRA__) || \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) struct DowMisData2_F6FCItem item[]; #endif }; struct DowMisData2_FDData { byte field0_0x0; byte field1_0x1; byte field2_0x2; byte field3_0x3; }; struct DowMisData2_FD { struct DowMisData2Head head; struct DowMisData2_FDData data; }; struct DowMisData2_FEData { byte field0_0x0; byte field1_0x1; byte field2_0x2; }; struct DowMisData2_FE { struct DowMisData2Head head; struct DowMisData2_FEData data; }; #if !defined(__GHIDRA__) #undef byte #undef sbyte #undef word #undef sword #undef dword #undef sdword #endif #endif /* DOGSOFWAR_TRACKDATA_DOWMIS_H */ |
11 May 2022, 08:19 | #22 |
research
Join Date: May 2018
Location: Germany
Posts: 21
|
The default branch (0x00..0x6F) is the infantry. It inserts a new foot troops soldier (only if there is one of the 25 slots free) at a given position with a specific behavior.
Code:
enum DowMisData2_SeqNum { DowMisData2_SeqNum_Start = 0xB8, DowMisData2_SeqNum_End = 0xFF }; enum DowMisData2_OpCode1 { /* struct DowMisData2_EAEF */ DowMisData2_OpCode1_EAEFMin = 0xEA, DowMisData2_OpCode1_EAEFMax = 0xEF, /* struct DowMisData2_F0F5 */ DowMisData2_OpCode1_F0F5Min = 0xF0, DowMisData2_OpCode1_F0F5Max = 0xF5, /* struct DowMisData2_FE */ DowMisData2_OpCode1_FE = 0xFE, /* struct DowMisData2_FD */ DowMisData2_OpCode1_FD = 0xFD, /* struct DowMisData2_F6FC */ DowMisData2_OpCode1_F6FCMin = 0xF6, DowMisData2_OpCode1_F6FCMax = 0xFC, /* struct DowMisData2Foot (default case) */ DowMisData2_OpCode1_FootMin = 0x00, DowMisData2_OpCode1_FootMax = 0x6F /* everything else is undefined behaviour */ }; struct DowMisData2Head { uint8_t seq_num; /* enum DowMisData2_SeqNum */ uint8_t opcode1; /* enum DowMisData2_OpCode1 */ }; struct DowMisData2FootData { /* foot.pos_x = dow_byte_range_to_short(data.pos_x, 0xBF) + 100 */ /* foot.pos_x[36,291] = data.pos_x[-64,191] + 100 */ uint8_t pos_x; /* foot.pos_y = dow_byte_range_to_short(data.pos_y, 0xB0) + 100 */ /* foot.pos_y[21,276] = data.pos_y[-79,176] + 100 */ uint8_t pos_y; }; struct DowMisData2Foot { struct DowMisData2Head head; struct DowMisData2FootData data; }; Code:
/* game code (two's complement): int16_t s = (u_max < b) ? (int8_t)b : b */ short dow_byte_range_to_short(unsigned char b, unsigned char u_max) { return (b <= u_max) ? (short)b : (short)((short)b - (short)0x100); }; /* int16_t s = (int8_t)b */ short dow_byte_to_short(unsigned char b) { return dow_byte_range_to_short(b, 0x7F); }; (too big - attached) As usual, 0xFF means 'end' (active soldier is removed from list). 0xFD and 0xFE is shooting (bullet vs. grenade). 0xFC starts a new movement: Code:
struct { uint8_t opcode2; /* 0xFC */ uint8_t step_count; int8_t step_x; int8_t step_y; }; Code:
struct { uint8_t opcode2; /* 0x00..0x0C */ uint8_t reserved; /* not used */ uint8_t step_count; int8_t step_x; int8_t step_y; }; Last edited by NicoDE; 11 May 2022 at 11:00. |
11 May 2022, 17:07 | #23 |
research
Join Date: May 2018
Location: Germany
Posts: 21
|
To resolve the mystery about the opcode2 in the foot definition item:
Code:
/* first byte in a dow_mis_foot_action[] entry is an index into this table */ /* the value in this table is a relativ offset into dow_mis_foot_shoot[] */ unsigned short dow_mis_foot_shoot_offset[0x0C - 0x00 + 1] = { 0x0000, 0x0009, 0x0016, 0x0023, 0x002B, 0x0034, 0x0035, 0x0036, 0x003F, 0x004C, 0x0059, 0x0061, 0x006A }; /* references to dow_mis_foot_shoot_info[].shoot_op, */ /* where 0xFF means repeat from the initial offset */ unsigned char dow_mis_foot_shoot[0x006B] = { /* clang-format off */ /* [0x00] = 0x0000 */ 0x00, 0x00, 0x01, 0x02, 0x03, 0x03, 0x02, 0x01, 0xFF, /* [0x01] = 0x0009 */ 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0xFF, /* [0x02] = 0x0016 */ 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0xFF, /* [0x03] = 0x0023 */ 0x0C, 0x0C, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0xFF, /* [0x04] = 0x002B */ 0x0C, 0x0C, 0x0C, 0x23, 0x23, 0x24, 0x25, 0x26, 0xFF, /* [0x05] = 0x0034 */ 0xFF, /* [0x06] = 0x0035 */ 0xFF, /* [0x07] = 0x0036 */ 0x11, 0x11, 0x12, 0x13, 0x14, 0x14, 0x13, 0x12, 0xFF, /* [0x08] = 0x003F */ 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0xFF, /* [0x09] = 0x004C */ 0x19, 0x19, 0x19, 0x1A, 0x1A, 0x1A, 0x1B, 0x1B, 0x1B, 0x1C, 0x1C, 0x1C, 0xFF, /* [0x0A] = 0x0059 */ 0x1D, 0x1D, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0xFF, /* [0x0B] = 0x0061 */ 0x1D, 0x1D, 0x1D, 0x23, 0x23, 0x24, 0x25, 0x26, 0xFF, /* [0x0C] = 0x006A */ 0xFF /* clang-format on */ }; struct DowMisFootShootInfo { short shoot_op; short step_x; short step_y; short rel_x; /* relative to foot object pos_x */ short rel_y; /* relative to foot object pos_y */ } const dow_mis_foot_shoot_info[24] = { {0x00, 0, 3, 4, 15}, {0x01, 0, 3, 4, 15}, {0x02, 0, 3, 4, 15}, {0x03, 0, 3, 4, 15}, {0x08, -3, 0, 0, 10}, {0x09, -3, 0, 0, 10}, {0x0A, -3, 0, 0, 10}, {0x0B, -3, 0, 0, 10}, {0x04, 3, 0, 15, 10}, {0x05, 3, 0, 15, 10}, {0x06, 3, 0, 15, 10}, {0x07, 3, 0, 15, 10}, {0x11, 0, 3, 8, 15}, {0x12, 0, 3, 8, 15}, {0x13, 0, 3, 8, 15}, {0x14, 0, 3, 8, 15}, {0x19, -3, 0, 0, 10}, {0x1A, -3, 0, 0, 10}, {0x1B, -3, 0, 0, 10}, {0x1C, -3, 0, 0, 10}, {0x15, 3, 0, 15, 10}, {0x16, 3, 0, 15, 10}, {0x17, 3, 0, 15, 10}, {0x18, 3, 0, 15, 10} }; |
13 May 2022, 08:52 | #24 |
research
Join Date: May 2018
Location: Germany
Posts: 21
|
While making progress in the background, I want to document one of the easier ones: opcode1 0xFE inserts/sets the target (the mission is completed when colliding with it).
Code:
struct DowMisData2TargetData { /* target.pos_x = dow_byte_range_to_short(data.pos_x, 0xBF) + 100 */ uint8_t pos_x; /* target.pos_y = dow_byte_range_to_short(data.pos_y, 0xB0) */ uint8_t pos_y; /* index into image data */ uint8_t image_id; }; struct DowMisData2Target { struct DowMisData2Head head; /* opcode1 = 0xFE */ struct DowMisData2TargetData data; }; ps: if TIMBO has been entered, you can directly complete the mission with P pps: seems like there are objects hidden in the mission terrain data - I changed the mission 7 (Florida) event table offsets from 17BB1AED19681B92160A1C3B14B400 to 0016001600160016001600160016FF (immediately end all event lists), and there is still one gun turret in the level (see attached screenshot)... Last edited by NicoDE; 13 May 2022 at 10:27. |
13 May 2022, 10:23 | #25 |
Registered User
Join Date: Jun 2012
Location: mt meru / hyberborea
Posts: 33
|
wow, this is truly amazing work, thanks a lot!
/me fires up editor to hack this into openDOW // edit if i can figure this all out - do you happen to be in the IRC at times ? Last edited by rofl0r; 13 May 2022 at 10:38. |
13 May 2022, 10:29 | #26 |
This cat is no more
Join Date: Dec 2004
Location: FRANCE
Age: 52
Posts: 8,160
|
great collaboration guys!
I suggest that you keep the format of the original data when creating the editor, so you can re-inject the new levels in the original engine with a hacked whdload slave. |
13 May 2022, 10:37 | #27 |
research
Join Date: May 2018
Location: Germany
Posts: 21
|
There might be still more research required. The game seems to use different coordinates/offsets for object/player positions/collision/drawing... but that can be clarified later when all object types and properties are analyzed...
ps: well, there is even code to draw bullets differently per mission (I guess due to dark/bright terrain). pps: seems with TIMBO active you can use 1-9 to play the different SFX, and besides F5 (toggle invisibility) and SPACE (hold to display back buffer) there seems to be another hidden debug toggle F10 (found no usage yet) - don't press press TAB or NUM-RETURN with TIMBO active ;-) Last edited by NicoDE; 13 May 2022 at 10:44. |
25 May 2022, 15:09 | #28 | |
research
Join Date: May 2018
Location: Germany
Posts: 21
|
Quote:
The unsigned PCM data shouldn't be directly used in a WAV container (it uses the full byte range, therefore the values and the midpoint are out-of-spec). The best container format for preservation would be 8SVX (using the signed/xor-ed data). Everything else should properly emulate, resample and filter the data during conversion. |
|
Currently Active Users Viewing This Thread: 1 (0 members and 1 guests) | |
Thread Tools | |
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Dogs of War -like game | Firestone | Looking for a game name ? | 19 | 06 January 2016 16:50 |
Comment from Dogs of War coder on Youtube | laffer | Retrogaming General Discussion | 7 | 29 September 2009 00:16 |
Dogs of War longplay | laffer | Retrogaming General Discussion | 3 | 22 March 2008 00:43 |
Dogs Of War | Macdilla | New to Emulation or Amiga scene | 3 | 25 January 2008 19:16 |
Dogs of War | lhate56k | support.Games | 0 | 28 August 2004 16:33 |
|
|