display-name: "Games/One Must Fall 2097"
meta: {"Working": ["Some fullscreen images and backgrounds", "Text", "Sound"]}

if (file.name like "*.pcx")
{
	interpret-as "image-PCX"
}

if (file.name like ["english.dat", "german.dat"])
{
	folder: "Language files"

	unsigned32 first-offset
	at 0:

	string-count: first-offset / 36

	file[string-count]: i
	{
		unsigned32 offset
		fixed-string(32) desc
		name: "String " + i
		interpret-as "omf-text"
		encrypted "omf-text-xor"
	}
}

file-format omf-text
{
	text
}

function omf-text-xor
{
	xor-start: file.size & 0xFF

	loop (file.size)
	{
		read unsigned8 encrypted
		write unsigned8 (encrypted ^ xor-start)
		xor-start = (xor-start + 1) % 0x100
	}
}

if (file.name == "sounds.dat")
{
	unsigned32 block-0
	unsigned32 header-size

	at 4:
	file[header-size / 4]: i
	{
		name: "Sound " + i
		unsigned32 offset
		interpret-as "omf-sound"

		skip-if (((i > 5) and (i < 11)) or (i == 17) or (i == 18) or (i == 21) or (i == 58) or (i == 59) or (i >= 72))
	}
}

file-format omf-sound
{
	sound
	{
		samples-per-second: 8000
		bits-per-sample: 8
		channels: 1
	}
}

if (file.name like "*.pic")
{
	interpret-as-internally "omf-pic-pal-archive"

	folder: "Pilot images"

	unsigned32 image-count
	data(0xC4) unknown

	unsigned32[image-count] offsets
	offsets[image-count] = file.size

	loop (image-count): i
	{
		//at offsets[i]:

		unsigned8 is-player
		unsigned16 gender //0 = male, 1 = female

		data(144) unknown

		palette: "arena0.bk\\palette"

		unsigned8 unknown

		unsigned16 length
		signed16 x-pos
		signed16 y-pos

		unsigned16 width
		width = width + 1

		unsigned16 height
		height = height + 1

		unsigned8 index
		unsigned8 data-missing

		if (data-missing == 0)
		{
			//Image data goes here!
			compressed[file.remaining-bytes] "omf-sprite"
			{
				image
				{
					format: "I8"
				}
			}
		}
	}
}

file-format omf-pic-pal
{
	palette
	{
		format: "R6G6B6"
		size: 48
	}
}

file-format omf-pic-pal-archive
{
	unsigned32 image-count
	data(0xC4) unknown

	unsigned32[image-count] offsets
	offsets[image-count] = file.size

	file [image-count]: i
	{
		offset: offsets[i] + 3
		name: "" + i
		interpret-as "omf-pic-pal"
	}
}

if (file.name == "graphchr.dat")
{
	folder: "Fonts"

	image
	{
		format: "I1"
		width: 8
		height: 8
		tiles-across: 32
		data-size: file.size
	}
}

if (file.name == "charsmal.dat")
{
	folder: "Fonts"

	image
	{
		format: "I1"
		width: 8
		height: 6
		tiles-across: 32
		data-size: file.size
	}
}

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

if (file.name like "*.mus")
{
	folder: "Audio drivers"
}

if (file.name like "*end.bin")
{
	b800-text
}

if (file.name like "*.doc")
{
	text
}

if ((file.name like "*.bk") and !(file.name like "credits*"))
{
	folder: "Backgrounds"

	unsigned32 unknown-1
	unsigned8 unknown-2
	unsigned16 width
	unsigned16 height

	animation-start: file.position
	loop
	{
		unsigned32 next-offset
		unsigned8 animation-number

		at next-offset:
	} while (animation-number < 50)

	at file.position + 5:

	image-start: file.position

	file[1]
	{
		name: "palette"
		offset: file.position + (320 * 200) + 1
		size: 256 * 3
		interpret-as "bk-palette"
	}

	at animation-start:

	frame: 0

	loop
	{
		unsigned32 next-offset
		unsigned8 animation-number

		if (animation-number < 50)
		{
			unsigned8 animation-unknown
			unsigned8 animation-unknownA
			unsigned8 animation-unknownB
			unsigned8 repeat
			unsigned16 probability
			unsigned8 animation-unknownE
			unsigned16 something-size
			data(something-size) something

			this-offset: file.position

			signed16 start-x
			signed16 start-y
			data(4) unknown
			unsigned16 collision-count
			unsigned8 frame-count
			unsigned32[collision-count] collisions

			//Strings
			pascal-string(unsigned16) animation-string
			unsigned8 null-terminator
			unsigned8 extra-strings

			loop (extra-strings)
			{
				pascal-string(unsigned16) animation-string
				unsigned8 null-terminator
			}

			file[1]
			{
				offset: this-offset
				name: "Frame " + frame //animation-string
				frame = frame + 1
				size: file.size - this-offset
				interpret-as "omf-anim"
			}

			at next-offset:
		}

	} while (animation-number < 50)

	file[1]
	{
		name: "background"
		offset: image-start
		size: 320*200
		interpret-as "bk-image"
	}
}

if (file.name like "*.af")
{
	data(0x1D) header

	file
	{
		unsigned8 animation-number

		if (animation-number < 70)
		{
			offset: file.position
			name: "Animation " + animation-number
			interpret-as "omf-anim"

			//Skip data
			data(8) skip
			unsigned16 collision-count
			unsigned8 frame-count
			data(collision-count * 4) skip

			//Strings
			pascal-string(unsigned16) animation-string

			unsigned8 null-terminator
			unsigned8 extra-strings

			loop (extra-strings)
			{
				pascal-string(unsigned16) animation-string
				unsigned8 null-terminator
			}

			loop (frame-count)
			{
				unsigned16 length
				data(9) skip
				unsigned8 data-missing

				if (data-missing == 0)
				{
					data(length) sprite
				}
			}

			data(42) move-footer
			pascal-string(unsigned16) footer-string
		}
	} while (animation-number < 70)
}

file-format omf-anim
{
	signed16 start-x
	signed16 start-y
	data(4) unknown
	unsigned16 collision-count
	unsigned8 frame-count
	unsigned32[collision-count] collisions

	//Strings
	pascal-string(unsigned16) animation-string

	unsigned8 null-terminator
	unsigned8 extra-strings

	loop (extra-strings)
	{
		pascal-string(unsigned16) animation-string
		unsigned8 null-terminator
	}

	loop (frame-count)
	{
		unsigned16 length
		signed16 x-pos
		signed16 y-pos

		unsigned16 width
		width = width + 1

		unsigned16 height
		height = height + 1

		unsigned8 index
		unsigned8 data-missing

		if (width < 10000) //HACK for various BK files...?
		{
			if (data-missing == 0)
			{
				//Image data goes here!
				compressed[length] "omf-sprite"
				{
					image
					{
						if (exists("palette"))
						{
							palette: "palette"
						}
						else
						{
							palette: "north_am.pic\\1"
						}
						format: "I8"
						transparent-index: 0
					}
				}
			}
		}
	}
}

file-format bk-image
{
	image
	{
		format: "I8"
		width: 320
		height: 200
		palette: "palette"
	}

	unsigned8 palette-count
}

file-format bk-palette
{
	palette
	{
		format: "R6G6B6"
		size: 256
	}
}

function omf-sprite
{
	results: [](width * height)

	x: 0
	y: 0

	running: true

	loop while ((running == true) and (y < height))
	{
		read unsigned16 instruction
		opcode: instruction & 0x3
		op-data: instruction >> 2

		if (opcode == 0)
		{
			x = op-data
		}
		else if (opcode == 1)
		{
			loop (op-data)
			{
				read unsigned8 pixel
				results[y * width + x] = pixel
				x = x + 1
			}
			x = 0
		}
		else if (opcode == 2)
		{
			y = op-data
		}
		else
		{
			running = false
		}
	}

	write unsigned8 results
}

if (file.name like "*.trn")
{
	folder: "Tournaments"

	file[1]
	{
		offset: 0
		size: 42
		name: "Properties"
		interpret-as "omf-trn-props"
	}

	unsigned32 opponent-count

	at 300:
	unsigned32[opponent-count + 1] offsets

	file[opponent-count]: i
	{
		name: "Enemy block " + i
		offset: offsets[i]
		size: offsets[i + 1] - offsets[i]
		encrypted "omf-trn-block-encryption"
	}	

	at offsets[offsets.length - 1]:

	sprite-start: file.position

	loop (10)
	{
		unsigned16 length
		signed16 x-pos
		signed16 y-pos

		unsigned16 width
		width = width + 1

		unsigned16 height
		height = height + 1

		unsigned8 index
		unsigned8 data-missing

		data(length) sprite-data
	}

	file[1]
	{
		name: "Logos"
		offset: sprite-start
		size: file.position - sprite-start
		interpret-as "omf-trn-logos"
		palette-name: file.name + "\\palette"
	}

	file[1]
	{
		name: "Palette"
		offset: file.position
		size: 120
		compressed "omf-trn-pal-offset"
		interpret-as "omf-trn-pal"
	}

	data(120) pal

	desc-start: file.position

	loop(21)
	{
		unsigned16 len
		data(len) strings
	}

	file[1]
	{
		name: "strings"
		size: file.position - desc-start
		offset: desc-start
		interpret-as "omf-trn-strings"
	}

	file[1]
	{
		name: "victory texts"
		size: file.remaining-bytes
		offset: file.position
		interpret-as "omf-trn-vic"
	}
}

function omf-trn-block-encryption
{
	xor-start: 428 & 0xFF

	loop (428)
	{
		read unsigned8 encrypted
		write unsigned8 (encrypted ^ xor-start)
		xor-start = (xor-start + 1) % 0x100
	}

	loop(file.remaining-bytes)
	{
		read unsigned8 c
		write unsigned8 c
	}
}

file-format omf-trn-vic
{
	loop (1100)
	{
		unsigned16 string-len
		if (string-len != 0)
		{
			fixed-string(string-len) vic-string

			text
			{
				string-data: vic-string + "\r\n"
			}
		}
	}
}

file-format omf-trn-strings
{
	unsigned16 pic-len
	fixed-string(pic-len) pic-string
	titles: [](10)
	descs: [](10)

	loop (10): i
	{
		unsigned16 title-len

		if (title-len == 0)
		{
			titles[i] = ""
		}
		else
		{
			fixed-string(title-len) this-title
			titles[i] = this-title
		}

		unsigned16 desc-len

		if (desc-len == 0)
		{
			descs[i] = ""
		}
		else
		{
			fixed-string(desc-len) this-desc
			descs[i] = this-desc
		}
	}

	property-list
	{
		pic-file: pic-string
		if (titles[0] != "")
		{
			title-english: titles[0]
		}	
		if (descs[0] != "")
		{
			desc-english: descs[0]
		}	
		if (titles[1] != "")
		{
			title-german: titles[1]
		}	
		if (descs[1] != "")
		{
			desc-german: descs[1]
		}	
	}
}

file-format omf-trn-pal
{
	palette
	{
		size: 168
		format: "R6G6B6"
	}
}

function omf-trn-pal-offset
{
	loop(128 * 3)
	{
		write unsigned8 0x3F
	}

	loop(120)
	{
		read unsigned8 c
		write unsigned8 c
	}
}

file-format omf-trn-logos
{
	loop(10)
	{
		unsigned16 length
		signed16 x-pos
		signed16 y-pos

		unsigned16 width
		width = width + 1

		unsigned16 height
		height = height + 1

		unsigned8 index
		unsigned8 data-missing

		if (length != 0)
		{
			//Image data goes here!
			compressed[file.remaining-bytes] "omf-sprite"
			{
				image
				{
					format: "I8"
					palette: palette-name
				}
			}
		}
	}
}

file-format omf-trn-props
{
	folder: ""
	property-list
	{
		unsigned32 opponent-count
		data(4) victory-texts-offset
		fixed-string(14) ending-bk-file
		unsigned32 winnings-multiplier
		data(4) unknown
		unsigned32 registration-fee
		unsigned32 assumed-initial-value
		unsigned32 tournament-id
	}
}

