display-name: "Games/Jazz Jackrabbit"
meta: {"Working": ["Tiles", "Sprites"], "Thanks to": ["Mark Spear", "The OpenJazz team"]}

if (file.name like "planet.*")
{
	data(2) unknown
	pascal-string(unsigned8) planet-name

	palette
	{
		size: 256
		format: "R6G6B6"
	}

	image
	{
		format: "I8"
		width: 64
		height: 55
	}
}

if (file.name like "*.lbm")
{
	data(48) unknown

	palette
	{
		size: 256
		format: "R8G8B8"
	}

	data(8) unknown

	image
	{
		format: "I8"
		width: 320
		height: 200
	}
}


if (file.name like ["level?.0??", "bonusmap*"])
{
	folder: "Levels"
}

if (file.name like "blocks*")
{
	folder: "Tiles"

	unsigned16 compressed-size

	decompressed-size: 768
	compressed [compressed-size] "Jazz" 
	{
		palette
		{
			size: 256
			format: "R6G6B6"
		}
	}

	loop(2) //2 more palettes
	{
		unsigned16 compressed-size
		compressed [compressed-size] "Jazz"
		{
			data(768) paletteIgnore
		}
	}

	decompressed-size: 32 * 32

	loop (4)
	{
		unsigned16 unknown

		if (file.remaining-bytes > 2)
		{
			image-layout-width: 32 * 10
	
			image-layout
			{
				loop (60)
				{
					unsigned16 compressed-size
					compressed [compressed-size] "Jazz"
					{
						image
						{
							format: "I8"
							width: 32
							height: 32
						}
					}
				}
			}
		}
	}
}

//TODO: jumping around thanks to palette being after image...
//Allow self referential i.e. palette: file.name
//There's no reason that shouldn't work, getting palette media shouldn't trigger image draw...

if (file.name like ["bonus*.0*", "cdbonus*"] and (file.name != "bonus.000") and (file.name != "bonus.0sc"))
{
	folder: "Tiles"

	unsigned16 compressed-size

	at compressed-size + 2:

	unsigned16 compressed-size
	decompressed-size: 768
	compressed [compressed-size] "Jazz"
	{
		palette
		{
			size: 256
			format: "R6G6B6"
		}
	}

	at 0:
	unsigned16 compressed-size
	decompressed-size: 26624
	compressed [compressed-size] "Jazz" 
	{
		image
		{
			format: "I8"
			width: 832
			height: 26624 / 832
		}
	}

	unsigned16 compressed-size
	decompressed-size: 768
	compressed [compressed-size] "Jazz"
	{
		palette
		{
			size: 256
			format: "R6G6B6"
		}
	}

	decompressed-size: 32 * 32 * 120

	unsigned16 compressed-size
	compressed [compressed-size] "Jazz" 
	{
		image
		{
			format: "I8"
			width: 32
			height: 32
			tile-count: 60
			tiles-across: 10
		}
	}
}

if (file.name like "sounds.???")
{
	folder: "Sounds"

	at file.size - 4:
	unsigned32 header-offset

	at header-offset:
	file while (file.position < (file.size - 4))
	{
		fixed-string(12) name
		unsigned32 offset
		unsigned16 size

		skip-if (offset >= 0x7FFFFFFF)
	}
}

if (file.name like "*.0fn")
{
	folder: "Fonts"

	/*fixed-string(19) magic == "Digital Dimensions\x1a"

	unsigned8 vSpace
	unsigned8 hSpace

	unsigned16 unknown

	file[64]: i
	{
		unsigned16 char-size
		
		skip-if (char-size <= 4)

		decompressed-size: char-size - 4
	
		unsigned16 compressed-size

		size: compressed-size
		offset: file.position

		name: "Character " + i

		data(compressed-size) char-data
		compressed "jazz-rle"
	}*/
}

if (file.name like "*.psm")
{
	folder: "Music"
}

if (file.name == "bonus.000")
{
	folder: "Unhandled sprites"
}

if (file.name like ["sprites.???", "mainchar.???"])
{
	folder: "Sprites"

	unsigned16 sprite-count

	if (!(file.name like "mainchar.???"))
	{
		unsigned8[sprite-count] hOffset //needs << 2
		unsigned8[sprite-count] vOffset
	}

	image-layout-width: 320
	
	image-layout
	{
		loop (sprite-count)
		{
			unsigned16 width-pre
			if (width-pre != 0xFFFF)
			{
				width: width-pre * 4
				unsigned16 height
				unsigned16 thisSpriteSize // needs << 2
				unsigned16 maskOffset
				unsigned16 nextSprite // << 2

				if (maskOffset == 0)
				{
					image
					{
						format: "I8"
						interlaced: 4
						transparent-index: 254
						palette: "blocks.000"
					}
				}
				else
				{
					at file.position + maskOffset:

					//read mask
					compressed [width * height / 4] "BitExpander" 
					{
						encrypted [width * height] "JazzSprite2" 
						{
							unsigned8[width * height] mask
						}
					}

					data(width / 4) unknown //something else here

					nextSprite = file.position + (nextSprite << 2)

					//TODO: sometimes sprites don't have enough data.  Fixme.
					if (file.remaining-bytes >= width * height)
					{				
						encrypted [width * height] "JazzSprite" 
						{
							image
							{
								format: "I8"
								interlaced: 4
								transparent-index: 254
								palette: "blocks.000"
							}
						}
					}			

					at nextSprite:
				}
			}
		}
	}
}

function BitExpander
{
	loop while (file.remaining-bytes > 0)
	{
		read unsigned8 token
		loop (4)
		{
			write unsigned8 (token & 1)
			token = token >> 1
		}		
	}	
}

function JazzSprite2
{
	x: 0
	y: 0	

	w:width
	h:height

	loop (height)
	{
        	loop (width)
		{
			read unsigned8 token
			
			write at ((((y >> 2) + ((x & 3) * (h >> 2))) * w) + (x >> 2) + (((y & 3) + ((h & 3) * (x & 3))) * (w >> 2))) unsigned8 token 
			x = x + 1
		}

		y = y + 1
		x = 0
	}
}

function JazzSprite
{
	counter: 0	
	loop (width * height)
	{
		if (mask[counter] == 1)
		{
			// The unmasked portions are transparent, so no masked portion
			// should be transparent.

			m: 254

			loop while (m == 254)
			{
				read unsigned8 mn
				m = mn
			}

			// Use the acceptable pixel
			write unsigned8 m

		}
		else
		{
				write unsigned8 254 // Use the transparent pixel
		}

		counter = counter + 1		
	}
}

function Jazz
{
	loop while (file.remaining-bytes > 0)
    {
        read unsigned8 control		

        if (control == 0)
        {
            read unsigned8 token
		    write unsigned8 token       

        }
		else if (control < 0x80)
        {
            loop (control)
			{
				read unsigned8 token
		        write unsigned8 token
			}
        }
        else
        {
			read unsigned8 token	
			loop (control - 0x80)
			{
                write unsigned8 token
			}
		}
	}
}

function jazz-rle
{
	running: true

	loop while (running)
    	{
        	read unsigned8 control		

        	if (control == 0)
        	{
            		running = false    
        	}
		else if (control > 0x7F)
        	{
			read unsigned8 token
            		loop (control - 127)
			{
		        	write unsigned8 token
			}
        	}
        	else
        	{
			loop (control)
			{
				read unsigned8 token
                		write unsigned8 token
			}
		}
	}
}

/*if (file.name like "*.0sc")
{
	folder: "Images"

	fixed-string(13) signature == "Digital Dimensions\x1A"
	data(14) unknown
	unsigned16 imageCount
	//rest of fixed header unknown, variable size till first asset ID +/- 1 byte

	// 02 = Image, I think.


	// 2A = Music
	//       Format: {
	//		AsciiString songName
	//	}

	// 58 = Font
	//	Format: {
	//		unsigned16 instanceID
	//		AnsciiString fontName
	//	}
}*/ 