The DOSBox Raw OPL (DRO) format is used for storing captured OPL data from a game running in the cross-platform DOS emulator DOSBox.
The file begins with a header:
|BYTE||cSignature||"DBRAWOPL" (not NULL-terminated)|
|UINT16LE||iVersionMajor||Version number (high)|
|UINT16LE||iVersionMinor||Version number (low)|
The rest of the header depends on the version. If iVersionMajor is 5 and iVersionMinor is 6, then the version would be called "5.6" here.
The first version of the DRO Format was used up to and including DOSBox version 0.72. Note that the major and minor field names were swapped beginning with DOSBox 0.73, so this version used to be 1.0, but after DOSBox 0.73 it became 0.1 instead.
The version 0.1 header follows on directly from the main file header.
|UINT32LE||iLengthMS||Length of the song in milliseconds|
|UINT32LE||iLengthBytes||Length of the song data in bytes|
|UINT8||iHardwareType||Flag listing the hardware used in the song|
|UINT8||iHardwareExtra||Rest of hardware type or song data (see below)|
iHardwareType is one of the following values:
In early files, the field was a UINT8, however in most common (recent) files it is a UINT32LE with only the first byte used. Unfortunately the version number was not changed between these revisions, so the only way to correctly identify the formats is to check the three iHardwareExtra bytes. If these are all zero then they can safely be ignored (iHardwareType was a UINT32.) If any of the three bytes in iHardwareExtra are non-zero, then this is an early revision of the format and those three bytes are actually song data.
Directly following the header is iLengthBytes bytes of OPL data. The first byte in the song data will be the OPL register, followed by the byte to send to that register. The "register" can also be one of these control values:
|Register value||Data size||Purpose|
|0x00||1||Delay. The single data byte following should be incremented by one, and is then the delay in milliseconds.|
|0x01||2||Delay. Same as above only there are two bytes following, in the form of a UINT16LE integer.|
|0x02||0||Switch to "low" OPL chip (#0)|
|0x03||0||Switch to "high" OPL chip (#1)|
|0x04||2||Escape - the next two bytes are normal register/value pairs even though the register might be 0x00-0x04|
|Other||1||Anything else should be treated as an OPL register, with the byte following being the data to send to that register.|
The delays all need to be incremented by one, as having a delay of zero milliseconds would be a waste of space. Codes 0x02 and 0x03 switch between two OPL chips in dual OPL2 mode, or between register banks in OPL3 mode. These should only be used if the hardware flag in the header indicates the file is in dual-OPL2 or OPL3 format.
All delays are in milliseconds (which can be treated as 1000Hz if converting between other formats.)
Version 2.0 of the DRO Format was introduced with DOSBox 0.73.
The version 2.0 header also follows on directly from the main file header.
|UINT32LE||iLengthPairs||Length of the song in register/value pairs (TODO: confirm - 1 == one register + one value, or two bytes)|
|UINT32LE||iLengthMS||Length of the song data ("chunk" -?) in milliseconds|
|UINT8||iHardwareType||Flag listing the hardware used in the song|
|UINT8||iCompression||Compression type, zero means no compression (currently only zero is used)|
|UINT8||iShortDelayCode||Command code for short delay (1-256ms)|
|UINT8||iLongDelayCode||Command code for short delay (> 256ms)|
|UINT8||iCodemapLength||Number of entries in codemap table|
|UINT8[iCodemapLength]||iCodemap||Codemap table (see below)|
iHardwareType is one of the following values: (which are different in v1.0 and v2.0)
iFormat is one of the following values:
|0||Commands and data are interleaved (default/as per v1.0)|
|1||Maybe all commands, followed by all data (unused? - TODO: Confirm)|
- iLengthMS is at a different offset in v1.0 and v2.0
- The iShortDelayCode value must have one added to it, as per v1.0 (so a short delay of 2 == a 3ms delay)
- The iLongDelayCode value must have one added to it, then be multiplied by 256 (or "<< 8"), so a long delay of 2 == a 768ms delay
- Because the high bit in each codeword is used to refer to the second OPL2 chip (or the second set of registers in an OPL3), there are only 128 possible codewords (0-127). Therefore iCodemapLength must always be 128 or less.
- DOSBox dumps a snapshot of the current register state at the beginning of a capture. (Any other registers are assumed initialised to zero.) This means that even an OPL2 only song will appear to have some data sent to the second OPL chip, because of this initial snapshot. To really know whether the second OPL chip is in use, you can find out if it is actually making any sound, like so:
- Examine bit 5 (0x20) on registers 0xB0 to 0xB8. If this bit is set, a note is playing and the chip is being used.
- Check whether bitmask 0x1F on register 0xBD is greater than 0x10 (i.e.
if (reg[0xBD] & 0x1F > 0x10) ...). If so, the chip is in percussive mode and a rhythm instrument is being played, so the chip is being used.
The codemap table maps index numbers to OPL registers. As there are 256 possible OPL registers but only a subset of these actually used, the mapping table allows up to 128 OPL registers to be used in a song. The other 128 (with the high bit set) are used for the second OPL2 chip in a dual-OPL2 capture.
The table is a list of iCodemapLength bytes, with the index used later in the file. For example this code table:
01 04 05 08 BD
Means that when the song references register #0 the data should be sent to OPL register 0x01, when the song references register #4 the data should go to OPL register 0xBD. If the song references register #128 (i.e. register 128+0), data should be sent to OPL register 0x01 on the second chip (in dual OPL2 mode) or the second set of registers (OPL3 mode). Likewise register #132 (128+4) is OPL register 0xBD on the second chip.
Directly following the header is the song data in register index and value pairs.
|UINT8||iRegisterIndex||Register to write to. This is an index into the codemap table.|
|UINT8||iValue||Value to write to the OPL register|
This structure is repeated iLengthPairs times. Note that while iRegisterIndex is normally an index into the codemap table, it can also match iShortDelayCode or iLongDelayCode if iValue is to be treated as a delay length (see above.) If the high bit is set (
iRegisterIndex & 0x80) then it is a register on the second OPL chip in a dual-OPL2 song (so the high bit should be removed and the value looked up as normal, but then sent to the second OPL chip instead.)
The following tools are able to work with files in this format.
|Name||Platform||Play?||Create new?||Modify?||Convert/export to other?||Import from other?||Access hidden data?||Edit metadata?||Notes|
|Camoto||Linux/Win||Yes||Yes||No||Yes; many||Yes; many||N/A||N/A||v1/v2|
|DOSBox||Windows/Mac/Linux||No||Yes||No||No||No||N/A||N/A||v2-only since 0.73. Produces .dro files as a result of capturing Adlib data from DOS games and applications|
|DRO Trimmer||Any (Python)||Yes||No||Yes||No||No||N/A||N/A||Edits .dro files to remove unwanted notes from the start and end|
|DRO2IMF||Windows/Mac/Linux||No||No||No||Yes; .imf||No||N/A||N/A||Converts v1/v2 .dro to .imf|
|DRO2MIDI||Windows/Mac/Linux||No||No||No||Yes; .mid||No||N/A||N/A||Converts v1/v2 .dro and .imf to .mid|
|OpenMPT||Windows||No||Yes||No||No||Yes; many||N/A||N/A||OpenMPT 1.30 and newer can export a DRO v1 register dump for songs that use OPL instruments.|
- Rdos' RAW format serves the same purpose, only these files were originally created through the RAC (Rdos Adlib Capture) TSR running under native DOS.
- The id Software Music Format (IMF) stores Adlib data in a similar manner in order to provide background music in many Apogee games.
- In the AdPlug distribution, tests/samurai.dro is the early one-byte format, and doofus.dro is the later four-byte variant.