void ScriptManager::parseScrFile(const Common::String &fileName, bool isGlobal) { Common::File file; if (!file.open(fileName)) { warning("Script file not found: %s", fileName.c_str()); return; } while(!file.eos()) { Common::String line = file.readLine(); if (file.err()) { warning("Error parsing scr file: %s", fileName.c_str()); return; } trimCommentsAndWhiteSpace(&line); if (line.empty()) continue; if (line.matchString("puzzle:*", true)) { Puzzle *puzzle = new Puzzle(); sscanf(line.c_str(),"puzzle:%u",&(puzzle->key)); parsePuzzle(puzzle, file); if (isGlobal) { _globalPuzzles.push_back(puzzle); } else { _activePuzzles.push_back(puzzle); } } else if (line.matchString("control:*", true)) { parseControl(line, file); } } }
bool ThemeEngine::themeConfigUsable(const Common::FSNode &node, Common::String &themeName) { Common::File stream; bool foundHeader = false; if (node.getName().matchString("*.zip", true) && !node.isDirectory()) { Common::Archive *zipArchive = Common::makeZipArchive(node); if (zipArchive && zipArchive->hasFile("THEMERC")) { // Open THEMERC from the ZIP file. stream.open("THEMERC", *zipArchive); } // Delete the ZIP archive again. Note: This only works because // stream.open() only uses ZipArchive::createReadStreamForMember, // and that in turn happens to read all the data for a given // archive member into a memory block. So there will be no dangling // reference to zipArchive anywhere. This could change if we // ever modify ZipArchive::createReadStreamForMember. delete zipArchive; } else if (node.isDirectory()) { Common::FSNode headerfile = node.getChild("THEMERC"); if (!headerfile.exists() || !headerfile.isReadable() || headerfile.isDirectory()) return false; stream.open(headerfile); } if (stream.isOpen()) { Common::String stxHeader = stream.readLine(); foundHeader = themeConfigParseHeader(stxHeader, themeName); } return foundHeader; }
void Resource::readStaticText() { Common::File *labTextFile = openDataFile("Lab:Rooms/LabText"); for (int i = 0; i < 48; i++) _staticText[i] = labTextFile->readLine(); delete labTextFile; }
bool ThemeEngine::loadThemeXML(const Common::String &themeId) { assert(_parser); assert(_themeArchive); _themeName.clear(); // // Now that we have a Common::Archive, verify that it contains a valid THEMERC File // Common::File themercFile; themercFile.open("THEMERC", *_themeArchive); if (!themercFile.isOpen()) { warning("Theme '%s' contains no 'THEMERC' file.", themeId.c_str()); return false; } Common::String stxHeader = themercFile.readLine(); if (!themeConfigParseHeader(stxHeader, _themeName) || _themeName.empty()) { warning("Corrupted 'THEMERC' file in theme '%s'", themeId.c_str()); return false; } Common::ArchiveMemberList members; if (0 == _themeArchive->listMatchingMembers(members, "*.stx")) { warning("Found no STX files for theme '%s'.", themeId.c_str()); return false; } // // Loop over all STX files, load and parse them // for (Common::ArchiveMemberList::iterator i = members.begin(); i != members.end(); ++i) { assert((*i)->getName().hasSuffix(".stx")); if (_parser->loadStream((*i)->createReadStream()) == false) { warning("Failed to load STX file '%s'", (*i)->getDisplayName().c_str()); _parser->close(); return false; } if (_parser->parse() == false) { warning("Failed to parse STX file '%s'", (*i)->getDisplayName().c_str()); _parser->close(); return false; } _parser->close(); } assert(!_themeName.empty()); return true; }
TLib::TLib(MemoryManager &memManager, const Common::String &filename) : _memoryManager(memManager) { // If the resource strings list isn't yet loaded, load them if (_resStrings.size() == 0) { Common::File f; if (f.open("tsage.cfg")) { while (!f.eos()) { _resStrings.push_back(f.readLine()); } f.close(); } } if (!_file.open(filename)) error("Missing file %s", filename.c_str()); loadIndex(); }
bool ThemeEngine::themeConfigUsable(const Common::ArchiveMember &member, Common::String &themeName) { Common::File stream; bool foundHeader = false; if (member.getName().matchString("*.zip", true)) { Common::Archive *zipArchive = Common::makeZipArchive(member.createReadStream()); if (zipArchive && zipArchive->hasFile("THEMERC")) { stream.open("THEMERC", *zipArchive); } delete zipArchive; } if (stream.isOpen()) { Common::String stxHeader = stream.readLine(); foundHeader = themeConfigParseHeader(stxHeader, themeName); } return foundHeader; }
bool Sword2Engine::initStartMenu() { // Print out a list of all the start points available. // There should be a linc produced file called startup.txt. // This file should contain ascii numbers of all the resource game // objects that are screen managers. // We query each in turn and setup an array of start structures. // If the file doesn't exist then we say so and return a 0. Common::File fp; // ok, load in the master screen manager file _totalStartups = 0; _totalScreenManagers = 0; if (!fp.open("startup.inf")) { warning("Cannot open startup.inf - the debugger won't have a start menu"); return false; } // The startup.inf file which contains a list of all the files. Now // extract the filenames int start_ids[MAX_starts]; int lineno = 0; while (!fp.eos() && !fp.err()) { Common::String line = fp.readLine(); // Skip empty lines or, more likely, the end of the stream. if (line.size() == 0) continue; char *errptr; int id; lineno++; id = strtol(line.c_str(), &errptr, 10); if (*errptr) { warning("startup.inf:%d: Invalid string '%s'", lineno, line.c_str()); continue; } if (!_resman->checkValid(id)) { warning("startup.inf:%d: Invalid resource %d", lineno, id); continue; } if (_resman->fetchType(id) != SCREEN_MANAGER) { warning("startup.inf:%d: '%s' (%d) is not a screen manager", lineno, _resman->fetchName(id), id); continue; } start_ids[_totalScreenManagers] = id; if (++_totalScreenManagers >= MAX_starts) { warning("Too many entries in startup.inf"); break; } } // An I/O error before EOS? That's bad, but this is not a vital file. if (fp.err() && !fp.eos()) warning("I/O error while reading startup.inf"); fp.close(); // Using this method the Gode generated resource.inf must have #0d0a // on the last entry debug(1, "%d screen manager objects", _totalScreenManagers); // Open each object and make a query call. The object must fill in a // startup structure. It may fill in several if it wishes - for // instance a startup could be set for later in the game where // specific vars are set for (uint i = 0; i < _totalScreenManagers; i++) { _startRes = start_ids[i]; debug(2, "Querying screen manager %d", _startRes); // Open each one and run through the interpreter. Script 0 is // the query request script. We have already made reasonably // sure the resource is ok. _logic->runResScript(_startRes, 0); } return 1; }
/** * Plays an animated cutscene. * @param id the id of the file */ bool MoviePlayer::load(uint32 id) { Common::File f; Common::String filename; if (_decoderType == kVideoDecoderDXA) _bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]); else _bgSoundStream = NULL; if (SwordEngine::_systemVars.showText) { filename = Common::String::format("%s.txt", sequenceList[id]); if (f.open(filename)) { Common::String line; int lineNo = 0; int lastEnd = -1; _movieTexts.clear(); while (!f.eos() && !f.err()) { line = f.readLine(); lineNo++; if (line.empty() || line[0] == '#') { continue; } const char *ptr = line.c_str(); // TODO: Better error handling int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10); int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10); while (*ptr && Common::isSpace(*ptr)) ptr++; if (startFrame > endFrame) { warning("%s:%d: startFrame (%d) > endFrame (%d)", filename.c_str(), lineNo, startFrame, endFrame); continue; } if (startFrame <= lastEnd) { warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd); continue; } int color = 0; if (*ptr == '@') { ++ptr; color = strtoul(ptr, const_cast<char **>(&ptr), 10); while (*ptr && Common::isSpace(*ptr)) ptr++; } _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; } f.close(); } } switch (_decoderType) { case kVideoDecoderDXA: filename = Common::String::format("%s.dxa", sequenceList[id]); break; case kVideoDecoderSMK: filename = Common::String::format("%s.smk", sequenceList[id]); break; case kVideoDecoderPSX: filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]); // Need to switch to true color initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0); // Need to load here in case it fails in which case we'd need // to go back to paletted mode if (_decoder->loadFile(filename)) { return true; } else { initGraphics(g_system->getWidth(), g_system->getHeight(), true); return false; } break; } return _decoder->loadFile(filename.c_str()); }
void Screen::rollCredits() { uint32 loopingMusicId = _vm->_sound->getLoopingMusicId(); // Prepare for the credits by fading down, stoping the music, etc. _vm->_mouse->setMouse(0); _vm->_sound->muteFx(true); _vm->_sound->muteSpeech(true); waitForFade(); fadeDown(); waitForFade(); _vm->_mouse->closeMenuImmediately(); // There are three files which I believe are involved in showing the // credits: // // credits.bmp - The "Smacker" logo, stored as follows: // // width 2 bytes, little endian // height 2 bytes, little endian // palette 3 * 256 bytes // data width * height bytes // // Note that the maximum colour component in the palette is 0x3F. // This is the same resolution as the _paletteMatch table. I doubt // that this is a coincidence, but let's use the image palette // directly anyway, just to be safe. // // credits.clu - The credits text (credits.txt in PSX version) // // This is simply a text file with CRLF line endings. // '^' is not shown, but used to mark the center of the line. // '@' is used as a placeholder for the "Smacker" logo. At least // when it appears alone. // Remaining lines are centered. // The German version also contains character code 9 for no // apparent reason. We ignore them. // // fonts.clu - The credits font? // // FIXME: At this time I don't know how to interpret fonts.clu. For // now, let's just the standard speech font instead. SpriteInfo spriteInfo; Common::File f; int i; spriteInfo.isText = false; // Read the "Smacker" logo uint16 logoWidth = 0; uint16 logoHeight = 0; byte *logoData = NULL; byte palette[256 * 3]; if (f.open("credits.bmp")) { logoWidth = f.readUint16LE(); logoHeight = f.readUint16LE(); for (i = 0; i < 256; i++) { palette[i * 3 + 0] = f.readByte() << 2; palette[i * 3 + 1] = f.readByte() << 2; palette[i * 3 + 2] = f.readByte() << 2; } logoData = (byte *)malloc(logoWidth * logoHeight); f.read(logoData, logoWidth * logoHeight); f.close(); } else { warning("Can't find credits.bmp"); memset(palette, 0, sizeof(palette)); palette[14 * 3 + 0] = 252; palette[14 * 3 + 1] = 252; palette[14 * 3 + 2] = 252; } setPalette(0, 256, palette, RDPAL_INSTANT); // Read the credits text Common::Array<CreditsLine *> creditsLines; int lineCount = 0; int lineTop = 400; int paragraphStart = 0; bool hasCenterMark = false; if (Sword2Engine::isPsx()) { if (!f.open("credits.txt")) { warning("Can't find credits.txt"); free(logoData); return; } } else { if (!f.open("credits.clu")) { warning("Can't find credits.clu"); free(logoData); return; } } while (1) { char buffer[80]; char *line = f.readLine(buffer, sizeof(buffer)); if (line) { // Replace invalid character codes prevent the 'dud' // symbol from showing up in the credits. for (byte *ptr = (byte *)line; *ptr; ptr++) { switch (*ptr) { case 9: // The German credits contain these. // Convert them to spaces. *ptr = 32; break; case 10: // LF is treated as end of line. *ptr = 0; break; case 170: // The Spanish credits contain these. // Convert them to periods. *ptr = '.'; default: break; } } } if (!line || *line == 0) { if (!hasCenterMark) { for (i = paragraphStart; i < lineCount; i++) creditsLines[i]->type = LINE_CENTER; } paragraphStart = lineCount; hasCenterMark = false; if (paragraphStart == lineCount) lineTop += CREDITS_LINE_SPACING; if (!line) break; continue; } char *center_mark = strchr(line, '^'); if (center_mark) { // The current paragraph has at least one center mark. hasCenterMark = true; if (center_mark != line) { creditsLines.push_back(new CreditsLine); // The center mark is somewhere inside the // line. Split it into left and right side. *center_mark = 0; creditsLines[lineCount]->top = lineTop; creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT; creditsLines[lineCount]->type = LINE_LEFT; creditsLines[lineCount]->str = strdup(line); lineCount++; *center_mark = '^'; } line = center_mark; } creditsLines.push_back(new CreditsLine); creditsLines[lineCount]->top = lineTop; if (*line == '^') { creditsLines[lineCount]->type = LINE_RIGHT; line++; } else creditsLines[lineCount]->type = LINE_LEFT; if (strcmp(line, "@") == 0) { creditsLines[lineCount]->height = logoHeight; lineTop += logoHeight; } else { creditsLines[lineCount]->height = CREDITS_FONT_HEIGHT; lineTop += CREDITS_LINE_SPACING; } creditsLines[lineCount]->str = strdup(line); lineCount++; } f.close(); // We could easily add some ScummVM stuff to the credits, if we wanted // to. On the other hand, anyone with the attention span to actually // read all the credits probably already knows. :-) // Start the music and roll the credits // The credits music (which can also be heard briefly in the "carib" // cutscene) is played once. _vm->_sound->streamCompMusic(309, false); clearScene(); fadeUp(0); spriteInfo.scale = 0; spriteInfo.scaledWidth = 0; spriteInfo.scaledHeight = 0; spriteInfo.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION | RDSPR_TRANS; spriteInfo.blend = 0; int startLine = 0; int scrollPos = 0; bool abortCredits = false; int scrollSteps = lineTop + CREDITS_FONT_HEIGHT; uint32 musicStart = getTick(); // Ideally the music should last just a tiny bit longer than the // credits. Note that musicTimeRemaining() will return 0 if the music // is muted, so we need a sensible fallback for that case. uint32 musicLength = MAX((int32)(1000 * (_vm->_sound->musicTimeRemaining() - 3)), 25 * (int32)scrollSteps); while (scrollPos < scrollSteps && !_vm->shouldQuit()) { clearScene(); for (i = startLine; i < lineCount; i++) { if (!creditsLines[i]) continue; // Free any sprites that have scrolled off the screen if (creditsLines[i]->top + creditsLines[i]->height < scrollPos) { debug(2, "Freeing line %d: '%s'", i, creditsLines[i]->str); delete creditsLines[i]; creditsLines[i] = NULL; startLine = i + 1; } else if (creditsLines[i]->top < scrollPos + 400) { if (!creditsLines[i]->sprite) { debug(2, "Creating line %d: '%s'", i, creditsLines[i]->str); creditsLines[i]->sprite = _vm->_fontRenderer->makeTextSprite((byte *)creditsLines[i]->str, 600, 14, _vm->_speechFontId, 0); } FrameHeader frame; frame.read(creditsLines[i]->sprite); spriteInfo.y = creditsLines[i]->top - scrollPos; spriteInfo.w = frame.width; spriteInfo.h = frame.height; spriteInfo.data = creditsLines[i]->sprite + FrameHeader::size(); spriteInfo.isText = true; switch (creditsLines[i]->type) { case LINE_LEFT: spriteInfo.x = RENDERWIDE / 2 - 5 - frame.width; break; case LINE_RIGHT: spriteInfo.x = RENDERWIDE / 2 + 5; break; case LINE_CENTER: if (strcmp(creditsLines[i]->str, "@") == 0) { spriteInfo.data = logoData; spriteInfo.x = (RENDERWIDE - logoWidth) / 2; spriteInfo.w = logoWidth; spriteInfo.h = logoHeight; } else spriteInfo.x = (RENDERWIDE - frame.width) / 2; break; } if (spriteInfo.data) drawSprite(&spriteInfo); } else break; } updateDisplay(); KeyboardEvent *ke = _vm->keyboardEvent(); if (ke && ke->kbd.keycode == Common::KEYCODE_ESCAPE) { if (!abortCredits) { abortCredits = true; fadeDown(); } } if (abortCredits && getFadeStatus() == RDFADE_BLACK) break; _vm->sleepUntil(musicStart + (musicLength * scrollPos) / scrollSteps + _pauseTicks); scrollPos++; } // We're done. Clean up and try to put everything back where it was // before the credits. for (i = 0; i < lineCount; i++) { delete creditsLines[i]; } free(logoData); if (!abortCredits) { fadeDown(); // The music should either have stopped or be about to stop, so // wait for it to really happen. while (_vm->_sound->musicTimeRemaining() && !_vm->shouldQuit()) { updateDisplay(false); _vm->_system->delayMillis(100); } } if (_vm->shouldQuit()) return; waitForFade(); _vm->_sound->muteFx(false); _vm->_sound->muteSpeech(false); if (loopingMusicId) _vm->_sound->streamCompMusic(loopingMusicId, true); else _vm->_sound->stopMusic(false); if (!_vm->_mouse->getMouseStatus() || _vm->_mouse->isChoosing()) _vm->_mouse->setMouse(NORMAL_MOUSE_ID); if (_vm->_logic->readVar(DEAD)) _vm->_mouse->buildSystemMenu(); }
Common::Error GagEngine::StateScript() { Common::Error status(Common::kNoError); Common::File file; if(file.open(_script, *_archive)) { // simple script parsing: skim through, find section and execute commands ParserState parse_state(PS_SECTION_SEARCH); Common::String buffer; Common::String command_name; bool done(false); while(!file.eos()) { // add space for multiline command support Common::String line = file.readLine() + ' '; if(file.err()) { status = Common::Error(Common::kReadingFailed, _script + ", readLine()"); break; } bool skip_line(false); for(Common::String::const_iterator it = line.begin(); it != line.end(); ++it) { switch(parse_state) { case PS_SECTION_SEARCH: // section if(*it == '[') { buffer.clear(); parse_state = PS_SECTION_NAME; } break; case PS_SECTION_NAME: // section end if(*it == ']') { #ifdef DEBUG_SKIM_SCRIPT parse_state = PS_SECTION_BODY; #else if(buffer == _section) parse_state = PS_SECTION_BODY; else if(buffer == _END_SECTION) { status = Common::Error(Common::kUnknownError, "[" + _section + "] script section not found"); skip_line = true; done = true; } else parse_state = PS_SECTION_SEARCH; #endif } else buffer += *it; break; case PS_SECTION_BODY: // section if(*it == '[') { #ifdef DEBUG_SKIM_SCRIPT buffer.clear(); parse_state = PS_SECTION_NAME; #else skip_line = true; done = true; #endif } // comment else if(*it == '*') { skip_line = true; } //NOTE: invalid syntax else if(*it == '-' || *it == '/' || (*it >= 'A' && *it <= 'Z')) { #ifndef DEBUG_SKIM_SCRIPT warning("invalid script syntax [file: %s, section: %s, line: \"%s\"], skipped", _script.c_str(), _section.c_str(), line.c_str()); #endif skip_line = true; } // command name else if((*it >= 'a' && *it <= 'z')) { buffer.clear(); buffer += *it; parse_state = PS_COMMAND_NAME; } break; case PS_COMMAND_NAME: // command value if(*it == '=') { command_name = buffer; buffer.clear(); parse_state = PS_COMMAND_VALUE; } else buffer += *it; break; case PS_COMMAND_VALUE: // command value end if(*it == ';') { command_name.trim(); buffer.trim(); #ifndef DEBUG_SKIM_SCRIPT debug("%s=%s;", command_name.c_str(), buffer.c_str()); #endif Common::HashMap<Common::String, Common::Error (GagEngine::*)(const Common::String &)>::const_iterator f = _commandCallbacks.find(command_name); if(f != _commandCallbacks.end()) { status = (this->*f->_value)(buffer); if(status.getCode() != Common::kNoError) { skip_line = true; done = true; } } parse_state = PS_SECTION_BODY; } else buffer += *it; break; } if(skip_line) break; } if(done) break; } } else { status = Common::Error(Common::kReadingFailed, _script + ", open()"); } return status; }
bool ResourceManager::init() { uint32 i, j; // Until proven differently, assume we're on CD 1. This is so the start // dialog will be able to play any music at all. setCD(1); // We read in the resource info which tells us the names of the // resource cluster files ultimately, although there might be groups // within the clusters at this point it makes no difference. We only // wish to know what resource files there are and what is in each Common::File file; if (!file.open("resource.inf")) { GUIErrorMessage("Broken Sword II: Cannot open resource.inf"); return false; } // The resource.inf file is a simple text file containing the names of // all the resource files. while (1) { char *buf = _resFiles[_totalClusters].fileName; uint len = sizeof(_resFiles[_totalClusters].fileName); if (!file.readLine(buf, len)) break; int pos = strlen(buf); if (buf[pos - 1] == 0x0A) buf[pos - 1] = 0; _resFiles[_totalClusters].numEntries = -1; _resFiles[_totalClusters].entryTab = NULL; if (++_totalClusters >= MAX_res_files) { GUIErrorMessage("Broken Sword II: Too many entries in resource.inf"); return false; } } file.close(); // Now load in the binary id to res conversion table if (!file.open("resource.tab")) { GUIErrorMessage("Broken Sword II: Cannot open resource.tab"); return false; } // Find how many resources uint32 size = file.size(); _totalResFiles = size / 4; // Table seems ok so malloc some space _resConvTable = (uint16 *)malloc(size); for (i = 0; i < size / 2; i++) _resConvTable[i] = file.readUint16LE(); if (file.eos() || file.err()) { file.close(); GUIErrorMessage("Broken Sword II: Cannot read resource.tab"); return false; } file.close(); // Check that we have cd.inf file, unless we are running PSX // version, which has all files on one disc. if (!file.open("cd.inf") && !Sword2Engine::isPsx()) { GUIErrorMessage("Broken Sword II: Cannot open cd.inf"); return false; } CdInf *cdInf = new CdInf[_totalClusters]; for (i = 0; i < _totalClusters; i++) { if (Sword2Engine::isPsx()) { // We are running PSX version, artificially fill CdInf structure cdInf[i].cd = CD1; } else { // We are running PC version, read cd.inf file file.read(cdInf[i].clusterName, sizeof(cdInf[i].clusterName)); cdInf[i].cd = file.readByte(); if (file.eos() || file.err()) { delete[] cdInf; file.close(); GUIErrorMessage("Broken Sword II: Cannot read cd.inf"); return false; } } // It has been reported that there are two different versions // of the cd.inf file: One where all clusters on CD also have // the LOCAL_CACHE bit set. This bit is no longer used. To // avoid future problems, let's normalize the flag once and for // all here. if (cdInf[i].cd & LOCAL_PERM) cdInf[i].cd = 0; else if (cdInf[i].cd & CD1) cdInf[i].cd = 1; else if (cdInf[i].cd & CD2) cdInf[i].cd = 2; else cdInf[i].cd = 0; // Any file on "CD 0" may be needed at all times. Verify that // it exists. Any other missing cluster will be requested with // an "insert CD" message. Of course, the file may still vanish // during game-play (oh, that wascally wabbit!) in which case // the resource manager will print a fatal error. if (cdInf[i].cd == 0 && !Common::File::exists((char *)cdInf[i].clusterName)) { GUIErrorMessage("Broken Sword II: Cannot find " + Common::String((char *)cdInf[i].clusterName)); delete[] cdInf; return false; } } file.close(); // We check the presence of resource files in cd.inf // This is ok in PC version, but in PSX version we don't // have cd.inf so we'll have to skip this. if (!Sword2Engine::isPsx()) { for (i = 0; i < _totalClusters; i++) { for (j = 0; j < _totalClusters; j++) { if (scumm_stricmp((char *)cdInf[j].clusterName, _resFiles[i].fileName) == 0) break; } if (j == _totalClusters) { delete[] cdInf; GUIErrorMessage(Common::String(_resFiles[i].fileName) + " is not in cd.inf"); return false; } _resFiles[i].cd = cdInf[j].cd; } } delete[] cdInf; debug(1, "%d resources in %d cluster files", _totalResFiles, _totalClusters); for (i = 0; i < _totalClusters; i++) debug(2, "filename of cluster %d: -%s (%d)", i, _resFiles[i].fileName, _resFiles[i].cd); _resList = (Resource *)malloc(_totalResFiles * sizeof(Resource)); for (i = 0; i < _totalResFiles; i++) { _resList[i].ptr = NULL; _resList[i].size = 0; _resList[i].refCount = 0; _resList[i].prev = _resList[i].next = NULL; } return true; }
Common::Error GroovieEngine::run() { _script = new Script(this, _gameDescription->version); // Initialize the graphics switch (_gameDescription->version) { case kGroovieV2: // Request the mode with the highest precision available initGraphics(640, 480, true, NULL); // Save the enabled mode as it can be both an RGB mode or CLUT8 _pixelFormat = _system->getScreenFormat(); _mode8bit = (_pixelFormat == Graphics::PixelFormat::createFormatCLUT8()); break; case kGroovieT7G: initGraphics(640, 480, true); _pixelFormat = Graphics::PixelFormat::createFormatCLUT8(); break; } // Create debugger. It requires GFX to be initialized _debugger = new Debugger(this); _script->setDebugger(_debugger); // Create the graphics manager _graphicsMan = new GraphicsMan(this); // Create the resource and cursor managers and the video player // Prepare the font too switch (_gameDescription->version) { case kGroovieT7G: if (getPlatform() == Common::kPlatformMacintosh) { _macResFork = new Common::MacResManager(); if (!_macResFork->open(_gameDescription->desc.filesDescriptions[0].fileName)) error("Could not open %s as a resource fork", _gameDescription->desc.filesDescriptions[0].fileName); // The Macintosh release used system fonts. We use GUI fonts. _font = FontMan.getFontByUsage(Graphics::FontManager::kBigGUIFont); } else { Common::File fontfile; if (!fontfile.open("sphinx.fnt")) { error("Couldn't open sphinx.fnt"); return Common::kNoGameDataFoundError; } else if (!_sphinxFont.load(fontfile)) { error("Error loading sphinx.fnt"); return Common::kUnknownError; } fontfile.close(); _font = &_sphinxFont; } _resMan = new ResMan_t7g(_macResFork); _grvCursorMan = new GrvCursorMan_t7g(_system, _macResFork); _videoPlayer = new VDXPlayer(this); break; case kGroovieV2: _resMan = new ResMan_v2(); _grvCursorMan = new GrvCursorMan_v2(_system); _videoPlayer = new ROQPlayer(this); break; } // Create the music player if (getPlatform() == Common::kPlatformMacintosh) _musicPlayer = new MusicPlayerMac(this); else _musicPlayer = new MusicPlayerXMI(this, _gameDescription->version == kGroovieT7G ? "fat" : "sample"); // Load volume levels syncSoundSettings(); // Get the name of the main script Common::String filename = _gameDescription->desc.filesDescriptions[0].fileName; if (_gameDescription->version == kGroovieT7G) { // Run The 7th Guest's demo if requested if (ConfMan.hasKey("demo_mode") && ConfMan.getBool("demo_mode")) filename = "demo.grv"; else if (getPlatform() == Common::kPlatformMacintosh) filename = "script.grv"; // Stored inside the executable's resource fork } else if (_gameDescription->version == kGroovieV2) { // Open the disk index Common::File disk; if (!disk.open(filename)) { error("Couldn't open %s", filename.c_str()); return Common::kNoGameDataFoundError; } // Search the entry bool found = false; int index = 0; while (!found && !disk.eos()) { Common::String line = disk.readLine(); if (line.hasPrefix("title: ")) { // A new entry index++; } else if (line.hasPrefix("boot: ") && index == _gameDescription->indexEntry) { // It's the boot of the entry we're looking for, // get the script filename filename = line.c_str() + 6; found = true; } } // Couldn't find the entry if (!found) { error("Couldn't find entry %d in %s", _gameDescription->indexEntry, filename.c_str()); return Common::kUnknownError; } } // Check the script file extension if (!filename.hasSuffix(".grv")) { error("%s isn't a valid script filename", filename.c_str()); return Common::kUnknownError; } // Load the script if (!_script->loadScript(filename)) { error("Couldn't load the script file %s", filename.c_str()); return Common::kUnknownError; } // Should I load a saved game? if (ConfMan.hasKey("save_slot")) { // Get the requested slot int slot = ConfMan.getInt("save_slot"); _script->directGameLoad(slot); } // Check that the game files and the audio tracks aren't together run from // the same cd checkCD(); // Game timer counter uint16 tmr = 0; // Initialize the CD int cd_num = ConfMan.getInt("cdrom"); if (cd_num >= 0) _system->getAudioCDManager()->openCD(cd_num); while (!shouldQuit()) { // Give the debugger a chance to act _debugger->onFrame(); // Handle input Common::Event ev; while (_eventMan->pollEvent(ev)) { switch (ev.type) { case Common::EVENT_KEYDOWN: // CTRL-D: Attach the debugger if ((ev.kbd.flags & Common::KBD_CTRL) && ev.kbd.keycode == Common::KEYCODE_d) _debugger->attach(); // Send the event to the scripts _script->setKbdChar(ev.kbd.ascii); // Continue the script execution to handle the key _waitingForInput = false; break; case Common::EVENT_MAINMENU: // Closing the GMM case Common::EVENT_MOUSEMOVE: // Continue the script execution, the mouse pointer // may fall inside a different hotspot now _waitingForInput = false; break; case Common::EVENT_LBUTTONDOWN: // Send the event to the scripts _script->setMouseClick(1); // Continue the script execution to handle // the click _waitingForInput = false; break; case Common::EVENT_RBUTTONDOWN: // Send the event to the scripts (to skip the video) _script->setMouseClick(2); break; case Common::EVENT_QUIT: quitGame(); break; default: break; } } // The event loop may have triggered the quit status. In this case, // stop the execution. if (shouldQuit()) { continue; } if (_waitingForInput) { // Still waiting for input, just update the mouse, game timer and then wait a bit more _grvCursorMan->animate(); _system->updateScreen(); tmr++; // Wait a little bit between increments. While mouse is moving, this triggers // only negligably slower. if (tmr > 4) { _script->timerTick(); tmr = 0; } _system->delayMillis(50); } else if (_graphicsMan->isFading()) { // We're waiting for a fading to end, let the CPU rest // for a while and continue _system->delayMillis(30); } else { // Everything's fine, execute another script step _script->step(); } // Update the screen if required _graphicsMan->update(); } return Common::kNoError; }
void LeverControl::parseLevFile(const Common::String &fileName) { Common::File file; if (!file.open(fileName)) { warning("LEV file %s could could be opened", fileName.c_str()); return; } Common::String line = file.readLine(); while (!file.eos()) { if (line.matchString("*animation_id*", true)) { // Not used } else if (line.matchString("*filename*", true)) { char fileNameBuffer[25]; sscanf(line.c_str(), "%*[^:]:%25[^~]~", fileNameBuffer); Common::String animationFileName(fileNameBuffer); if (animationFileName.hasSuffix(".avi")) { _animation.avi = new ZorkAVIDecoder(); _animation.avi->loadFile(animationFileName); _fileType = AVI; } else if (animationFileName.hasSuffix(".rlf")) { _animation.rlf = new RlfAnimation(animationFileName, false); _fileType = RLF; } } else if (line.matchString("*skipcolor*", true)) { // Not used } else if (line.matchString("*anim_coords*", true)) { int left, top, right, bottom; sscanf(line.c_str(), "%*[^:]:%d %d %d %d~", &left, &top, &right, &bottom); _animationCoords.left = left; _animationCoords.top = top; _animationCoords.right = right; _animationCoords.bottom = bottom; } else if (line.matchString("*mirrored*", true)) { uint mirrored; sscanf(line.c_str(), "%*[^:]:%u~", &mirrored); _mirrored = mirrored == 0 ? false : true; } else if (line.matchString("*frames*", true)) { sscanf(line.c_str(), "%*[^:]:%u~", &_frameCount); _frameInfo = new FrameInfo[_frameCount]; } else if (line.matchString("*elsewhere*", true)) { // Not used } else if (line.matchString("*out_of_control*", true)) { // Not used } else if (line.matchString("*start_pos*", true)) { sscanf(line.c_str(), "%*[^:]:%u~", &_startFrame); _currentFrame = _startFrame; } else if (line.matchString("*hotspot_deltas*", true)) { uint x; uint y; sscanf(line.c_str(), "%*[^:]:%u %u~", &x, &y); _hotspotDelta.x = x; _hotspotDelta.y = y; } else { uint frameNumber; uint x, y; if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { _frameInfo[frameNumber].hotspot.left = x; _frameInfo[frameNumber].hotspot.top = y; _frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x; _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; } Common::StringTokenizer tokenizer(line, " ^=()"); tokenizer.nextToken(); tokenizer.nextToken(); Common::String token = tokenizer.nextToken(); while (!tokenizer.empty()) { if (token == "D") { token = tokenizer.nextToken(); uint angle; uint toFrame; sscanf(token.c_str(), "%u,%u", &toFrame, &angle); _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); } else if (token.hasPrefix("P")) { // Format: P(<from> to <to>) tokenizer.nextToken(); tokenizer.nextToken(); token = tokenizer.nextToken(); uint to = atoi(token.c_str()); _frameInfo[frameNumber].returnRoute.push_back(to); } token = tokenizer.nextToken(); } } line = file.readLine(); } }
void LeverControl::parseLevFile(const Common::String &fileName) { Common::File file; if (!_engine->getSearchManager()->openFile(file, fileName)) { warning("LEV file %s could could be opened", fileName.c_str()); return; } Common::String line; Common::String param; Common::String values; while (!file.eos()) { line = file.readLine(); getLevParams(line, param, values); if (param.matchString("animation_id", true)) { // Not used } else if (param.matchString("filename", true)) { _animation = _engine->loadAnimation(values); } else if (param.matchString("skipcolor", true)) { // Not used } else if (param.matchString("anim_coords", true)) { int left, top, right, bottom; sscanf(values.c_str(), "%d %d %d %d", &left, &top, &right, &bottom); _animationCoords.left = left; _animationCoords.top = top; _animationCoords.right = right; _animationCoords.bottom = bottom; } else if (param.matchString("mirrored", true)) { uint mirrored; sscanf(values.c_str(), "%u", &mirrored); _mirrored = mirrored == 0 ? false : true; } else if (param.matchString("frames", true)) { sscanf(values.c_str(), "%u", &_frameCount); _frameInfo = new FrameInfo[_frameCount]; } else if (param.matchString("elsewhere", true)) { // Not used } else if (param.matchString("out_of_control", true)) { // Not used } else if (param.matchString("start_pos", true)) { sscanf(values.c_str(), "%u", &_startFrame); _currentFrame = _startFrame; } else if (param.matchString("hotspot_deltas", true)) { uint x; uint y; sscanf(values.c_str(), "%u %u", &x, &y); _hotspotDelta.x = x; _hotspotDelta.y = y; } else if (param.matchString("venus_id", true)) { _venusId = atoi(values.c_str()); } else { uint frameNumber; uint x, y; line.toLowercase(); if (sscanf(line.c_str(), "%u:%u %u", &frameNumber, &x, &y) == 3) { _frameInfo[frameNumber].hotspot.left = x; _frameInfo[frameNumber].hotspot.top = y; _frameInfo[frameNumber].hotspot.right = x + _hotspotDelta.x; _frameInfo[frameNumber].hotspot.bottom = y + _hotspotDelta.y; } Common::StringTokenizer tokenizer(line, " ^=()~"); tokenizer.nextToken(); tokenizer.nextToken(); Common::String token = tokenizer.nextToken(); while (!tokenizer.empty()) { if (token == "d") { token = tokenizer.nextToken(); uint angle; uint toFrame; sscanf(token.c_str(), "%u,%u", &toFrame, &angle); _frameInfo[frameNumber].directions.push_back(Direction(angle, toFrame)); } else if (token.hasPrefix("p")) { // Format: P(<from> to <to>) tokenizer.nextToken(); tokenizer.nextToken(); token = tokenizer.nextToken(); uint to = atoi(token.c_str()); _frameInfo[frameNumber].returnRoute.push_back(to); } token = tokenizer.nextToken(); } } // Don't read lines in this place because last will not be parsed. } }
/** * Plays an animated cutscene. * @param id the id of the file */ bool MoviePlayer::load(uint32 id) { Common::String filename; if (SwordEngine::_systemVars.showText) { Common::File f; filename = Common::String::format("%s.txt", sequenceList[id]); if (f.open(filename)) { Common::String line; int lineNo = 0; int lastEnd = -1; _movieTexts.clear(); while (!f.eos() && !f.err()) { line = f.readLine(); lineNo++; if (line.empty() || line[0] == '#') { continue; } const char *ptr = line.c_str(); // TODO: Better error handling int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10); int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10); while (*ptr && Common::isSpace(*ptr)) ptr++; if (startFrame > endFrame) { warning("%s:%d: startFrame (%d) > endFrame (%d)", filename.c_str(), lineNo, startFrame, endFrame); continue; } if (startFrame <= lastEnd) { warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd); continue; } int color = 0; if (*ptr == '@') { ++ptr; color = strtoul(ptr, const_cast<char **>(&ptr), 10); while (*ptr && Common::isSpace(*ptr)) ptr++; } _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; } } } switch (_decoderType) { case kVideoDecoderDXA: filename = Common::String::format("%s.dxa", sequenceList[id]); break; case kVideoDecoderSMK: filename = Common::String::format("%s.smk", sequenceList[id]); break; case kVideoDecoderPSX: filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]); break; case kVideoDecoderMP2: filename = Common::String::format("%s.mp2", sequenceList[id]); break; } // Need to switch to true color for PSX/MP2 videos if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0); if (!_decoder->loadFile(filename)) { // Go back to 8bpp color if (_decoderType == kVideoDecoderPSX || _decoderType == kVideoDecoderMP2) initGraphics(g_system->getWidth(), g_system->getHeight(), true); return false; } // For DXA/MP2, also add the external sound file if (_decoderType == kVideoDecoderDXA || _decoderType == kVideoDecoderMP2) _decoder->addStreamFileTrack(sequenceList[id]); _decoder->start(); return true; }