/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

// Disable symbol overrides so that we can use system headers.
#define FORBIDDEN_SYMBOL_ALLOW_ALL

// HACK to allow building with the SDL backend on MinGW
// see bug #1800764 "TOOLS: MinGW tools building broken"
#ifdef main
#undef main
#endif // main

#include "create_kyradat.h"

#include "search.h"
#include "pak.h"

#include "tables.h"
#include "extract.h"

#include "md5.h"
#include "common/language.h"
#include "common/platform.h"

#include <string>
#include <map>
#include <algorithm>
#include <map>

enum {
	kKyraDatVersion = 84
};

const ExtractFilename extractFilenames[] = {
	// GENERIC ID MAP
	{ kIdMap, -1, true },

	// INTRO / OUTRO sequences
	{ k1ForestSeq, kTypeForestSeqData, false },
	{ k1KallakWritingSeq, kTypeRawData, false },
	{ k1KyrandiaLogoSeq, kTypeRawData, false },
	{ k1KallakMalcolmSeq, kTypeRawData, false },
	{ k1MalcolmTreeSeq, kTypeRawData, false },
	{ k1WestwoodLogoSeq, kTypeRawData, false },
	{ k1Demo1Seq, kTypeRawData, false },
	{ k1Demo2Seq, kTypeRawData, false },
	{ k1Demo3Seq, kTypeRawData, false },
	{ k1Demo4Seq, kTypeRawData, false },
	{ k1OutroReunionSeq, kTypeRawData, false },

	// INTRO / OUTRO strings
	{ k1IntroCPSStrings, kTypeStringList, false },
	{ k1IntroCOLStrings, kTypeStringList, false },
	{ k1IntroWSAStrings, kTypeStringList, false },
	{ k1IntroStrings, kTypeStringList, true },
	{ k1OutroHomeString, kTypeStringList, true },

	// INGAME strings
	{ k1ItemNames, kTypeStringList, true },
	{ k1TakenStrings, kTypeStringList, true },
	{ k1PlacedStrings, kTypeStringList, true },
	{ k1DroppedStrings, kTypeStringList, true },
	{ k1NoDropStrings, kTypeStringList, true },
	{ k1PutDownString, kTypeStringList, true },
	{ k1WaitAmuletString, kTypeStringList, true },
	{ k1BlackJewelString, kTypeStringList, true },
	{ k1PoisonGoneString, kTypeStringList, true },
	{ k1HealingTipString, kTypeStringList, true },
	{ k1ThePoisonStrings, kTypeStringList, true },
	{ k1FluteStrings, kTypeStringList, true },
	{ k1WispJewelStrings, kTypeStringList, true },
	{ k1MagicJewelStrings, kTypeStringList, true },
	{ k1FlaskFullString, kTypeStringList, true },
	{ k1FullFlaskString, kTypeStringList, true },
	{ k1VeryCleverString, kTypeStringList, true },
	{ k1NewGameString, kTypeStringList, true },

	// GUI strings table
	{ k1GUIStrings, kTypeStringList, true },
	{ k1ConfigStrings, kTypeStringList, true },

	// ROOM table/filenames
	{ k1RoomList, kTypeRoomList, false },
	{ k1RoomFilenames, kTypeStringList, false },

	// SHAPE tables
	{ k1DefaultShapes, kTypeShapeList, false },
	{ k1Healing1Shapes, kTypeShapeList, false },
	{ k1Healing2Shapes, kTypeShapeList, false },
	{ k1PoisonDeathShapes, kTypeShapeList, false },
	{ k1FluteShapes, kTypeShapeList, false },
	{ k1Winter1Shapes, kTypeShapeList, false },
	{ k1Winter2Shapes, kTypeShapeList, false },
	{ k1Winter3Shapes, kTypeShapeList, false },
	{ k1DrinkShapes, kTypeShapeList, false },
	{ k1WispShapes, kTypeShapeList, false },
	{ k1MagicAnimShapes, kTypeShapeList, false },
	{ k1BranStoneShapes, kTypeShapeList, false },

	// IMAGE filename table
	{ k1CharacterImageFilenames, kTypeStringList, false },

	// AUDIO filename table
	{ k1AudioTracks, kTypeStringList, false },
	{ k1AudioTracks2, kTypeStringList, false },
	{ k1AudioTracksIntro, kTypeStringList, false },

	// AMULET anim
	{ k1AmuleteAnimSeq, kTypeRawData, false },

	// PALETTE table
	{ k1SpecialPalette1, kTypeRawData, false },
	{ k1SpecialPalette2, kTypeRawData, false },
	{ k1SpecialPalette3, kTypeRawData, false },
	{ k1SpecialPalette4, kTypeRawData, false },
	{ k1SpecialPalette5, kTypeRawData, false },
	{ k1SpecialPalette6, kTypeRawData, false },
	{ k1SpecialPalette7, kTypeRawData, false },
	{ k1SpecialPalette8, kTypeRawData, false },
	{ k1SpecialPalette9, kTypeRawData, false },
	{ k1SpecialPalette10, kTypeRawData, false },
	{ k1SpecialPalette11, kTypeRawData, false },
	{ k1SpecialPalette12, kTypeRawData, false },
	{ k1SpecialPalette13, kTypeRawData, false },
	{ k1SpecialPalette14, kTypeRawData, false },
	{ k1SpecialPalette15, kTypeRawData, false },
	{ k1SpecialPalette16, kTypeRawData, false },
	{ k1SpecialPalette17, kTypeRawData, false },
	{ k1SpecialPalette18, kTypeRawData, false },
	{ k1SpecialPalette19, kTypeRawData, false },
	{ k1SpecialPalette20, kTypeRawData, false },
	{ k1SpecialPalette21, kTypeRawData, false },
	{ k1SpecialPalette22, kTypeRawData, false },
	{ k1SpecialPalette23, kTypeRawData, false },
	{ k1SpecialPalette24, kTypeRawData, false },
	{ k1SpecialPalette25, kTypeRawData, false },
	{ k1SpecialPalette26, kTypeRawData, false },
	{ k1SpecialPalette27, kTypeRawData, false },
	{ k1SpecialPalette28, kTypeRawData, false },
	{ k1SpecialPalette29, kTypeRawData, false },
	{ k1SpecialPalette30, kTypeRawData, false },
	{ k1SpecialPalette31, kTypeRawData, false },
	{ k1SpecialPalette32, kTypeRawData, false },
	{ k1SpecialPalette33, kTypeRawData, false },

	// CREDITS (used in FM-TOWNS and AMIGA)
	{ k1CreditsStrings, kTypeRawData, true },

	// FM-TOWNS specific
	{ k1TownsMusicFadeTable, k3TypeRaw16to8, false },
	{ k1TownsSFXwdTable, kTypeTownsWDSfxTable, false },
	{ k1TownsSFXbtTable, kTypeRawData, false },
	{ k1TownsCDATable, kTypeRawData, false },

	// PC98 specific
	{ k1PC98StoryStrings, kTypeStringList, true },
	{ k1PC98IntroSfx, kTypeRawData, false },

	// AMIGA specific
	{ k1AmigaIntroSFXTable, kTypeAmigaSfxTable, false },
	{ k1AmigaGameSFXTable, kTypeAmigaSfxTable, false },

	// HAND OF FATE

	// Sequence Player
	{ k2SeqplayPakFiles, kTypeStringList, false },
	{ k2SeqplayCredits, kTypeRawData, false },
	{ k2SeqplayCreditsSpecial, kTypeStringList, false },
	{ k2SeqplayStrings, kTypeStringList, true },
	{ k2SeqplaySfxFiles, k2TypeSoundList, false },
	{ k2SeqplayTlkFiles, k2TypeLangSoundList, true },
	{ k2SeqplaySeqData, k2TypeSeqData, false },
	{ k2SeqplayIntroTracks, kTypeStringList, false },
	{ k2SeqplayFinaleTracks, kTypeStringList, false },
	{ k2SeqplayIntroCDA, kTypeRawData, false },
	{ k2SeqplayFinaleCDA, kTypeRawData, false },
	{ k2SeqplayShapeAnimData, k2TypeShpDataV1, false },

	// Ingame
	{ k2IngamePakFiles, kTypeStringList, false },
	{ k2IngameSfxFiles, k2TypeSize10StringList, false },
	{ k2IngameSfxIndex, kTypeRawData, false },
	{ k2IngameTracks, kTypeStringList, false },
	{ k2IngameCDA, kTypeRawData, false },
	{ k2IngameTalkObjIndex, kTypeRawData, false },
	{ k2IngameTimJpStrings, kTypeStringList, false },
	{ k2IngameShapeAnimData, k2TypeShpDataV2, false },
	{ k2IngameTlkDemoStrings, kTypeStringList, true },


	// MALCOLM'S REVENGE
	{ k3MainMenuStrings, kTypeStringList, false },
	{ k3MusicFiles, k2TypeSoundList, false },
	{ k3ScoreTable, kTypeRawData, false },
	{ k3SfxFiles, k2TypeSfxList, false },
	{ k3SfxMap, k3TypeRaw16to8, false },
	{ k3ItemAnimData, k3TypeShpData, false },
	{ k3ItemMagicTable, k3TypeRaw16to8, false },
	{ k3ItemStringMap, kTypeRawData, false },

	// EYE OF THE BEHOLDER COMMON
	{ kEoBBaseChargenStrings1, kTypeStringList, true },
	{ kEoBBaseChargenStrings2, kTypeStringList, true },
	{ kEoBBaseChargenStartLevels, kTypeRawData, false },
	{ kEoBBaseChargenStatStrings, kTypeStringList, true},
	{ kEoBBaseChargenRaceSexStrings, kTypeStringList, true },
	{ kEoBBaseChargenClassStrings, kTypeStringList, true },
	{ kEoBBaseChargenAlignmentStrings, kTypeStringList, true },
	{ kEoBBaseChargenEnterGameStrings, kTypeStringList, true },
	{ kEoBBaseChargenClassMinStats, k3TypeRaw16to8, false },
	{ kEoBBaseChargenRaceMinStats, k3TypeRaw16to8, false },
	{ kEoBBaseChargenRaceMaxStats, kLoLTypeRaw16, false },

	{ kEoBBaseSaveThrowTable1, kTypeRawData, false },
	{ kEoBBaseSaveThrowTable2, kTypeRawData, false },
	{ kEoBBaseSaveThrowTable3, kTypeRawData, false },
	{ kEoBBaseSaveThrowTable4, kTypeRawData, false },
	{ kEoBBaseSaveThrwLvlIndex, kTypeRawData, false },
	{ kEoBBaseSaveThrwModDiv, kTypeRawData, false },
	{ kEoBBaseSaveThrwModExt, kTypeRawData, false },

	{ kEoBBasePryDoorStrings, kTypeStringList, true },
	{ kEoBBaseWarningStrings, kTypeStringList, true },

	{ kEoBBaseItemSuffixStringsRings, kTypeStringList, true },
	{ kEoBBaseItemSuffixStringsPotions, kTypeStringList, true },
	{ kEoBBaseItemSuffixStringsWands, kTypeStringList, true },

	{ kEoBBaseRipItemStrings, kTypeStringList, true },
	{ kEoBBaseCursedString, kTypeStringList, true },
	{ kEoBBaseEnchantedString, kTypeStringList, false },
	{ kEoBBaseMagicObjectStrings, kTypeStringList, true },
	{ kEoBBaseMagicObject5String, kTypeStringList, true },
	{ kEoBBasePatternSuffix, kTypeStringList, true },
	{ kEoBBasePatternGrFix1, kTypeStringList, true },
	{ kEoBBasePatternGrFix2, kTypeStringList, true },
	{ kEoBBaseValidateArmorString, kTypeStringList, true },
	{ kEoBBaseValidateCursedString, kTypeStringList, true },
	{ kEoBBaseValidateNoDropString, kTypeStringList, true },
	{ kEoBBasePotionStrings, kTypeStringList, true },
	{ kEoBBaseWandString, kTypeStringList, true },
	{ kEoBBaseItemMisuseStrings, kTypeStringList, true },

	{ kEoBBaseTakenStrings, kTypeStringList, true },
	{ kEoBBasePotionEffectStrings, kTypeStringList, true },

	{ kEoBBaseYesNoStrings, kTypeStringList, true },
	{ kRpgCommonMoreStrings, kTypeStringList, true },
	{ kEoBBaseNpcMaxStrings, kTypeStringList, true },
	{ kEoBBaseOkStrings, kTypeStringList, true },
	{ kEoBBaseNpcJoinStrings, kTypeStringList, true },
	{ kEoBBaseCancelStrings, kTypeStringList, true },
	{ kEoBBaseAbortStrings, kTypeStringList, true },

	{ kEoBBaseMenuStringsMain, kTypeStringList, true },
	{ kEoBBaseMenuStringsSaveLoad, kTypeStringList, true },
	{ kEoBBaseMenuStringsOnOff, kTypeStringList, true },
	{ kEoBBaseMenuStringsSpells, kTypeStringList, true },
	{ kEoBBaseMenuStringsRest, kTypeStringList, true },
	{ kEoBBaseMenuStringsDrop, kTypeStringList, true },
	{ kEoBBaseMenuStringsExit, kTypeStringList, true },
	{ kEoBBaseMenuStringsStarve, kTypeStringList, true },
	{ kEoBBaseMenuStringsScribe, kTypeStringList, true },
	{ kEoBBaseMenuStringsDrop2, kTypeStringList, true },
	{ kEoBBaseMenuStringsHead, kTypeStringList, true },
	{ kEoBBaseMenuStringsPoison, kTypeStringList, true },
	{ kEoBBaseMenuStringsMgc, kTypeStringList, true },
	{ kEoBBaseMenuStringsPrefs, kTypeStringList, true },
	{ kEoBBaseMenuStringsRest2, kTypeStringList, true },
	{ kEoBBaseMenuStringsRest3, kTypeStringList, true },
	{ kEoBBaseMenuStringsRest4, kTypeStringList, true },
	{ kEoBBaseMenuStringsDefeat, kTypeStringList, true },
	{ kEoBBaseMenuStringsTransfer, kTypeStringList, true },
	{ kEoBBaseMenuStringsSpec, kTypeStringList, true },
	{ kEoBBaseMenuStringsSpellNo, kTypeStringList, false },
	{ kEoBBaseMenuYesNoStrings, kTypeStringList, true },

	{ kEoBBaseSpellLevelsMage, kTypeRawData, false },
	{ kEoBBaseSpellLevelsCleric, kTypeRawData, false },
	{ kEoBBaseNumSpellsCleric, kTypeRawData, false },
	{ kEoBBaseNumSpellsWisAdj, kTypeRawData, false },
	{ kEoBBaseNumSpellsPal, kTypeRawData, false },
	{ kEoBBaseNumSpellsMage, kTypeRawData, false },

	{ kEoBBaseCharGuiStringsHp, kTypeStringList, true },
	{ kEoBBaseCharGuiStringsWp1, kTypeStringList, true },
	{ kEoBBaseCharGuiStringsWp2, kTypeStringList, true },
	{ kEoBBaseCharGuiStringsWr, kTypeStringList, true },
	{ kEoBBaseCharGuiStringsSt1, kTypeStringList, true },
	{ kEoBBaseCharGuiStringsSt2, kTypeStringList, true },
	{ kEoBBaseCharGuiStringsIn, kTypeStringList, true },

	{ kEoBBaseCharStatusStrings7, kTypeStringList, true },
	{ kEoBBaseCharStatusStrings81, kTypeStringList, true },
	{ kEoBBaseCharStatusStrings82, kTypeStringList, true },
	{ kEoBBaseCharStatusStrings9, kTypeStringList, true },
	{ kEoBBaseCharStatusStrings12, kTypeStringList, true },
	{ kEoBBaseCharStatusStrings131, kTypeStringList, true },
	{ kEoBBaseCharStatusStrings132, kTypeStringList, true },

	{ kEoBBaseLevelGainStrings, kTypeStringList, true },
	{ kEoBBaseExperienceTable0, kLoLTypeRaw32, false },
	{ kEoBBaseExperienceTable1, kLoLTypeRaw32, false },
	{ kEoBBaseExperienceTable2, kLoLTypeRaw32, false },
	{ kEoBBaseExperienceTable3, kLoLTypeRaw32, false },
	{ kEoBBaseExperienceTable4, kLoLTypeRaw32, false },

	{ kEoBBaseWllFlagPreset, kTypeRawData, false },
	{ kEoBBaseDscShapeCoords, kLoLTypeRaw16, false },
	{ kEoBBaseDscDoorScaleOffs, kTypeRawData, false },
	{ kEoBBaseDscDoorScaleMult1, kTypeRawData, false },
	{ kEoBBaseDscDoorScaleMult2, kTypeRawData, false },
	{ kEoBBaseDscDoorScaleMult3, kTypeRawData, false },
	{ kEoBBaseDscDoorScaleMult4, kTypeRawData, false },
	{ kEoBBaseDscDoorScaleMult5, kTypeRawData, false },
	{ kEoBBaseDscDoorScaleMult6, kTypeRawData, false },
	{ kEoBBaseDscDoorType5Offs, kTypeRawData, false },
	{ kEoBBaseDscDoorXE, kTypeRawData, false },
	{ kEoBBaseDscDoorY1, kTypeRawData, false },
	{ kEoBBaseDscDoorY3, kTypeRawData, false },
	{ kEoBBaseDscDoorY4, kTypeRawData, false },
	{ kEoBBaseDscDoorY5, kTypeRawData, false },
	{ kEoBBaseDscDoorY6, kTypeRawData, false },
	{ kEoBBaseDscDoorY7, kTypeRawData, false },
	{ kEoBBaseDscDoorCoordsExt, kLoLTypeRaw16, false },

	{ kEoBBaseDscItemPosIndex, kTypeRawData, false },
	{ kEoBBaseDscItemShpX, kLoLTypeRaw16, false },
	{ kEoBBaseDscItemPosUnk, kTypeRawData, false },
	{ kEoBBaseDscItemTileIndex, kTypeRawData, false },
	{ kEoBBaseDscItemShapeMap, kTypeRawData, false },
	{ kEoBBaseDscTelptrShpCoords, kTypeRawData, false },

	{ kEoBBasePortalSeqData, kTypeRawData, false },
	{ kEoBBaseManDef, kTypeRawData, true },
	{ kEoBBaseManWord, kTypeStringList, true },
	{ kEoBBaseManPrompt, kTypeStringList, true },

	{ kEoBBaseDscMonsterFrmOffsTbl1, kTypeRawData, false },
	{ kEoBBaseDscMonsterFrmOffsTbl2, kTypeRawData, false },

	{ kEoBBaseInvSlotX, kLoLTypeRaw16, false },
	{ kEoBBaseInvSlotY, kTypeRawData, false },
	{ kEoBBaseSlotValidationFlags, kLoLTypeRaw16, false },

	{ kEoBBaseProjectileWeaponTypes, kTypeRawData, false },
	{ kEoBBaseWandTypes, kTypeRawData, false },

	{ kEoBBaseDrawObjPosIndex, kTypeRawData, false },
	{ kEoBBaseFlightObjFlipIndex, kTypeRawData, false },
	{ kEoBBaseFlightObjShpMap, kTypeRawData, false },
	{ kEoBBaseFlightObjSclIndex, kTypeRawData, false },

	{ kEoBBaseBookNumbers, kTypeStringList, true },
	{ kEoBBaseMageSpellsList, kTypeStringList, true },
	{ kEoBBaseClericSpellsList, kTypeStringList, true },
	{ kEoBBaseSpellNames, kTypeStringList, true },

	{ kEoBBaseMagicStrings1, kTypeStringList, true },
	{ kEoBBaseMagicStrings2, kTypeStringList, true },
	{ kEoBBaseMagicStrings3, kTypeStringList, true },
	{ kEoBBaseMagicStrings4, kTypeStringList, true },
	{ kEoBBaseMagicStrings6, kTypeStringList, true },
	{ kEoBBaseMagicStrings7, kTypeStringList, true },
	{ kEoBBaseMagicStrings8, kTypeStringList, true },

	{ kEoBBaseExpObjectTlMode, kTypeRawData, false },
	{ kEoBBaseExpObjectTblIndex, kTypeRawData, false },
	{ kEoBBaseExpObjectShpStart, kTypeRawData, false },
	{ kEoBBaseExpObjectTbl1, kTypeRawData, false },
	{ kEoBBaseExpObjectTbl2, kTypeRawData, false },
	{ kEoBBaseExpObjectTbl3, kTypeRawData, false },
	{ kEoBBaseExpObjectY, k3TypeRaw16to8, false },

	{ kEoBBaseSparkDefSteps, kTypeRawData, false },
	{ kEoBBaseSparkDefSubSteps, kTypeRawData, false },
	{ kEoBBaseSparkDefShift, kTypeRawData, false },
	{ kEoBBaseSparkDefAdd, kTypeRawData, false },
	{ kEoBBaseSparkDefX, k3TypeRaw16to8, false },
	{ kEoBBaseSparkDefY, kTypeRawData, false },
	{ kEoBBaseSparkOfFlags1, kLoLTypeRaw32, false },
	{ kEoBBaseSparkOfFlags2, kLoLTypeRaw32, false },
	{ kEoBBaseSparkOfShift, kTypeRawData, false },
	{ kEoBBaseSparkOfX, kTypeRawData, false },
	{ kEoBBaseSparkOfY, kTypeRawData, false },
	{ kEoBBaseSpellProperties, kTypeRawData, false },
	{ kEoBBaseMagicFlightProps, kTypeRawData, false },
	{ kEoBBaseTurnUndeadEffect, kTypeRawData, false },
	{ kEoBBaseBurningHandsDest, kTypeRawData, false },
	{ kEoBBaseConeOfColdDest1, kTypeRawData, false },
	{ kEoBBaseConeOfColdDest2, kTypeRawData, false },
	{ kEoBBaseConeOfColdDest3, kTypeRawData, false },
	{ kEoBBaseConeOfColdDest4, kTypeRawData, false },
	{ kEoBBaseConeOfColdGfxTbl, k3TypeRaw16to8, false },

	// EYE OF THE BEHOLDER I
	{ kEoB1MainMenuStrings, kTypeStringList, true },
	{ kEoB1BonusStrings, kTypeStringList, true },

	{ kEoB1IntroFilesOpening, kTypeStringList, false },
	{ kEoB1IntroFilesTower, kTypeStringList, false },
	{ kEoB1IntroFilesOrb, kTypeStringList, false },
	{ kEoB1IntroFilesWdEntry, kTypeStringList, false },
	{ kEoB1IntroFilesKing, kTypeStringList, false },
	{ kEoB1IntroFilesHands, kTypeStringList, false },
	{ kEoB1IntroFilesWdExit, kTypeStringList, false },
	{ kEoB1IntroFilesTunnel, kTypeStringList, false },
	{ kEoB1IntroOpeningFrmDelay, k3TypeRaw16to8, false },
	{ kEoB1IntroWdEncodeX, kTypeRawData, false },
	{ kEoB1IntroWdEncodeY, kTypeRawData, false },
	{ kEoB1IntroWdEncodeWH, kTypeRawData, false },
	{ kEoB1IntroWdDsX, kLoLTypeRaw16, false },
	{ kEoB1IntroWdDsY, kTypeRawData, false },
	{ kEoB1IntroTvlX1, kTypeRawData, false },
	{ kEoB1IntroTvlY1, kTypeRawData, false },
	{ kEoB1IntroTvlX2, kTypeRawData, false },
	{ kEoB1IntroTvlY2, kTypeRawData, false },
	{ kEoB1IntroTvlW, kTypeRawData, false },
	{ kEoB1IntroTvlH, kTypeRawData, false },

	{ kEoB1DoorShapeDefs, kTypeRawData, false },
	{ kEoB1DoorSwitchShapeDefs, kTypeRawData, false },
	{ kEoB1DoorSwitchCoords, kTypeRawData, false },
	{ kEoB1MonsterProperties, kTypeRawData, false },
	{ kEoB1EnemyMageSpellList, kTypeRawData, false },
	{ kEoB1EnemyMageSfx, kTypeRawData, false },
	{ kEoB1BeholderSpellList, kTypeRawData, false },
	{ kEoB1BeholderSfx, kTypeRawData, false },
	{ kEoB1TurnUndeadString, kTypeStringList, true },

	{ kEoB1CgaMappingDefault, kTypeRawData, false },
	{ kEoB1CgaMappingAlt, kTypeRawData, false },
	{ kEoB1CgaMappingInv, kTypeRawData, false },
	{ kEoB1CgaMappingItemsL, kTypeRawData, false },
	{ kEoB1CgaMappingItemsS, kTypeRawData, false },
	{ kEoB1CgaMappingThrown, kTypeRawData, false },
	{ kEoB1CgaMappingIcons, kTypeRawData, false },
	{ kEoB1CgaMappingDeco, kTypeRawData, false },
	{ kEoB1CgaLevelMappingIndex, kTypeRawData, false },
	{ kEoB1CgaMappingLevel0, kTypeRawData, false },
	{ kEoB1CgaMappingLevel1, kTypeRawData, false },
	{ kEoB1CgaMappingLevel2, kTypeRawData, false },
	{ kEoB1CgaMappingLevel3, kTypeRawData, false },
	{ kEoB1CgaMappingLevel4, kTypeRawData, false },

	{ kEoB1NpcShpData, kTypeRawData, false },
	{ kEoB1NpcSubShpIndex1, kTypeRawData, false },
	{ kEoB1NpcSubShpIndex2, kTypeRawData, false },
	{ kEoB1NpcSubShpY, kTypeRawData, false },
	{ kEoB1Npc0Strings, kTypeStringList, true },
	{ kEoB1Npc11Strings, kTypeStringList, true },
	{ kEoB1Npc12Strings, kTypeStringList, true },
	{ kEoB1Npc21Strings, kTypeStringList, true },
	{ kEoB1Npc22Strings, kTypeStringList, true },
	{ kEoB1Npc31Strings, kTypeStringList, true },
	{ kEoB1Npc32Strings, kTypeStringList, true },
	{ kEoB1Npc4Strings, kTypeStringList, true },
	{ kEoB1Npc5Strings, kTypeStringList, true },
	{ kEoB1Npc6Strings, kTypeStringList, true },
	{ kEoB1Npc7Strings, kTypeStringList, true },

	// EYE OF THE BEHOLDER II
	{ kEoB2MainMenuStrings, kTypeStringList, true },

	{ kEoB2TransferPortraitFrames, kLoLTypeRaw16, false },
	{ kEoB2TransferConvertTable, kTypeRawData, false },
	{ kEoB2TransferItemTable, kTypeRawData, false },
	{ kEoB2TransferExpTable, kLoLTypeRaw32, false },
	{ kEoB2TransferStrings1, kTypeStringList, true },
	{ kEoB2TransferStrings2, kTypeStringList, true },
	{ kEoB2TransferLabels, kTypeStringList, true },

	{ kEoB2IntroStrings, k2TypeSfxList, true },
	{ kEoB2IntroCPSFiles, kTypeStringList, true },
	{ kEob2IntroAnimData00, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData01, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData02, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData03, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData04, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData05, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData06, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData07, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData08, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData09, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData10, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData11, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData12, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData13, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData14, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData15, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData16, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData17, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData18, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData19, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData20, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData21, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData22, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData23, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData24, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData25, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData26, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData27, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData28, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData29, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData30, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData31, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData32, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData33, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData34, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData35, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData36, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData37, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData38, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData39, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData40, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData41, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData42, kEoB2TypeSeqData, false },
	{ kEob2IntroAnimData43, kEoB2TypeSeqData, false },
	{ kEoB2IntroShapes00, kEoB2TypeShapeData, false },
	{ kEoB2IntroShapes01, kEoB2TypeShapeData, false },
	{ kEoB2IntroShapes04, kEoB2TypeShapeData, false },
	{ kEoB2IntroShapes07, kEoB2TypeShapeData, false },

	{ kEoB2FinaleStrings, k2TypeSfxList, true },
	{ kEoB2CreditsData, kTypeRawData, true },
	{ kEoB2FinaleCPSFiles, kTypeStringList, true },
	{ kEob2FinaleAnimData00, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData01, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData02, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData03, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData04, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData05, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData06, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData07, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData08, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData09, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData10, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData11, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData12, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData13, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData14, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData15, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData16, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData17, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData18, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData19, kEoB2TypeSeqData, false },
	{ kEob2FinaleAnimData20, kEoB2TypeSeqData, false },
	{ kEoB2FinaleShapes00, kEoB2TypeShapeData, false },
	{ kEoB2FinaleShapes03, kEoB2TypeShapeData, false },
	{ kEoB2FinaleShapes07, kEoB2TypeShapeData, false },
	{ kEoB2FinaleShapes09, kEoB2TypeShapeData, false },
	{ kEoB2FinaleShapes10, kEoB2TypeShapeData, false },
	{ kEoB2NpcShapeData, kTypeRawData, false },
	{ kEoBBaseClassModifierFlags, kTypeRawData, false },
	{ kEoBBaseMonsterStepTable01, kTypeRawData, false },
	{ kEoBBaseMonsterStepTable02, kTypeRawData, false },
	{ kEoBBaseMonsterStepTable1, kTypeRawData, false },
	{ kEoBBaseMonsterStepTable2, k3TypeRaw16to8, false },
	{ kEoBBaseMonsterStepTable3, k3TypeRaw16to8, false },
	{ kEoBBaseMonsterCloseAttPosTable1, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttPosTable21, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttPosTable22, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttUnkTable, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttChkTable1, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttChkTable2, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttDstTable1, kTypeRawData, false },
	{ kEoBBaseMonsterCloseAttDstTable2, kTypeRawData, false },
	{ kEoBBaseMonsterProximityTable, kTypeRawData, false },
	{ kEoBBaseFindBlockMonstersTable, kTypeRawData, false },
	{ kEoBBaseMonsterDirChangeTable, kTypeRawData, false },
	{ kEoBBaseMonsterDistAttStrings, kTypeStringList, true },
	{ kEoBBaseEncodeMonsterDefs, kLoLTypeRaw16, false },
	{ kEoBBaseNpcPresets, kEoBTypeNpcData, false },
	{ kEoB2Npc1Strings, kTypeStringList, true },
	{ kEoB2Npc2Strings, kTypeStringList, true },
	{ kEoB2MonsterDustStrings, kTypeStringList, true },
	{ kEoB2DreamSteps, kTypeRawData, false },
	{ kEoB2KheldranStrings, kTypeStringList, true },
	{ kEoB2HornStrings, kTypeStringList, true },
	{ kEoB2HornSounds, kTypeRawData, false },
	{ kEoB2WallOfForceDsX, kLoLTypeRaw16, false },
	{ kEoB2WallOfForceDsY, kTypeRawData, false },
	{ kEoB2WallOfForceNumW, kTypeRawData, false },
	{ kEoB2WallOfForceNumH, kTypeRawData, false },
	{ kEoB2WallOfForceShpId, kTypeRawData, false },

	// LANDS OF LORE

	// Ingame
	{ kLoLIngamePakFiles, k2TypeSfxList, false },

	{ kLoLCharacterDefs, kLoLTypeCharData, true },
	{ kLoLIngameSfxFiles, k2TypeSfxList, false },
	{ kLoLIngameSfxIndex, kTypeRawData, false },
	{ kLoLMusicTrackMap, kTypeRawData, false },
	{ kLoLIngameGMSfxIndex, kTypeRawData, false },
	{ kLoLIngameMT32SfxIndex, kTypeRawData, false },
	{ kLoLIngamePcSpkSfxIndex, kTypeRawData, false },
	{ kLoLSpellProperties, kLoLTypeSpellData, false },
	{ kLoLGameShapeMap, kTypeRawData, false },
	{ kLoLSceneItemOffs, kTypeRawData, false },
	{ kLoLCharInvIndex, k3TypeRaw16to8, false },
	{ kLoLCharInvDefs, kTypeRawData, false },
	{ kLoLCharDefsMan, kLoLTypeRaw16, false },
	{ kLoLCharDefsWoman, kLoLTypeRaw16, false },
	{ kLoLCharDefsKieran, kLoLTypeRaw16, false },
	{ kLoLCharDefsAkshel, kLoLTypeRaw16, false },
	{ kLoLExpRequirements, kLoLTypeRaw32, false },
	{ kLoLMonsterModifiers1, kLoLTypeRaw16, false },
	{ kLoLMonsterModifiers2, kLoLTypeRaw16, false },
	{ kLoLMonsterModifiers3, kLoLTypeRaw16, false },
	{ kLoLMonsterModifiers4, kLoLTypeRaw16, false },
	{ kLoLMonsterShiftOffsets, kTypeRawData, false },
	{ kLoLMonsterDirFlags, kTypeRawData, false },
	{ kLoLMonsterScaleY, kTypeRawData, false },
	{ kLoLMonsterScaleX, kTypeRawData, false },
	{ kLoLMonsterScaleWH, kLoLTypeRaw16, false },
	{ kLoLFlyingObjectShp, kLoLTypeFlightShpData, false },
	{ kLoLInventoryDesc, kLoLTypeRaw16, false },
	{ kLoLLevelShpList, k2TypeSfxList, false },
	{ kLoLLevelDatList, k2TypeSfxList, false },
	{ kLoLCompassDefs, kLoLTypeCompassData, false },
	{ kLoLItemPrices, kLoLTypeRaw16, false },
	{ kLoLStashSetup, kTypeRawData, false },

	{ kLoLDscWalls, kTypeRawData, false },
	{ kRpgCommonDscShapeIndex, kTypeRawData, false },
	{ kLoLDscOvlMap, kTypeRawData, false },
	{ kLoLDscScaleWidthData, kLoLTypeRaw16, false },
	{ kLoLDscScaleHeightData, kLoLTypeRaw16, false },
	{ kRpgCommonDscX, kLoLTypeRaw16, false },
	{ kLoLDscY, kTypeRawData, false },
	{ kRpgCommonDscTileIndex, kTypeRawData, false },
	{ kRpgCommonDscUnk2, kTypeRawData, false },
	{ kRpgCommonDscDoorShapeIndex, kTypeRawData, false },
	{ kRpgCommonDscDimData1, kTypeRawData, false },
	{ kRpgCommonDscDimData2, kTypeRawData, false },
	{ kRpgCommonDscBlockMap, kTypeRawData, false },
	{ kRpgCommonDscDimMap, kTypeRawData, false },
	{ kLoLDscDoorScale, kLoLTypeRaw16, false },
	{ kLoLDscOvlIndex, k3TypeRaw16to8, false },
	{ kRpgCommonDscBlockIndex, kTypeRawData, false },
	{ kLoLDscDoor4, kLoLTypeRaw16, false },
	{ kRpgCommonDscDoorY2, kTypeRawData, false },
	{ kRpgCommonDscDoorFrameY1, kTypeRawData, false },
	{ kRpgCommonDscDoorFrameY2, kTypeRawData, false },
	{ kRpgCommonDscDoorFrameIndex1, kTypeRawData, false },
	{ kRpgCommonDscDoorFrameIndex2, kTypeRawData, false },
	{ kLoLDscDoorX, kLoLTypeRaw16, false },
	{ kLoLDscDoorY, kLoLTypeRaw16, false },

	{ kLoLScrollXTop, k3TypeRaw16to8, false },
	{ kLoLScrollYTop, k3TypeRaw16to8, false },
	{ kLoLScrollXBottom, k3TypeRaw16to8, false },
	{ kLoLScrollYBottom, k3TypeRaw16to8, false },

	{ kLoLButtonDefs, kLoLTypeButtonDef, false },
	{ kLoLButtonList1, k3TypeRaw16to8, false },
	{ kLoLButtonList2, k3TypeRaw16to8, false },
	{ kLoLButtonList3, k3TypeRaw16to8, false },
	{ kLoLButtonList4, k3TypeRaw16to8, false },
	{ kLoLButtonList5, k3TypeRaw16to8, false },
	{ kLoLButtonList6, k3TypeRaw16to8, false },
	{ kLoLButtonList7, k3TypeRaw16to8, false },
	{ kLoLButtonList8, k3TypeRaw16to8, false },

	{ kLoLLegendData, kTypeRawData, false },
	{ kLoLMapCursorOvl, kTypeRawData, false },
	{ kLoLMapStringId, kLoLTypeRaw16, false },

	{ kLoLSpellbookAnim, k3TypeRaw16to8, false },
	{ kLoLSpellbookCoords, k3TypeRaw16to8, false },
	{ kLoLHealShapeFrames, kTypeRawData, false },
	{ kLoLLightningDefs, kTypeRawData, false },
	{ kLoLFireballCoords, kLoLTypeRaw16, false },

	{ kLoLCredits, kTypeRawData, false },

	{ kLoLHistory, kTypeRawData, false },

	{ -1, 0, 0 }
};

const ExtractFilename *getFilenameDesc(const int id) {
	for (const ExtractFilename *i = extractFilenames; i->id != -1; ++i) {
		if (i->id == id)
			return i;
	}
	return 0;
}

bool isLangSpecific(const int id) {
	const ExtractFilename *desc = getFilenameDesc(id);
	if (!desc)
		return false;
	return desc->langSpecific;
}

// misc tables

const TypeTable gameTable[] = {
	{ kKyra1, 0 },
	{ kKyra2, 1 },
	{ kKyra3, 2 },
	{ kEoB1, 3 },
	{ kEoB2, 4 },
	{ kLoL, 5 },
	{ -1, -1 }
};

byte getGameID(int game) {
	return std::find(gameTable, ARRAYEND(gameTable) - 1, game)->value;
}

const TypeTable languageTable[] = {
	{ UNK_LANG, 0 },
	{ EN_ANY, 1 },
	{ FR_FRA, 2 },
	{ DE_DEU, 3 },
	{ ES_ESP, 4 },
	{ IT_ITA, 5 },
	{ JA_JPN, 6 },
	{ RU_RUS, 7 },
	{ -1, -1 }
};

byte getLanguageID(int lang) {
	return std::find(languageTable, ARRAYEND(languageTable) - 1, lang)->value;
}

const TypeTable platformTable[] = {
	{ kPlatformDOS, 0 },
	{ kPlatformAmiga, 1 },
	{ kPlatformFMTowns, 2 },
	{ kPlatformPC98, 3 },
	{ kPlatformMacintosh, 4 },
	{ -1, -1 }
};

byte getPlatformID(int platform) {
	return std::find(platformTable, ARRAYEND(platformTable) - 1, platform)->value;
}

const TypeTable specialTable[] = {
	{ kNoSpecial, 0 },
	{ kTalkieVersion, 1 },
	{ kDemoVersion, 2 },
	{ kTalkieDemoVersion, 3 },
	{ kOldFloppy, 4 },
	{ -1, -1 }
};

byte getSpecialID(int special) {
	return std::find(specialTable, ARRAYEND(specialTable) - 1, special)->value;
}

// filename processing

uint32 getFilename(const ExtractInformation *info, const int id) {
	const ExtractFilename *fDesc = getFilenameDesc(id);

	if (!fDesc)
		return 0;

	// GAME, PLATFORM, SPECIAL, ID, LANG
	return ((getGameID(info->game) & 0xF) << 24) |
	       ((getPlatformID(info->platform) & 0xF) << 20) |
	       ((getSpecialID(info->special) & 0xF) << 16) |
	       ((id & 0xFFF) << 4) |
	       ((getLanguageID(fDesc->langSpecific ? info->lang : UNK_LANG) & 0xF) << 0);
}

// TODO: Get rid of this
bool getFilename(char *dstFilename, const ExtractInformation *info, const int id) {
	sprintf(dstFilename, "%08X", getFilename(info, id));
	return true;
}

// index generation

typedef uint16 GameDef;

GameDef createGameDef(const ExtractInformation *eI) {
	return ((getGameID(eI->game) & 0xF) << 12) |
	       ((getPlatformID(eI->platform) & 0xF) << 8) |
	       ((getSpecialID(eI->special) & 0xF) << 4) |
	       ((getLanguageID(eI->lang) & 0xF) << 0);
}

struct Index {
	Index() : version(0), includedGames(0), gameList() {}

	uint32 version;
	uint32 includedGames;

	typedef std::list<GameDef> GameList;
	GameList gameList;
};

Index parseIndex(const uint8 *data, uint32 size) {
	Index result;

	if (size < 8)
		return result;

	result.version = READ_BE_UINT32(data); data += 4;
	result.includedGames = READ_BE_UINT32(data); data += 4;

	if (result.includedGames * 2 + 8 != size) {
		result.version = result.includedGames = 0;
		return result;
	}

	for (uint32 i = 0; i < result.includedGames; ++i) {
		GameDef game = READ_BE_UINT16(data); data += 2;
		result.gameList.push_back(game);
	}

	return result;
}

bool updateIndex(PAKFile &out, const ExtractInformation *eI) {
	uint32 size = 0;
	const uint8 *data = out.getFileData("INDEX", &size);

	Index index;
	if (data)
		index = parseIndex(data, size);

	GameDef gameDef = createGameDef(eI);
	if (index.version == kKyraDatVersion) {
		if (std::find(index.gameList.begin(), index.gameList.end(), gameDef) == index.gameList.end()) {
			++index.includedGames;
			index.gameList.push_back(gameDef);
		} else {
			// Already included in the game list, thus we do not need any further processing here.
			return true;
		}
	} else {
		index.version = kKyraDatVersion;
		index.includedGames = 1;
		index.gameList.push_back(gameDef);
	}

	const uint32 indexBufferSize = 8 + index.includedGames * 2;
	uint8 *indexBuffer = new uint8[indexBufferSize];
	assert(indexBuffer);
	uint8 *dst = indexBuffer;
	WRITE_BE_UINT32(dst, index.version); dst += 4;
	WRITE_BE_UINT32(dst, index.includedGames); dst += 4;
	for (Index::GameList::const_iterator i = index.gameList.begin(); i != index.gameList.end(); ++i) {
		WRITE_BE_UINT16(dst, *i); dst += 2;
	}

	out.removeFile("INDEX");
	if (!out.addFile("INDEX", indexBuffer, indexBufferSize)) {
		fprintf(stderr, "ERROR: couldn't update kyra.dat INDEX\n");
		delete[] indexBuffer;
		return false;
	}

	return true;
}

bool checkIndex(PAKFile &file) {
	uint32 size = 0;
	const uint8 *data = file.getFileData("INDEX", &size);
	if (!data)
		return false;

	Index index = parseIndex(data, size);

	if (index.version != kKyraDatVersion)
		return false;
	if (index.includedGames * 2 + 8 != size)
		return false;

	return true;
}

// main processing

void printHelp(const char *f) {
	printf("Usage:\n");
	printf("%s output inputfiles ...\n", f);
}

bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size);
const Game *findGame(const byte *buffer, const uint32 size);

typedef std::map<std::string, std::string> MD5Map;
MD5Map createMD5Sums(int files, const char * const *filenames);

struct File {
	File() : data(0), size(0) {}
	File(uint8 *d, uint32 s) : data(d), size(s) {}

	uint8 *data;
	uint32 size;
};
typedef std::map<const Game *, File> GameMap;
GameMap createGameMap(const MD5Map &map);

int main(int argc, char *argv[]) {
	if (argc < 3) {
		printHelp(argv[0]);
		return -1;
	}

	// Special case for developer mode of this tool:
	// With "--create filename offset size" the tool will output
	// a search entry for the specifed data in the specified file.
	if (!strcmp(argv[1], "--create")) {
		if (argc < 5) {
			printf("Developer usage: %s --create input_file hex_offset hex_size\n", argv[0]);
			return -1;
		}

		uint32 offset, size;
		sscanf(argv[3], "%x", &offset);
		sscanf(argv[4], "%x", &size);

		FILE *input = fopen(argv[2], "rb");
		if (!input)
			error("Couldn't open file '%s'", argv[2]);

		byte *buffer = new byte[size];
		fseek(input, offset, SEEK_SET);
		if (fread(buffer, 1, size, input) != size) {
			delete[] buffer;
			error("Couldn't read from file '%s'", argv[2]);
		}

		fclose(input);

		SearchData d = SearchCreator::create(buffer, size);
		delete[] buffer;

		printf("{ 0x%.08X, 0x%.08X, { {", d.size, d.byteSum);
		for (int j = 0; j < 16; ++j) {
			printf(" 0x%.2X", d.hash.digest[j]);
			if (j != 15)
				printf(",");
			else
				printf(" } } }\n");
		}

		return 0;
	}

	PAKFile out;
	out.loadFile(argv[1], false);

	// When the output file is no valid kyra.dat file, we will delete
	// all the output.
	if (!checkIndex(out))
		out.clearFile();

	MD5Map inputFiles = createMD5Sums(argc - 2, &argv[2]);

	GameMap games = createGameMap(inputFiles);

	// Check for unused input files
	MD5Map unusedFiles = inputFiles;
	for (GameMap::const_iterator i = games.begin(); i != games.end(); ++i) {
		unusedFiles.erase(i->first->md5[0]);
		if (i->first->md5[1])
			unusedFiles.erase(i->first->md5[1]);
	}

	for (MD5Map::const_iterator i = unusedFiles.begin(); i != unusedFiles.end(); ++i)
		printf("Input file '%s' with md5 sum '%s' is not known.\n", i->second.c_str(), i->first.c_str());

	unusedFiles.clear();

	// Short circuit, in case no games are found.
	if (games.empty()) {
		printf("No games found. Exiting prematurely\n");
		return -1;
	}

	// Process all games found
	for (GameMap::const_iterator i = games.begin(); i != games.end(); ++i) {
		MD5Map::const_iterator f1 = inputFiles.find(i->first->md5[0]);
		MD5Map::const_iterator f2 = inputFiles.end();
		if (i->first->md5[1])
			f2 = inputFiles.find(i->first->md5[1]);

		if (f2 != inputFiles.end())
			printf("Processing files '%s' and '%s'...\n", f1->second.c_str(), f2->second.c_str());
		else
			printf("Processing file '%s'...\n", f1->second.c_str());

		if (!process(out, i->first, i->second.data, i->second.size))
			printf("FAILED\n");
		else
			printf("OK\n");
	}

	// Free up memory
	for (GameMap::iterator i = games.begin(); i != games.end(); ++i)
		delete[] i->second.data;
	games.clear();
	inputFiles.clear();

	if (!out.saveFile(argv[1]))
	error("couldn't save changes to '%s'", argv[1]);

	uint8 digest[16];
	if (!md5_file(argv[1], digest, 0))
		error("couldn't calc. md5 for file '%s'", argv[1]);
	FILE *f = fopen(argv[1], "ab");
	if (!f)
		error("couldn't open file '%s'", argv[1]);
	if (fwrite(digest, 1, 16, f) != 16)
		error("couldn't write md5sum to file '%s'", argv[1]);
	fclose(f);

	return 0;
}

MD5Map createMD5Sums(int files, const char * const *filenames) {
	MD5Map result;

	while (files--) {
		const char *inputFile = *filenames++;
		FILE *input = fopen(inputFile, "rb");

		uint32 size = fileSize(input);
		fseek(input, 0, SEEK_SET);

		byte *buffer = new uint8[size];
		assert(buffer);

		if (fread(buffer, 1, size, input) != size) {
			warning("couldn't read from file '%s', skipping it", inputFile);
			delete[] buffer;
			fclose(input);
			continue;
		}
		fclose(input);

		md5_context ctx;
		uint8 digest[16];
		char md5Str[33];

		md5_starts(&ctx);
		md5_update(&ctx, buffer, size);
		md5_finish(&ctx, digest);

		for (int j = 0; j < 16; ++j)
			sprintf(md5Str + j*2, "%02x", (int)digest[j]);

		delete[] buffer;

		result[md5Str] = inputFile;
	}

	return result;
}

GameMap createGameMap(const MD5Map &map) {
	GameMap result;

	for (const Game * const *g = gameDescs; *g != 0; ++g) {
		for (const Game *sub = *g; sub->game != -1; ++sub) {
			MD5Map::const_iterator file1 = map.find(sub->md5[0]);
			if (file1 == map.end())
				continue;

			MD5Map::const_iterator file2 = map.end();
			if (sub->md5[1] != 0) {
				file2 = map.find(sub->md5[1]);
				if (file2 == map.end())
					continue;
			}

			FILE *f1 = fopen(file1->second.c_str(), "rb");
			FILE *f2 = 0;

			if (file2 != map.end())
				f2 = fopen(file2->second.c_str(), "rb");

			uint32 file1Size = fileSize(f1);
			uint32 file2Size = 0;
			if (f2)
				file2Size = fileSize(f2);

			uint8 *buffer = new uint8[file1Size + file2Size];
			assert(buffer);

			fread(buffer, 1, file1Size, f1);
			if (f2)
				fread(buffer + file1Size, 1, file2Size, f2);

			fclose(f1);
			if (f2)
				fclose(f2);

			result[sub] = File(buffer, file1Size + file2Size);
		}
	}

	return result;
}

const char *getIdString(const int id) {
	switch (id) {
	case k1ForestSeq:
		return "k1ForestSeq";
	case k1KallakWritingSeq:
		return "k1KallakWritingSeq";
	case k1KyrandiaLogoSeq:
		return "k1KyrandiaLogoSeq";
	case k1KallakMalcolmSeq:
		return "k1KallakMalcolmSeq";
	case k1MalcolmTreeSeq:
		return "k1MalcolmTreeSeq";
	case k1WestwoodLogoSeq:
		return "k1WestwoodLogoSeq";
	case k1Demo1Seq:
		return "k1Demo1Seq";
	case k1Demo2Seq:
		return "k1Demo2Seq";
	case k1Demo3Seq:
		return "k1Demo3Seq";
	case k1Demo4Seq:
		return "k1Demo4Seq";
	case k1AmuleteAnimSeq:
		return "k1AmuleteAnimSeq";
	case k1OutroReunionSeq:
		return "k1OutroReunionSeq";
	case k1IntroCPSStrings:
		return "k1IntroCPSStrings";
	case k1IntroCOLStrings:
		return "k1IntroCOLStrings";
	case k1IntroWSAStrings:
		return "k1IntroWSAStrings";
	case k1IntroStrings:
		return "k1IntroStrings";
	case k1OutroHomeString:
		return "k1OutroHomeString";
	case k1RoomFilenames:
		return "k1RoomFilenames";
	case k1RoomList:
		return "k1RoomList";
	case k1CharacterImageFilenames:
		return "k1CharacterImageFilenames";
	case k1AudioTracks:
		return "k1AudioTracks";
	case k1AudioTracks2:
		return "k1AudioTracks2";
	case k1AudioTracksIntro:
		return "k1AudioTracksIntro";
	case k1ItemNames:
		return "k1ItemNames";
	case k1TakenStrings:
		return "k1TakenStrings";
	case k1PlacedStrings:
		return "k1PlacedStrings";
	case k1DroppedStrings:
		return "k1DroppedStrings";
	case k1NoDropStrings:
		return "k1NoDropStrings";
	case k1PutDownString:
		return "k1PutDownString";
	case k1WaitAmuletString:
		return "k1WaitAmuletString";
	case k1BlackJewelString:
		return "k1BlackJewelString";
	case k1PoisonGoneString:
		return "k1PoisonGoneString";
	case k1HealingTipString:
		return "k1HealingTipString";
	case k1WispJewelStrings:
		return "k1WispJewelStrings";
	case k1MagicJewelStrings:
		return "k1MagicJewelStrings";
	case k1ThePoisonStrings:
		return "k1ThePoisonStrings";
	case k1FluteStrings:
		return "k1FluteStrings";
	case k1FlaskFullString:
		return "k1FlaskFullString";
	case k1FullFlaskString:
		return "k1FullFlaskString";
	case k1VeryCleverString:
		return "k1VeryCleverString";
	case k1NewGameString:
		return "k1NewGameString";
	case k1DefaultShapes:
		return "k1DefaultShapes";
	case k1Healing1Shapes:
		return "k1Healing1Shapes";
	case k1Healing2Shapes:
		return "k1Healing2Shapes";
	case k1PoisonDeathShapes:
		return "k1PoisonDeathShapes";
	case k1FluteShapes:
		return "k1FluteShapes";
	case k1Winter1Shapes:
		return "k1Winter1Shapes";
	case k1Winter2Shapes:
		return "k1Winter2Shapes";
	case k1Winter3Shapes:
		return "k1Winter3Shapes";
	case k1DrinkShapes:
		return "k1DrinkShapes";
	case k1WispShapes:
		return "k1WispShapes";
	case k1MagicAnimShapes:
		return "k1MagicAnimShapes";
	case k1BranStoneShapes:
		return "k1BranStoneShapes";
	case k1SpecialPalette1:
		return "k1SpecialPalette1";
	case k1SpecialPalette2:
		return "k1SpecialPalette2";
	case k1SpecialPalette3:
		return "k1SpecialPalette3";
	case k1SpecialPalette4:
		return "k1SpecialPalette4";
	case k1SpecialPalette5:
		return "k1SpecialPalette5";
	case k1SpecialPalette6:
		return "k1SpecialPalette6";
	case k1SpecialPalette7:
		return "k1SpecialPalette7";
	case k1SpecialPalette8:
		return "k1SpecialPalette8";
	case k1SpecialPalette9:
		return "k1SpecialPalette9";
	case k1SpecialPalette10:
		return "k1SpecialPalette10";
	case k1SpecialPalette11:
		return "k1SpecialPalette11";
	case k1SpecialPalette12:
		return "k1SpecialPalette12";
	case k1SpecialPalette13:
		return "k1SpecialPalette13";
	case k1SpecialPalette14:
		return "k1SpecialPalette14";
	case k1SpecialPalette15:
		return "k1SpecialPalette15";
	case k1SpecialPalette16:
		return "k1SpecialPalette16";
	case k1SpecialPalette17:
		return "k1SpecialPalette17";
	case k1SpecialPalette18:
		return "k1SpecialPalette18";
	case k1SpecialPalette19:
		return "k1SpecialPalette19";
	case k1SpecialPalette20:
		return "k1SpecialPalette20";
	case k1SpecialPalette21:
		return "k1SpecialPalette21";
	case k1SpecialPalette22:
		return "k1SpecialPalette22";
	case k1SpecialPalette23:
		return "k1SpecialPalette23";
	case k1SpecialPalette24:
		return "k1SpecialPalette24";
	case k1SpecialPalette25:
		return "k1SpecialPalette25";
	case k1SpecialPalette26:
		return "k1SpecialPalette26";
	case k1SpecialPalette27:
		return "k1SpecialPalette27";
	case k1SpecialPalette28:
		return "k1SpecialPalette28";
	case k1SpecialPalette29:
		return "k1SpecialPalette29";
	case k1SpecialPalette30:
		return "k1SpecialPalette30";
	case k1SpecialPalette31:
		return "k1SpecialPalette31";
	case k1SpecialPalette32:
		return "k1SpecialPalette32";
	case k1SpecialPalette33:
		return "k1SpecialPalette33";
	case k1GUIStrings:
		return "k1GUIStrings";
	case k1ConfigStrings:
		return "k1ConfigStrings";
	case k1TownsMusicFadeTable:
		return "k1TownsMusicFadeTable";
	case k1TownsSFXwdTable:
		return "k1TownsSFXwdTable";
	case k1TownsSFXbtTable:
		return "k1TownsSFXbtTable";
	case k1TownsCDATable:
		return "k1TownsCDATable";
	case k1PC98StoryStrings:
		return "k1PC98StoryStrings";
	case k1PC98IntroSfx:
		return "k1PC98IntroSfx";
	case k1CreditsStrings:
		return "k1CreditsStrings";
	case k1AmigaIntroSFXTable:
		return "k1AmigaIntroSFXTable";
	case k1AmigaGameSFXTable:
		return "k1AmigaGameSFXTable";
	case k2SeqplayPakFiles:
		return "k2SeqplayPakFiles";
	case k2SeqplayStrings:
		return "k2SeqplayStrings";
	case k2SeqplaySfxFiles:
		return "k2SeqplaySfxFiles";
	case k2SeqplayTlkFiles:
		return "k2SeqplayTlkFiles";
	case k2SeqplaySeqData:
		return "k2SeqplaySeqData";
	case k2SeqplayCredits:
		return "k2SeqplayCredits";
	case k2SeqplayCreditsSpecial:
		return "k2SeqplayCreditsSpecial";
	case k2SeqplayIntroTracks:
		return "k2SeqplayIntroTracks";
	case k2SeqplayFinaleTracks:
		return "k2SeqplayFinaleTracks";
	case k2SeqplayIntroCDA:
		return "k2SeqplayIntroCDA";
	case k2SeqplayFinaleCDA:
		return "k2SeqplayFinaleCDA";
	case k2SeqplayShapeAnimData:
		return "k2SeqplayShapeAnimData";
	case k2IngamePakFiles:
		return "k2IngamePakFiles";
	case k2IngameSfxFiles:
		return "k2IngameSfxFiles";
	case k2IngameSfxIndex:
		return "k2IngameSfxIndex";
	case k2IngameTracks:
		return "k2IngameTracks";
	case k2IngameCDA:
		return "k2IngameCDA";
	case k2IngameTalkObjIndex:
		return "k2IngameTalkObjIndex";
	case k2IngameTimJpStrings:
		return "k2IngameTimJpStrings";
	case k2IngameShapeAnimData:
		return "k2IngameShapeAnimData";
	case k2IngameTlkDemoStrings:
		return "k2IngameTlkDemoStrings";
	case k3MainMenuStrings:
		return "k3MainMenuStrings";
	case k3MusicFiles:
		return "k3MusicFiles";
	case k3ScoreTable:
		return "k3ScoreTable";
	case k3SfxFiles:
		return "k3SfxFiles";
	case k3SfxMap:
		return "k3SfxMap";
	case k3ItemAnimData:
		return "k3ItemAnimData";
	case k3ItemMagicTable:
		return "k3ItemMagicTable";
	case k3ItemStringMap:
		return "k3ItemStringMap";
	case kEoBBaseChargenStrings1:
		return "kEoBBaseChargenStrings1";
	case kEoBBaseChargenStrings2:
		return "kEoBBaseChargenStrings2";
	case kEoBBaseChargenStartLevels:
		return "kEoBBaseChargenStartLevels";
	case kEoBBaseChargenStatStrings:
		return "kEoBBaseChargenStatStrings";
	case kEoBBaseChargenRaceSexStrings:
		return "kEoBBaseChargenRaceSexStrings";
	case kEoBBaseChargenClassStrings:
		return "kEoBBaseChargenClassStrings";
	case kEoBBaseChargenAlignmentStrings:
		return "kEoBBaseChargenAlignmentStrings";
	case kEoBBaseChargenEnterGameStrings:
		return "kEoBBaseChargenEnterGameStrings";
	case kEoBBaseChargenClassMinStats:
		return "kEoBBaseChargenClassMinStats";
	case kEoBBaseChargenRaceMinStats:
		return "kEoBBaseChargenRaceMinStats";
	case kEoBBaseChargenRaceMaxStats:
		return "kEoBBaseChargenRaceMaxStats";
	case kEoBBaseSaveThrowTable1:
		return "kEoBBaseSaveThrowTable1";
	case kEoBBaseSaveThrowTable2:
		return "kEoBBaseSaveThrowTable2";
	case kEoBBaseSaveThrowTable3:
		return "kEoBBaseSaveThrowTable3";
	case kEoBBaseSaveThrowTable4:
		return "kEoBBaseSaveThrowTable4";
	case kEoBBaseSaveThrwLvlIndex:
		return "kEoBBaseSaveThrwLvlIndex";
	case kEoBBaseSaveThrwModDiv:
		return "kEoBBaseSaveThrwModDiv";
	case kEoBBaseSaveThrwModExt:
		return "kEoBBaseSaveThrwModExt";
	case kEoBBasePryDoorStrings:
		return "kEoBBasePryDoorStrings";
	case kEoBBaseWarningStrings:
		return "kEoBBaseWarningStrings";
	case kEoBBaseItemSuffixStringsRings:
		return "kEoBBaseItemSuffixStringsRings";
	case kEoBBaseItemSuffixStringsPotions:
		return "kEoBBaseItemSuffixStringsPotions";
	case kEoBBaseItemSuffixStringsWands:
		return "kEoBBaseItemSuffixStringsWands";
	case kEoBBaseRipItemStrings:
		return "kEoBBaseRipItemStrings";
	case kEoBBaseCursedString:
		return "kEoBBaseCursedString";
	case kEoBBaseEnchantedString:
		return "kEoBBaseEnchantedString";
	case kEoBBaseMagicObjectStrings:
		return "kEoBBaseMagicObjectStrings";
	case kEoBBaseMagicObject5String:
		return "kEoBBaseMagicObject5String";
	case kEoBBasePatternSuffix:
		return "kEoBBasePatternSuffix";
	case kEoBBasePatternGrFix1:
		return "kEoBBasePatternGrFix1";
	case kEoBBasePatternGrFix2:
		return "kEoBBasePatternGrFix2";
	case kEoBBaseValidateArmorString:
		return "kEoBBaseValidateArmorString";
	case kEoBBaseValidateCursedString:
		return "kEoBBaseValidateCursedString";
	case kEoBBaseValidateNoDropString:
		return "kEoBBaseValidateNoDropString";
	case kEoBBasePotionStrings:
		return "kEoBBasePotionStrings";
	case kEoBBaseWandString:
		return "kEoBBaseWandString";
	case kEoBBaseItemMisuseStrings:
		return "kEoBBaseItemMisuseStrings";
	case kEoBBaseTakenStrings:
		return "kEoBBaseTakenStrings";
	case kEoBBasePotionEffectStrings:
		return "kEoBBasePotionEffectStrings";
	case kEoBBaseYesNoStrings:
		return "kEoBBaseYesNoStrings";
	case kRpgCommonMoreStrings:
		return "kRpgCommonMoreStrings";
	case kEoBBaseNpcMaxStrings:
		return "kEoBBaseNpcMaxStrings";
	case kEoBBaseOkStrings:
		return "kEoBBaseOkStrings";
	case kEoBBaseNpcJoinStrings:
		return "kEoBBaseNpcJoinStrings";
	case kEoBBaseCancelStrings:
		return "kEoBBaseCancelStrings";
	case kEoBBaseAbortStrings:
		return "kEoBBaseAbortStrings";
	case kEoBBaseMenuStringsMain:
		return "kEoBBaseMenuStringsMain";
	case kEoBBaseMenuStringsSaveLoad:
		return "kEoBBaseMenuStringsSaveLoad";
	case kEoBBaseMenuStringsOnOff:
		return "kEoBBaseMenuStringsOnOff";
	case kEoBBaseMenuStringsSpells:
		return "kEoBBaseMenuStringsSpells";
	case kEoBBaseMenuStringsRest:
		return "kEoBBaseMenuStringsRest";
	case kEoBBaseMenuStringsDrop:
		return "kEoBBaseMenuStringsDrop";
	case kEoBBaseMenuStringsExit:
		return "kEoBBaseMenuStringsExit";
	case kEoBBaseMenuStringsStarve:
		return "kEoBBaseMenuStringsStarve";
	case kEoBBaseMenuStringsScribe:
		return "kEoBBaseMenuStringsScribe";
	case kEoBBaseMenuStringsDrop2:
		return "kEoBBaseMenuStringsDrop2";
	case kEoBBaseMenuStringsHead:
		return "kEoBBaseMenuStringsHead";
	case kEoBBaseMenuStringsPoison:
		return "kEoBBaseMenuStringsPoison";
	case kEoBBaseMenuStringsMgc:
		return "kEoBBaseMenuStringsMgc";
	case kEoBBaseMenuStringsPrefs:
		return "kEoBBaseMenuStringsPrefs";
	case kEoBBaseMenuStringsRest2:
		return "kEoBBaseMenuStringsRest2";
	case kEoBBaseMenuStringsRest3:
		return "kEoBBaseMenuStringsRest3";
	case kEoBBaseMenuStringsRest4:
		return "kEoBBaseMenuStringsRest4";
	case kEoBBaseMenuStringsDefeat:
		return "kEoBBaseMenuStringsDefeat";
	case kEoBBaseMenuStringsTransfer:
		return "kEoBBaseMenuStringsTransfer";
	case kEoBBaseMenuStringsSpec:
		return "kEoBBaseMenuStringsSpec";
	case kEoBBaseMenuStringsSpellNo:
		return "kEoBBaseMenuStringsSpellNo";
	case kEoBBaseMenuYesNoStrings:
		return "kEoBBaseMenuYesNoStrings";
	case kEoBBaseSpellLevelsMage:
		return "kEoBBaseSpellLevelsMage";
	case kEoBBaseSpellLevelsCleric:
		return "kEoBBaseSpellLevelsCleric";
	case kEoBBaseNumSpellsCleric:
		return "kEoBBaseNumSpellsCleric";
	case kEoBBaseNumSpellsWisAdj:
		return "kEoBBaseNumSpellsWisAdj";
	case kEoBBaseNumSpellsPal:
		return "kEoBBaseNumSpellsPal";
	case kEoBBaseNumSpellsMage:
		return "kEoBBaseNumSpellsMage";
	case kEoBBaseCharGuiStringsHp:
		return "kEoBBaseCharGuiStringsHp";
	case kEoBBaseCharGuiStringsWp1:
		return "kEoBBaseCharGuiStringsWp1";
	case kEoBBaseCharGuiStringsWp2:
		return "kEoBBaseCharGuiStringsWp2";
	case kEoBBaseCharGuiStringsWr:
		return "kEoBBaseCharGuiStringsWr";
	case kEoBBaseCharGuiStringsSt1:
		return "kEoBBaseCharGuiStringsSt1";
	case kEoBBaseCharGuiStringsSt2:
		return "kEoBBaseCharGuiStringsSt2";
	case kEoBBaseCharGuiStringsIn:
		return "kEoBBaseCharGuiStringsIn";
	case kEoBBaseCharStatusStrings7:
		return "kEoBBaseCharStatusStrings7";
	case kEoBBaseCharStatusStrings81:
		return "kEoBBaseCharStatusStrings81";
	case kEoBBaseCharStatusStrings82:
		return "kEoBBaseCharStatusStrings82";
	case kEoBBaseCharStatusStrings9:
		return "kEoBBaseCharStatusStrings9";
	case kEoBBaseCharStatusStrings12:
		return "kEoBBaseCharStatusStrings12";
	case kEoBBaseCharStatusStrings131:
		return "kEoBBaseCharStatusStrings131";
	case kEoBBaseCharStatusStrings132:
		return "kEoBBaseCharStatusStrings132";
	case kEoBBaseLevelGainStrings:
		return "kEoBBaseLevelGainStrings";
	case kEoBBaseExperienceTable0:
		return "kEoBBaseExperienceTable0";
	case kEoBBaseExperienceTable1:
		return "kEoBBaseExperienceTable1";
	case kEoBBaseExperienceTable2:
		return "kEoBBaseExperienceTable2";
	case kEoBBaseExperienceTable3:
		return "kEoBBaseExperienceTable3";
	case kEoBBaseExperienceTable4:
		return "kEoBBaseExperienceTable4";
	case kEoBBaseWllFlagPreset:
		return "kEoBBaseWllFlagPreset";
	case kEoBBaseDscShapeCoords:
		return "kEoBBaseDscShapeCoords";
	case kEoBBaseDscDoorScaleOffs:
		return "kEoBBaseDscDoorScaleOffs";
	case kEoBBaseDscDoorScaleMult1:
		return "kEoBBaseDscDoorScaleMult1";
	case kEoBBaseDscDoorScaleMult2:
		return "kEoBBaseDscDoorScaleMult2";
	case kEoBBaseDscDoorScaleMult3:
		return "kEoBBaseDscDoorScaleMult3";
	case kEoBBaseDscDoorScaleMult4:
		return "kEoBBaseDscDoorScaleMult4";
	case kEoBBaseDscDoorScaleMult5:
		return "kEoBBaseDscDoorScaleMult5";
	case kEoBBaseDscDoorScaleMult6:
		return "kEoBBaseDscDoorScaleMult6";
	case kEoBBaseDscDoorType5Offs:
		return "kEoBBaseDscDoorType5Offs";
	case kEoBBaseDscDoorXE:
		return "kEoBBaseDscDoorXE";
	case kEoBBaseDscDoorY1:
		return "kEoBBaseDscDoorY1";
	case kEoBBaseDscDoorY3:
		return "kEoBBaseDscDoorY3";
	case kEoBBaseDscDoorY4:
		return "kEoBBaseDscDoorY4";
	case kEoBBaseDscDoorY5:
		return "kEoBBaseDscDoorY5";
	case kEoBBaseDscDoorY6:
		return "kEoBBaseDscDoorY6";
	case kEoBBaseDscDoorY7:
		return "kEoBBaseDscDoorY7";
	case kEoBBaseDscDoorCoordsExt:
		return "kEoBBaseDscDoorCoordsExt";
	case kEoBBaseDscItemPosIndex:
		return "kEoBBaseDscItemPosIndex";
	case kEoBBaseDscItemShpX:
		return "kEoBBaseDscItemShpX";
	case kEoBBaseDscItemPosUnk:
		return "kEoBBaseDscItemPosUnk";
	case kEoBBaseDscItemTileIndex:
		return "kEoBBaseDscItemTileIndex";
	case kEoBBaseDscItemShapeMap:
		return "kEoBBaseDscItemShapeMap";
	case kEoBBaseDscMonsterFrmOffsTbl1:
		return "kEoBBaseDscMonsterFrmOffsTbl1";
	case kEoBBaseDscMonsterFrmOffsTbl2:
		return "kEoBBaseDscMonsterFrmOffsTbl2";
	case kEoBBaseInvSlotX:
		return "kEoBBaseInvSlotX";
	case kEoBBaseInvSlotY:
		return "kEoBBaseInvSlotY";
	case kEoBBaseSlotValidationFlags:
		return "kEoBBaseSlotValidationFlags";
	case kEoBBaseProjectileWeaponTypes:
		return "kEoBBaseProjectileWeaponTypes";
	case kEoBBaseWandTypes:
		return "kEoBBaseWandTypes";
	case kEoBBaseDrawObjPosIndex:
		return "kEoBBaseDrawObjPosIndex";
	case kEoBBaseFlightObjFlipIndex:
		return "kEoBBaseFlightObjFlipIndex";
	case kEoBBaseFlightObjShpMap:
		return "kEoBBaseFlightObjShpMap";
	case kEoBBaseFlightObjSclIndex:
		return "kEoBBaseFlightObjSclIndex";
	case kEoBBaseDscTelptrShpCoords:
		return "kEoBBaseDscTelptrShpCoords";
	case kEoBBasePortalSeqData:
		return "kEoBBasePortalSeqData";
	case kEoBBaseManDef:
		return "kEoBBaseManDef";
	case kEoBBaseManWord:
		return "kEoBBaseManWord";
	case kEoBBaseManPrompt:
		return "kEoBBaseManPrompt";
	case kEoBBaseBookNumbers:
		return "kEoBBaseBookNumbers";
	case kEoBBaseMageSpellsList:
		return "kEoBBaseMageSpellsList";
	case kEoBBaseClericSpellsList:
		return "kEoBBaseClericSpellsList";
	case kEoBBaseSpellNames:
		return "kEoBBaseSpellNames";

	case kEoBBaseMagicStrings1:
		return "kEoBBaseMagicStrings1";
	case kEoBBaseMagicStrings2:
		return "kEoBBaseMagicStrings2";
	case kEoBBaseMagicStrings3:
		return "kEoBBaseMagicStrings3";
	case kEoBBaseMagicStrings4:
		return "kEoBBaseMagicStrings4";
	case kEoBBaseMagicStrings6:
		return "kEoBBaseMagicStrings6";
	case kEoBBaseMagicStrings7:
		return "kEoBBaseMagicStrings7";
	case kEoBBaseMagicStrings8:
		return "kEoBBaseMagicStrings8";
	case kEoBBaseExpObjectTlMode:
		return "kEoBBaseExpObjectTlMode";
	case kEoBBaseExpObjectTblIndex:
		return "kEoBBaseExpObjectTblIndex";
	case kEoBBaseExpObjectShpStart:
		return "kEoBBaseExpObjectShpStart";
	case kEoBBaseExpObjectTbl1:
		return "kEoBBaseExpObjectTbl1";
	case kEoBBaseExpObjectTbl2:
		return "kEoBBaseExpObjectTbl2";
	case kEoBBaseExpObjectTbl3:
		return "kEoBBaseExpObjectTbl3";
	case kEoBBaseExpObjectY:
		return "kEoBBaseExpObjectY";
	case kEoBBaseSparkDefSteps:
		return "kEoBBaseSparkDefSteps";
	case kEoBBaseSparkDefSubSteps:
		return "kEoBBaseSparkDefSubSteps";
	case kEoBBaseSparkDefShift:
		return "kEoBBaseSparkDefShift";
	case kEoBBaseSparkDefAdd:
		return "kEoBBaseSparkDefAdd";
	case kEoBBaseSparkDefX:
		return "kEoBBaseSparkDefX";
	case kEoBBaseSparkDefY:
		return "kEoBBaseSparkDefY";
	case kEoBBaseSparkOfFlags1:
		return "kEoBBaseSparkOfFlags1";
	case kEoBBaseSparkOfFlags2:
		return "kEoBBaseSparkOfFlags2";
	case kEoBBaseSparkOfShift:
		return "kEoBBaseSparkOfShift";
	case kEoBBaseSparkOfX:
		return "kEoBBaseSparkOfX";
	case kEoBBaseSparkOfY:
		return "kEoBBaseSparkOfY";
	case kEoBBaseSpellProperties:
		return "kEoBBaseSpellProperties";
	case kEoBBaseMagicFlightProps:
		return "kEoBBaseMagicFlightProps";
	case kEoBBaseTurnUndeadEffect:
		return "kEoBBaseTurnUndeadEffect";
	case kEoBBaseBurningHandsDest:
		return "kEoBBaseBurningHandsDest";
	case kEoBBaseConeOfColdDest1:
		return "kEoBBaseConeOfColdDest1";
	case kEoBBaseConeOfColdDest2:
		return "kEoBBaseConeOfColdDest2";
	case kEoBBaseConeOfColdDest3:
		return "kEoBBaseConeOfColdDest3";
	case kEoBBaseConeOfColdDest4:
		return "kEoBBaseConeOfColdDest4";
	case kEoBBaseConeOfColdGfxTbl:
		return "kEoBBaseConeOfColdGfxTbl";
	case kEoB1MainMenuStrings:
		return "kEoB1MainMenuStrings";
	case kEoB1BonusStrings:
		return "kEoB1BonusStrings";
	case kEoB1IntroFilesOpening:
		return "kEoB1IntroFilesOpening";
	case kEoB1IntroFilesTower:
		return "kEoB1IntroFilesTower";
	case kEoB1IntroFilesOrb:
		return "kEoB1IntroFilesOrb";
	case kEoB1IntroFilesWdEntry:
		return "kEoB1IntroFilesWdEntry";
	case kEoB1IntroFilesKing:
		return "kEoB1IntroFilesKing";
	case kEoB1IntroFilesHands:
		return "kEoB1IntroFilesHands";
	case kEoB1IntroFilesWdExit:
		return "kEoB1IntroFilesWdExit";
	case kEoB1IntroFilesTunnel:
		return "kEoB1IntroFilesTunnel";
	case kEoB1IntroOpeningFrmDelay:
		return "kEoB1IntroOpeningFrmDelay";
	case kEoB1IntroWdEncodeX:
		return "kEoB1IntroWdEncodeX";
	case kEoB1IntroWdEncodeY:
		return "kEoB1IntroWdEncodeY";
	case kEoB1IntroWdEncodeWH:
		return "kEoB1IntroWdEncodeWH";
	case kEoB1IntroWdDsX:
		return "kEoB1IntroWdDsX";
	case kEoB1IntroWdDsY:
		return "kEoB1IntroWdDsY";
	case kEoB1IntroTvlX1:
		return "kEoB1IntroTvlX1";
	case kEoB1IntroTvlY1:
		return "kEoB1IntroTvlY1";
	case kEoB1IntroTvlX2:
		return "kEoB1IntroTvlX2";
	case kEoB1IntroTvlY2:
		return "kEoB1IntroTvlY2";
	case kEoB1IntroTvlW:
		return "kEoB1IntroTvlW";
	case kEoB1IntroTvlH:
		return "kEoB1IntroTvlH";
	case kEoB1DoorShapeDefs:
		return "kEoB1DoorShapeDefs";
	case kEoB1DoorSwitchCoords:
		return "kEoB1DoorSwitchCoords";
	case kEoB1MonsterProperties:
		return "kEoB1MonsterProperties";
	case kEoB1EnemyMageSpellList:
		return "kEoB1EnemyMageSpellList";
	case kEoB1EnemyMageSfx:
		return "kEoB1EnemyMageSfx";
	case kEoB1BeholderSpellList:
		return "kEoB1BeholderSpellList";
	case kEoB1BeholderSfx:
		return "kEoB1BeholderSfx";
	case kEoB1TurnUndeadString:
		return "kEoB1TurnUndeadString";
	case kEoB1CgaMappingDefault:
		return "kEoB1CgaMappingDefault";
	case kEoB1CgaMappingAlt:
		return "kEoB1CgaMappingAlt";
	case kEoB1CgaMappingInv:
		return "kEoB1CgaMappingInv";
	case kEoB1CgaMappingItemsL:
		return "kEoB1CgaMappingItemsL";
	case kEoB1CgaMappingItemsS:
		return "kEoB1CgaMappingItemsS";
	case kEoB1CgaMappingThrown:
		return "kEoB1CgaMappingThrown";
	case kEoB1CgaMappingIcons:
		return "kEoB1CgaMappingIcons";
	case kEoB1CgaMappingDeco:
		return "kEoB1CgaMappingDeco";
	case kEoB1CgaLevelMappingIndex:
		return "kEoB1CgaLevelMappingIndex";
	case kEoB1CgaMappingLevel0:
		return "kEoB1CgaMappingLevel0";
	case kEoB1CgaMappingLevel1:
		return "kEoB1CgaMappingLevel1";
	case kEoB1CgaMappingLevel2:
		return "kEoB1CgaMappingLevel2";
	case kEoB1CgaMappingLevel3:
		return "kEoB1CgaMappingLevel3";
	case kEoB1CgaMappingLevel4:
		return "kEoB1CgaMappingLevel4";
	case kEoB1NpcShpData:
		return "kEoB1NpcShpData";
	case kEoB1NpcSubShpIndex1:
		return "kEoB1NpcSubShpIndex1";
	case kEoB1NpcSubShpIndex2:
		return "kEoB1NpcSubShpIndex2";
	case kEoB1NpcSubShpY:
		return "kEoB1NpcSubShpY";
	case kEoB1Npc0Strings:
		return "kEoB1Npc0Strings";
	case kEoB1Npc11Strings:
		return "kEoB1Npc11Strings";
	case kEoB1Npc12Strings:
		return "kEoB1Npc12Strings";
	case kEoB1Npc21Strings:
		return "kEoB1Npc21Strings";
	case kEoB1Npc22Strings:
		return "kEoB1Npc22Strings";
	case kEoB1Npc31Strings:
		return "kEoB1Npc31Strings";
	case kEoB1Npc32Strings:
		return "kEoB1Npc32Strings";
	case kEoB1Npc4Strings:
		return "kEoB1Npc4Strings";
	case kEoB1Npc5Strings:
		return "kEoB1Npc5Strings";
	case kEoB1Npc6Strings:
		return "kEoB1Npc6Strings";
	case kEoB1Npc7Strings:
		return "kEoB1Npc7Strings";
	case kEoB2MainMenuStrings:
		return "kEoB2MainMenuStrings";
	case kEoB2TransferPortraitFrames:
		return "kEoB2TransferPortraitFrames";
	case kEoB2TransferConvertTable:
		return "kEoB2TransferConvertTable";
	case kEoB2TransferItemTable:
		return "kEoB2TransferItemTable";
	case kEoB2TransferExpTable:
		return "kEoB2TransferExpTable";
	case kEoB2TransferStrings1:
		return "kEoB2TransferStrings1";
	case kEoB2TransferStrings2:
		return "kEoB2TransferStrings2";
	case kEoB2TransferLabels:
		return "kEoB2TransferLabels";
	case kEoB2IntroStrings:
		return "kEoB2IntroStrings";
	case kEoB2IntroCPSFiles:
		return "kEoB2IntroCPSFiles";
	case kEob2IntroAnimData00:
		return "kEob2IntroAnimData00";
	case kEob2IntroAnimData01:
		return "kEob2IntroAnimData01";
	case kEob2IntroAnimData02:
		return "kEob2IntroAnimData02";
	case kEob2IntroAnimData03:
		return "kEob2IntroAnimData03";
	case kEob2IntroAnimData04:
		return "kEob2IntroAnimData04";
	case kEob2IntroAnimData05:
		return "kEob2IntroAnimData05";
	case kEob2IntroAnimData06:
		return "kEob2IntroAnimData06";
	case kEob2IntroAnimData07:
		return "kEob2IntroAnimData07";
	case kEob2IntroAnimData08:
		return "kEob2IntroAnimData08";
	case kEob2IntroAnimData09:
		return "kEob2IntroAnimData09";
	case kEob2IntroAnimData10:
		return "kEob2IntroAnimData10";
	case kEob2IntroAnimData11:
		return "kEob2IntroAnimData11";
	case kEob2IntroAnimData12:
		return "kEob2IntroAnimData12";
	case kEob2IntroAnimData13:
		return "kEob2IntroAnimData13";
	case kEob2IntroAnimData14:
		return "kEob2IntroAnimData14";
	case kEob2IntroAnimData15:
		return "kEob2IntroAnimData15";
	case kEob2IntroAnimData16:
		return "kEob2IntroAnimData16";
	case kEob2IntroAnimData17:
		return "kEob2IntroAnimData17";
	case kEob2IntroAnimData18:
		return "kEob2IntroAnimData18";
	case kEob2IntroAnimData19:
		return "kEob2IntroAnimData19";
	case kEob2IntroAnimData20:
		return "kEob2IntroAnimData20";
	case kEob2IntroAnimData21:
		return "kEob2IntroAnimData21";
	case kEob2IntroAnimData22:
		return "kEob2IntroAnimData22";
	case kEob2IntroAnimData23:
		return "kEob2IntroAnimData23";
	case kEob2IntroAnimData24:
		return "kEob2IntroAnimData24";
	case kEob2IntroAnimData25:
		return "kEob2IntroAnimData25";
	case kEob2IntroAnimData26:
		return "kEob2IntroAnimData26";
	case kEob2IntroAnimData27:
		return "kEob2IntroAnimData27";
	case kEob2IntroAnimData28:
		return "kEob2IntroAnimData28";
	case kEob2IntroAnimData29:
		return "kEob2IntroAnimData29";
	case kEob2IntroAnimData30:
		return "kEob2IntroAnimData30";
	case kEob2IntroAnimData31:
		return "kEob2IntroAnimData31";
	case kEob2IntroAnimData32:
		return "kEob2IntroAnimData32";
	case kEob2IntroAnimData33:
		return "kEob2IntroAnimData33";
	case kEob2IntroAnimData34:
		return "kEob2IntroAnimData34";
	case kEob2IntroAnimData35:
		return "kEob2IntroAnimData35";
	case kEob2IntroAnimData36:
		return "kEob2IntroAnimData36";
	case kEob2IntroAnimData37:
		return "kEob2IntroAnimData37";
	case kEob2IntroAnimData38:
		return "kEob2IntroAnimData38";
	case kEob2IntroAnimData39:
		return "kEob2IntroAnimData39";
	case kEob2IntroAnimData40:
		return "kEob2IntroAnimData40";
	case kEob2IntroAnimData41:
		return "kEob2IntroAnimData41";
	case kEob2IntroAnimData42:
		return "kEob2IntroAnimData42";
	case kEob2IntroAnimData43:
		return "kEob2IntroAnimData43";
	case kEoB2IntroShapes00:
		return "kEoB2IntroShapes00";
	case kEoB2IntroShapes01:
		return "kEoB2IntroShapes01";
	case kEoB2IntroShapes04:
		return "kEoB2IntroShapes04";
	case kEoB2IntroShapes07:
		return "kEoB2IntroShapes07";
	case kEoB2FinaleStrings:
		return "kEoB2FinaleStrings";
	case kEoB2CreditsData:
		return "kEoB2CreditsData";
	case kEoB2FinaleCPSFiles:
		return "kEoB2FinaleCPSFiles";
	case kEob2FinaleAnimData00:
		return "kEob2FinaleAnimData00";
	case kEob2FinaleAnimData01:
		return "kEob2FinaleAnimData01";
	case kEob2FinaleAnimData02:
		return "kEob2FinaleAnimData02";
	case kEob2FinaleAnimData03:
		return "kEob2FinaleAnimData03";
	case kEob2FinaleAnimData04:
		return "kEob2FinaleAnimData04";
	case kEob2FinaleAnimData05:
		return "kEob2FinaleAnimData05";
	case kEob2FinaleAnimData06:
		return "kEob2FinaleAnimData06";
	case kEob2FinaleAnimData07:
		return "kEob2FinaleAnimData07";
	case kEob2FinaleAnimData08:
		return "kEob2FinaleAnimData08";
	case kEob2FinaleAnimData09:
		return "kEob2FinaleAnimData09";
	case kEob2FinaleAnimData10:
		return "kEob2FinaleAnimData10";
	case kEob2FinaleAnimData11:
		return "kEob2FinaleAnimData11";
	case kEob2FinaleAnimData12:
		return "kEob2FinaleAnimData12";
	case kEob2FinaleAnimData13:
		return "kEob2FinaleAnimData13";
	case kEob2FinaleAnimData14:
		return "kEob2FinaleAnimData14";
	case kEob2FinaleAnimData15:
		return "kEob2FinaleAnimData15";
	case kEob2FinaleAnimData16:
		return "kEob2FinaleAnimData16";
	case kEob2FinaleAnimData17:
		return "kEob2FinaleAnimData17";
	case kEob2FinaleAnimData18:
		return "kEob2FinaleAnimData18";
	case kEob2FinaleAnimData19:
		return "kEob2FinaleAnimData19";
	case kEob2FinaleAnimData20:
		return "kEob2FinaleAnimData20";
	case kEoB2FinaleShapes00:
		return "kEoB2FinaleShapes00";
	case kEoB2FinaleShapes03:
		return "kEoB2FinaleShapes03";
	case kEoB2FinaleShapes07:
		return "kEoB2FinaleShapes07";
	case kEoB2FinaleShapes09:
		return "kEoB2FinaleShapes09";
	case kEoB2FinaleShapes10:
		return "kEoB2FinaleShapes10";
	case kEoB2NpcShapeData:
		return "kEoB2NpcShapeData";
	case kEoBBaseClassModifierFlags:
		return "kEoBBaseClassModifierFlags";
	case kEoBBaseMonsterStepTable01:
		return "kEoBBaseMonsterStepTable01";
	case kEoBBaseMonsterStepTable02:
		return "kEoBBaseMonsterStepTable02";
	case kEoBBaseMonsterStepTable1:
		return "kEoBBaseMonsterStepTable1";
	case kEoBBaseMonsterStepTable2:
		return "kEoBBaseMonsterStepTable2";
	case kEoBBaseMonsterStepTable3:
		return "kEoBBaseMonsterStepTable3";
	case kEoBBaseMonsterCloseAttPosTable1:
		return "kEoBBaseMonsterCloseAttPosTable1";
	case kEoBBaseMonsterCloseAttPosTable21:
		return "kEoBBaseMonsterCloseAttPosTable21";
	case kEoBBaseMonsterCloseAttPosTable22:
		return "kEoBBaseMonsterCloseAttPosTable22";
	case kEoBBaseMonsterCloseAttUnkTable:
		return "kEoBBaseMonsterCloseAttUnkTable";
	case kEoBBaseMonsterCloseAttChkTable1:
		return "kEoBBaseMonsterCloseAttChkTable1";
	case kEoBBaseMonsterCloseAttChkTable2:
		return "kEoBBaseMonsterCloseAttChkTable2";
	case kEoBBaseMonsterCloseAttDstTable1:
		return "kEoBBaseMonsterCloseAttDstTable1";
	case kEoBBaseMonsterCloseAttDstTable2:
		return "kEoBBaseMonsterCloseAttDstTable2";
	case kEoBBaseMonsterProximityTable:
		return "kEoBBaseMonsterProximityTable";
	case kEoBBaseFindBlockMonstersTable:
		return "kEoBBaseFindBlockMonstersTable";
	case kEoBBaseMonsterDirChangeTable:
		return "kEoBBaseMonsterDirChangeTable";
	case kEoBBaseMonsterDistAttStrings:
		return "kEoBBaseMonsterDistAttStrings";
	case kEoBBaseEncodeMonsterDefs:
		return "kEoBBaseEncodeMonsterDefs";
	case kEoBBaseNpcPresets:
		return "kEoBBaseNpcPresets";
	case kEoB2Npc1Strings:
		return "kEoB2Npc1Strings";
	case kEoB2Npc2Strings:
		return "kEoB2Npc2Strings";
	case kEoB2MonsterDustStrings:
		return "kEoB2MonsterDustStrings";
	case kEoB2DreamSteps:
		return "kEoB2DreamSteps";
	case kEoB2KheldranStrings:
		return "kEoB2KheldranStrings";
	case kEoB2HornStrings:
		return "kEoB2HornStrings";
	case kEoB2HornSounds:
		return "kEoB2HornSounds";
	case kEoB2WallOfForceDsX:
		return "kEoB2WallOfForceDsX";
	case kEoB2WallOfForceDsY:
		return "kEoB2WallOfForceDsY";
	case kEoB2WallOfForceNumW:
		return "kEoB2WallOfForceNumW";
	case kEoB2WallOfForceNumH:
		return "kEoB2WallOfForceNumH";
	case kEoB2WallOfForceShpId:
		return "kEoB2WallOfForceShpId";
	case kLoLIngamePakFiles:
		return "kLoLIngamePakFiles";
	case kLoLCharacterDefs:
		return "kLoLCharacterDefs";
	case kLoLIngameSfxFiles:
		return "kLoLIngameSfxFiles";
	case kLoLIngameSfxIndex:
		return "kLoLIngameSfxIndex";
	case kLoLMusicTrackMap:
		return "kLoLMusicTrackMap";
	case kLoLIngameGMSfxIndex:
		return "kLoLIngameGMSfxIndex";
	case kLoLIngameMT32SfxIndex:
		return "kLoLIngameMT32SfxIndex";
	case kLoLIngamePcSpkSfxIndex:
		return "kLoLIngamePcSpkSfxIndex";
	case kLoLSpellProperties:
		return "kLoLSpellProperties";
	case kLoLGameShapeMap:
		return "kLoLGameShapeMap";
	case kLoLSceneItemOffs:
		return "kLoLSceneItemOffs";
	case kLoLCharInvIndex:
		return "kLoLCharInvIndex";
	case kLoLCharInvDefs:
		return "kLoLCharInvDefs";
	case kLoLCharDefsMan:
		return "kLoLCharDefsMan";
	case kLoLCharDefsWoman:
		return "kLoLCharDefsWoman";
	case kLoLCharDefsKieran:
		return "kLoLCharDefsKieran";
	case kLoLCharDefsAkshel:
		return "kLoLCharDefsAkshel";
	case kLoLExpRequirements:
		return "kLoLExpRequirements";
	case kLoLMonsterModifiers1:
		return "kLoLMonsterModifiers1";
	case kLoLMonsterModifiers2:
		return "kLoLMonsterModifiers2";
	case kLoLMonsterModifiers3:
		return "kLoLMonsterModifiers3";
	case kLoLMonsterModifiers4:
		return "kLoLMonsterModifiers4";
	case kLoLMonsterShiftOffsets:
		return "kLoLMonsterShiftOffsets";
	case kLoLMonsterDirFlags:
		return "kLoLMonsterDirFlags";
	case kLoLMonsterScaleY:
		return "kLoLMonsterScaleY";
	case kLoLMonsterScaleX:
		return "kLoLMonsterScaleX";
	case kLoLMonsterScaleWH:
		return "kLoLMonsterScaleWH";
	case kLoLFlyingObjectShp:
		return "kLoLFlyingObjectShp";
	case kLoLInventoryDesc:
		return "kLoLInventoryDesc";
	case kLoLLevelShpList:
		return "kLoLLevelShpList";
	case kLoLLevelDatList:
		return "kLoLLevelDatList";
	case kLoLCompassDefs:
		return "kLoLCompassDefs";
	case kLoLItemPrices:
		return "kLoLItemPrices";
	case kLoLStashSetup:
		return "kLoLStashSetup";
	case kLoLDscWalls:
		return "kLoLDscWalls";
	case kRpgCommonDscShapeIndex:
		return "kRpgCommonDscShapeIndex";
	case kLoLDscOvlMap:
		return "kLoLDscOvlMap";
	case kLoLDscScaleWidthData:
		return "kLoLDscScaleWidthData";
	case kLoLDscScaleHeightData:
		return "kLoLDscScaleHeightData";
	case kRpgCommonDscX:
		return "kRpgCommonDscX";
	case kLoLDscY:
		return "kLoLDscY";
	case kRpgCommonDscTileIndex:
		return "kRpgCommonDscTileIndex";
	case kRpgCommonDscUnk2:
		return "kRpgCommonDscUnk2";
	case kRpgCommonDscDoorShapeIndex:
		return "kRpgCommonDscDoorShapeIndex";
	case kRpgCommonDscDimData1:
		return "kRpgCommonDscDimData1";
	case kRpgCommonDscDimData2:
		return "kRpgCommonDscDimData2";
	case kRpgCommonDscBlockMap:
		return "kRpgCommonDscBlockMap";
	case kRpgCommonDscDimMap:
		return "kRpgCommonDscDimMap";
	case kLoLDscOvlIndex:
		return "kLoLDscOvlIndex";
	case kRpgCommonDscBlockIndex:
		return "kRpgCommonDscBlockIndex";
	case kRpgCommonDscDoorY2:
		return "kRpgCommonDscDoorY2";
	case kRpgCommonDscDoorFrameY1:
		return "kRpgCommonDscDoorFrameY1";
	case kRpgCommonDscDoorFrameY2:
		return "kRpgCommonDscDoorFrameY2";
	case kRpgCommonDscDoorFrameIndex1:
		return "kRpgCommonDscDoorFrameIndex1";
	case kRpgCommonDscDoorFrameIndex2:
		return "kRpgCommonDscDoorFrameIndex2";
	case kLoLDscDoorScale:
		return "kLoLDscDoorScale";
	case kLoLDscDoor4:
		return "kLoLDscDoor4";
	case kLoLDscDoorX:
		return "kLoLDscDoorX";
	case kLoLDscDoorY:
		return "kLoLDscDoorY";
	case kLoLScrollXTop:
		return "kLoLScrollXTop";
	case kLoLScrollYTop:
		return "kLoLScrollYTop";
	case kLoLScrollXBottom:
		return "kLoLScrollXBottom";
	case kLoLScrollYBottom:
		return "kLoLScrollYBottom";
	case kLoLButtonDefs:
		return "kLoLButtonDefs";
	case kLoLButtonList1:
		return "kLoLButtonList1";
	case kLoLButtonList2:
		return "kLoLButtonList2";
	case kLoLButtonList3:
		return "kLoLButtonList3";
	case kLoLButtonList4:
		return "kLoLButtonList4";
	case kLoLButtonList5:
		return "kLoLButtonList5";
	case kLoLButtonList6:
		return "kLoLButtonList6";
	case kLoLButtonList7:
		return "kLoLButtonList7";
	case kLoLButtonList8:
		return "kLoLButtonList8";
	case kLoLLegendData:
		return "kLoLLegendData";
	case kLoLMapCursorOvl:
		return "kLoLMapCursorOvl";
	case kLoLMapStringId:
		return "kLoLMapStringId";
	case kLoLSpellbookAnim:
		return "kLoLSpellbookAnim";
	case kLoLSpellbookCoords:
		return "kLoLSpellbookCoords";
	case kLoLHealShapeFrames:
		return "kLoLHealShapeFrames";
	case kLoLLightningDefs:
		return "kLoLLightningDefs";
	case kLoLFireballCoords:
		return "kLoLFireballCoords";
	case kLoLCredits:
		return "kLoLCredits";
	case kLoLHistory:
		return "kLoLHistory";
	default:
		return "Unknown";
	}
}

struct ExtractData {
	ExtractData() : desc(), offset() {}
	ExtractData(ExtractEntrySearchData d, uint32 o) : desc(d), offset(o) {}

	ExtractEntrySearchData desc;
	uint32 offset;
};

typedef std::pair<int, ExtractEntrySearchData> SearchMapEntry;
typedef std::multimap<int, ExtractEntrySearchData> SearchMap;

typedef std::pair<int, ExtractData> ExtractMapEntry;
typedef std::multimap<int, ExtractData> ExtractMap;

bool getExtractionData(const Game *g, Search &search, ExtractMap &map);

bool createIDMap(PAKFile &out, const ExtractInformation *eI, const int *needList);

bool process(PAKFile &out, const Game *g, const byte *data, const uint32 size) {
	char filename[128];

	Search search(data, size);
	ExtractMap ids;

	if (!getExtractionData(g, search, ids))
		return false;

	const int *needList = getNeedList(g);
	if (!needList) {
		fprintf(stderr, "ERROR: No entry need list available\n");
		return false;
	}

	ExtractInformation extractInfo;
	extractInfo.game = g->game;
	extractInfo.platform = g->platform;
	extractInfo.special = g->special;

	for (ExtractMap::const_iterator i = ids.begin(); i != ids.end(); ++i) {
		const int id = i->first;
		extractInfo.lang = i->second.desc.lang;

		const ExtractFilename *fDesc = getFilenameDesc(id);

		if (!fDesc) {
			fprintf(stderr, "ERROR: couldn't find file description for id %d/%s\n", id, getIdString(id));
			return false;
		}

		filename[0] = 0;
		if (!getFilename(filename, &extractInfo, id)) {
			fprintf(stderr, "ERROR: couldn't get filename for id %d/%s\n", id, getIdString(id));
			return false;
		}

		const ExtractType *tDesc = findExtractType(fDesc->type);

		if (!tDesc) {
			fprintf(stderr, "ERROR: couldn't find type description for id %d/%s (%d)\n", id, getIdString(id), fDesc->type);
			return false;
		}

		PAKFile::cFileList *list = out.getFileList();
		if (list && list->findEntry(filename) != 0)
			continue;

		if (!tDesc->extract(out, &extractInfo, data + i->second.offset, i->second.desc.hint.size, filename, id)) {
			fprintf(stderr, "ERROR: couldn't extract id %d/%s\n", id, getIdString(id));
			return false;
		}
	}

	for (int i = 0; i < 3; ++i) {
		if (g->lang[i] == -1)
			continue;

		extractInfo.lang = g->lang[i];
		if (!createIDMap(out, &extractInfo, needList))
			return false;

		if (!updateIndex(out, &extractInfo)) {
			error("couldn't update INDEX file, stop processing of all files");
			return false;
		}
	}

	return true;
}

bool createIDMap(PAKFile &out, const ExtractInformation *eI, const int *needList) {
	int dataEntries = 0;
	// Count entries in the need list
	for (const int *n = needList; *n != -1; ++n)
		++dataEntries;

	const int mapSize = 2 + dataEntries * (2 + 1 + 4);
	uint8 *map = new uint8[mapSize];
	uint8 *dst = map;

	WRITE_BE_UINT16(dst, dataEntries); dst += 2;
	for (const int *id = needList; *id != -1; ++id) {
		WRITE_BE_UINT16(dst, *id); dst += 2;
		const ExtractFilename *fDesc = getFilenameDesc(*id);
		if (!fDesc)
			return false;
		*dst++ = getTypeID(fDesc->type);
		WRITE_BE_UINT32(dst, getFilename(eI, *id)); dst += 4;
	}

	char filename[12];
	if (!getFilename(filename, eI, 0)) {
		fprintf(stderr, "ERROR: Could not create ID map for game\n");
		return false;
	}

	out.removeFile(filename);
	if (!out.addFile(filename, map, mapSize)) {
		fprintf(stderr, "ERROR: Could not add ID map \"%s\" to kyra.dat\n", filename);
		return false;
	}

	return true;
}

// Uncomment this to get various debug information about the detection table entries.
//#define DEBUG_EXTRACTION_TABLES

bool setupSearch(const Game *g, const int *needList, Search &search, SearchMap &searchData) {
	for (const int *entry = needList; *entry != -1; ++entry) {
		ExtractEntryList providers = getProvidersForId(*entry);

		if (providers.empty()) {
			fprintf(stderr, "ERROR: No provider for id %d/%s\n", *entry, getIdString(*entry));
			return false;
		} else {
			for (ExtractEntryList::const_iterator i = providers.begin(); i != providers.end(); ++i) {
				// Only add generic or partly matching providers here.
#ifndef DEBUG_EXTRACTION_TABLES
				if ((i->lang == UNK_LANG || i->lang == g->lang[0] || i->lang == g->lang[1] || i->lang == g->lang[2]) &&
				    (i->platform == kPlatformUnknown || (i->platform == g->platform))) {
#endif
					search.addData(i->hint);
					searchData.insert(SearchMapEntry(*entry, *i));
#ifndef DEBUG_EXTRACTION_TABLES
				}
#endif
			}
		}
	}

	return true;
}

typedef std::list<ExtractMap::const_iterator> MatchList;
MatchList filterPlatformMatches(const Game *g, std::pair<ExtractMap::const_iterator, ExtractMap::const_iterator> range) {
	bool hasPlatformMatch = false;
	for (ExtractMap::const_iterator i = range.first; i != range.second; ++i) {
		if (i->second.desc.platform == g->platform) {
			hasPlatformMatch = true;
			break;
		}
	}

	MatchList result;
	if (hasPlatformMatch) {
		for (ExtractMap::const_iterator i = range.first; i != range.second; ++i) {
			if (i->second.desc.platform == g->platform)
				result.push_back(i);
		}
	} else {
		for (ExtractMap::const_iterator i = range.first; i != range.second; ++i)
			result.push_back(i);
	}

	return result;
}

MatchList filterLanguageMatches(const int lang, const MatchList &input) {
	std::list<ExtractMap::const_iterator> result;

	for (MatchList::const_iterator i = input.begin(); i != input.end(); ++i) {
		if ((*i)->second.desc.lang == lang)
			result.push_back(*i);
	}

	return result;
}

MatchList::const_iterator filterOutBestMatch(const MatchList &input) {
	MatchList::const_iterator result = input.begin();

	if (input.size() > 1)
		warning("Multiple entries found for id %d/%s", (*result)->first, getIdString((*result)->first));

	for (MatchList::const_iterator i = input.begin(); i != input.end(); ++i) {
		// Reduce all entries to one single entry.
		//
		// We use the following rules for this (in this order):
		// - Prefer the entry with the higest size
		// - Prefer the entry, which starts at the smallest offest
		//
		// TODO: These rules might not be safe for all games, but hopefully
		// they will work fine. If there are any problems it should be rather
		// easy to identify them, since we print out a warning for multiple
		// entries found.
		if ((*result)->second.desc.hint.size <= (*i)->second.desc.hint.size) {
			if ((*result)->second.offset >= (*i)->second.offset)
				result = i;
		}
	}

	return result;
}

bool getExtractionData(const Game *g, Search &search, ExtractMap &map) {
	SearchMap searchMap;

	const int *needList = getNeedList(g);
	if (!needList) {
		fprintf(stderr, "ERROR: No entry need list available\n");
		return false;
	}

	if (!setupSearch(g, needList, search, searchMap))
		return false;

	// Process the data search
	Search::ResultList results;
	search.search(results);

	if (results.empty()) {
		fprintf(stderr, "ERROR: Couldn't find any required data\n");
		return false;
	}

	ExtractMap temporaryExtractMap;
	for (const int *entry = needList; *entry != -1; ++entry) {
		typedef std::pair<SearchMap::const_iterator, SearchMap::const_iterator> KeyRange;
		KeyRange idRange = searchMap.equal_range(*entry);

		for (Search::ResultList::const_iterator i = results.begin(); i != results.end(); ++i) {
			for (SearchMap::const_iterator j = idRange.first; j != idRange.second; ++j) {
				if (j->second.hint == i->data)
					temporaryExtractMap.insert(ExtractMapEntry(*entry, ExtractData(j->second, i->offset)));
			}
		}
	}

	// Free up some memory
	results.clear();
	searchMap.clear();

	bool result = true;

	for (const int *entry = needList; *entry != -1; ++entry) {
		MatchList possibleMatches = filterPlatformMatches(g, temporaryExtractMap.equal_range(*entry));

		if (possibleMatches.empty()) {
			fprintf(stderr, "ERROR: No entry found for id %d/%s\n", *entry, getIdString(*entry));
			result = false;
			continue;
		}

		if (isLangSpecific(*entry)) {
			for (int i = 0; i < 3; ++i) {
				if (g->lang[i] == -1)
					continue;

				MatchList langMatches = filterLanguageMatches(g->lang[i], possibleMatches);
				MatchList::const_iterator bestMatch = filterOutBestMatch(langMatches);

				if (bestMatch == langMatches.end()) {
					// TODO: Add nice language name to output message.
					fprintf(stderr, "ERROR: No entry found for id %d/%s for language %d\n", *entry, getIdString(*entry), g->lang[i]);
					result = false;
					continue;
				}

#ifdef DEBUG_EXTRACTION_TABLES
				if (((*bestMatch)->second.desc.platform != kPlatformUnknown && (*bestMatch)->second.desc.platform != g->platform))
					printf("%s: %.8X %.8X %d %d\n", getIdString(*entry), (*bestMatch)->second.desc.hint.size, (*bestMatch)->second.desc.hint.byteSum, (*bestMatch)->second.desc.lang, (*bestMatch)->second.desc.platform);
#endif

				map.insert(**bestMatch);
			}
		} else {
			MatchList::const_iterator bestMatch = filterOutBestMatch(possibleMatches);

			if (bestMatch == possibleMatches.end()) {
				fprintf(stderr, "ERROR: No entry found for id %d/%s\n", *entry, getIdString(*entry));
				result = false;
				continue;
			}

#ifdef DEBUG_EXTRACTION_TABLES
			if (((*bestMatch)->second.desc.platform != kPlatformUnknown && (*bestMatch)->second.desc.platform != g->platform))
				printf("%s: %.8X %.8X %d %d\n", getIdString(*entry), (*bestMatch)->second.desc.hint.size, (*bestMatch)->second.desc.hint.byteSum, (*bestMatch)->second.desc.lang, (*bestMatch)->second.desc.platform);
#endif

			map.insert(**bestMatch);
		}
	}

	return result;
}
