MegaTech VOL Format/Decoding Example

From ModdingWiki
Jump to navigation Jump to search

Decoding example for Cobra Mission's GC format.

Sample data from the second Girl's Photo in inventory, third GC entry in PIC3.VOL. Decoding routine provided by dascandy

d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55


We will be decoding the byte sequence above. To start, we take the first two bytes to preload the bit buffer. The nibble buffer stays empty, and the back buffer is also empty.

  • BITS: 1101 1000 1101 1000
  • NIBBLE: empty
  • BACKBUFFER: empty
  • OUTPUT: empty
  • CONSUMED INPUT: d8 d8
  • REMAINING INPUT: 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

The first command we get is 110, copy 4 bytes from the input to the back buffer and output.

  • BITS: 1 1000 1101 1000
  • NIBBLE: empty
  • BACKBUFFER: 01 01 01 01 (1 entry)
  • OUTPUT: 01 01 01 01
  • CONSUMED INPUT: d8 d8 01 01 01 01
  • REMAINING INPUT: e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

The second command is also 110, so we do this same thing again.

  • BITS: 00 1101 1000
  • NIBBLE: empty
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 (2 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0
  • REMAINING INPUT: 55 e3 e3 e2 e2 61 61 a1 21 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

Now, the value we get from the bit buffer is 00. This is an code that needs an additional nibble, so we'll read it:

  • BITS: 1101 1000
  • NIBBLE: 5
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 (2 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55
  • REMAINING INPUT: e3 e3 e2 e2 61 61 a1 21 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

We got a 5. This lands us in the 4-9 for that one, so we fetch another nibble (5) to get the amount to copy, and use the first 5 (minus 4) to get the offset to read at. That results in 7 values to copy from 2 entries back.

  • BITS: 1101 1000
  • NIBBLE: empty
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 (2 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55
  • REMAINING INPUT: e3 e3 e2 e2 61 61 a1 21 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

Now we get the 110 command twice more. Skipping ahead for those two:

  • BITS: 00
  • NIBBLE: empty
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 (4 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21
  • REMAINING INPUT: 1b ec 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

Reading the next command we get a command 00. Before we continue though, the bit buffer is empty and it *first* refills.

  • BITS: 1110 1100 0001 1011
  • NIBBLE: empty
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 (4 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21
  • REMAINING INPUT: 47 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

Now we run the second 00 command fetching its data. It needs a nibble to know what offset to run at, and it fetches a 7 for that - same as last, except a larger pointer, going 8 back. It fetches another nibble for the count, and gets the 4 (from the same byte). It will then copy 6 values (4 + 2) from an offset of 8 entries back. The compressor most likely stopped there to avoid copying the interruption.

  • BITS: 1110 1100 0001 1011
  • NIBBLE: empty
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 (4 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47
  • REMAINING INPUT: 70 46 b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

We retrieve a command 1110; this refers to the copy move table. We also fetch a nibble to find out which subcommand it runs, and it's a zero. This copies a number of values from a short distance back. It starts by fetching a full byte - note that we are ignoring the nibble we have in the nibble buffer for this - and we get a 0x46. It splits this into a 2-bit index into the offset table getting an offset of -2, and a 6-bit count of entries to copy (to be increased by 17+1, so 24). We therefore copy from a relative offset of -2 entries, 24 times.

  • BITS: 1100 0001 1011
  • NIBBLE: 7
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 (4 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46
  • REMAINING INPUT: b5 b5 ab a1 55 b2 81 94 88 94 37 b8 00 55 55

We get another 110, so we copy an entry to the buffer and output.

  • BITS: 0 0001 1011
  • NIBBLE: 7
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 b5 b5 ab a1 (5 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 b5 b5 ab a1
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46 b5 b5 ab a1
  • REMAINING INPUT: 55 b2 81 94 88 94 37 b8 00 55 55

Again, we get a 00, so we copy from the back. We first fetch the nibble value from the nibble buffer now, so we have a 7. The second nibble we fetch is a 5, and we will copy 7 values (from the 5 plus two) from 8 back (from the 7 in the offset table).

  • BITS: 001 1011
  • NIBBLE: 5
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 b5 b5 ab a1 (5 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 b5 b5 ab a1 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46 b5 b5 ab a1 55
  • REMAINING INPUT: b2 81 94 88 94 37 b8 00 55 55

We again get a 00 command, so we take the 5 from the nibble buffer (offset -2) and fetch a new nibble from the input to get 4 values to copy.

  • BITS: 1 1011
  • NIBBLE: b
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 b5 b5 ab a1 (5 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 b5 b5 ab a1 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46 b5 b5 ab a1 55
  • REMAINING INPUT: 81 94 88 94 37 b8 00 55 55

Another 110, we copy out 4 values.

  • BITS: 11
  • NIBBLE: b
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 b5 b5 ab a1 81 94 88 94 (6 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 b5 b5 ab a1 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 81 94 88 94
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46 b5 b5 ab a1 55 81 94 88 94
  • REMAINING INPUT: 37 b8 00 55 55

We fetch two bits, but need more for the command, so we refill the bit buffer mid-command and keep reading.

  • BITS: (11) 1011 1000 0011 0111
  • NIBBLE: b
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 b5 b5 ab a1 81 94 88 94 (6 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 b5 b5 ab a1 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 81 94 88 94
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46 b5 b5 ab a1 55 81 94 88 94 37 b8
  • REMAINING INPUT: 00 55 55

We find a 1110, we fetch a nibble to find out what it wants us to do. This time it's 0xb or 1011 in binary.

  • BITS: 11 1000 0011 0111
  • NIBBLE: b
  • BACKBUFFER: 01 01 01 01 e0 e0 e0 e0 e3 e3 e2 e2 61 61 a1 21 b5 b5 ab a1 81 94 88 94 (6 entries)
  • OUTPUT: 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e3 e3 e2 e2 61 61 a1 21 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 b5 b5 ab a1 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 e0 e0 e0 e0 01 01 01 01 81 94 88 94 00 55 88 55
  • CONSUMED INPUT: d8 d8 01 01 01 01 e0 e0 e0 e0 55 e3 e3 e2 e2 61 61 a1 21 47 70 46 b5 b5 ab a1 55 81 94 88 94 37 b8 00 55 55
  • REMAINING INPUT:

We're done (with this part).

Verification by BlackStar:

Using the Dosbox debugger, a breakpoint was set the moment the pixel data for the first few lines was sent to videomemory. Data segment at point of inspection:

0FB7:0000   CB 00 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0010   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0020   E0 E0 01 01 01 01 E3 E3 E2 E2 61 61 A1 21 E0 E0 
0FB7:0030   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0040   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0050   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0060   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0070   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0080   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0090   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:00A0   E0 E0 01 01 01 01 B5 B5 AB A1 01 01 01 01 E0 E0 
0FB7:00B0   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:00C0   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:00D0   E0 E0 01 01 01 01 81 94 88 94 00 55 88 55 00 D5 
0FB7:00E0   88 D5 00 55 88 55 00 55 88 55 00 55 88 55 00 55 
0FB7:00F0   88 55 01 41 81 41 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0100   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0110   E0 E0 01 01 01 01 E0 E0 E0 E0 01 01 01 01 E0 E0 
0FB7:0120   E0 E0 01 01 01 01 E0 E0 E0 E0 00 00 00 00 00 00 
0FB7:0130   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:0140   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:0150   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:0160   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:0170   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:0180   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:0190   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:01A0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
0FB7:01B0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00