/* ScummVM - Scumm Interpreter * Copyright (C) 2004-2005 The ScummVM project * * 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. * * $Header$ * */ #include "common/stdafx.h" #include "kyra/kyra.h" #include "kyra/screen.h" #include "kyra/resource.h" namespace Kyra { #define RESFILE_VERSION 2 #define GAME_FLAGS (GF_FLOPPY | GF_TALKIE | GF_DEMO | GF_AUDIOCD) #define LANGUAGE_FLAGS (GF_ENGLISH | GF_FRENCH | GF_GERMAN | GF_SPANISH | GF_LNGUNK) byte *getFile(PAKFile &res, const char *filename) { uint32 size = 0; size = res.getFileSize(filename); if (!size) return 0; const byte *src = res.getFile(filename); byte *dst = new byte[size]; memcpy(dst, src, size); return dst; } struct LanguageTypes { uint32 flags; const char *ext; }; static LanguageTypes languages[] = { { GF_ENGLISH, "ENG" }, // this is the default language { GF_FRENCH, "FRE" }, { GF_GERMAN, "GER" }, { GF_SPANISH, "SPA" }, { 0, 0 } }; void KyraEngine::res_loadResources(int type) { debug(9, "res_loadResources(%d)", type); PAKFile resFile("KYRA.DAT"); if (!resFile.isValid() || !resFile.isOpen()) { error("couldn't open Kyrandia resource file ('KYRA.DAT') make sure you got one file for your version"); } uint32 version = 0; uint32 gameID = 0; uint32 featuresValue = 0; bool loadNativeLanguage = true; byte *temp = 0; if (_features & GF_TALKIE) { temp = getFile(resFile, "INDEX.CD"); } else if (_features & GF_DEMO) { temp = getFile(resFile, "INDEX.DEM"); } else { temp = getFile(resFile, "INDEX"); } if (!temp) { error("no matching INDEX file found"); } version = READ_BE_UINT32(temp); gameID = READ_BE_UINT32((temp+4)); featuresValue = READ_BE_UINT32((temp+8)); delete [] temp; temp = 0; if (version < RESFILE_VERSION) { error("invalid resource file version (%d)", version); } if (gameID != _game) { error("invalid game id (%d)", gameID); } if ((featuresValue & GAME_FLAGS) != (_features & GAME_FLAGS)) { error("your data file has a different game flags (0x%.08X has the data and your version has 0x%.08X)", (featuresValue & GAME_FLAGS), (_features & GAME_FLAGS)); } if (!((featuresValue & LANGUAGE_FLAGS) & (_features & LANGUAGE_FLAGS))) { char buffer[240]; sprintf(buffer, "your data file has support for:"); if (featuresValue & GF_ENGLISH) { sprintf(buffer + strlen(buffer), " English"); } if (featuresValue & GF_FRENCH) { sprintf(buffer + strlen(buffer), " French"); } if (featuresValue & GF_GERMAN) { sprintf(buffer + strlen(buffer), " German"); } if (featuresValue & GF_SPANISH) { sprintf(buffer + strlen(buffer), " Spanish"); } sprintf(buffer + strlen(buffer), " but not your language ("); if (_features & GF_ENGLISH) { sprintf(buffer + strlen(buffer), "English"); } else if (_features & GF_FRENCH) { sprintf(buffer + strlen(buffer), "French"); } else if (_features & GF_GERMAN) { sprintf(buffer + strlen(buffer), "German"); } else if (_features & GF_SPANISH) { sprintf(buffer + strlen(buffer), "Spanish"); } else { sprintf(buffer + strlen(buffer), "unknown"); } sprintf(buffer + strlen(buffer), ")"); warning(buffer); loadNativeLanguage = false; } #define getFileEx(x, y) \ if (_features & GF_TALKIE) { \ temp = getFile(x, y ".CD"); \ } else if (_features & GF_DEMO) { \ temp = getFile(x, y ".DEM"); \ } else { \ temp = getFile(x, y); \ } #define loadRawFile(x, y, z) \ getFileEx(x, y) \ if (temp) { \ z = temp; \ temp = 0; \ } #define loadTable(x, y, z, a) \ getFileEx(x, y) \ if (temp) { \ res_loadTable(temp, z, a); \ delete [] temp; \ temp = 0; \ } #define loadRooms(x, y, z, a) \ getFileEx(x, y) \ if (temp) { \ res_loadRoomTable(temp, z, a); \ delete [] temp; \ temp = 0; \ } #define loadShapes(x, y, z, a) \ getFileEx(x, y) \ if (temp) { \ res_loadShapeTable(temp, z, a); \ delete [] temp; \ temp = 0; \ } if ((type & RES_INTRO) || type == RES_ALL) { loadRawFile(resFile, "FOREST.SEQ", _seq_Forest); loadRawFile(resFile, "KALLAK-WRITING.SEQ", _seq_KallakWriting); loadRawFile(resFile, "KYRANDIA-LOGO.SEQ", _seq_KyrandiaLogo); loadRawFile(resFile, "KALLAK-MALCOLM.SEQ", _seq_KallakMalcolm); loadRawFile(resFile, "MALCOLM-TREE.SEQ", _seq_MalcolmTree); loadRawFile(resFile, "WESTWOOD-LOGO.SEQ", _seq_WestwoodLogo); loadRawFile(resFile, "DEMO1.SEQ", _seq_Demo1); loadRawFile(resFile, "DEMO2.SEQ", _seq_Demo2); loadRawFile(resFile, "DEMO3.SEQ", _seq_Demo3); loadRawFile(resFile, "DEMO4.SEQ", _seq_Demo4); loadTable(resFile, "INTRO-CPS.TXT", (byte***)&_seq_CPSTable, &_seq_CPSTable_Size); loadTable(resFile, "INTRO-COL.TXT", (byte***)&_seq_COLTable, &_seq_COLTable_Size); loadTable(resFile, "INTRO-WSA.TXT", (byte***)&_seq_WSATable, &_seq_WSATable_Size); res_loadLangTable("INTRO-STRINGS.", &resFile, (byte***)&_seq_textsTable, &_seq_textsTable_Size, loadNativeLanguage); } if ((type & RES_INGAME) || type == RES_ALL) { loadTable(resFile, "ROOM-FILENAMES.TXT", (byte***)&_roomFilenameTable, &_roomFilenameTableSize); loadRooms(resFile, "ROOM-TABLE.ROOM", &_roomTable, &_roomTableSize); loadTable(resFile, "CHAR-IMAGE.TXT", (byte***)&_characterImageTable, &_characterImageTableSize); loadShapes(resFile, "SHAPES-DEFAULT.SHP", &_defaultShapeTable, &_defaultShapeTableSize); } #undef loadRooms #undef loadTable #undef loadRawFile #undef getFileEx } void KyraEngine::res_unloadResources(int type) { debug(9, "res_unloadResources(%d)", type); if ((type & RES_INTRO) || type == RES_ALL) { for (int i = 0; i < _seq_WSATable_Size; ++i) { delete [] _seq_WSATable[i]; } delete [] _seq_WSATable; _seq_WSATable_Size = 0; _seq_WSATable = 0; for (int i = 0; i < _seq_CPSTable_Size; ++i) { delete [] _seq_CPSTable[i]; } delete [] _seq_CPSTable; _seq_CPSTable_Size = 0; _seq_CPSTable = 0; for (int i = 0; i < _seq_COLTable_Size; ++i) { delete [] _seq_COLTable[i]; } delete [] _seq_COLTable; _seq_COLTable_Size = 0; _seq_COLTable = 0; for (int i = 0; i < _seq_textsTable_Size; ++i) { delete [] _seq_textsTable[i]; } delete [] _seq_textsTable; _seq_textsTable_Size = 0; _seq_textsTable = 0; delete [] _seq_Forest; _seq_Forest = 0; delete [] _seq_KallakWriting; _seq_KallakWriting = 0; delete [] _seq_KyrandiaLogo; _seq_KyrandiaLogo = 0; delete [] _seq_KallakMalcolm; _seq_KallakMalcolm = 0; delete [] _seq_MalcolmTree; _seq_MalcolmTree = 0; delete [] _seq_WestwoodLogo; _seq_WestwoodLogo = 0; delete [] _seq_Demo1; _seq_Demo1 = 0; delete [] _seq_Demo2; _seq_Demo2 = 0; delete [] _seq_Demo3; _seq_Demo3 = 0; delete [] _seq_Demo4; _seq_Demo4 = 0; } if ((type & RES_INGAME) || type == RES_ALL) { for (int i = 0; i < _roomFilenameTableSize; ++i) { delete [] _roomFilenameTable[i]; } delete [] _roomFilenameTable; _roomFilenameTableSize = 0; _roomFilenameTable = 0; delete [] _roomTable; _roomTable = 0; _roomTableSize = 0; for (int i = 0; i < _characterImageTableSize; ++i) { delete [] _characterImageTable[i]; } delete [] _characterImageTable; _characterImageTableSize = 0; delete [] _defaultShapeTable; _defaultShapeTableSize = 0; } } void KyraEngine::res_loadLangTable(const char *filename, PAKFile *res, byte ***loadTo, int *size, bool nativ) { char file[36]; for (int i = 0; languages[i].ext; ++i) { if (languages[i].flags != (_features & LANGUAGE_FLAGS) && nativ) { continue; } strcpy(file, filename); strcat(file, languages[i].ext); if (_features & GF_TALKIE) { strcat(file, ".CD"); } else if (_features & GF_DEMO) { strcat(file, ".DEM"); } byte *temp = getFile(*res, file); if (temp) { res_loadTable(temp, loadTo, size); delete [] temp; temp = 0; } else { if (!nativ) continue; } break; } } void KyraEngine::res_loadTable(const byte *src, byte ***loadTo, int *size) { uint32 count = READ_BE_UINT32(src); src += 4; *size = count; *loadTo = new byte*[count]; const char *curPos = (const char*)src; for (uint32 i = 0; i < count; ++i) { int strLen = strlen(curPos); (*loadTo)[i] = new byte[strLen+1]; memcpy((*loadTo)[i], curPos, strLen+1); curPos += strLen+1; } } void KyraEngine::res_loadRoomTable(const byte *src, Room **loadTo, int *size) { uint32 count = READ_BE_UINT32(src); src += 4; *size = count; *loadTo = new Room[count]; for (uint32 i = 0; i < count; ++i) { (*loadTo)[i].nameIndex = *src++; (*loadTo)[i].northExit = READ_LE_UINT16(src); src += 2; (*loadTo)[i].eastExit = READ_LE_UINT16(src); src += 2; (*loadTo)[i].southExit = READ_LE_UINT16(src); src += 2; (*loadTo)[i].westExit = READ_LE_UINT16(src); src += 2; memset(&(*loadTo)[i].itemsTable[0], 0xFF, sizeof(byte)*6); memset(&(*loadTo)[i].itemsTable[6], 0, sizeof(byte)*6); memset((*loadTo)[i].itemsXPos, 0, sizeof(uint16)*12); memset((*loadTo)[i].itemsYPos, 0, sizeof(uint8)*12); memset((*loadTo)[i].unkField3, 0, sizeof((*loadTo)[i].unkField3)); } } void KyraEngine::res_loadShapeTable(const byte *src, Shape **loadTo, int *size) { uint32 count = READ_BE_UINT32(src); src += 4; *size = count; *loadTo = new Shape[count]; for (uint32 i = 0; i < count; ++i) { (*loadTo)[i].imageIndex = *src++; (*loadTo)[i].x = *src++; (*loadTo)[i].y = *src++; (*loadTo)[i].w = *src++; (*loadTo)[i].h = *src++; (*loadTo)[i].xOffset = *src++; (*loadTo)[i].yOffset = *src++; } } const ScreenDim Screen::_screenDimTable[] = { { 0x00, 0x00, 0x28, 0xC8, 0x0F, 0x0C, 0x00, 0x00 }, { 0x08, 0x48, 0x18, 0x38, 0x0F, 0x0C, 0x00, 0x00 }, { 0x01, 0x08, 0x26, 0x80, 0x0F, 0x0C, 0x00, 0x00 }, { 0x00, 0xC2, 0x28, 0x06, 0x0F, 0x0C, 0x00, 0x00 }, { 0x00, 0x90, 0x28, 0x38, 0x04, 0x0C, 0x00, 0x00 }, { 0x01, 0x94, 0x26, 0x30, 0x04, 0x1B, 0x00, 0x00 }, { 0x00, 0x90, 0x28, 0x38, 0x0F, 0x0D, 0x00, 0x00 }, { 0x01, 0x96, 0x26, 0x32, 0x0F, 0x0D, 0x00, 0x00 }, { 0x00, 0x00, 0x28, 0x88, 0x0F, 0x0C, 0x00, 0x00 }, { 0x01, 0x20, 0x26, 0x80, 0x0F, 0x0C, 0x00, 0x00 }, { 0x03, 0x28, 0x22, 0x46, 0x0F, 0x0D, 0x00, 0x00 } }; const int Screen::_screenDimTableCount = ARRAYSIZE(_screenDimTable); // CD Version *could* use an different opcodeTable #define Opcode(x) &KyraEngine::x KyraEngine::OpcodeProc KyraEngine::_opcodeTable[] = { // 0x00 Opcode(cmd_magicInMouseItem), Opcode(cmd_characterSays), Opcode(cmd_pauseTicks), Opcode(cmd_drawSceneAnimShape), // 0x04 Opcode(cmd_queryGameFlag), Opcode(cmd_setGameFlag), Opcode(cmd_resetGameFlag), Opcode(cmd_runNPCScript), // 0x08 Opcode(cmd_setSpecialExitList), Opcode(cmd_blockInWalkableRegion), Opcode(cmd_blockOutWalkableRegion), Opcode(cmd_walkPlayerToPoint), // 0x0c Opcode(cmd_dropItemInScene), Opcode(cmd_drawAnimShapeIntoScene), Opcode(cmd_createMouseItem), Opcode(cmd_savePageToDisk), // 0x10 Opcode(cmd_sceneAnimOn), Opcode(cmd_sceneAnimOff), Opcode(cmd_getElapsedSeconds), Opcode(cmd_mouseIsPointer), // 0x14 Opcode(cmd_destroyMouseItem), Opcode(cmd_runSceneAnimUntilDone), Opcode(cmd_fadeSpecialPalette), Opcode(cmd_playAdlibSound), // 0x18 Opcode(cmd_playAdlibScore), Opcode(cmd_phaseInSameScene), Opcode(cmd_setScenePhasingFlag), Opcode(cmd_resetScenePhasingFlag), // 0x1c Opcode(cmd_queryScenePhasingFlag), Opcode(cmd_sceneToDirection), Opcode(cmd_setBirthstoneGem), Opcode(cmd_placeItemInGenericMapScene), // 0x20 Opcode(cmd_setBrandonStatusBit), Opcode(cmd_pauseSeconds), Opcode(cmd_getCharactersLocation), Opcode(cmd_runNPCSubscript), // 0x24 Opcode(cmd_magicOutMouseItem), Opcode(cmd_internalAnimOn), Opcode(cmd_forceBrandonToNormal), Opcode(cmd_poisonDeathNow), // 0x28 Opcode(cmd_setScaleMode), Opcode(cmd_openWSAFile), Opcode(cmd_closeWSAFile), Opcode(cmd_runWSAFromBeginningToEnd), // 0x2c Opcode(cmd_displayWSAFrame), Opcode(cmd_enterNewScene), Opcode(cmd_setSpecialEnterXAndY), Opcode(cmd_runWSAFrames), // 0x30 Opcode(cmd_popBrandonIntoScene), Opcode(cmd_restoreAllObjectBackgrounds), Opcode(cmd_setCustomPaletteRange), Opcode(cmd_loadPageFromDisk), // 0x34 Opcode(cmd_customPrintTalkString), Opcode(cmd_restoreCustomPrintBackground), Opcode(cmd_hideMouse), Opcode(cmd_showMouse), // 0x38 Opcode(cmd_getCharacterX), Opcode(cmd_getCharacterY), Opcode(cmd_changeCharactersFacing), Opcode(cmd_CopyWSARegion), // 0x3c Opcode(cmd_printText), Opcode(cmd_random), Opcode(cmd_loadSoundFile), Opcode(cmd_displayWSAFrameOnHidPage), // 0x40 Opcode(cmd_displayWSASequentialFrames), Opcode(cmd_drawCharacterStanding), Opcode(cmd_internalAnimOff), Opcode(cmd_changeCharactersXAndY), // 0x44 Opcode(cmd_clearSceneAnimatorBeacon), Opcode(cmd_querySceneAnimatorBeacon), Opcode(cmd_refreshSceneAnimator), Opcode(cmd_placeItemInOffScene), // 0x48 Opcode(cmd_wipeDownMouseItem), Opcode(cmd_placeCharacterInOtherScene), Opcode(cmd_getKey), Opcode(cmd_specificItemInInventory), // 0x4c Opcode(cmd_popMobileNPCIntoScene), Opcode(cmd_mobileCharacterInScene), Opcode(cmd_hideMobileCharacter), Opcode(cmd_unhideMobileCharacter), // 0x50 Opcode(cmd_setCharactersLocation), Opcode(cmd_walkCharacterToPoint), Opcode(cmd_specialEventDisplayBrynnsNote), Opcode(cmd_specialEventRemoveBrynnsNote), // 0x54 Opcode(cmd_setLogicPage), Opcode(cmd_fatPrint), Opcode(cmd_preserveAllObjectBackgrounds), Opcode(cmd_updateSceneAnimations), // 0x58 Opcode(cmd_sceneAnimationActive), Opcode(cmd_setCharactersMovementDelay), Opcode(cmd_getCharactersFacing), Opcode(cmd_bkgdScrollSceneAndMasksRight), // 0x5c Opcode(cmd_dispelMagicAnimation), Opcode(cmd_findBrightestFireberry), Opcode(cmd_setFireberryGlowPalette), Opcode(cmd_setDeathHandlerFlag), // 0x60 Opcode(cmd_drinkPotionAnimation), Opcode(cmd_makeAmuletAppear), Opcode(cmd_drawItemShapeIntoScene), Opcode(cmd_setCharactersCurrentFrame), // 0x64 Opcode(cmd_waitForConfirmationMouseClick), Opcode(cmd_pageFlip), Opcode(cmd_setSceneFile), Opcode(cmd_getItemInMarbleVase), // 0x68 Opcode(cmd_setItemInMarbleVase), Opcode(cmd_addItemToInventory), Opcode(cmd_intPrint), Opcode(cmd_shakeScreen), // 0x6c Opcode(cmd_createAmuletJewel), Opcode(cmd_setSceneAnimCurrXY), Opcode(cmd_Poison_Brandon_And_Remaps), Opcode(cmd_fillFlaskWithWater), // 0x70 Opcode(cmd_getCharactersMovementDelay), Opcode(cmd_getBirthstoneGem), Opcode(cmd_queryBrandonStatusBit), Opcode(cmd_playFluteAnimation), // 0x74 Opcode(cmd_playWinterScrollSequence), Opcode(cmd_getIdolGem), Opcode(cmd_setIdolGem), Opcode(cmd_totalItemsInScene), // 0x78 Opcode(cmd_restoreBrandonsMovementDelay), Opcode(cmd_setMousePos), Opcode(cmd_getMouseState), Opcode(cmd_setEntranceMouseCursorTrack), // 0x7c Opcode(cmd_itemAppearsOnGround), Opcode(cmd_setNoDrawShapesFlag), Opcode(cmd_fadeEntirePalette), Opcode(cmd_itemOnGroundHere), // 0x80 Opcode(cmd_queryCauldronState), Opcode(cmd_setCauldronState), Opcode(cmd_queryCrystalState), Opcode(cmd_setCrystalState), // 0x84 Opcode(cmd_setPaletteRange), Opcode(cmd_shrinkBrandonDown), Opcode(cmd_growBrandonUp), Opcode(cmd_setBrandonScaleXAndY), // 0x88 Opcode(cmd_resetScaleMode), Opcode(cmd_getScaleDepthTableValue), Opcode(cmd_setScaleDepthTableValue), Opcode(cmd_message), // 0x8c Opcode(cmd_checkClickOnNPC), Opcode(cmd_getFoyerItem), Opcode(cmd_setFoyerItem), Opcode(cmd_setNoItemDropRegion), // 0x90 Opcode(cmd_walkMalcolmOn), Opcode(cmd_passiveProtection), Opcode(cmd_setPlayingLoop), Opcode(cmd_brandonToStoneSequence), // 0x94 Opcode(cmd_brandonHealingSequence), Opcode(cmd_protectCommandLine), Opcode(cmd_pauseMusicSeconds), Opcode(cmd_resetMaskRegion), // 0x98 Opcode(cmd_setPaletteChangeFlag), Opcode(cmd_fillRect), Opcode(cmd_dummy) }; #undef Opcode const int KyraEngine::_opcodeTableSize = ARRAYSIZE(_opcodeTable); const char *KyraEngine::_xmidiFiles[] = { "INTRO.XMI", "KYRA1A.XMI", "KYRA1B.XMI", "KYRA2A.XMI", "KYRA3A.XMI", "KYRA4A.XMI", "KYRA4B.XMI", "KYRA5A.XMI", "KYRA5B.XMI", "KYRAMISC.XMI" }; const int KyraEngine::_xmidiFilesCount = ARRAYSIZE(_xmidiFiles); const int8 KyraEngine::_charXPosTable[] = { 0, 4, 4, 4, 0, -4, -4, -4 }; const int8 KyraEngine::_addXPosTable[] = { 4, 4, 0, -4, -4, -4, 0, 4 }; const int8 KyraEngine::_charYPosTable[] = { -2, -2, 0, 2, 2, 2, 0, -2 }; const int8 KyraEngine::_addYPosTable[] = { 0, -2, -2, -2, 0, 2, 2, 2 }; } // End of namespace Kyra