/** * Reads boot file for program environment. Fatal error if not there or * file checksum is bad. De-crypts structure while checking checksum */ void FileManager::readBootFile() { debugC(1, kDebugFile, "readBootFile()"); Common::File ofp; if (!ofp.open(getBootFilename())) { if (_vm->_gameVariant == kGameVariantH1Dos) { //TODO initialize properly _boot structure warning("readBootFile - Skipping as H1 Dos may be a freeware"); memset(_vm->_boot.distrib, '\0', sizeof(_vm->_boot.distrib)); _vm->_boot.registered = kRegFreeware; return; } else { error("Missing startup file '%s'", getBootFilename()); } } if (ofp.size() < (int32)sizeof(_vm->_boot)) error("Corrupted startup file"); _vm->_boot.checksum = ofp.readByte(); _vm->_boot.registered = ofp.readByte(); ofp.read(_vm->_boot.pbswitch, sizeof(_vm->_boot.pbswitch)); ofp.read(_vm->_boot.distrib, sizeof(_vm->_boot.distrib)); _vm->_boot.exit_len = ofp.readUint16LE(); byte *p = (byte *)&_vm->_boot; byte checksum = 0; for (uint32 i = 0; i < sizeof(_vm->_boot); i++) { checksum ^= p[i]; p[i] ^= s_bootCyper[i % s_bootCyperLen]; } ofp.close(); if (checksum) error("Corrupted startup file"); }
/** * Read sound (or music) file data. Call with SILENCE to free-up * any allocated memory. Also returns size of data */ sound_pt FileManager::getSound(const int16 sound, uint16 *size) { debugC(1, kDebugFile, "getSound(%d)", sound); // No more to do if SILENCE (called for cleanup purposes) if (sound == _vm->_soundSilence) return 0; // Open sounds file Common::File fp; // Handle to SOUND_FILE if (!fp.open(getSoundFilename())) { warning("Hugo Error: File not found %s", getSoundFilename()); return 0; } if (!has_read_header) { if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr)) error("Wrong sound file format"); has_read_header = true; } *size = s_hdr[sound].size; if (*size == 0) error("Wrong sound file format or missing sound %d", sound); // Allocate memory for sound or music, if possible sound_pt soundPtr = (byte *)malloc(s_hdr[sound].size); // Ptr to sound data assert(soundPtr); // Seek to data and read it fp.seek(s_hdr[sound].offset, SEEK_SET); if (fp.read(soundPtr, s_hdr[sound].size) != s_hdr[sound].size) error("Wrong sound file format"); fp.close(); return soundPtr; }
VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop, byte volume) { // First, check to see if that video is already playing for (uint32 i = 0; i < _videoStreams.size(); i++) if (_videoStreams[i].filename == filename) return i; // Otherwise, create a new entry VideoEntry entry; entry.clear(); entry.video = new Video::QuickTimeDecoder(); entry.x = x; entry.y = y; entry.filename = filename; entry.loop = loop; entry.enabled = true; Common::File *file = new Common::File(); if (!file->open(filename)) { delete file; return NULL_VID_HANDLE; } entry->loadStream(file); entry->setVolume(volume); entry->start(); // Search for any deleted videos so we can take a formerly used slot for (uint32 i = 0; i < _videoStreams.size(); i++) if (!_videoStreams[i].video) { _videoStreams[i] = entry; return i; } // Otherwise, just add it to the list _videoStreams.push_back(entry); return _videoStreams.size() - 1; }
Common::SeekableReadStream *SaveManager::getSlotFile(uint slot) { Common::SeekableReadStream *saveFile = g_system->getSavefileManager()->openForLoading(_engine->generateSaveFileName(slot)); if (saveFile == NULL) { // Try to load standard save file Common::String filename; if (_engine->getGameId() == GID_GRANDINQUISITOR) filename = Common::String::format("inqsav%u.sav", slot); else if (_engine->getGameId() == GID_NEMESIS) filename = Common::String::format("nemsav%u.sav", slot); saveFile = _engine->getSearchManager()->openFile(filename); if (saveFile == NULL) { Common::File *tmpFile = new Common::File; if (!tmpFile->open(filename)) { delete tmpFile; } else { saveFile = tmpFile; } } } return saveFile; }
bool GfxPalette::loadClut(uint16 clutId) { // loadClut() will load a color lookup table from a clu file and set // the palette found in the file. This is to be used with Phantasmagoria 2. unloadClut(); Common::String filename = Common::String::format("%d.clu", clutId); Common::File clut; if (!clut.open(filename) || clut.size() != 0x10000 + 236 * 3) return false; // Read in the lookup table // It maps each RGB565 color to a palette index _clutTable = new byte[0x10000]; clut.read(_clutTable, 0x10000); Palette pal; memset(&pal, 0, sizeof(Palette)); // Setup 1:1 mapping for (int i = 0; i < 256; i++) pal.mapping[i] = i; // Now load in the palette for (int i = 1; i <= 236; i++) { pal.colors[i].used = 1; pal.colors[i].r = clut.readByte(); pal.colors[i].g = clut.readByte(); pal.colors[i].b = clut.readByte(); } set(&pal, true); setOnScreen(); return true; }
// Will try to set amiga palette by using "spal" file. If not found, we return false bool GfxPalette::setAmiga() { Common::File file; if (file.open("spal")) { for (int curColor = 0; curColor < 32; curColor++) { byte byte1 = file.readByte(); byte byte2 = file.readByte(); if (file.eos()) error("Amiga palette file ends prematurely"); _sysPalette.colors[curColor].used = 1; _sysPalette.colors[curColor].r = (byte1 & 0x0F) * 0x11; _sysPalette.colors[curColor].g = ((byte2 & 0xF0) >> 4) * 0x11; _sysPalette.colors[curColor].b = (byte2 & 0x0F) * 0x11; } // Directly set the palette, because setOnScreen() wont do a thing for amiga copySysPaletteToScreen(); return true; } return false; }
bool MD5Check::advanceCheck(int *pos, int *total) { if (_iterator < 0) { return false; } const MD5Sum &sum = (*_files)[_iterator++]; if (pos) { *pos = _iterator; } if (total) { *total = _files->size(); } if ((uint32)_iterator == _files->size()) { _iterator = -1; } Common::File file; if (file.open(sum.filename)) { Common::String md5 = Common::computeStreamMD5AsString(file); if (!checkMD5(sum, md5.c_str())) { warning("'%s' may be corrupted. MD5: '%s'", sum.filename, md5.c_str()); GUI::displayErrorDialog(Common::String::format("The game data file %s may be corrupted.\nIf you are sure it is " "not please provide the ResidualVM team the following code, along with the file name, the language and a " "description of your game version (i.e. dvd-box or jewelcase):\n%s", sum.filename, md5.c_str()).c_str()); return false; } } else { warning("Could not open %s for checking", sum.filename); GUI::displayErrorDialog(Common::String::format("Could not open the file %s for checking.\nIt may be missing or " "you may not have the rights to open it.\nGo to http://wiki.residualvm.org/index.php/Datafiles to see a list " "of the needed files.", sum.filename).c_str()); return false; } return true; }
void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile) { Common::File file; if (!file.open(inputFile)) return; Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine); Common::DumpFile output; output.open(outputFile); output.writeUint32BE(MKTAG('R', 'I', 'F', 'F')); output.writeUint32LE(file.size() * 2 + 36); output.writeUint32BE(MKTAG('W', 'A', 'V', 'E')); output.writeUint32BE(MKTAG('f', 'm', 't', ' ')); output.writeUint32LE(16); output.writeUint16LE(1); uint16 numChannels; if (audioStream->isStereo()) { numChannels = 2; output.writeUint16LE(2); } else { numChannels = 1; output.writeUint16LE(1); } output.writeUint32LE(audioStream->getRate()); output.writeUint32LE(audioStream->getRate() * numChannels * 2); output.writeUint16LE(numChannels * 2); output.writeUint16LE(16); output.writeUint32BE(MKTAG('d', 'a', 't', 'a')); output.writeUint32LE(file.size() * 2); int16 *buffer = new int16[file.size()]; audioStream->readBuffer(buffer, file.size()); output.write(buffer, file.size() * 2); delete[] buffer; }
/** * Draw background animation * @remarks Originally called 'show_one' */ void Background::draw(int16 destX, int16 destY, byte sprId) { assert(sprId < 40); if (_sprites[sprId]._x > kOnDisk) { if (destX < 0) { destX = _sprites[sprId]._x * 8; destY = _sprites[sprId]._y; } drawSprite(destX, destY, _sprites[sprId]); } else { Common::File f; if (!f.open(_filename)) // Filename was set in loadBackgroundSprites(). return; // We skip because some rooms don't have sprites in the background. f.seek(_offsets[sprId]); SpriteType sprite; sprite._type = (PictureType)(f.readByte()); sprite._x = f.readSint16LE(); sprite._y = f.readSint16LE(); sprite._width = f.readSint16LE(); sprite._height = f.readSint16LE(); sprite._size = f.readSint32LE(); f.skip(2); // Natural and Memorize are used in Load() sprite._picture = _vm->_graphics->loadPictureRaw(f, sprite._width * 8, sprite._height + 1); if (destX < 0) { destX = sprite._x * 8; destY = sprite._y; } drawSprite(destX, destY, sprite); sprite._picture.free(); f.close(); } }
BaseSound::BaseSound(Audio::Mixer *mixer, const Common::String &filename, uint32 base, bool bigEndian) : _mixer(mixer), _filename(filename), _offsets(NULL) { uint res = 0; uint32 size; Common::File file; if (!file.open(_filename)) error("BaseSound: Could not open file \"%s\"", filename.c_str()); file.seek(base + sizeof(uint32), SEEK_SET); if (bigEndian) size = file.readUint32BE(); else size = file.readUint32LE(); // The Feeble Files uses set amount of voice offsets if (size == 0) size = 40000; res = size / sizeof(uint32); _offsets = (uint32 *)malloc(size + sizeof(uint32)); _freeOffsets = true; file.seek(base, SEEK_SET); for (uint i = 0; i < res; i++) { if (bigEndian) _offsets[i] = base + file.readUint32BE(); else _offsets[i] = base + file.readUint32LE(); } _offsets[res] = file.size(); }
void GameModule::load(const char *filename) { debug(0, "GameModule::load()"); unload(); Common::File fd; if (!fd.open(filename)) error("GameModule::load() Could not open %s", filename); loadBgSprites(fd); loadCameraInits(fd); loadWalkRects(fd); loadSceneExits(fd); loadBgObjects(fd); loadAnimations(fd); loadSceneObjectDefs(fd); loadSceneObjectInits(fd); loadActions(fd); loadGuiSpriteIndices(fd); loadInventoryItemSpriteIndices(fd); loadInventoryItemInfos(fd); loadDialogItemSpriteIndices(fd); loadSceneSounds(fd); loadPreloadSounds(fd); fd.seek(0xC); _fieldC = fd.readUint32LE(); fd.seek(0x1A8); _buttheadObjectIndex = fd.readUint32LE(); fd.close(); debug(0, "GameModule::load() OK"); }
Common::SeekableReadStream *InstallShieldCabinet::createReadStreamForMember(const Common::String &name) const { if (!_map.contains(name)) return 0; const FileEntry &entry = _map[name]; Common::File archiveFile; archiveFile.open(_installShieldFilename); archiveFile.seek(entry.offset); if (!(entry.flags & 0x04)) { // Not compressed return archiveFile.readStream(entry.uncompressedSize); } #ifdef USE_ZLIB byte *src = (byte *)malloc(entry.compressedSize); byte *dst = (byte *)malloc(entry.uncompressedSize); archiveFile.read(src, entry.compressedSize); bool result = Common::inflateZlibHeaderless(dst, entry.uncompressedSize, src, entry.compressedSize); free(src); if (!result) { warning("failed to inflate CAB file '%s'", name.c_str()); free(dst); return 0; } return new Common::MemoryReadStream(dst, entry.uncompressedSize, DisposeAfterUse::YES); #else warning("zlib required to extract compressed CAB file '%s'", name.c_str()); return 0; #endif }
Common::SeekableReadStream *Resources::openFile(const Common::String &fileName) { debugC(1, kDebugResource, "openFile(%s)", fileName.c_str()); // first try to find files outside of .pak // some patched files have not been included in package. if (Common::File::exists(fileName)) { Common::File *file = new Common::File(); bool opened = file->open(fileName); if (!opened) { delete file; return 0; } return file; } else { for (uint32 i = 0; i < _pakFiles.size(); i++) { Common::SeekableReadStream *stream = 0; stream = _pakFiles[i]->createReadStream(fileName); if (stream) return stream; } return 0; } }
bool Font::loadFont(const Common::String &filename) { // Free previously loaded font (if any) freeFont(); Common::File f; f.open(filename); if (f.isOpen()) { debugC(6, kDraciGeneralDebugLevel, "Opened font file %s", filename.c_str()); } else { debugC(6, kDraciGeneralDebugLevel, "Error opening font file %s", filename.c_str()); return false; } _maxCharWidth = f.readByte(); _fontHeight = f.readByte(); // Read in the widths of the glyphs _charWidths = new uint8[kCharNum]; for (uint i = 0; i < kCharNum; ++i) { _charWidths[i] = f.readByte(); } // Calculate size of font data uint fontDataSize = kCharNum * _maxCharWidth * _fontHeight; // Read in all glyphs _charData = new byte[fontDataSize]; f.read(_charData, fontDataSize); debugC(5, kDraciGeneralDebugLevel, "Font %s loaded", filename.c_str()); return true; }
bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buffer, bool onlyHeader) { size_t soundResourceLength; bool result = false; GameSoundType resourceType = kSoundPCM; int rate = 0, size = 0; Common::File *file; if (resourceId == (uint32)-1) { return false; } #ifdef ENABLE_IHNM //TODO: move to resource_res so we can use normal "getResourceData" and "getFile" methods if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { char soundFileName[40]; int dirIndex = resourceId / 64; if ((context->fileType() & GAME_VOICEFILE) != 0) { if (_voiceSerial == 0) { sprintf(soundFileName, "Voices/VoicesS/Voices%d/VoicesS%03x", dirIndex, resourceId); } else { sprintf(soundFileName, "Voices/Voices%d/Voices%d/Voices%d%03x", _voiceSerial, dirIndex, _voiceSerial, resourceId); } } else { sprintf(soundFileName, "SFX/SFX%d/SFX%03x", dirIndex, resourceId); } file = new Common::File(); file->open(soundFileName); soundResourceLength = file->size(); } else #endif { ResourceData* resourceData = context->getResourceData(resourceId); file = context->getFile(resourceData); file->seek(resourceData->offset); soundResourceLength = resourceData->size; } Common::SeekableReadStream &readS = *file; bool uncompressedSound = false; if (soundResourceLength >= 8) { byte header[8]; readS.read(&header, 8); readS.seek(readS.pos() - 8); if (!memcmp(header, "Creative", 8)) { resourceType = kSoundVOC; } else if (!memcmp(header, "RIFF", 4) != 0) { resourceType = kSoundWAV; } else if (!memcmp(header, "FORM", 4) != 0) { resourceType = kSoundAIFF; } else if (!memcmp(header, "ajkg", 4) != 0) { resourceType = kSoundShorten; } // If patch data exists for sound resource 4 (used in ITE intro), don't treat this sound as compressed // Patch data for this resource is in file p2_a.iaf or p2_a.voc if (_vm->getGameId() == GID_ITE && resourceId == 4 && context->getResourceData(resourceId)->patchData != NULL) uncompressedSound = true; // FIXME: Currently, the SFX.RES file in IHNM cannot be compressed if (_vm->getGameId() == GID_IHNM && (context->fileType() & GAME_SOUNDFILE)) uncompressedSound = true; if (context->isCompressed() && !uncompressedSound) { if (header[0] == char(0)) { resourceType = kSoundMP3; } else if (header[0] == char(1)) { resourceType = kSoundOGG; } else if (header[0] == char(2)) { resourceType = kSoundFLAC; } } } // Default sound type is 16-bit signed PCM, used in ITE byte rawFlags = Audio::FLAG_16BITS; if (_vm->getGameId() == GID_ITE) { if (context->fileType() & GAME_MACBINARY) { // ITE Mac has sound in the Mac snd format resourceType = kSoundMacSND; } else if (_vm->getFeatures() & GF_8BIT_UNSIGNED_PCM) { // older ITE demos rawFlags |= Audio::FLAG_UNSIGNED; rawFlags &= ~Audio::FLAG_16BITS; } else if (!uncompressedSound && !scumm_stricmp(context->fileName(), "voicesd.rsc")) { // Voice files in newer ITE demo versions are OKI ADPCM (VOX) encoded. resourceType = kSoundVOX; } } buffer.stream = 0; // Check for LE sounds if (!context->isBigEndian()) rawFlags |= Audio::FLAG_LITTLE_ENDIAN; switch (resourceType) { case kSoundPCM: { // In ITE CD German, some voices are absent and contain just 5 zero bytes. // Round down to an even number when the audio is 16-bit so makeRawStream // will accept the data (needs to be an even size for 16-bit data). // See bug #1256701 if ((soundResourceLength & 1) && (rawFlags & Audio::FLAG_16BITS)) soundResourceLength &= ~1; Audio::SeekableAudioStream *audStream = Audio::makeRawStream(READ_STREAM(soundResourceLength), 22050, rawFlags); buffer.stream = audStream; buffer.streamLength = audStream->getLength(); result = true; } break; case kSoundVOX: buffer.stream = Audio::makeADPCMStream(READ_STREAM(soundResourceLength), DisposeAfterUse::YES, soundResourceLength, Audio::kADPCMOki, 22050, 1); buffer.streamLength = Audio::Timestamp(0, soundResourceLength * 2, buffer.stream->getRate()); result = true; break; case kSoundMacSND: { Audio::SeekableAudioStream *audStream = Audio::makeMacSndStream(READ_STREAM(soundResourceLength), DisposeAfterUse::YES); buffer.stream = audStream; buffer.streamLength = audStream->getLength(); result = true; } break; case kSoundAIFF: { Audio::SeekableAudioStream *audStream = Audio::makeAIFFStream(READ_STREAM(soundResourceLength), DisposeAfterUse::YES); buffer.stream = audStream; buffer.streamLength = audStream->getLength(); result = true; } break; case kSoundVOC: { Audio::SeekableAudioStream *audStream = Audio::makeVOCStream(READ_STREAM(soundResourceLength), Audio::FLAG_UNSIGNED, DisposeAfterUse::YES); buffer.stream = audStream; buffer.streamLength = audStream->getLength(); result = true; } break; case kSoundWAV: case kSoundShorten: if (resourceType == kSoundWAV) { result = Audio::loadWAVFromStream(readS, size, rate, rawFlags); #ifdef ENABLE_SAGA2 } else if (resourceType == kSoundShorten) { result = loadShortenFromStream(readS, size, rate, rawFlags); #endif } if (result) { Audio::SeekableAudioStream *audStream = Audio::makeRawStream(READ_STREAM(size), rate, rawFlags); buffer.stream = audStream; buffer.streamLength = audStream->getLength(); } break; case kSoundMP3: case kSoundOGG: case kSoundFLAC: { readS.skip(9); // skip sfx header Audio::SeekableAudioStream *audStream = 0; Common::SeekableReadStream *memStream = READ_STREAM(soundResourceLength - 9); if (resourceType == kSoundMP3) { #ifdef USE_MAD audStream = Audio::makeMP3Stream(memStream, DisposeAfterUse::YES); #endif } else if (resourceType == kSoundOGG) { #ifdef USE_VORBIS audStream = Audio::makeVorbisStream(memStream, DisposeAfterUse::YES); #endif } else /* if (resourceType == kSoundFLAC) */ { #ifdef USE_FLAC audStream = Audio::makeFLACStream(memStream, DisposeAfterUse::YES); #endif } if (audStream) { buffer.stream = audStream; buffer.streamLength = audStream->getLength(); result = true; } else { delete memStream; } } break; default: error("SndRes::load Unknown sound type"); } if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { delete file; } if (onlyHeader) { delete buffer.stream; buffer.stream = 0; } return result; }
void GhostRoom::loadPictures() { Common::File file; if (!file.open("spooky.avd")) error("AVALANCHE: GhostRoom: File not found: spooky.avd"); file.seek(44); // Initializing ghost's array. for (int i = 0; i < 5; i++) { for (int j = 0; j < 2; j++) { for (int y = 0; y < 66; y++) { for (int x = 0; x < 26; x++) _ghost[i][j][y][x] = 0; } } } // Read in the pictures of the ghost. for (int i = 0; i < 5; i++) { ChunkBlock cb = readChunkBlock(file); for (int j = 0; j < 2; j++) { for (int y = 0; y <= cb._height; y++) file.read(_ghost[i][j][y], cb._width / 8); } } for (int i = 0; i < 2; i++) _eyes[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); _exclamation = _vm->_graphics->ghostLoadPicture(file, dummyCoord); // Actually this function not just loads, but also draws the images, but they are part of the background // and they are need to be drawn only once. _vm->_graphics->ghostDrawBackgroundItems(file); for (int i = 0; i < 3; i++) _bat[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); // Initializing glerk's array. for (int i = 0; i < 6; i++) { for (int j = 0; j < 4; j++) { for (int y = 0; y < 35; y++) { for (int x = 0; x < 9; x++) _glerk[i][j][y][x] = 0; } } } // Read in the pictures of the "glerk". for (int i = 0; i < 6; i++) { ChunkBlock cb = readChunkBlock(file); for (int j = 0; j < 4; j++) { for (int y = 0; y <= cb._height; y++) file.read(_glerk[i][j][y], cb._width / 8); } } for (int i = 0; i < 6; i++) _aargh[i] = _vm->_graphics->ghostLoadPicture(file, _aarghWhere[i]); for (int i = 0; i < 5; i++) _greenEyes[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); for (int i = 0; i < 2; i++) { for (int j = 0; j < 6; j++) _greldet[j][i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); } file.close(); }
bool FrotzMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) { const char *const EXTENSIONS[] = { ".z1", ".z2", ".z3", ".z4", ".z5", ".z6", ".z7", ".z8", ".dat", ".zip", nullptr }; // Loop through the files of the folder for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { // Check for a recognised filename if (file->isDirectory()) continue; Common::String filename = file->getName(); bool hasExt = Blorb::hasBlorbExt(filename), isBlorb = false; for (const char *const *ext = &EXTENSIONS[0]; *ext && !hasExt; ++ext) hasExt = filename.hasSuffixIgnoreCase(*ext); if (!hasExt) continue; // Open up the file and calculate the md5, and get the serial Common::File gameFile; if (!gameFile.open(*file)) continue; Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000); size_t filesize = gameFile.size(); char serial[9] = ""; bool emptyBlorb = false; gameFile.seek(0); isBlorb = Blorb::isBlorb(gameFile, ID_ZCOD); if (!isBlorb) { if (Blorb::hasBlorbExt(filename)) { gameFile.close(); continue; } gameFile.seek(18); strcpy(&serial[0], "\""); gameFile.read(&serial[1], 6); strcpy(&serial[7], "\""); } else { Blorb b(*file, INTERPRETER_FROTZ); Common::SeekableReadStream *f = b.createReadStreamForMember("game"); emptyBlorb = f == nullptr; if (!emptyBlorb) { f->seek(18); strcpy(&serial[0], "\""); f->read(&serial[1], 6); strcpy(&serial[7], "\""); delete f; } } gameFile.close(); // Check for known games. Note that there has been some variation in exact filesizes // for Infocom games due to padding at the end of files. So we match on md5s for the // first 5Kb, and only worry about filesize for more recent Blorb based Zcode games const FrotzGameDescription *p = FROTZ_GAMES; while (p->_gameId && p->_md5 && (md5 != p->_md5 || (filesize != p->_filesize && isBlorb))) ++p; DetectedGame gd; if (!p->_gameId) { // Generic .dat/.zip files don't get reported as matches unless they have a known md5 if (filename.hasSuffixIgnoreCase(".dat") || filename.hasSuffixIgnoreCase(".zip") || emptyBlorb) continue; if (gDebugLevel > 0) { // Print an entry suitable for putting into the detection_tables.h, using the // name of the parent folder the game is in as the presumed game Id Common::String folderName = file->getParent().getName(); if (folderName.hasSuffix("\\")) folderName.deleteLastChar(); Common::String fname = filename; const char *dot = strchr(fname.c_str(), '.'); if (dot) fname = Common::String(fname.c_str(), dot); debug("ENTRY0(\"%s\", %s, \"%s\", %u),", fname.c_str(), strlen(serial) ? serial : "nullptr", md5.c_str(), (uint)filesize); } const PlainGameDescriptor &desc = ZCODE_GAME_LIST[0]; gd = DetectedGame(desc.gameId, desc.description, Common::UNK_LANG, Common::kPlatformUnknown); } else { GameDescriptor gameDesc = findGame(p->_gameId); gd = DetectedGame(p->_gameId, gameDesc._description, p->_language, Common::kPlatformUnknown, p->_extra); gd.setGUIOptions(p->_guiOptions); } gd.addExtraEntry("filename", filename); gameList.push_back(gd); } return !gameList.empty(); }
static ADGameDescList detectGame(const Common::FSList &fslist, const ADParams ¶ms, Common::Language language, Common::Platform platform, const Common::String &extra) { FileMap allFiles; SizeMD5Map filesSizeMD5; const ADGameFileDescription *fileDesc; const ADGameDescription *g; const byte *descPtr; if (fslist.empty()) return ADGameDescList(); Common::FSNode parent = fslist.begin()->getParent(); debug(3, "Starting detection in dir '%s'", parent.getPath().c_str()); // First we compose a hashmap of all files in fslist. // Includes nifty stuff like removing trailing dots and ignoring case. composeFileHashMap(fslist, allFiles, (params.depth == 0 ? 1 : params.depth), params.directoryGlobs); // Check which files are included in some ADGameDescription *and* present // in fslist. Compute MD5s and file sizes for these files. for (descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize) { g = (const ADGameDescription *)descPtr; for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String fname = fileDesc->fileName; SizeMD5 tmp; if (filesSizeMD5.contains(fname)) continue; // FIXME/TODO: We don't handle the case that a file is listed as a regular // file and as one with resource fork. if (g->flags & ADGF_MACRESFORK) { Common::MacResManager *macResMan = new Common::MacResManager(); if (macResMan->open(parent, fname)) { tmp.md5 = macResMan->computeResForkMD5AsString(params.md5Bytes); tmp.size = macResMan->getResForkDataSize(); debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); filesSizeMD5[fname] = tmp; } delete macResMan; } else { if (allFiles.contains(fname)) { debug(3, "+ %s", fname.c_str()); Common::File testFile; if (testFile.open(allFiles[fname])) { tmp.size = (int32)testFile.size(); tmp.md5 = Common::computeStreamMD5AsString(testFile, params.md5Bytes); } else { tmp.size = -1; } debug(3, "> '%s': '%s'", fname.c_str(), tmp.md5.c_str()); filesSizeMD5[fname] = tmp; } } } } ADGameDescList matched; int maxFilesMatched = 0; bool gotAnyMatchesWithAllFiles = false; // MD5 based matching uint i; for (i = 0, descPtr = params.descs; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += params.descItemSize, ++i) { g = (const ADGameDescription *)descPtr; bool fileMissing = false; // Do not even bother to look at entries which do not have matching // language and platform (if specified). if ((language != Common::UNK_LANG && g->language != Common::UNK_LANG && g->language != language && !(language == Common::EN_ANY && (g->flags & ADGF_ADDENGLISH))) || (platform != Common::kPlatformUnknown && g->platform != Common::kPlatformUnknown && g->platform != platform)) { continue; } if ((params.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->extra != extra) continue; bool allFilesPresent = true; int curFilesMatched = 0; // Try to match all files for this game for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { Common::String tstr = fileDesc->fileName; if (!filesSizeMD5.contains(tstr)) { fileMissing = true; allFilesPresent = false; break; } if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) { debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fileDesc->md5, filesSizeMD5[tstr].md5.c_str()); fileMissing = true; break; } if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) { debug(3, "Size Mismatch. Skipping"); fileMissing = true; break; } debug(3, "Matched file: %s", tstr.c_str()); curFilesMatched++; } // We found at least one entry with all required files present. // That means that we got new variant of the game. // // Without this check we would have erroneous checksum display // where only located files will be enlisted. // // Potentially this could rule out variants where some particular file // is really missing, but the developers should better know about such // cases. if (allFilesPresent) gotAnyMatchesWithAllFiles = true; if (!fileMissing) { debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameid, g->extra, getPlatformDescription(g->platform), getLanguageDescription(g->language), i); if (curFilesMatched > maxFilesMatched) { debug(2, " ... new best match, removing all previous candidates"); maxFilesMatched = curFilesMatched; matched.clear(); // Remove any prior, lower ranked matches. matched.push_back(g); } else if (curFilesMatched == maxFilesMatched) { matched.push_back(g); } else { debug(2, " ... skipped"); } } else { debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameid, g->extra, getPlatformDescription(g->platform), getLanguageDescription(g->language), i); } } // We didn't find a match if (matched.empty()) { if (!filesSizeMD5.empty() && gotAnyMatchesWithAllFiles) { reportUnknown(parent, filesSizeMD5); } // Filename based fallback if (params.fileBasedFallback != 0) matched = detectGameFilebased(allFiles, params); } return matched; }
void NDSFile::open(Common::File &file) const { if (!file.open(_fileName)) throw Common::Exception(Common::kOpenError); }
void loadKoreanStrings() { Common::File fp; char fname[128]; strcpy(fname, "sub/"); strcat(fname, _gameID); strcat(fname, ".dat"); if(!fp.open(fname)) { warning("WARNING: Cannot load Korean V2 subtitle!, %s\n", fname); return; // strcpy(fname, ""); // strcat(fname, _gameID); // strcat(fname, ".dat"); // // if(!fp.open(fname)) { // warning("WARNING: Cannot load Korean V2 subtitle!, %s\n", fname); // return; // } } int k; char *buf = new char[1024]; int len = 0; for(k = 0; !fp.eos(); k++) { FGETS(buf, 1023, fp); //printf("%d: <%s>\n", k+1, buf); } // 파일의 라인 수를 계산 _numKLines = k; // memory size = file size k = fp.size(); fp.seek(0, SEEK_SET); _KBuffer = new char *[_numKLines]; //printf("_KBuffer size = %d\n", (int)k); _KBuffer[0] = new char[k]; //printf("_KBuffer[0] = 0x%x\n", (int)_KBuffer[0]); for(int i = 0; i < _numKLines; i++) { FGETS(buf, 1023, fp); buf[1023] = 0; len = strlen(buf); //_KBuffer[i] = new char[strlen(buf) + 1]; //\0 if (i > 0) _KBuffer[i] = _KBuffer[i-1] + strlen(_KBuffer[i-1])+1; if (len > 1000) warning("_KBuffer[%d]:%lx, len=%d\n", i, (long)_KBuffer[i], len); if(strlen(buf)) { strcpy(_KBuffer[i], buf); char *b = _KBuffer[i]; if(b[strlen(b) - 1] == '\n' || b[strlen(b) - 1] == 0x0a || b[strlen(b) - 1] == 0x0d) b[strlen(b) - 1] = 0; if(b[strlen(b) - 1] == '\n' || b[strlen(b) - 1] == 0x0a || b[strlen(b) - 1] == 0x0d) b[strlen(b) - 1] = 0; } else _KBuffer[i][0] = 0; } fp.close(); warning("Korean subtitle file loaded -- total %d lines, %ld bytes\n", _numKLines, _KBuffer[_numKLines-1]-_KBuffer[0]+len); delete[] buf; return; }
/** * Loads and draws the chosen page of the help. * @remarks Originally called 'getme' */ void Help::switchPage(byte which) { // Help icons are 80x20. _highlightWas = 177; // Forget where the highlight was. Common::File file; if (!file.open("help.avd")) error("AVALANCHE: Help: File not found: help.avd"); file.seek(which * 2); uint16 offset = file.readUint16LE(); file.seek(offset); Common::String title = getLine(file); _vm->_graphics->drawFilledRectangle(Common::Rect(0, 0, 640, 200), kColorBlue); _vm->_graphics->drawFilledRectangle(Common::Rect(8, 40, 450, 200), kColorWhite); byte index = file.readByte(); _vm->_graphics->helpDrawButton(-177, index); // Plot the title: _vm->_graphics->drawNormalText(title, _vm->_font, 8, 629 - 8 * title.size(), 26, kColorBlack); _vm->_graphics->drawNormalText(title, _vm->_font, 8, 630 - 8 * title.size(), 25, kColorCyan); _vm->_graphics->helpDrawBigText("help!", 549, 1, kColorBlack); _vm->_graphics->helpDrawBigText("help!", 550, 0, kColorCyan); byte y = 0; do { Common::String line = getLine(file); if (!line.empty()) { if (line.compareTo(Common::String('!')) == 0) // End of the help text is signalled with a '!'. break; if (line[0] == '\\') { line.deleteChar(0); _vm->_graphics->drawNormalText(line, _vm->_font, 8, 16, 41 + y * 10, kColorRed); } else _vm->_graphics->drawNormalText(line, _vm->_font, 8, 16, 41 + y * 10, kColorBlack); } y++; } while (true); // We are now at the end of the text. Next we must read the icons: y = 0; _buttonNum = 0; while (!file.eos()) { int trigger = file.readByte(); if (trigger == 177) break; switch (trigger) { case 254: // Escape trigger = 27; break; case 214: // PageUp trigger = 280; break; case 216: // PageDown trigger = 281; break; default: // A - Z // The characters are stored in the file in uppercase, but we need the lowercase versions for KeyCode: trigger = tolower(trigger); break; } _buttons[y]._trigger = Common::KeyCode(trigger); index = file.readByte(); if (_buttons[y]._trigger != Common::KEYCODE_INVALID) _vm->_graphics->helpDrawButton(13 + (y + 1) * 27, index); _buttons[y]._whither = file.readByte(); // This is the position to jump to. Common::String text = ""; switch (_buttons[y]._trigger) { case Common::KEYCODE_ESCAPE: text = Common::String("Esc"); break; case Common::KEYCODE_PAGEUP: text = Common::String(24); break; case Common::KEYCODE_PAGEDOWN: text = Common::String(25); break; default: text = Common::String(toupper(_buttons[y]._trigger)); break; } _vm->_graphics->helpDrawBigText(text, 589 - (text.size() * 8), 18 + (y + 1) * 27, kColorBlack); _vm->_graphics->helpDrawBigText(text, 590 - (text.size() * 8), 17 + (y + 1) * 27, kColorCyan); y++; _buttonNum++; } _vm->_graphics->refreshScreen(); file.close(); }
/** * Loads Hugo.dat file, which contains all the hardcoded data in the original executables */ bool HugoEngine::loadHugoDat() { Common::File in; in.open("hugo.dat"); if (!in.isOpen()) { Common::String errorMessage = "You're missing the 'hugo.dat' file. Get it from the ScummVM website"; GUIErrorMessage(errorMessage); warning("%s", errorMessage.c_str()); return false; } // Read header char buf[4]; in.read(buf, 4); if (memcmp(buf, "HUGO", 4)) { Common::String errorMessage = "File 'hugo.dat' is corrupt. Get it from the ScummVM website"; GUIErrorMessage(errorMessage); return false; } int majVer = in.readByte(); int minVer = in.readByte(); if ((majVer != HUGO_DAT_VER_MAJ) || (minVer != HUGO_DAT_VER_MIN)) { Common::String errorMessage = Common::String::format("File 'hugo.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", HUGO_DAT_VER_MAJ, HUGO_DAT_VER_MIN, majVer, minVer); GUIErrorMessage(errorMessage); return false; } _numVariant = in.readUint16BE(); _screen->loadPalette(in); _screen->loadFontArr(in); _text->loadAllTexts(in); _intro->loadIntroData(in); _parser->loadArrayReqs(in); _parser->loadCatchallList(in); _parser->loadBackgroundObjects(in); _parser->loadCmdList(in); _mouse->loadHotspots(in); _inventory->loadInvent(in); _object->loadObjectUses(in); _object->loadObjectArr(in); _object->loadNumObj(in); _scheduler->loadPoints(in); _scheduler->loadScreenAct(in); _scheduler->loadActListArr(in); _scheduler->loadAlNewscrIndex(in); _hero = &_object->_objects[kHeroIndex]; // This always points to hero _screenPtr = &(_object->_objects[kHeroIndex]._screenIndex); // Current screen is hero's _heroImage = kHeroIndex; // Current in use hero image for (int varnt = 0; varnt < _numVariant; varnt++) { if (varnt == _gameVariant) { _tunesNbr = in.readSByte(); _soundSilence = in.readSByte(); _soundTest = in.readSByte(); } else { in.readSByte(); in.readSByte(); in.readSByte(); } } int numElem; //Read _defltTunes for (int varnt = 0; varnt < _numVariant; varnt++) { numElem = in.readUint16BE(); if (varnt == _gameVariant) { _defltTunes = (int16 *)malloc(sizeof(int16) * numElem); for (int i = 0; i < numElem; i++) _defltTunes[i] = in.readSint16BE(); } else { for (int i = 0; i < numElem; i++) in.readSint16BE(); } } //Read _screenStates size for (int varnt = 0; varnt < _numVariant; varnt++) { numElem = in.readUint16BE(); if (varnt == _gameVariant) { _numStates = numElem; _screenStates = (byte *)malloc(sizeof(byte) * numElem); memset(_screenStates, 0, sizeof(byte) * numElem); } } //Read look, take and drop special verbs indexes for (int varnt = 0; varnt < _numVariant; varnt++) { if (varnt == _gameVariant) { _look = in.readUint16BE(); _take = in.readUint16BE(); _drop = in.readUint16BE(); } else { in.readUint16BE(); in.readUint16BE(); in.readUint16BE(); } } _sound->loadIntroSong(in); _topMenu->loadBmpArr(in); return true; }
/** * Fallback detection scans the list of Discworld 2 targets to see if it can detect an installation * where the files haven't been renamed (i.e. don't have the '1' just before the extension) */ const ADGameDescription *TinselMetaEngine::fallbackDetect(const Common::FSList &fslist) const { Common::String extra; FileMap allFiles; SizeMD5Map filesSizeMD5; const ADGameFileDescription *fileDesc; const Tinsel::TinselGameDescription *g; if (fslist.empty()) return NULL; // TODO: The following code is essentially a slightly modified copy of the // complete code of function detectGame() in engines/advancedDetector.cpp. // That quite some hefty and undesirable code duplication. Its only purpose // seems to be to treat filenames of the form "foo1.ext" as "foo.ext". // It would be nice to avoid this code duplication. // First we compose a hashmap of all files in fslist. for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) { if (!scumm_stricmp(file->getName().c_str(), "dw2")) { // Probably Discworld 2 subfolder on CD, so add it's contents as well Common::FSList files; if (file->getChildren(files, Common::FSNode::kListAll)) { Common::FSList::const_iterator file2; for (file2 = files.begin(); file2 != files.end(); ++file2) { if (file2->isDirectory()) continue; Common::String fname = file2->getName(); allFiles[fname] = *file2; } } } continue; } Common::String tstr = file->getName(); allFiles[tstr] = *file; // Record the presence of this file } // Check which files are included in some dw2 ADGameDescription *and* present // in fslist without a '1' suffix character. Compute MD5s and file sizes for these files. for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) { if (strcmp(g->desc.gameid, "dw2") != 0) continue; for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { // Get the next filename, stripping off any '1' suffix character char tempFilename[50]; strcpy(tempFilename, fileDesc->fileName); char *pOne = strchr(tempFilename, '1'); if (pOne) { do { *pOne = *(pOne + 1); pOne++; } while (*pOne); } Common::String fname(tempFilename); if (allFiles.contains(fname) && !filesSizeMD5.contains(fname)) { SizeMD5 tmp; Common::File testFile; if (testFile.open(allFiles[fname])) { tmp.size = (int32)testFile.size(); tmp.md5 = computeStreamMD5AsString(testFile, detectionParams.md5Bytes); } else { tmp.size = -1; } filesSizeMD5[fname] = tmp; } } } ADGameDescList matched; int maxFilesMatched = 0; // MD5 based matching for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) { if (strcmp(g->desc.gameid, "dw2") != 0) continue; bool fileMissing = false; if ((detectionParams.flags & kADFlagUseExtraAsHint) && !extra.empty() && g->desc.extra != extra) continue; bool allFilesPresent = true; // Try to match all files for this game for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { // Get the next filename, stripping off any '1' suffix character char tempFilename[50]; strcpy(tempFilename, fileDesc->fileName); char *pOne = strchr(tempFilename, '1'); if (pOne) { do { *pOne = *(pOne + 1); pOne++; } while (*pOne); } Common::String tstr(tempFilename); if (!filesSizeMD5.contains(tstr)) { fileMissing = true; allFilesPresent = false; break; } if (fileDesc->md5 != NULL && fileDesc->md5 != filesSizeMD5[tstr].md5) { fileMissing = true; break; } if (fileDesc->fileSize != -1 && fileDesc->fileSize != filesSizeMD5[tstr].size) { fileMissing = true; break; } } if (!fileMissing) { // Count the number of matching files. Then, only keep those // entries which match a maximal amount of files. int curFilesMatched = 0; for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) curFilesMatched++; if (curFilesMatched > maxFilesMatched) { maxFilesMatched = curFilesMatched; matched.clear(); // Remove any prior, lower ranked matches. matched.push_back((const ADGameDescription *)g); } else if (curFilesMatched == maxFilesMatched) { matched.push_back((const ADGameDescription *)g); } } } // We didn't find a match if (matched.empty()) return NULL; return *matched.begin(); }
Common::ErrorCode Blorb::load() { // First, chew through the file and index the chunks Common::File f; if (!f.open(_filename) || f.size() < 12) return Common::kReadingFailed; if (f.readUint32BE() != ID_FORM) return Common::kReadingFailed; f.readUint32BE(); if (f.readUint32BE() != ID_IFRS) return Common::kReadingFailed; if (f.readUint32BE() != ID_RIdx) return Common::kReadingFailed; f.readUint32BE(); uint count = f.readUint32BE(); // First read in the resource index for (uint idx = 0; idx < count; ++idx) { ChunkEntry ce; ce._type = f.readUint32BE(); ce._number = f.readUint32BE(); ce._offset = f.readUint32BE(); _chunks.push_back(ce); } // Further iterate through the resources for (uint idx = 0; idx < _chunks.size(); ++idx) { ChunkEntry &ce = _chunks[idx]; f.seek(ce._offset); ce._offset += 8; ce._id = f.readUint32BE(); ce._size = f.readUint32BE(); if (ce._type == ID_Pict) { ce._filename = Common::String::format("pic%u", ce._number); if (ce._id == ID_JPEG) ce._filename += ".jpg"; else if (ce._id == ID_PNG) ce._filename += ".png"; else if (ce._id == ID_Rect) ce._filename += ".rect"; } else if (ce._type == ID_Snd) { ce._filename = Common::String::format("sound%u", ce._number); if (ce._id == ID_MIDI) ce._filename += ".midi"; else if (ce._id == ID_MP3) ce._filename += ".mp3"; else if (ce._id == ID_WAVE) ce._filename += ".wav"; else if (ce._id == ID_AIFF || ce._id == ID_FORM) ce._filename += ".aiff"; else if (ce._id == ID_OGG) ce._filename += ".ogg"; else if (ce._id == ID_MOD) ce._filename += ".mod"; } else if (ce._type == ID_Data) { ce._filename = Common::String::format("data%u", ce._number); } else if (ce._type == ID_Exec) { char buffer[5]; WRITE_BE_UINT32(buffer, ce._id); buffer[4] = '\0'; Common::String type(buffer); if ( (_interpType == INTERPRETER_FROTZ && type == "ZCOD") || (_interpType == INTERPRETER_GLULXE && type == "GLUL") || (_interpType == INTERPRETER_TADS2 && type == "TAD2") || (_interpType == INTERPRETER_TADS3 && type == "TAD3") || (_interpType == INTERPRETER_HUGO && type == "HUGO") ) { // Game executable ce._filename = "game"; } else { ce._filename = type; } } } return Common::kNoError; }
bool PatchedFile::load(Common::SeekableReadStream *file, const Common::String &patchName) { uint8 md5_p[16], md5_f[16]; uint32 zctrllen, zdatalen, zextralen; Common::File patch; _patchName = patchName; // Open the patch if (!patch.open(_patchName)) { error("Unable to open patchfile %s", _patchName.c_str()); return false; } // Check for appropriate signature if (patch.readUint32BE() != MKTAG('P','A','T','R')) { error("%s patchfile is corrupted", _patchName.c_str()); return false; } // Check the version number if (patch.readUint16LE() != _kVersionMajor || patch.readUint16LE() > _kVersionMinor) { error("%s has a wrong version number (must be major = %d, minor <= %d)", _patchName.c_str(), _kVersionMajor, _kVersionMinor); return false; } _flags = patch.readUint32LE(); // Check if the file to patch match Common::computeStreamMD5(*file, md5_f, _kMd5size); file->seek(0, SEEK_SET); patch.read(md5_p, 16); if (memcmp(md5_p, md5_f, 16) != 0 || (uint32)file->size() != patch.readUint32LE()) { Debug::debug(Debug::Patchr,"%s targets a different file", _patchName.c_str()); return false; } // Read lengths from header _newSize = patch.readUint32LE(); zctrllen = patch.readUint32LE(); zdatalen = patch.readUint32LE(); zextralen = patch.readUint32LE(); patch.close(); // Opens ctrl, diff and extra substreams Common::File *tmp; tmp = new Common::File; tmp->open(_patchName); _ctrl = new Common::SeekableSubReadStream(tmp, _kHeaderSize, _kHeaderSize + zctrllen, DisposeAfterUse::YES); if (_flags & FLAG_COMPRESS_CTRL) _ctrl = Common::wrapCompressedReadStream(_ctrl); //ctrl stream sanity checks if (_ctrl->size() % (3 * sizeof(uint32)) != 0) { error("%s patchfile is corrupted", _patchName.c_str()); return false; } instrLeft = _ctrl->size() / (3 * sizeof(uint32)); tmp = new Common::File; tmp->open(_patchName); _diff = new Common::SeekableSubReadStream(tmp, _kHeaderSize + zctrllen, _kHeaderSize + zctrllen + zdatalen, DisposeAfterUse::YES); _diff = Common::wrapCompressedReadStream(_diff); if (_flags & FLAG_MIX_DIFF_EXTRA) _extra = _diff; else { tmp = new Common::File; tmp->open(_patchName); _extra = new Common::SeekableSubReadStream(tmp, _kHeaderSize + zctrllen + zdatalen, _kHeaderSize + zctrllen + zdatalen + zextralen, DisposeAfterUse::YES); _extra = Common::wrapCompressedReadStream(_extra); } _file = file; readNextInst(); return true; }
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(); }
bool WinFont::loadFromFNT(const Common::String &fileName) { Common::File file; return file.open(fileName) && loadFromFNT(file); }
/** * 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()); }
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; }
/** * Search file in Cat file */ byte *FileManager::searchCat(const Common::String &file, CatMode mode, bool &fileFoundFl) { byte *ptr = NULL; fileFoundFl = true; Common::File f; Common::String filename = file; Common::String secondaryFilename = ""; filename.toUppercase(); switch (mode) { case RES_INI: if (!f.exists("RES_INI.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("RES_INI.CAT"); secondaryFilename = "RES_INI.RES"; break; case RES_REP: if (!f.exists("RES_REP.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("RES_REP.CAT"); secondaryFilename = "RES_REP.RES"; break; case RES_LIN: if (!f.exists("RES_LIN.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("RES_LIN.CAT"); secondaryFilename = "RES_LIN.RES"; break; case RES_PER: if (!f.exists("RES_PER.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("RES_PER.CAT"); secondaryFilename = "RES_PER.RES"; break; case RES_PIC: if (!f.exists("PIC.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("PIC.CAT"); break; case RES_SAN: if (!f.exists("RES_SAN.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("RES_SAN.CAT"); break; case RES_SLI: if (!f.exists("RES_SLI.CAT")) { fileFoundFl = false; return NULL; } ptr = loadFile("RES_SLI.CAT"); break; case RES_VOI: { Common::String tmpFilename; if (_vm->getPlatform() == Common::kPlatformOS2 || _vm->getPlatform() == Common::kPlatformBeOS) tmpFilename = "ENG_VOI.CAT"; // Win95 and Linux versions uses another set of names else { switch (_vm->_globals->_language) { case LANG_EN: tmpFilename = "RES_VAN.CAT"; break; case LANG_FR: tmpFilename = "RES_VFR.CAT"; break; case LANG_SP: tmpFilename = "RES_VES.CAT"; break; } } if (!f.exists(tmpFilename)) { fileFoundFl = false; return NULL; } ptr = loadFile(tmpFilename); break; } default: break; } // Scan for an entry in the catalogue byte *result; bool matchFlag = false; int offsetVal = 0; while (!matchFlag) { Common::String name = (const char *)ptr + offsetVal; if (name == filename) { // Found entry for file, so get it's details from the catalogue entry const byte *pData = ptr + offsetVal; _catalogPos = READ_LE_UINT32(pData + 15); _catalogSize = READ_LE_UINT32(pData + 19); matchFlag = true; } if (name == "FINIS") { _vm->_globals->freeMemory(ptr); fileFoundFl = false; return NULL; } offsetVal += 23; } _vm->_globals->freeMemory(ptr); if (secondaryFilename != "") { if (!f.open(secondaryFilename)) error("CHARGE_FICHIER"); f.seek(_catalogPos); byte *catData = _vm->_globals->allocMemory(_catalogSize); if (catData == g_PTRNUL) error("CHARGE_FICHIER"); readStream(f, catData, _catalogSize); f.close(); result = catData; } else { result = NULL; } return result; }