GBA Doom 2 - .XM / .MOD Extracted Tracker Modules

Kippykip

CEO of cancelled
THE ONE
Some very interesting info about GBA Doom 2 once again.
About nearly a decade ago I reverse engineered some of GBA Doom 2, but there were a couple mysteries I never quite figured out.
* Maps (which was later cracked by Randy87)
* Compressed graphics
* Fonts
* Audio

Claude once again, was able to figure out a lot more details since it can easily reverse engineer code itself. Turns out there's some kind of Torus "DPAK" file, similar to a WAD.
It was able to convert the music format back into a tracker module.
Here's the notes:
Code:
GBA Doom II - Music & Sound Format Notes
=========================================
Reverse engineered from DoomII_Recomp.gba (the player code lives at 0x0802F000-0x08031900).
All offsets below are file offsets into DoomII_Recomp.gba unless stated otherwise.
All values are little endian.

Short version
-------------
The music is ProTracker MOD music. Torus converted the original MODs into their own packed
container and plays them with a ProTracker-style replayer (same Amiga period table, same effect numbers, same speed/tick model).
That makes it a near 1:1 fit for an .XM module.


1. Where it lives: the "DPAK" archive
-------------------------------------
At 0x37CA4 there is a resource directory:

  0x00  4 bytes  "DPAK"
  0x04  2 bytes  0x0040 (unknown, maybe version or max entries)
  0x06  10 bytes "Torus" + zero padding
  0x10  entries, 16 bytes each, 66 of them:
          4 bytes  type
          4 bytes  offset (relative to the start of "DPAK")
          4 bytes  size
          4 bytes  always 0

  Types seen: 1 = small table, 2 = misc tables, 3/4 = game data, 5 = 56 level/graphic chunks,
              6 = big graphics block (0xCE9938 bytes), 7 = MUSIC ("MUSC" chunk, 0x234DAC bytes).

The type 7 entry points at 0xDC1AB8 in this ROM. That is the music chunk.


2. The MUSC chunk (starts at 0xDC1AB8)
--------------------------------------
  0x00  4 bytes  "MUSC"
  0x04  4 bytes  offset to the SONG TABLE        (0x0F27C4)  - relative to "MUSC"
  0x08  4 bytes  offset to the MUSIC SAMPLE BANK (0x0F210C)  - relative to "MUSC"
  0x0C  4 bytes  offset to the SFX SAMPLE BANK   (0x16CFF8)  - relative to "MUSC"
  0x10  ...      raw music sample data (8-bit signed PCM) up to the music sample bank.

The game checks the "MUSC" magic before playing (code at 0x0802FD52 / 0x0802FDD2).


3. Sample banks (music bank and SFX bank use the same layout)
-------------------------------------------------------------
  4 bytes  count (SFX bank: 98 = exact entry count. Music bank: says 120, but 143 entries
           are actually present up to the song table and songs use instruments 1-143.)
  then 12-byte entries, one per instrument:

  0x00  int32   (offset << 8) | flags
                 - offset = signed (value >> 8), RELATIVE TO THIS ENTRY'S OWN ADDRESS,
                   points at 8-bit signed PCM.
                 - low byte: loop count. Music = 0 (normal), SFX = 1 (play once).
  0x04  uint16  sample length in WORDS (x2 = bytes)            <- same as MOD
  0x06  uint8   finetune (0-15, MOD style: 8-15 = -8..-1)      <- same as MOD
  0x07  uint8   default volume (0-64)                         <- same as MOD
  0x08  uint16  loop start in WORDS                           <- same as MOD
  0x0A  uint16  loop length in WORDS, 0 = no loop             (MOD uses 1 for "no loop")

  Music bank: 134 of the 143 slots are used, 9 are empty (all zero). Several instruments share
  the same sample data. 46 instruments loop.

  Sample rate: same as the original Amiga MOD. The player's pitch maths (see section 6)
  plays period 428 (ProTracker C-2) at ~8312 Hz, Amiga PAL is ~8287 Hz. The old "12000hz?"
  guess in Documentation.txt was the right ballpark; the real rate depends on the note.


4. Song table
-------------
  4 bytes   number of songs (19)
  19 x 4    offset of each song, relative to the start of the song table (the count field)

  Song header (8 bytes):
  0x00  4 bytes  0x00145768 in every song (unknown, ignored by the player as far as I can see)
  0x04  1 byte   song length (number of order entries)
  0x05  1 byte   number of patterns
  0x06  1 byte   number of channels (4, 5 or 6)
  0x07  1 byte   0x77 in every song (unknown)
  0x08  order list: song length x uint16. Each entry is the BYTE OFFSET of that pattern,
        relative to the start of the song header. (So pattern number =
        (offset - (8 + 2 x length)) / (channels x 256).)
  then  the patterns, unpacked.

  Each pattern: 64 rows x channels x 4 bytes. Row-major (row 0 ch0, row 0 ch1, ...).

  Songs found (index: orders / patterns / channels / instruments used):
     0: 42/34/6  ins 1-6        10: 19/15/5  ins 74-81
     1: 13/12/6  ins 7-15       11:  8/ 8/4  ins 82-83
     2: 61/46/4  ins 16-22      12: 26/26/4  ins 84-91
     3: 24/11/4  ins 23-29      13: 13/13/5  ins 92-100
     4: 16/10/4  ins 30-38      14: 37/36/5  ins 101-109
     5: 24/20/5  ins 39-42      15: 68/49/5  ins 110-118
     6: 40/28/4  ins 43-50      16: 20/19/4  ins 120-125
     7: 30/30/4  ins 51-58      17: 20/19/4  ins 126-128
     8: 46/22/5  ins 59-66      18:  2/ 2/6  ins 130-143
     9: 35/26/4  ins 67-73
  Which song plays on which map is not mapped yet.


5. Pattern cell (4 bytes)
-------------------------
  byte 0  high nibble = effect command (0-F, ProTracker numbering)
          low nibble  = ignored by the player (leftover junk from conversion)
  byte 1  instrument (1-143, index into the MUSIC bank; 0 = none)
  byte 2  note (1-72; 0 = none). Index into the period table below:
          note 1 = period 1712, note 13 = 856 (PT C-1), note 25 = 428 (PT C-2), ...
          i.e. one octave lower than ProTracker's lowest, 6 octaves total.
  byte 3  effect parameter

  Effects actually implemented by the player:
    0xy  arpeggio                 9xx  sample offset (xx*256)                     
    1xx  porta up                 Axy  volume slide
    2xx  porta down               Bxx  position jump
    3xx  tone portamento          Cxx  set volume
    4xy  vibrato                  Dxx  pattern break (row is plain hex, NOT decimal like PT)
    5xy  tone porta + vol slide   Fxx  set speed (only 01-1F; tempo values 20+ are IGNORED)
    6xy  vibrato + vol slide
    7xy  tremolo
    E1x/E2x fine porta, E4x vibrato waveform, E6x pattern loop, E7x tremolo waveform,
    E9x retrigger, EAx/EBx fine volume slide, ECx note cut, EEx pattern delay.
  Not implemented (no-ops): 8xx panning, E0x, E3x, E5x (set finetune), E8x, EDx (note delay), EFx.


6. Player / mixer facts
-----------------------
  - Sound init:   0x0802FC70 (called from 0x0800C764 with mode 0)
  - Hardware setup: 0x08030308. Direct Sound A + B, fed by DMA1/DMA2 from two 0x200-byte
    double buffers. Timer 0 drives the sample rate.
  - Mode 0 (what the game uses): Timer0 reload 0xFAE1 -> 16777216 / 1311 = ~12797 Hz mix rate.
    (Mode 1 = 0xFBB6 -> ~15280 Hz, unused.)
  - The mixer renders 256 samples per call and runs one replayer tick per call:
    12797 / 256 = ~50 ticks/sec = ProTracker's 125 BPM. Tempo is fixed, only speed changes.
    Default speed = 6.
  - Pitch: step = (139 << 16) / period   (139 = mode 0 constant at 0x0803037C)
    The mixer (ARM, copied to IWRAM, ROM copy at 0x08FFB8F4) keeps the sample position as
    17.15 fixed point and adds step once per output sample, nearest-neighbour, no interpolation.
    -> playback Hz = 139 * 2 * 12797 / period = ~3,557,600 / period
    (Amiga PAL is 3,546,895 / period, so same rate, 0.3% sharp.)
    [Earlier revision of this file wrongly read the position as 16.16 and said "half rate".]
  - Period table: 0x08030D08, 16 finetunes x 72 notes x uint16 (finetune * 0x90 bytes).
    Vibrato sine table at 0x08030CE8, arpeggio tick table at 0x08030CC8.
  - Row effects jump table 0x080309B0, tick effects 0x08030B50, Exx row effects 0x08030A60.
  - Stereo: channels alternate between the two Direct Sound buffers (even channels -> buffer
    A, odd -> buffer B), so it is hard-panned like an Amiga. Which side A is on is unverified.


7. Converting to a tracker module (gbad2_music2xm.py)
------------------------------------------------------
  python3 gbad2_music2xm.py DoomII.gba out_folder [--format xm|mod|both] [--pan 0-100] [--sfx]

.MOD (closest to the GBA playback rules):
  - the game's own period values and the samples go straight in, unchanged
  - effects copied as-is apart from the no-ops being removed and Dxx hex -> decimal
  - limitation: trackers force Amiga LRRL panning on MODs; the GBA alternates L/R per channel

.XM (correct panning, original samples untouched):
  - note n -> XM note n+24, finetune nibble x16 minus 14 (folds in the ~11 cent offset)
  - channel panning applied via the volume column whenever an instrument is triggered
  - FastTracker 2 effect rules differ slightly from ProTracker's (effect memory, vibrato),
    the converter strips 100/200/A00 etc. to cover the main differences

Both formats were checked in libopenmpt: every song loads, XM and MOD versions have identical
lengths (0:15 to 5:10) and render at the same pitch.

SFX (--sfx) are written as 8000 Hz WAVs; that rate is the old Documentation.txt guess and is
not verified against the SFX player code.

A bit more info on this "DPAK" format:

Code:
GBA Doom II - Torus "DPAK" Archive & "FPS_RAW_FILE" Notes
==========================================================
Reverse engineered from DoomII_Recomp.gba (game code A9DP52).
All values are little endian. Offsets are written as HEX/Decimal like the old Documentation.txt.
"ROM" offsets are file offsets into the .gba. "FRF+" offsets are relative to the start of FPS_RAW_FILE.
Confidence tags: [CONFIRMED] = checked against the data, [GUESS] = looks right but not proven.

Short version
-------------
Yep, the whole game is basically one big Torus .wad. There's a "DPAK" directory at 37CA4/228516,
and one of its entries is "FPS_RAW_FILE", a 13 MB blob that holds pretty much the entire 3D engine's data:
palettes, colourmaps, levels, thing tables, the graphic directory, ALL the wall/flat/sprite pixel data,
math tables and the English text.

And that magic number everyone had to add, 329CC0/3316928? It's not magic at all:

    DPAK start          37CA4
  + DPAK entry 61 offset A04DC     -> FPS_RAW_FILE starts at D8180/885120
  + FPS_RAW_FILE header field 0x48 = 251B40
  = 329CC0   <- the "graphics data base"

So the old tools were extracting things correctly, they were just using a hardcoded result of
three lookups the game does at runtime. The game code holds one literal pointer, 08037CA4 (stored at
ROM 2CED4), and finds everything else through the DPAK and FPS_RAW_FILE headers.
This is the part that should carry over to other Torus engine GBA games: find "DPAK" + "Torus",
follow the type 6 entry, read the FPS_RAW_FILE header, and you get the base for free.


1. The DPAK directory (ROM 37CA4/228516) [CONFIRMED]
------------------------------------------------------
  0x00  4 bytes   "DPAK"
  0x04  2 bytes   0x0040/64 = number of used entries (64 used, then 2 blank slots)
  0x06  10 bytes  "Torus" + zero padding
  0x10  66 entries, 16 bytes each:
          4 bytes  type
          4 bytes  offset (relative to the "DPAK" text, NOT the start of the rom)
          4 bytes  size
          4 bytes  always 0
        Type 0 = blank slot (entries 64 and 65).

  #      Type  ROM offset        Size       What it is
  0-2    2     380B4, 38F9C,     EE8,1B08,  Font/misc tables
                 3AAA4             1B40
  3      3     3C5E4             31D98      2D tile graphics + 16 colour palettes (see section 5)
  4      4     6E37C             DEA4       "d2weapons": named tint palettes, "StructureEnglish"
  5-60   5     7C220 - D7484     varies     56 full 2D images (title, intermission, BOSSBACK etc, section 4)
  61     6     D8180             CE9938     FPS_RAW_FILE (section 2 & 3)
  62     7     DC1AB8            234DAC     MUSC music + sounds (see GBA_Doom2_Music_Format.txt)
  63     1     FF6864            6D8        Small u32 table

To get a ROM offset from an entry: 37CA4 + entry offset.


2. FPS_RAW_FILE header (ROM D8180/885120)
-----------------------------------------
"RAW FILE" is a good name for it. It's laid out like a memory image of one big struct, with every pointer
stored as an offset from the start of FPS_RAW_FILE. It even ends with a footer that says "ENDOFRAW".

  FRF+   Value      Meaning
  0x00   0000029A   666. Magic number. Of course it's 666.            [CONFIRMED]
  0x04   12 bytes   "FPS_RAW_FILE"                                     [CONFIRMED]
  0x10   000001F5   501   unknown count
  0x14   00000451   1105  unknown count
  0x18   00000769   1897  count of u32s in the thing table below? (7588 bytes / 4 = 1897 exactly) [GUESS]
  0x1C   00213390   -> Thing/sprite definition table                     (section 3.6)
  0x20   00215134   -> 12 byte record table, probably states/frames       (section 3.7)
  0x24   0000000F   15 = count for the next table                        [CONFIRMED]
  0x28   00217BAC   -> 15 records of 44 bytes, probably weapon info       (section 3.8)
  0x2C   00000E73   3699 = last index of the graphic directory (3700 entries, 0 to 3699) [CONFIRMED by size]
  0x30   00217E40   -> GRAPHIC DIRECTORY (the old "file listings")         (section 3.9)
  0x34   00000022   34 = number of levels                                [CONFIRMED]
  0x38   00037D8C   -> LEVEL DIRECTORY                                    (section 3.5)
  0x3C   FFFFFFFF   unused slot (FFFFFFFF = null pointer)
  0x40   0000568C   -> 96 RGB palettes (PLAYPAL)                          (section 3.3)
  0x44   0001768C   -> Colourmap / light tables                           (section 3.4)
  0x48   00251B40   -> GRAPHICS DATA BASE. 329CC0 comes from here!       [CONFIRMED]
  0x4C   00CDB984   -> end of graphics data / free space after it         (section 3.10)
  0x50   FFFFFFFF   unused
  0x54   FFFFFFFF   unused
  0x58   00CDF350   -> u16 lookup table, 1024 entries                     (section 3.11)
  0x5C   00CDFB50   -> u32 reciprocal table                               (section 3.11)
  0x60   00000001   number of languages (1)                              [CONFIRMED]
  0x64   00CE1B58   -> language list ("ENGLISH")                          (section 3.12)
  0x68   00000176   374 = number of text strings                         [CONFIRMED]
  0x6C   00CE1B70   -> string table                                       (section 3.12)
  0x70   FFFFFFFF   unused
  0x74   00CE4F0C   -> "21-\" records                                     (section 3.13)
  0x78   00CE5418   -> mostly FF block                                    (section 3.13)
  0x7C   00000004   unknown
  0x80   00CE9928   -> footer ("ENDOFRAW")                                [CONFIRMED]
  0x84   0000000B   unknown
  0x88   00000000   unknown
  0x8C   FFFFFFFF   unused
  0x90   000000AC   -> small table right after the header                 (section 3.1)
  0x94   00000308   -> u32 reciprocal table                               (section 3.2)
  0x98   00004508   -> 256 byte table
  0x9C   00004608   -> 1024 x u32 table
  0xA0   00005608   -> small 0x84 byte block
  0xA4   FFFFFFFF   unused
  0xA8   FFFFFFFF   unused

Every pointer above, as a ROM offset (D8180 + value):
  AC->D822C  308->D8488  4508->DC688  4608->DC788  5608->DD788  568C->DD80C  1768C->EF80C
  37D8C->10FF0C  213390->2EB510  215134->2ED2B4  217BAC->2EFD2C  217E40->2EFFC0  251B40->329CC0
  CDB984->DB3B04  CDF350->DB74D0  CDFB50->DB7CD0  CE1B58->DB9CD8  CE1B70->DB9CF0  CE4F0C->DBD08C
  CE5418->DBD598  CE9928->DC1AA8


3. What's inside FPS_RAW_FILE
-----------------------------

3.1 FRF+AC, ROM D822C (0x25C bytes) [GUESS]
  Starts with 0x25A then ~150 u32s, mostly small even numbers with FFFFFFFF holes.
  Looks like a hash/bucket table or an index remap. Not decoded.

3.2 Math tables, FRF+308 to FRF+568C [CONFIRMED that they're math tables]
  FRF+308  u32 reciprocals: 0, 80000, 40000, 2AAAA, 20000, 19999... = (1/n) in 13.19 fixed point.
  FRF+4508 256 bytes of slowly rising values (gamma/sqrt style curve).
  FRF+4608 1024 x u32 reciprocal style table.
  FRF+5608 0x84 byte block (small header then FF padding).

3.3 Palettes, FRF+568C, ROM DD80C/907276 [CONFIRMED]
  96 x 768 byte RGB palettes, exactly 0x12000 bytes. These are the 96 PLAYPALs from the old docs.

3.4 Colourmaps, FRF+1768C, ROM EF80C/981004 [GUESS]
  0x20700 bytes of palette index remaps that run up to the level directory.
  Not the PC COLORMAP layout (row 0 isn't an identity map), so the light levels are probably
  arranged differently. Needs a proper look.

3.5 Level directory, FRF+37D8C, ROM 10FF0C/1113868 [CONFIRMED layout, contents partly]
  34 entries of 16 bytes (32 maps + 2 extra, probably the link-cable deathmatch maps):
    4 bytes  offset of a "ZW" compressed block (main level data)
    4 bytes  a count (MAP01 = 0x17B, MAP02 = 0x451 ...)
    4 bytes  offset of a bitfield block (reject style?)
    4 bytes  offset of a u16 list (rising values, looks like an index/blockmap list)
  All offsets are FRF+ relative. The level data runs from FRF+37FAC up to FRF+213390.
  Each level starts with a run of 16 byte records (FFFFFFFF 00000000 FFFFFFFF FFFFFFFF ...).
  The main block starts with "ZW" + decompressed size, e.g. MAP01: "5A 57 5C 28 01 00" = 0x1285C bytes.

  "ZW" is Torus' own compression (also used by the type 5 images below). Not cracked yet.
  Cracking ZW is the key to editing levels.

3.6 Thing/sprite definitions, FRF+213390, ROM 2EB510 [GUESS on meaning]
  0x1DA4 bytes. Records 52 (0x34) bytes apart, each one holds a 4 letter PC sprite name
  (AMMO, BROK, SBOX, CELP, CELL, CLIP, ROCK, SHEL, ARM2 ... TELE, BSTG) at +0x28 from where I'd guess
  the record starts. Lines up with Doom's mobj/sprite list.

3.7 FRF+215134, ROM 2ED2B4 [GUESS]
  12 byte records: "01 00 01 00 00 00 00 00 00 00 00 00", "01 00 07 00 00 00 01 00 ..."
  The 2nd number goes up by 6 each record. Probably the state/frame table.

3.8 FRF+217BAC, ROM 2EFD2C [CONFIRMED count/size]
  15 records x 44 bytes (count is header field 0x24). Probably weapon definitions.

3.9 GRAPHIC DIRECTORY, FRF+217E40, ROM 2EFFC0/3080128 [CONFIRMED]
  This is the "file listings" from the old Documentation.txt, but the old tools started it 4 bytes
  too early, at 2EFFBC. 3700 records x 64 bytes, running up to exactly the graphics data base.
  So the "4 bytes Unknown, maybe identifier?" at the start of the old .H header is really the
  LAST field of the previous record. Correct layout:

  0x00  4 bytes  Height
  0x04  4 bytes  Width (number of columns, = number of entries in the .LOC offset table)
  0x08  4 bytes  Number of mip levels. 8 for everything, except skies which are 1.
  0x0C  4 bytes  X offset (sprite origin, the "X-Center" from the old docs). 0 on walls/flats.
  0x10  4 bytes  Y offset (sprite origin, top offset like the PC). 0 on walls/flats.
  0x14  4 bytes  X1 offset  \
  ...                         > 8 offsets, add the graphics data base (329CC0) like before
  0x30  4 bytes  X8 offset  /
  0x34  8 bytes  Name. PC style names for walls/flats, Torus names for sprites (BoxBull, bossd1e...).
                 Flats put a "*" in the 8th character, e.g. "FLOOR0_*", "GRASS1" + 00 + "*".
  0x3C  4 bytes  Flags/info, split into bytes:
          byte 0 = type flags. Values seen:
                   46 = sprite (3136 of them)          08 = wall texture (360)
                   00 = flat (108)                     02 = masked mid texture (MIDBARS, SW1EXIT...) (21)
                   18 = animated (FWATER, SLIME) (62)  09 = sky (3)
                   98 = SKINFACE, METAL, ZIMMER... (9) 92 = SKSPINE2 (1)
          byte 1 = varies per graphic (9F, 8C, 66...). Maybe an average colour index for far away/
                   automap drawing [GUESS]
          byte 2 = small signed number, mostly 0 (also 1, -1, -2, -3) [unknown]
          byte 3 = 0

  Example: BoxBull (AMMOA0) is record 0, height 32, width 32, 8 mips, X 16, Y 32, X1 offset 0.
  329CC0 + 0 = the AMMOA0 .LOC table, which is exactly the one the old docs used as the sprite example.
  1579 unique names across 3700 records, the rest are rotation duplicates.

3.10 Graphics data, FRF+251B40 to FRF+CDB984, ROM 329CC0 to DB3B04
  All the .LOC tables and column pixels, same format as in the old docs.
  After it is a mostly FF block up to FRF+CDF350 (free space, a few non-FF bytes at FRF+CDEA84).

3.11 FRF+CDF350 (u16 x 1024: 0,0,0,1,1,1,2,2,3...) and FRF+CDFB50 (u32: 0, 0, 0, 55555555, 0,
  33333333, AAAAAAAA ...). Division/scaling tables for the renderer.

3.12 Text, FRF+CE1B58, ROM DB9CD8 [CONFIRMED]
  Language list: 4 bytes 0, "ENGLISH" + 0, then 0DF0ADBA ("BAADF00D") filler.
  String table at FRF+CE1B70: 374 records of 16 bytes:
    4 bytes  FFFFFFFC
    4 bytes  string ID (1015, 1008, 10A4 ...)
    4 bytes  0
    4 bytes  FRF+ offset of the string
  The strings start at FRF+CE32D0, ROM DBB450. Zero terminated, "\u" at the start = a message
  that goes to the top of the screen ("\uPicked up the armor.", "Entryway", key door messages...).
  This is where you'd change pickup messages and level names.

3.13 FRF+CE4F0C "21-\" records (0x50C bytes, FF padded), then FRF+CE5418 a 0x4510 byte block that's
  nearly all FF. Not decoded.

3.14 Footer, FRF+CE9928, ROM DC1AA8: "C8 00 32 00 32 00 2C 01" + "ENDOFRAW". MUSC starts right after.


4. Type 5 images (DPAK entries 5-60) [CONFIRMED]
------------------------------------------------
  0x00  2 bytes  Width
  0x02  2 bytes  Height
  0x04  2 bytes  0x000C, offset to the palette
  0x06  2 bytes  0x020C, offset to the pixels
  0x08  2 bytes  Flags. 0x0008 = raw pixels, 0x0108 = "ZW" compressed pixels
  0x0A  2 bytes  0x73DD on every image (key colour?)
  0x0C  512 bytes  256 colour RGB555 palette
  0x20C pixels, 8 bit, left to right
  BOSSBACK from the old bin folder (713124/AE1A4) is entry 44's pixels: AE1A4 = ADF98 + 20C.
  Compressed ones start with "ZW" + u32 decompressed size (00009600 = 240x160).


5. Type 3 block (ROM 3C5E4) [GUESS]
-----------------------------------
  u16 3, u16 1, u16 0x0C, u16 0x3C, u16 0x12E0, u16 FFFF, then 12 u32 offsets, then 0x95/149
  16 colour RGB555 palettes (colour 0 is 7FE0, the transparent key). That's GBA tile/OBJ graphics,
  so this is most likely the HUD, status bar, menu and weapon sprites, and where the old docs'
  SPRITE555 palettes ("only uses 1 of 16 colours per line") really come from.
  It is NOT the level data, that's in FPS_RAW_FILE.


6. Notes for other Torus engine games
-------------------------------------
  * Search the ROM for "DPAK" followed by "Torus". The game code holds one pointer to it (here 08037CA4).
  * Entry types look like engine-wide resource types (6 = FPS_RAW_FILE, 7 = MUSC). Check if the
    other games use the same numbers.
  * FPS_RAW_FILE has magic 666 and ends in ENDOFRAW, so its size is easy to verify.
  * The header layout is probably shared between games, but field order could shift if the engine
    version differs. Check that header 0x48 lands right after the graphic directory
    (directory start + count x 64) to confirm.


7. Still to do
--------------
  * Crack "ZW" compression (needed for level data and the compressed full screen images).
  * Decode the level block, thing table (3.6), states (3.7), weapon table (3.8).
  * Colourmap layout (3.4) and the 0xAC table (3.1).
  * Meaning of graphic directory byte 1/2 and flags 0x80/0x10.

Anyway, I've attached the converted modules.
They're compatible in OpenMPT and Milkytracker. Enjoy!
 

Attachments

  • GBAD2_music_converted.zip
    1.9 MB · Views: 3
  • gbad2_music2xm.py
    13.2 KB · Views: 3
Top