display-name: "Games/Biomenace"
meta: 
{
	"Working": ["Packed file extraction", "Images", "Levels", "Audio"],
	"Thanks to": "Andy Durdin"
}

episode-table:
{
	["*.bm1"]: { "extension": "bm1", "graphics-name": "egagraph.bm1", "num16u": 1512, "num16m": 2682, "numsound": 42 },
	["*.bm2"]: { "extension": "bm2", "graphics-name": "egagraph.bm2", "num16u": 1728, "num16m": 2466, "numsound": 40 },
	["*.bm3"]: { "extension": "bm3", "graphics-name": "egagraph.bm3", "num16u": 1080, "num16m": 2682, "numsound": 40 }
}

if (file.name like "egagraph.bm?")
{
	//grab decompress table
	from "egadict." + episode-table[file.name].extension
	{
		unsigned16[512] huff-table
	}

	//link in the header, stored in the exe
	bitmap-count: 0
	masked-bitmap-count: 0
	sprite-count: 0

	packed-name: file.name
	header-name: "egahead." + episode-table[file.name].extension

	from header-name
	{
		file[1]
		{
			name: "Bitmaps header"
			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size
				bitmap-count = decompressed-size / 4				
			}

			offset: _offset + 4

			compressed "keen-huffman"
		}

		file[1]
		{
			name: "Masked Bitmaps header"
			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size
				masked-bitmap-count = decompressed-size / 4				
			}

			offset: _offset + 4

			compressed "keen-huffman"
		}

		file[1]
		{
			name: "Sprites header"
			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size
				sprite-count = decompressed-size / 18				
			}

			offset: _offset + 4

			compressed "keen-huffman"
		}

		file[2]: index
		{
			name: "Font " + (index + 1)
			folder: "Fonts"

			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size			
			}

			offset: _offset + 4

			compressed "keen-huffman"
			interpret-as "keenengine-font"
		}

		file[bitmap-count]: index
		{
			name: "Bitmap " + (index + 1)
			bitmap-number: index

			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size			
			}

			offset: _offset + 4

			compressed "keen-huffman"
		}

		file[masked-bitmap-count]: index
		{
			name: "Masked Bitmap " + (index + 1)
			bitmap-number: index

			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size			
			}

			offset: _offset + 4

			compressed "keen-huffman"
		}

		file[sprite-count]: index
		{
			name: "Sprite " + (index + 1)
			bitmap-number: index

			unsigned24 _offset			

			from packed-name at _offset
			{
				unsigned32 decompressed-size			
			}

			offset: _offset + 4

			compressed "keen-huffman"
		}

		file[1]
		{
			name: "Unmasked 8x8 tiles"
			unsigned24 offset

			decompressed-size: 104 * 8 * 4
			compressed "keen-huffman"
			interpret-as "keenengine-unmasked8x8"
		}

		file[1]
		{
			name: "Masked 8x8 tiles"
			unsigned24 offset

			decompressed-size: 12 * 8 * 5
			compressed "keen-huffman"
			interpret-as "keenengine-masked8x8"
		}

		skips: 0

		file[1]
		{
			name: "Unmasked 16x16 tiles"

			loop
			{
				unsigned24 offset
				skips = skips + 1
			} while ( offset == 0xFFFFFF )

		}

		data((episode-table[file.name].num16u - skips) * 3) tile-offsets //unmasked 16x16 offsets

		skips = 0

		file[1]
		{
			name: "Masked 16x16 tiles"

			offset: 0

			loop
			{
				unsigned24 offset
				skips = skips + 1
			} while ( offset == 0xFFFFFF )

			//compressed "keen-huffman"
		}
	}
}

if (file.name like "audiot.bm?")
{
	packed-name: file.name
	file-number: 1
	from "audiohed." + episode-table[file.name].extension
	{
		last-offset: 0 - 1
		file while (file.remaining-bytes > 0)
		{
			name: "audio." + file-number
			unsigned32 offset

			skip-if (offset == last-offset) //don't include dummy offsets
			skip-if (file.position == file.size) //don't include offset to end of file

			if (file-number <= episode-table[file.name].numsound)
			{
				interpret-as "sound-PC-APOGEE-AUDIOT"
				name = "SFX\\sound " + file-number
			} else if (file-number > ((episode-table[file.name].numsound * 2) + 1))
			{
				interpret-as "music-imf-560-2"
				name = "Music\\music " + file-number
			} else if (file-number == ((episode-table[file.name].numsound * 2) + 1))
			{
				//ID marker
			}
			else
			{
				name = "sfx-adlib\\sfx " + (file-number - (episode-table[file.name].numsound))
				interpret-as "adlib-sound"
			}
				

			last-offset = offset
			file-number = file-number + 1
		}
	}
}

//Images

if (file.name like "bitmap *")
{
	from "Bitmaps header" at bitmap-number * 4
	{
		unsigned16 width-in-bytes
		unsigned16 height
	}

	image
	{
		format: "I4"
		width: width-in-bytes * 8
		bit-stride: width * height
	}
}

if (file.name like "Masked Bitmap *")
{
	from "Masked Bitmaps header" at bitmap-number * 4 
	{
		unsigned16 width-in-bytes
		unsigned16 height
	}

	image
	{
		format: "A1I4"
		width: width-in-bytes * 8
		bit-stride: width * height
		alpha-mask: 1
	}
}

if (file.name like "Sprite *")
{
	folder: "Sprites"

	from "Sprites header" at bitmap-number * 18
	{
		unsigned16 width-in-bytes
		unsigned16 height
		signed16 OrgX
		signed16 OrgY
		signed16 Rx1
		signed16 Ry1
		signed16 Rx2
		signed16 Ry2
		unsigned16 Shifts
	}

	image
	{
		format: "A1I4"	
		width: width-in-bytes * 8
		bit-stride: width * height
		alpha-mask: 1
	}
}

if (file.name == "unmasked 16x16 tiles")
{
	from header-name at (7 * 3) + (bitmap-count * 3) + (masked-bitmap-count * 3) + (sprite-count * 3)
	{
		unsigned24[episode-table[packed-name].num16u] offsets
	}
	current-tile: 0

	image-layout-width: 288
	image-layout
	{
		loop (episode-table[packed-name].num16u)
		{
			offset-to-image: offsets[current-tile]

			if (offset-to-image != 0xFFFFFF)
			{
				at offset-to-image - offset:

				decompressed-size: 128
				compressed[file.remaining-bytes] "keen-huffman"
				{
					image
					{
						format: "I4"
						width: 16
						height: 16
						bit-stride: width * height
					}
				}

			}
			else
			{
				image
				{
					format: "Blank"
					width: 16
					height: 16
				}
			}
			current-tile = current-tile + 1
		}			
	}
}

if (file.name == "masked 16x16 tiles")
{
	from header-name at (7 * 3) + (bitmap-count * 3) + (masked-bitmap-count * 3) + (sprite-count * 3) + (episode-table[packed-name].num16u * 3)
	{
		unsigned24[episode-table[packed-name].num16m] offsets
	}
	current-tile: 0

	image-layout-width: 288
	image-layout
	{
		loop (episode-table[packed-name].num16m)
		{
			offset-to-image: offsets[current-tile]

			if (offset-to-image != 0xFFFFFF)
			{
				at offset-to-image - offset:

				decompressed-size: 160
				compressed [file.remaining-bytes] "keen-huffman"
				{
					image
					{
						format: "A1I4"
						width: 16
						height: 16
						bit-stride: width * height
						alpha-mask: 1
					}
				}
			}
			else
			{
				image
				{
					format: "Blank"
					width: 16
					height: 16
				}
			}
			current-tile = current-tile + 1
		}
	}
}

if (file.name like "maptemp.bm?")
{
	packed-name: file.name
	packed-size: file.size

	level-graphics-name: episode-table[file.name].graphics-name

	from "maphead." + episode-table[file.name].extension
	{
		unsigned16 rlew-magic 
		file [100]
		{
			unsigned32 headerOffset

			offset: headerOffset
			size: packed-size

			skip-if headerOffset == 0xFFFFFFFF

			from packed-name at headerOffset
			{
				unsigned32[3] planeOffsets
				unsigned16[3] planeSizes
				unsigned16 width
				unsigned16 height
				fixed-string(16) name
				fixed-string(4) signature //!ID!

				interpret-as "bm-level"
			}
		}
	}
}

file-format bm-level
{
	level
	{
		grid-x: 16
		grid-y: 16

		at planeOffsets[0] - offset :
		unsigned16 decompressed-size
		magic-number: 0xABCD
		compressed [planeSizes[0]] "KeenRLE"
		{
			level-layer
			{
				data-type: "unsigned16"
				layer-name: "Background"
				layer-image: level-graphics-name + "\\Unmasked 16x16 tiles"
				order: 0
			}
		}

		at planeOffsets[1] - offset :
		unsigned16 decompressed-size
		magic-number: 0xABCD
		compressed [planeSizes[1]] "KeenRLE"
		{
			level-layer
			{
				data-type: "unsigned16"
				layer-name: "Foreground"
				layer-image: level-graphics-name + "\\Masked 16x16 tiles"
				order: 1
				ignore-tile: 0
			}
		}

		at planeOffsets[2] - offset :
		unsigned16 decompressed-size
		magic-number: 0xABCD
		compressed [planeSizes[2]] "KeenRLE"
		{
			level-layer
			{
				data-type: "unsigned16"
				layer-name: "Objects"
				layer-image: level-graphics-name + "\\Masked 16x16 tiles"
				order: 1
				ignore-tile: 0
				max-valid-tile: 0x1FF
				dump-tile-id: true
			}
		}
	}
}