Page 1 of 2

Night Trap 32X video compression

Posted: Sat Nov 09, 2013 3:47 am
by Paul Jensen
Does anybody want to take a crack at figuring out how to decode the video from Night Trap 32X?

I've been working on a video project for a video game class I'm teaching, using footage from Night Trap.

The versions for Sega systems are stored in .SGA format, which is partially documented. I used the info at this page to write a tool that converts the video from the original Sega CD version into matching .AVI and .WAV files, and I added the footage for my video project. It looks fine, but if possible I'd like to use the higher quality footage from one of the later releases of the game. I have the Sega CD 32X and PC versions, but it looks like the 32X version is probably my best bet since the video files are basically same format at the Sega CD version, and I wouldn't have to write a whole new tool.

The problem is the video is compressed using an unknown scheme, which apparently nobody has managed to crack yet. Here's an example of the type of data seen in the video files. It's the first frame of 59392800.SGA, which is the main Night Trap title. It's a (mostly) black screen.

Data:

Code: Select all

81 04 00 58 00 00 07 08 01 01 24 14 00 00 03 00
00 FF 01 00 FF 01 01 01 01 01 01 01 01 FF 01 00
FF 01 01 01 01 01 01 01 01 FF 01 00 FF 01 01 01
01 01 01 01 01 FF 01 00 FF 01 01 01 01 01 01 01
01 FF 01 00 FF 01 01 01 01 01 01 01 01 C8 01 00
FF 01 01 01 01 01 01 01 01 00 00 00
The first 12 bytes are a chunk header, broken down as follows:

Code: Select all

81 <-- Chunk type (Night Trap 32X Compressed Video)
04 <-- Stream number?
00 58 <-- Chunk length (88 bytes)
00 00 07 08 <-- Time indicator?
01 <-- Start column
01 <-- Start row
24 <-- Video width in tiles (36)
14 <-- Video height in tiles (20)
The data itself is 80 bytes long.

Code: Select all

00 00 03 00 00
FF 01 00
FF 01 01 01 01 01 01 01 01
FF 01 00
FF 01 01 01 01 01 01 01 01
FF 01 00
FF 01 01 01 01 01 01 01 01
FF 01 00
FF 01 01 01 01 01 01 01 01
FF 01 00
FF 01 01 01 01 01 01 01 01
C8 01 00
FF 01 01 01 01 01 01 01 01
00 00 00
This frame is 36x20 tiles, or 288x160 pixels. At 8bpp, that's 46080 bytes. Somehow all that data (plus palette data I presume) has been compressed into just 80 bytes, which I'm guessing is possible since it's just a black screen, and therefore has a lot of repeating data.

Most of the bytes are < 128 (highest bit = 0). I'm guessing those are literal bytes, and the FFs and the C8 (highest bit = 1) are compression descriptors, but I don't know exactly what they're describing.

What's weird is that there's still a lot of repeating data. You'd think that all those strings of 01s would get compressed.

I was thinking this the compression used here is some form of RLE, but others have guessed that it's a variant of LZSS.

The max compressed size for each video frame is, if I've got it right, 8956 bytes, or 5 sectors (10240 bytes) on the disc, minus 12 bytes for header data and 1272 bytes for audio.

It'd be a lot easier to figure this out if I knew exactly what the data looked like after it's been decoded, but I can't get Night Trap 32X to run right in any emulators that allow me to view / dump the contents of the VDP or CRAM.

Can anybody offer me a hand on this?

EDIT 1:
I took some screenshots using Kega Fusion, and now I have some more info to add.

The video appears to be broken down into 4x4 pixel blocks. Each block seems to have a max of 8 different colors, which would be 3 bits per pixel. At 16 pixels per block, that would be 48 bits, or 6 bytes per block.

The frame above is 36x20 tiles, or 72x40 blocks. That's 2880 blocks. At 6 bytes per block, that's a total of 17280 bytes of uncompressed pixel data per frame.

Still haven't figured out how the data is being compressed, though.

EDIT 2:
I looked a little more closely at the blocks, and it looks like the game is using compression similar (or identical?) toMicrosoft Video-1. Each 4x4 block is subdivided into 2x2 blocks (quads), each comprised of two colors.

Posted: Sat Nov 09, 2013 9:20 am
by Nemesis
That's an LZSS format, I'd bet anything on it. I've cracked a few similar encoding schemes. I don't have time to attempt to decode this implementation right now, but I'll have another look soon when I have a chance. It should be quick to determine how this frame is encoded. I'll need more sample data though, there's usually several different ways of encoding offset/count pairs, and this black frame is only likely to encounter one.

Re: Night Trap 32X video compression

Posted: Sat Nov 09, 2013 12:02 pm
by TascoDLX
I can get you going in the right direction. ;)

If you look at the data of the second frame, you should be able to spot the palette data [0000 0420 0421 0841 ... 18C4], all in order, fits the format (sort of dark colors, explained by the fade-in). Count them: there's 11, or in hex, 0x0B. Check the header:

Code: Select all

81 
04
0C 74
00 00 07 0A
01
0B <-- PALETTE SIZE*
24
14
Palette data immediately follows the header. Cool.

Now, go back to the first frame.

Header says 1 color, and it's 0000 (black). Hack the data. Change the color. Why not?

What do we get?

Image

OK so I guess technically it's a solid black screen. So much for assumptions. :roll:

So, after changing more data... long story short... 8x8 pixel tiles are laid out starting from the top left going right, then on down row-by-row. You already figured out the width and height in the header. The image is automatically centered as far as we can tell.

Let's look at the data:

Code: Select all

03 00 00
A lone solid tile in the top left corner. Op 03 happens to create a mesh tile of two colors. In this case, it's color 00 and color 00 :P

Next:

Code: Select all

FF 01 00 FF
01 01 01 01 01 01 01 01
The first byte is a tile repeat indicator. Its high bit is 1, so the other 7 bits are a count: here, it's 127, but we always add 2 so it becomes 129. So the following op will be repeated for the next 129 tiles.

Op 01 seems to be another 2-color op. See the 8 bytes that follow it? An 8x8 bit pattern. Each byte represents a pixel row, ordered from top to bottom, except note that the least significant bit of each byte is the leftmost pixel.

The next ops are all the same, and then we come to C8 01 00 FF..., which is the same op except the repeat count is only 74.

Let's hope math works today: 36*20 tiles = 720 = 1+129+129+129+129+129+74 tiles = 720! :D

Last op is 00 00 00, means this frame is finished.

Of course, that's only the tip of the iceberg. But that should at least give you an idea of the format if nothing else.

Posted: Sun Nov 10, 2013 4:56 am
by Paul Jensen
Woah! Nice job spotting that palette length value, TascoDLX! I totally missed that!

I checked further ahead in the file, and it looks like some of the palette lengths in the headers have a value of 0. I'm guessing it means "same as the previous frame".

So I guess now decoding the format is just a matter of figuring out how the codes are parsed.

Question: What is the significance of the FF and the end of the sequence FF 01 00 FF?
EDIT: Got it. It's palette entry FF, which must be reserved for black (or maybe transparent?). So, the string 01 00 FF 01 01 01 01 01 01 01 01 means "draw a 2-color 8x8 tile using color 00 if a bit is set to 0 and FF if a bit is set to 1, with 01 01 01 01 01 01 01 01 as the bitmap".

Is that a screenshot in your post? How did you edit the data in the ISO?
EDIT: I loaded up the ISO in a hex editor and did a search for the header of the first frame. Then I started messing around with values to see what happens.

Also, if you've got the time, could you try to break down the data in the second frame? I still can't wrap my head around it. I guess it'll be hard to do without knowing exactly how to interpret each code.

Posted: Sun Nov 10, 2013 2:44 pm
by TascoDLX
Yeah I just change the data with a hex editor and run the ISO in Fusion. I pause sometime before the frame I want to see and take a bunch of screenshots. Fusion will automatically frame advance when you take a screenshot. That works for me.
Paul Jensen wrote:Question: What is the significance of the FF and the end of the sequence FF 01 00 FF?
EDIT: Got it. It's palette entry FF, which must be reserved for black (or maybe transparent?). So, the string 01 00 FF 01 01 01 01 01 01 01 01 means "draw a 2-color 8x8 tile using color 00 if a bit is set to 0 and FF if a bit is set to 1, with 01 01 01 01 01 01 01 01 as the bitmap".
OK, so here is how a lot of these ops work. Some of them generate 8x8 pixel blocks, some generate 4x4, some generate 8x4. If there are any other possibiities, I haven't seem them yet. The general format is: 8-bit op number, followed by pairs of colors (indexes), followed by layout bits.

Some simple ops, like op 01, may specify only 1 pair of colors. Others may specify several pairs. If a particular color is unreferenced, it may be recorded as FF or 01. From what I've seen, an unreferenced color is often 01 if it's the first of the pair, FF if it's the last. Not sure if that's significant, but it probably doesn't matter (after all, it is unreferenced).

Also, I'm not sure transparency is used at all because the 32X uses frame buffer swapping, so it certainly wouldn't use it for I-frames. Other methods that use transparency are likely too intensive or at least require a time consuming buffer wipe before the frame is drawn. But yes, palette reuse may be a possibility, though I haven't looked into it.

About the layout bits: it might be easier to think of it not as a 1-to-1 bit pattern, but 2-to-2 -- two bits per pair of pixels. In general, every pair of pixels is assigned one of the specified color pairs (call the colors within a given pair x & y). For each pixel pair, read two layout bits. For these bits, 00=yy, 01=xy, 10=yx, 11=xx. These bits are shifted out to the right, so the lowest bits refer to the leftmost pixels. But if you prefer, you can still shift them out one at a time and just use 0=y & 1=x.

Example:

Op 1C
[generates 4x4 block]

Format: 1C $a $L
where $a is color pair Xa,Ya (one byte each)
and $L are the layout bits (4x4 op yields 16 pixels => 16 layout bits [2 bytes!])

We'll refer to the pixel pair layout of our output as such:
#1 #2
#3 #4
#5 #6
#7 #8
(note: two pixels per #, so it is indeed 4x4)

Color pair assignments:
#1($a) #2($a)
#3($a) #4($a)
#5($a) #6($a)
#7($a) #8($a)
(well, isn't this the trivial case)

Layout bits are ordered as follows (again, two bits per pixel pair):
byte 1: #4 #3 #2 #1
byte 2: #8 #7 #6 #5

How to decode:

Code: Select all

1C 01 02 1C 00
Op = 1C
(size = 4x4)
Color pair $a = 01 02 (Xa=01, Ya=02)
Layout bits $L = 1C 00
Layout bits in binary = 00011100, 00000000

Process pixels from left-to-right starting from the top.
Fetch layout bits starting with these
00011100, 00000000
(remember: we shift out right)

Following the proper order, assign layout bits to pixel pairs:
#1($a:00) #2($a:11)
#3($a:01) #4($a:00)
#5($a:00) #6($a:00)
#7($a:00) #8($a:00)

Craft the output:
(using the assignments: 00=yy, 01=xy, 10=yx, 11=xx)

Ya Ya Xa Xa
Xa Ya Ya Ya
Ya Ya Ya Ya
Ya Ya Ya Ya

So, the final output (4x4 block):
(using the specified colors Xa=01, Ya=02)

02 02 01 01
01 02 02 02
02 02 02 02
02 02 02 02

Any generated 4x4 blocks are appropriately aligned within 8x8 blocks. The order is as you'd expect.

1 2 5 6 ...
3 4 7 8 ...

So the 8x8 is filled before proceeding to the next. Alignment is also respected for 8x4 blocks: it's either the top half of the 8x8 or the bottom half. Everything should be kept aligned or we'll have a mess.

One more op (picked at random), briefly:

Op 35
[generates 4x4 block]

Format: 35 $a $b $c $d $e $f $L

Color pair assignments:
#1($a) #2($b)
#3($c) #4($b)
#5($d) #6($e)
#7($f) #8($e)

Let's try to decode one:

Code: Select all

35 07 00 0A FF 07 00 07 00 0A FF 07 00 EE EE
$a: Xa=07, Ya=00
$b: Xb=0A, Yb=FF
$c: Xc=07, Yc=00
$d: Xd=07, Yd=00
$e: Xe=0A, Ye=FF
$f: Xf=07, Yf=00
$L (in binary) = 11101110 11101110

Apply our layout bits:
#1($a:10) #2($b:11)
#3($c:10) #4($b:11)
#5($d:10) #6($e:11)
#7($f:10) #8($e:11)

(using 00=yy, 01=xy, 10=yx, 11=xx)

Output:

Ya Xa Xb Xb
Yc Xc Xb Xb
Yd Xd Xe Xe
Yf Xf Xe Xe

Final output:

00 07 0A 0A
00 07 0A 0A
00 07 0A 0A
00 07 0A 0A

There are quite a few ops, and there are likely numerous ways to generate the same output. I doubt that the method used by the game is totally efficient, but not sure that it matters. Video is still 15fps anyway. :P

I'll post some more op descriptions later. They should at least be easier to recognize now.

Posted: Mon Nov 11, 2013 2:15 pm
by Paul Jensen
This is great! I hope you're having fun working on this. I know my curiosity is piqued. :-)

I suppose we could figure out all of the codes by disassembling the game code and finding the right routines, but it might actually be more interesting ths way.

I've tested all of the opcodes from 00 to 1C, but I noticed that a lot of them produce junk tiles. Either that or I didn't test them right. How many have you been able to figure out so far?

I saw op 35 in the data I was looking at. I also saw an op 31 ocurring right before an op 1C, but I don't know what its purpose is yet.
TascoDLX wrote:Yeah I just change the data with a hex editor and run the ISO in Fusion. I pause sometime before the frame I want to see and take a bunch of screenshots. Fusion will automatically frame advance when you take a screenshot. That works for me.
If you want you can use the INSERT key to advance the frame without taking a screenshot. Then you won't have to delete the unused ones later. :-)

Posted: Tue Nov 12, 2013 7:08 am
by TascoDLX
Paul Jensen wrote:This is great! I hope you're having fun working on this. I know my curiosity is piqued. :-)

I suppose we could figure out all of the codes by disassembling the game code and finding the right routines, but it might actually be more interesting ths way.
Oh definitely, this is very interesting. A lot different than I thought it would be. Nah I don't really feel like tearing through disassembly anyway, this is way more fun. :D
Paul Jensen wrote:I've tested all of the opcodes from 00 to 1C, but I noticed that a lot of them produce junk tiles. Either that or I didn't test them right. How many have you been able to figure out so far?
64 so far. I'm surprised you got results from all of 00 to 1C unless you're looking at different frames than I am. It seems difficult to inject arbitrary opcodes because it's hard to predict their sizes. They are a little jumbled in that regard.

EDIT: nvm, I found a decent method for determining size. Just filling in the blanks now.
Paul Jensen wrote:I saw op 35 in the data I was looking at. I also saw an op 31 ocurring right before an op 1C, but I don't know what its purpose is yet.
The 3X range is:

Code: Select all

30 $A $B    31 $A $B    32 $A $B    33 $A $B
   $C $D       $C $D       $C $D       $C $D
   $E $F       $E $F       $E $F       $E $F
   $G $H       $G $F       $E $G       $E $F

34 $A $B    35 $A $B    36 $A $B    37 $A $B
   $C $B       $C $B       $C $B       $C $B
   $D $E       $D $E       $D $E       $D $E
   $F $G       $F $E       $D $F       $D $E

38 $A $B    39 $A $B    3A $A $B    3B $A $B
   $A $C       $A $C       $A $C       $A $C
   $D $E       $D $E       $D $E       $D $E
   $F $G       $F $E       $D $F       $D $E

3C $A $B    3D $A $B    3E $A $B    3F $A $B
   $A $B       $A $B       $A $B       $A $B
   $C $D       $C $D       $C $D       $C $D
   $E $F       $E $D       $C $E       $C $D
All 4x4, and there's actually a verifiable pattern there.

I also went through 40-57 -- they are all 8x8, 2-color, fixed layout. Basically dither patterns in different arrangements.
Paul Jensen wrote:If you want you can use the INSERT key to advance the frame without taking a screenshot. Then you won't have to delete the unused ones later. :-)
Well I wasn't gonna go into detail, but I actually hold down the key to take the screenshots so I get a bunch of them very quickly, then I just pick out the one I need. But I do it because my computer can handle it. Otherwise, you're way would be way more favorable. ;)

Posted: Wed Nov 13, 2013 4:44 am
by Paul Jensen
A quick update:

I used my SGA decode program to brute force my way through all of the opcodes. I plugged in all the ops whose length I already knew and then set up an elsewhere case to catch any unrecognized ops. It took a lot of trial and error, but now I know the parameter lengths of all the valid ops. I also know which ops are invalid.

At this point my program is able to parse its way through entire files without hitting any unrecognized ops, which to me is a good sign that I've got the lengths right. The program can also extract the audio now, but that's only because it's basically the same as in the Sega CD version.

Here's what I found. I hope it matches up with what you've discoverd so far, Tasco. (NOTE: commented exclamation marks represent verified ops, and unused/invalid ops are commented out.)

Code: Select all

                Select Case op
                    Case &H0
                        Return Nothing '!
                    Case &H1
                        opParamLen = 10 '!
                    Case &H2
                        opParamLen = 6 '!
                    Case &H3
                        opParamLen = 2 '!
                    Case &H4
                        opParamLen = 2 '!
                    Case &H5
                        opParamLen = 2 '!
                    Case &H6
                        opParamLen = 2 '!
                    Case &H7
                        opParamLen = 2 '!
                    Case &H8
                        opParamLen = 6 '!
                    Case &H9
                        opParamLen = 10 '!
                    Case &HA
                        opParamLen = 10 '!
                    Case &HB
                        opParamLen = 12 '!

                        'Case &HC
                        'Case &HD
                        'Case &HE
                        'Case &HF

                    Case &H10
                        opParamLen = 8 '!
                    Case &H11
                        opParamLen = 10 '!
                    Case &H12
                        opParamLen = 8 '!
                    Case &H13
                        opParamLen = 10 '!
                    Case &H14
                        opParamLen = 10 '!
                    Case &H15
                        opParamLen = 14 '!
                    Case &H16
                        opParamLen = 12 '!
                    Case &H17
                        opParamLen = 12 '!
                    Case &H18
                        opParamLen = 8 '!
                    Case &H19
                        opParamLen = 6 '!
                    Case &H1A
                        opParamLen = 14 '!
                    Case &H1B
                        opParamLen = 12 '!
                    Case &H1C
                        opParamLen = 4 '!
                    Case &H1D
                        opParamLen = 12 '!
                    Case &H1E
                        opParamLen = 12 '!
                    Case &H1F
                        opParamLen = 10 '!
                    Case &H20
                        opParamLen = 20 '!
                    Case &H21
                        opParamLen = 18 '!
                    Case &H22
                        opParamLen = 18 '!
                    Case &H23
                        opParamLen = 16 '!
                    Case &H24
                        opParamLen = 18 '!
                    Case &H25
                        opParamLen = 16 '!
                    Case &H26
                        opParamLen = 16 '!
                    Case &H27
                        opParamLen = 14 '!
                    Case &H28
                        opParamLen = 18 '!
                    Case &H29
                        opParamLen = 16 '!
                    Case &H2A
                        opParamLen = 16 '!
                    Case &H2B
                        opParamLen = 14 '!
                    Case &H2C
                        opParamLen = 16 '!
                    Case &H2D
                        opParamLen = 14 '!
                    Case &H2E
                        opParamLen = 14 '!
                    Case &H2F
                        opParamLen = 12 '!
                    Case &H30
                        opParamLen = 18 '!
                    Case &H31
                        opParamLen = 16 '!
                    Case &H32
                        opParamLen = 16 '!
                    Case &H33
                        opParamLen = 14 '!
                    Case &H34
                        opParamLen = 16 '!
                    Case &H35
                        opParamLen = 14 '!
                    Case &H36
                        opParamLen = 14 '!
                    Case &H37
                        opParamLen = 12 '!
                    Case &H38
                        opParamLen = 16 '!
                    Case &H39
                        opParamLen = 14 '!
                    Case &H3A
                        opParamLen = 14 '!
                    Case &H3B
                        opParamLen = 12 '!
                    Case &H3C
                        opParamLen = 14 '!
                    Case &H3D
                        opParamLen = 12 '!
                    Case &H3E
                        opParamLen = 12 '!
                    Case &H3F
                        opParamLen = 10 '!
                    Case &H40
                        opParamLen = 2 '!
                    Case &H41
                        opParamLen = 2 '!
                    Case &H42
                        opParamLen = 2 '!
                    Case &H43
                        opParamLen = 2 '!
                    Case &H44
                        opParamLen = 2 '!
                    Case &H45
                        opParamLen = 2 '!
                    Case &H46
                        opParamLen = 2 '!
                    Case &H47
                        opParamLen = 2 '!
                    Case &H48
                        opParamLen = 2 '!
                    Case &H49
                        opParamLen = 2 '!
                    Case &H4A
                        opParamLen = 2 '!
                    Case &H4B
                        opParamLen = 2 '!
                    Case &H4C
                        opParamLen = 2 '!
                    Case &H4D
                        opParamLen = 2 '!
                    Case &H4E
                        opParamLen = 2 '!
                    Case &H4F
                        opParamLen = 2 '!
                    Case &H50
                        opParamLen = 2 '!
                    Case &H51
                        opParamLen = 2 '!
                    Case &H52
                        opParamLen = 2 '!
                    Case &H53
                        opParamLen = 2 '!
                    Case &H54
                        opParamLen = 2 '!
                    Case &H55
                        opParamLen = 2 '!
                    Case &H56
                        opParamLen = 2 '!
                    Case &H57
                        opParamLen = 2 '!

                        'Case &H58
                        'Case &H59
                        'Case &H5A

                    Case &H5B
                        opParamLen = 0 '!

                        'Case &H5C
                        'Case &H5D
                        'Case &H5E
                        'Case &H5F
                        'Case &H60
                        'Case &H61
                        'Case &H62
                        'Case &H63
                        'Case &H64
                        'Case &H65
                        'Case &H66
                        'Case &H67
                        'Case &H68
                        'Case &H69
                        'Case &H6A
                        'Case &H6B
                        'Case &H6C
                        'Case &H6D
                        'Case &H6E
                        'Case &H6F
                        'Case &H70
                        'Case &H71
                        'Case &H72
                        'Case &H73
                        'Case &H74
                        'Case &H75
                        'Case &H76
                        'Case &H77
                        'Case &H78
                        'Case &H79
                        'Case &H7A
                        'Case &H7B
                        'Case &H7C
                        'Case &H7D
                        'Case &H7E
                        'Case &H7F
                End Select
I still don't know what many of these ops do, but at least now I can narrow my focus while testing them out.

I was able to draw some tiles earlier by zeroing out the video/audio data of 59392800.SGA and then adding in my own palette and ops.

Now I guess I can start testing more of them with less trial and error.

Op $5B is weird. As far as I can tell, it only ever appears as the first byte right after the end of the palette. I haven't tested it. Any idea what it does?

Posted: Wed Nov 13, 2013 6:46 am
by TascoDLX
Files:

Night Trap 32X Video Codebook - 2013.11.12.txt
NT32XVID-FIXED-OPS.PNG

Opcode listing is at the end of the text doc -- pretty much all verified. PNG contains all the 2-color fixed patterns, some of which can be referred to by multiple opcodes (don't know how much of that is intentional).

The code looks good, Paul. The lengths all check out, but I didn't have any luck with op 02. I'll have to check it again.
Paul Jensen wrote:Op $5B is weird. As far as I can tell, it only ever appears as the first byte right after the end of the palette. I haven't tested it. Any idea what it does?
5B is 8x8 blank, 5C is 8x4 blank, and 5D is 4x4 blank. I assume they all use color 00, but I haven't looked far into it.

If you need more explanation on anything, definitely let me know. I'm sure I glossed over a few things along the way.

Posted: Thu Nov 14, 2013 6:25 am
by Paul Jensen
Super cool! Thanks! I can't believe we (mostly you) were able cracked the code so quickly. I've already started adding it into my program.

Now I just have to hope I can get it finished in time to use the video for my project.

Posted: Fri Nov 15, 2013 5:53 am
by TascoDLX
Paul Jensen wrote:Super cool! Thanks! I can't believe we (mostly you) were able cracked the code so quickly.
No problem. To be fair, it could have been a lot more complicated if they decided to do more than just boost the colors of the Sega CD version. Luckily they settled for a relatively simple format, so it was pretty open.

Good luck on the project! :D

Almost there...

Posted: Sat Nov 16, 2013 1:18 am
by Paul Jensen
Here's what my program can do so far:

SGA Decoder screenshot

Here's a screenshot from Kega for reference:

Kega screenshot

As you can see, I've still got a few bugs to track down. If you closely compare both images, you'll see that the bits are all in the right places, but some of the palette assignments are swapped, shifted, etc. Also, the program isn't able to open up all of the files yet. But still, I'm pretty proud of what I've been able to do in a week (Thanks to Tasco Deluxe, or course!)

EDIT: Fixed the palette problem.

SGA Decoder screenshot 2

It looks like a few of the fixed pattern tiles are flipped, but that'll be easy to fix. Really almost there now.

EDIT: Found out what was wrong with the fixed patterns.

The patterns are horizontally flipped because I didn't reverse the bits. Going to go back through and verify them.

It works.

Posted: Thu Nov 21, 2013 7:06 am
by Paul Jensen
The program works. :D

I was able to sucessfully decode all of the video from Night Trap 32X. The output looks like a pixel-perfect match to screenshots from Kega.

So far the program can handle video types $81 and $C1, so basically it's only good for the Night Trap releases and (most of) the intro to Sewer Shark. I'll probably release the source once I clean it up a bit. It'd be cool if SGA format decoding (or encoding?) could be incorporated into VLC media player or FFMPEG someday, even if its usefulness is limited.

Posted: Thu Nov 21, 2013 12:45 pm
by TascoDLX
Congrats! Source code release would be cool, I'd check that out. I might even look into decoding other SGA vid packets. Never really looked beyond Night Trap & Sewer Shark.

Posted: Sat Nov 30, 2013 4:35 pm
by Paul Jensen
Still working on the program. I added support for video type $C8 (used in Sewer Shark), and I'm really close to figuring out how $C6 and its derivatives work. I also did a big rewrite to make it easier to add more types of data chunks, and added support for exporting AVI files that contain both a video stream and an audio stream (early versions could only output the streams as separate files).

I also discovered that when the DataMode entry in a header is $80 or higher, the video frame contains tile data, palette data, and a combo tilemap/palette map instead of one big bitmap, palettes, and a palette map. In this mode there is a two-byte value just after the normal header that indicates the number of tiles. I haven't figured out what all the possible data modes are yet (I've only seen $05, $40, and $85 so far), but hopefully I will soon.

EDIT: I figured out video type $C6. I also discovered a weird quirk of type $C8: tile data in even-numbered rows (base 1) is byte-swapped for some reason. AFAICT it applied to all $C8 videos, and also to one $C6 video that I know of. The crappy thing is nothing in the headers seem to distinguish videos with byte-swapped tiles from non byte-swapped ones. I can't see why Digital Pictures would have done this. It doesn't make sense from a processing standpoint to have to swap bytes.