display-name: "Games/Commander Keen 1-3"
meta: {"Working": ["All images", "Levels", "Audio"], "Thanks to": "Andy Durdin"}

//LZW compression parameters for Keen 1
lz-error: 256
lz-eof: 257
lz-start: 258
lz-startlocation: 6
lz-bigendian: true

filenames:
{
	"egahead.ck1": {"sprite-name": "egasprit.ck1", "latch-name": "egalatch.ck1"},
	"egahead.ck2": {"sprite-name": "egasprit.ck2", "latch-name": "egalatch.ck2"},
	"egahead.ck3": {"sprite-name": "egasprit.ck3", "latch-name": "egalatch.ck3"}
}

if (file.name like "egahead.ck?")
{
	header-file: file.name

	unsigned32 LatchPlaneSize	// Size of one plane of latch data
  	unsigned32 SpritePlaneSize	// Size of one plane of sprite data
	unsigned32 OffBitmapTable	// Offset in EGAHEAD to bitmap table
	unsigned32 OffSpriteTable	// Offset in EGAHEAD to sprite table
	unsigned16 Num8Tiles		// Number of 8x8 tiles
	unsigned32 Off8Tiles		// Offset of 8x8 tiles (relative to plane data)
	unsigned16 Num32Tiles		// Number of 32x32 tiles (always 0)
	unsigned32 Off32Tiles		// Offset of 32x32 tiles (relative to plane data)
	if (OffBitmapTable == 0x40)
	{
		unsigned16 NumUnknown
		unsigned32 OffUnknown
	}
	unsigned16 Num16Tiles		// Number of 16x16 tiles
	unsigned32 Off16Tiles		// Offset of 16x16 tiles (relative to plane data)
	unsigned16 NumBitmaps		// Number of bitmaps in table
	unsigned32 OffBitmaps		// Offset of bitmaps (relative to plane data)
	unsigned16 NumSprites		// Number of sprites
	unsigned32 OffSprites		// Offset of sprites (relative to plane data)
	unsigned16 IsCompressed	        // (Keen 1 only) Nonzero: LZ compressed data

	//get size of latch and sprite files
	from filenames[file.name].sprite-name
	{
		sprite-file-size: file.size
	}
	from filenames[file.name].latch-name
	{
		latch-file-size: file.size
	}

	at OffBitmapTable:

	file[NumBitmaps]
	{
		unsigned16 width-in-bytes
		width: width-in-bytes * 8
		unsigned16 height
		unsigned32 local-bitmap-offset
		bitmap-offset: local-bitmap-offset + OffBitmaps
		fixed-string(8) name
			
		interpret-as "keen13-bitmap"

		size: latch-file-size
		offset: 0
		filename: filenames[file.name].latch-name

		if (IsCompressed != 0)
		{
			decompressed-size: LatchPlaneSize * 4
			compressed "LZW"
		}
	}

	at OffSpriteTable:

	file[NumSprites]
	{
		unsigned16 width-in-bytes
		width: width-in-bytes * 8
		unsigned16 height
		unsigned16 offsetDelta
		unsigned16 offsetParas
		unsigned16 rx1
		unsigned16 ry1
		unsigned16 rx2
		unsigned16 ry2
		fixed-string(16) name

		interpret-as "keen13-sprite"

		sprite-offset: offsetParas * 16 + offsetDelta

		//as the various planes can be found all through the sprite file
		//we need to get the whole file
		size: sprite-file-size
		offset: 0

		data(32 * 3) unknown 	//dummy sprite headers

		filename: filenames[file.name].sprite-name

		if (IsCompressed != 0)
		{
			decompressed-size: SpritePlaneSize * 5
			compressed "LZW"
		}
	}

	file[1]
	{
		name: "8x8 Tiles"		
		
		size: latch-file-size
		offset: 0
		filename: filenames[file.name].latch-name

		if (IsCompressed != 0)
		{
			decompressed-size: LatchPlaneSize * 4
			compressed "LZW"
		}

		interpret-as "keen13-8x8Tile"
	}

	file[1]
	{
		name: "16x16 Tiles"

		size: latch-file-size
		offset: 0
		filename: filenames[file.name].latch-name

		if (IsCompressed != 0)
		{
			decompressed-size: LatchPlaneSize * 4
			compressed "LZW"
		}

		interpret-as "keen13-16x16Tile"
	}
}

//   Images
file-format keen13-sprite
{
	folder: "Sprites"

	at sprite-offset:

	image
	{
		format: "I4A1"
		data-size: file.size - sprite-offset	
		alpha-mask: 1
		bit-stride: SpritePlaneSize * 8
	}
}


file-format keen13-bitmap
{
	folder: "Bitmaps"

	at bitmap-offset:

	image
	{
		format: "I4"
		data-size: file.size - bitmap-offset
		bit-stride: LatchPlaneSize * 8		
	}
}

file-format keen13-8x8Tile
{
	folder: "Tiles"

	at Off8Tiles:

	image
	{
		format: "I4"
		data-size: file.size - Off8Tiles
		width: 8
		height: 8	

		tile-count: Num8Tiles
		per-image-offset: 8

		tile-count: Num8Tiles
		tiles-across: 320 / 8

		bit-stride: LatchPlaneSize * 8
	}
}

file-format keen13-16x16Tile
{
	folder: "Tiles"

	at Off16Tiles:

	image
	{
		format: "I4"
		data-size: file.size - Off16Tiles
		width: 16
		height: 16		

		tile-count: Num16Tiles
		per-image-offset: 32

		tiles-across: 208 / 16

		bit-stride: LatchPlaneSize * 8
	}
}

level-image-table:
{
	["level*.ck1"]:	"egahead.ck1",
	["level*.ck2"]:	"egahead.ck2",
	["level*.ck3"]:	"egahead.ck3"
}

level-sprite-mapping:
{
	["level*.ck1"]: {
		1: "yorpstand1",
		2: "gargstand1",
		3: "vortstand1",
		4: "canstand1",
		5: "tankstand1",
		23: "",
		255: "keenwalkr1",
		"*": "debug"
	},
	["level*.ck2"]: {
		1: "gruntstand1",
		2: "youthleft1",
		3: "eliteleft1",
		4: "scrubl1",
		5: "guardstand1",
		6: "platform1",
		7: "spark1",
		250: "",
		255: "keenwalkr1",
		64004: "",
		"*": "debug"
	},
	["level*.ck3"]: {
		1: "gruntstand1",
		2: "youthleft1",
		3: "womanl1",
		4: "meepl1",
		5: "ninjal1",
		6: "foobl1",
		7: "ball",
		8: "jack1",
		9: "platform1",
		10: "platform1",
		11: "gruntjumpr",
		12: "spark1",
		13: "heart1",
		14: "tankshot",
		15: "vtankshot",
		16: "", //no idea
		17: "", //no idea
		18: "", //no idea
		255: "keenwalkr1",
		"*": "debug"
	}
}

if (file.name like "level??.ck?")
{
	unsigned32 decompressed-size
	magic-number: 0xFEFE

	compressed [file.remaining-bytes] "KeenRLE"
	{
		unsigned16 width
		unsigned16 height
		unsigned16 level-plane-count //always 2
		unsigned32 zero //always 0
		unsigned32 ted-variables
		unsigned16 level-plane-size
		data(16) unknown

		level
		{
			grid-x: 16
			grid-y: 16

			level-layer
			{
				data-type: "unsigned16"
				layer-image: level-image-table[file.name] + "\\16x16 Tiles"
				order: 0
			}
			
			if (!(file.name like "level80.ck?"))
			{
				at level-plane-size + 32:

				level-layer
				{
					layer-name: "Objects"
					ignore-tile: 0
					data-type: "unsigned16"
					layer-images-prefix: level-image-table[file.name] + "\\"
					layer-images: level-sprite-mapping[file.name]
					order: 1
				}
			}
		}
	}
}

if (file.name like ["*text.ck?", "*txt.ck?", "previews.ck?"])
{
	text
}

snd-table-keen2: {
	0x1CF90: {"offset": 0x12930, "size": 0x310A},
	0x1CF62: {"offset": 0x12A40, "size": 0x310A}
}

snd-table-keen3: {
	0x1F26E: {"offset": 0x13DA0, "size": 0x3F60},
	0x1F280: {"offset": 0x13C70, "size": 0x3F60}
}

if (file.name == "keen2.exe")
{
	file[1]
	{
		name: "sounds.ck2"
		offset: snd-table-keen2[file.size].offset
		size: snd-table-keen2[file.size].size
	}
}

if (file.name == "keen3.exe")
{
	file[1]
	{
		name: "sounds.ck3"
		offset: snd-table-keen3[file.size].offset
		size: snd-table-keen3[file.size].size
	}
}

if (file.name like "sounds.ck?")
{
	interpret-as "archive-SND_DOS"
}

if (file.name like "preview1.ck?")
{
	folder: "Unknown images"
}

if (file.name like ["finale.ck?", "preview?.ck?"])
{
	compressed [file.size] "KeenFIN"
	{
		image
		{
			format: "I4"
			width: 320
			height: 200
			bit-stride: width * height
		}
	}
}