/* 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. * * $URL$ * $Id$ * */ #include "common/system.h" #include "common/file.h" #include "engines/advancedDetector.h" // for ADGF_DEMO #include "sci/sci.h" #include "sci/resource.h" #include "sci/engine/features.h" #include "sci/engine/state.h" #include "sci/engine/kernel.h" #include "sci/engine/kernel_types.h" #include "sci/engine/message.h" #include "sci/graphics/gui.h" #include "sci/graphics/menu.h" #include "sci/sound/audio.h" #include "sci/sound/music.h" namespace Sci { struct OldNewIdTableEntry { const char *oldId; const char *newId; SciVersion version; }; static const OldNewIdTableEntry s_oldNewTable[] = { { "arthur", "camelot", SCI_VERSION_NONE }, { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga { "brain", "castlebrain", SCI_VERSION_1_LATE }, { "demo", "christmas1988", SCI_VERSION_NONE }, { "card", "christmas1990", SCI_VERSION_1_EARLY, }, { "card", "christmas1992", SCI_VERSION_1_1 }, { "RH Budget", "cnick-longbow", SCI_VERSION_NONE }, // iceman is the same { "icedemo", "iceman", SCI_VERSION_NONE }, // longbow is the same { "eco", "ecoquest", SCI_VERSION_NONE }, { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full { "fp", "freddypharkas", SCI_VERSION_NONE }, { "emc", "funseeker", SCI_VERSION_NONE }, { "gk", "gk1", SCI_VERSION_NONE }, { "hoyledemo", "hoyle1", SCI_VERSION_NONE }, { "cardgames", "hoyle1", SCI_VERSION_NONE }, { "solitare", "hoyle2", SCI_VERSION_NONE }, // hoyle3 is the same // hoyle4 is the same { "brain", "islandbrain", SCI_VERSION_1_1 }, { "demo000", "kq1sci", SCI_VERSION_NONE }, { "kq1", "kq1sci", SCI_VERSION_NONE }, { "kq4", "kq4sci", SCI_VERSION_NONE }, { "mm1", "laurabow", SCI_VERSION_NONE }, { "cb1", "laurabow", SCI_VERSION_NONE }, { "lb2", "laurabow2", SCI_VERSION_NONE }, { "rh", "longbow", SCI_VERSION_NONE }, { "ll1", "lsl1sci", SCI_VERSION_NONE }, { "lsl1", "lsl1sci", SCI_VERSION_NONE }, // lsl2 is the same { "lsl3", "lsl3", SCI_VERSION_NONE }, { "ll5", "lsl5", SCI_VERSION_NONE }, // lsl5 is the same // lsl6 is the same { "mg", "mothergoose", SCI_VERSION_NONE }, { "twisty", "pepper", SCI_VERSION_NONE }, { "pq1", "pq1sci", SCI_VERSION_NONE }, { "pq", "pq2", SCI_VERSION_NONE }, // pq3 is the same // pq4 is the same { "tales", "fairytales", SCI_VERSION_NONE }, { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA { "trial", "qfg2", SCI_VERSION_NONE }, { "hq2demo", "qfg2", SCI_VERSION_NONE }, { "thegame", "slater", SCI_VERSION_NONE }, { "sq1demo", "sq1sci", SCI_VERSION_NONE }, { "sq1", "sq1sci", SCI_VERSION_NONE }, // sq3 is the same // sq4 is the same // sq5 is the same // torin is the same // TODO: SCI2.1, SCI3 IDs { "", "", SCI_VERSION_NONE } }; Common::String convertSierraGameId(const char *gameId, uint32 *gameFlags, ResourceManager *resMan) { // Convert the id to lower case, so that we match all upper/lower case variants. Common::String sierraId = gameId; sierraId.toLowercase(); // If the game has less than the expected scripts, it's a demo uint32 demoThreshold = 100; // ...but there are some exceptions if (sierraId == "brain" || sierraId == "lsl1" || sierraId == "mg" || sierraId == "pq" || sierraId == "jones" || sierraId == "cardgames" || sierraId == "solitare" || sierraId == "hoyle3" || sierraId == "hoyle4") demoThreshold = 40; if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4") demoThreshold = 150; Common::List *resources = resMan->listResources(kResourceTypeScript, -1); if (resources->size() < demoThreshold) { *gameFlags |= ADGF_DEMO; // Crazy Nick's Picks if (sierraId == "lsl1" && resources->size() == 34) return "cnick-lsl"; if (sierraId == "sq4" && resources->size() == 34) return "cnick-sq"; // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read) // Handle Astrochicken 1 (SQ3) and 2 (SQ4) if (sierraId == "sq3" && resources->size() == 20) return "astrochicken"; if (sierraId == "sq4") return "msastrochicken"; } for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) { if (sierraId == cur->oldId) { // Distinguish same IDs from the SCI version if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion()) continue; return cur->newId; } } if (sierraId == "glory") { // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1), // or qfg4 full (SCI2) // qfg1 VGA doesn't have view 1 if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) return "qfg1"; // qfg4 full is SCI2 if (getSciVersion() == SCI_VERSION_2) return "qfg4"; // qfg4 demo has less than 50 scripts if (resources->size() < 50) return "qfg4"; // Otherwise it's qfg3 return "qfg3"; } return sierraId; } #ifdef USE_OLD_MUSIC_FUNCTIONS int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion) { if (getSciVersion() > SCI_VERSION_0_LATE) sound_flags |= SFX_STATE_FLAG_MULTIPLAY; s->sfx_init_flags = sound_flags; s->_sound.sfx_init(g_sci->getResMan(), sound_flags, soundVersion); return 0; } #endif // Architectural stuff: Init/Unintialize engine int script_init_engine(EngineState *s) { s->_msgState = new MessageState(s->_segMan); s->gc_countdown = GC_INTERVAL - 1; SegmentId script_000_segment = s->_segMan->getScriptSegment(0, SCRIPT_GET_LOCK); if (script_000_segment <= 0) { debug(2, "Failed to instantiate script.000"); return 1; } s->script_000 = s->_segMan->getScript(script_000_segment); s->sys_strings = s->_segMan->allocateSysStrings(&s->sys_strings_segment); // Allocate static buffer for savegame and CWD directories SystemString *str = &s->sys_strings->_strings[SYS_STRING_SAVEDIR]; str->_name = "savedir"; str->_maxSize = MAX_SAVE_DIR_SIZE; str->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); s->r_acc = s->r_prev = NULL_REG; s->restAdjust = 0; s->_executionStack.clear(); // Start without any execution stack s->execution_stack_base = -1; // No vm is running yet s->restarting_flags = SCI_GAME_IS_NOT_RESTARTING; s->_breakpoints.clear(); // No breakpoints defined s->_activeBreakpointTypes = 0; if (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE) s->_segMan->setExportAreWide(true); else s->_segMan->setExportAreWide(false); debug(2, "Engine initialized"); return 0; } /*************************************************************/ /* Game instance stuff: Init/Unitialize state-dependant data */ /*************************************************************/ int game_init(EngineState *s) { // FIXME Use new VM instantiation code all over the place DataStack *stack; stack = s->_segMan->allocateStack(VM_STACK_SIZE, NULL); s->stack_base = stack->_entries; s->stack_top = stack->_entries + stack->_capacity; if (!script_instantiate(g_sci->getResMan(), s->_segMan, 0)) { warning("game_init(): Could not instantiate script 0"); return 1; } if (s->_voc) { s->_voc->parserIsValid = false; // Invalidate parser s->_voc->parser_event = NULL_REG; // Invalidate parser event s->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); } // Initialize menu TODO: Actually this should be another init() if (g_sci->_gfxMenu) g_sci->_gfxMenu->reset(); s->successor = NULL; // No successor SystemString *str = &s->sys_strings->_strings[SYS_STRING_PARSER_BASE]; str->_name = "parser-base"; str->_maxSize = MAX_PARSER_BASE; str->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); s->game_start_time = g_system->getMillis(); s->last_wait_time = s->game_start_time; srand(g_system->getMillis()); // Initialize random number generator // script_dissect(0, s->_selectorNames); // The first entry in the export table of script 0 points to the game object s->_gameObj = s->_segMan->lookupScriptExport(0, 0); uint32 gameFlags = 0; // unused s->_gameId = convertSierraGameId(s->_segMan->getObjectName(s->_gameObj), &gameFlags, g_sci->getResMan()); debug(2, " \"%s\" at %04x:%04x", s->_gameId.c_str(), PRINT_REG(s->_gameObj)); #ifdef USE_OLD_MUSIC_FUNCTIONS if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND) game_init_sound(s, 0, g_sci->_features->detectDoSoundType()); #endif // Load game language into printLang property of game object // FIXME: It's evil to achieve this as a side effect of a getter. // Much better to have an explicit init method for this. g_sci->getSciLanguage(); return 0; } int game_exit(EngineState *s) { s->_executionStack.clear(); if (!s->successor) { #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); // Reinit because some other code depends on having a valid state game_init_sound(s, SFX_STATE_FLAG_NOSOUND, g_sci->_features->detectDoSoundType()); #else g_sci->_audio->stopAllAudio(); s->_soundCmd->clearPlayList(); #endif } // Note: It's a bad idea to delete the segment manager here // when loading a game. // This function is called right after a game is loaded, and // the segment manager has already been initialized from the // save game. Deleting or resetting it here will result in // invalidating the loaded save state if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) s->_segMan->resetSegMan(); // TODO Free parser segment here // TODO Free scripts here // Close all opened file handles s->_fileHandles.clear(); s->_fileHandles.resize(5); return 0; } } // End of namespace Sci