void ERFFile::load() { Common::File erf; open(erf); readHeader(erf); if ((_id != kERFID) && (_id != kMODID) && (_id != kHAKID) && (_id != kSAVID)) throw Common::Exception("Not an ERF file"); if ((_version != kVersion1) && (_version != kVersion2)) throw Common::Exception("Unsupported ERF file version %08X", _version); if ((_version == kVersion2) && !_utf16le) throw Common::Exception("ERF file version 2.0, but not UTF-16LE"); try { ERFHeader erfHeader; readERFHeader (erf, erfHeader); readDescription(erf, erfHeader); if (!_noResources) readResources(erf, erfHeader); if (erf.err()) throw Common::Exception(Common::kReadError); } catch (Common::Exception &e) { e.add("Failed reading ERF file"); throw e; } }
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); } } }
void BIFFile::load() { Common::File bif; open(bif); readHeader(bif); if (_id != kBIFID) throw Common::Exception("Not a BIF file"); if ((_version != kVersion1) && (_version != kVersion11)) throw Common::Exception("Unsupported BIF file version %08X", _version); uint32 varResCount = bif.readUint32LE(); uint32 fixResCount = bif.readUint32LE(); if (fixResCount != 0) throw Common::Exception("TODO: Fixed BIF resources"); _iResources.resize(varResCount); uint32 offVarResTable = bif.readUint32LE(); try { readVarResTable(bif, offVarResTable); if (bif.err()) throw Common::Exception(Common::kReadError); } catch (Common::Exception &e) { e.add("Failed reading BIF file"); throw e; } }
void NDSFile::load() { Common::File nds; open(nds); if (!isNDS(nds)) throw Common::Exception("Not a support NDS ROM file"); nds.seek(0x40); uint32 fileNameTableOffset = nds.readUint32LE(); uint32 fileNameTableLength = nds.readUint32LE(); uint32 fatOffset = nds.readUint32LE(); //uint32 fatLength = nds.readUint32LE(); try { readNames(nds, fileNameTableOffset, fileNameTableLength); readFAT(nds, fatOffset); if (nds.err()) throw Common::Exception(Common::kReadError); } catch (Common::Exception &e) { e.add("Failed reading NDS file"); throw; } }
//Gets AGIPAL Data void GfxMgr::setAGIPal(int p0) { //If 0 from savefile, do not use if (p0 == 0) return; char filename[15]; sprintf(filename, "pal.%d", p0); Common::File agipal; if (!agipal.open(filename)) { warning("Couldn't open AGIPAL palette file '%s'. Not changing palette", filename); return; // Needed at least by Naturette 3 which uses AGIPAL but provides no palette files } //Chunk0 holds colors 0-7 agipal.read(&_agipalPalette[0], 24); //Chunk1 is the same as the chunk0 //Chunk2 chunk holds colors 8-15 agipal.seek(24, SEEK_CUR); agipal.read(&_agipalPalette[24], 24); //Chunk3 is the same as the chunk2 //Chunks4-7 are duplicates of chunks0-3 if (agipal.eos() || agipal.err()) { warning("Couldn't read AGIPAL palette from '%s'. Not changing palette", filename); return; } // Use only the lowest 6 bits of each color component (Red, Green and Blue) // because VGA used only 6 bits per color component (i.e. VGA had 18-bit colors). // This should now be identical to the original AGIPAL-hack's behavior. bool validVgaPalette = true; for (int i = 0; i < 16 * 3; i++) { if (_agipalPalette[i] >= (1 << 6)) { _agipalPalette[i] &= 0x3F; // Leave only the lowest 6 bits of each color component validVgaPalette = false; } } if (!validVgaPalette) warning("Invalid AGIPAL palette (Over 6 bits per color component) in '%s'. Using only the lowest 6 bits per color component", filename); _agipalFileNum = p0; initPalette(_agipalPalette); gfxSetPalette(); debug(1, "Using AGIPAL palette from '%s'", filename); }
int AgiEngine::loadWords(const char *fname) { Common::File fp; if (!fp.open(fname)) { warning("loadWords: can't open %s", fname); return errOK; // err_BadFileOpen } debug(0, "Loading dictionary: %s", fname); // Loop through alphabet, as words in the dictionary file are sorted by // first character for (int i = 0; i < 26; i++) { fp.seek(i * 2, SEEK_SET); int offset = fp.readUint16BE(); if (offset == 0) continue; fp.seek(offset, SEEK_SET); int k = fp.readByte(); while (!fp.eos() && !fp.err()) { // Read next word char c, str[64]; do { c = fp.readByte(); str[k++] = (c ^ 0x7F) & 0x7F; } while (!(c & 0x80) && k < (int)sizeof(str) - 1); str[k] = 0; // WORKAROUND: // The SQ0 fan game stores words starting with numbers (like '7up') // in its dictionary under the 'a' entry. We skip these. // See bug #3615061 if (str[0] == 'a' + i) { // And store it in our internal dictionary AgiWord *w = new AgiWord; w->word = myStrndup(str, k); w->id = fp.readUint16BE(); _game.words[i].push_back(w); } k = fp.readByte(); // Are there more words with an already known prefix? // WORKAROUND: We only break after already seeing words with the // right prefix, for the SQ0 words starting with digits filed under // 'a'. See above comment and bug #3615061. if (k == 0 && str[0] >= 'a' + i) break; } } return errOK; }
void ResMan::resOpen(uint32 id) { // load resource ID into memory MemHandle *memHandle = resHandle(id); if (!memHandle) return; if (memHandle->cond == MEM_FREED) { // memory has been freed uint32 size = resLength(id); _memMan->alloc(memHandle, size); Common::File *clusFile = resFile(id); assert(clusFile); clusFile->seek(resOffset(id)); clusFile->read(memHandle->data, size); if (clusFile->err() || clusFile->eos()) { error("Can't read %d bytes from offset %d from cluster file %s\nResource ID: %d (%08X)", size, resOffset(id), _prj.clu[(id >> 24) - 1].label, id, id); }
/** * Allocates enough RAM to hold the global Glitter variables. */ void RegisterGlobals(int num) { if (pGlobals == NULL) { numGlobals = num; hMasterScript = !TinselV2 ? 0 : READ_LE_UINT32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT)); // Allocate RAM for pGlobals and make sure it's allocated pGlobals = (int32 *)calloc(numGlobals, sizeof(int32)); if (pGlobals == NULL) { error("Cannot allocate memory for global data"); } // Allocate RAM for interpret contexts and make sure it's allocated icList = (INT_CONTEXT *)calloc(NUM_INTERPRET, sizeof(INT_CONTEXT)); if (icList == NULL) { error("Cannot allocate memory for interpret contexts"); } g_scheduler->setResourceCallback(FreeInterpretContextPr); } else { // Check size is still the same assert(numGlobals == num); memset(pGlobals, 0, numGlobals * sizeof(int32)); memset(icList, 0, NUM_INTERPRET * sizeof(INT_CONTEXT)); } if (TinselV2) { // read initial values CdCD(nullContext); Common::File f; if (!f.open(GLOBALS_FILENAME)) error(CANNOT_FIND_FILE, GLOBALS_FILENAME); int32 length = f.readSint32LE(); if (length != num) error(FILE_IS_CORRUPT, GLOBALS_FILENAME); for (int i = 0; i < length; ++i) pGlobals[i] = f.readSint32LE(); if (f.eos() || f.err()) error(FILE_IS_CORRUPT, GLOBALS_FILENAME); f.close(); } }
// AUDIOnnn.MAP contains 10-byte entries: // Early format: // w 5 bits resource type and 11 bits resource number // dw 7 bits volume number and 25 bits offset // dw size // Later format: // w nEntry // dw offset+volume (as in resource.map) // dw size // ending with 10 0xFFs int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) { Common::File file; if (!file.open(map->getLocationName())) return SCI_ERROR_RESMAP_NOT_FOUND; bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio; file.seek(0); for (;;) { uint16 n = file.readUint16LE(); uint32 offset = file.readUint32LE(); uint32 size = file.readUint32LE(); if (file.eos() || file.err()) { warning("Error while reading %s", map->getLocationName().c_str()); return SCI_ERROR_RESMAP_NOT_FOUND; } if (n == 0xffff) break; byte volume_nr; if (oldFormat) { n &= 0x07ff; // Mask out resource type volume_nr = offset >> 25; // most significant 7 bits offset &= 0x01ffffff; // least significant 25 bits } else { volume_nr = offset >> 28; // most significant 4 bits offset &= 0x0fffffff; // least significant 28 bits } ResourceSource *src = findVolume(map, volume_nr); if (src) { const ResourceId resId(kResourceTypeAudio, n); if (unload) removeAudioResource(resId); else addResource(resId, src, offset, size, map->getLocationName()); } else { warning("Failed to find audio volume %i", volume_nr); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; } }
/** * Read sound (or music) file data. Call with SILENCE to free-up * any allocated memory. Also returns size of data */ SoundPtr 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 (!_hasReadHeader) { for (int i = 0; i < kMaxSounds; i++) { _soundHdr[i]._size = fp.readUint16LE(); _soundHdr[i]._offset = fp.readUint32LE(); } if (fp.err()) error("Wrong sound file format"); _hasReadHeader = true; } *size = _soundHdr[sound]._size; if (*size == 0) error("Wrong sound file format or missing sound %d", sound); // Allocate memory for sound or music, if possible SoundPtr soundPtr = (byte *)malloc(_soundHdr[sound]._size); // Ptr to sound data assert(soundPtr); // Seek to data and read it fp.seek(_soundHdr[sound]._offset, SEEK_SET); if (fp.read(soundPtr, _soundHdr[sound]._size) != _soundHdr[sound]._size) error("Wrong sound file format"); fp.close(); return soundPtr; }
void NDSFile::load() { Common::File nds; open(nds); if (!readHeader(nds)) throw Common::Exception("Not a valid NDS ROM file"); try { readNames(nds, _fileNameTableOffset, _fileNameTableLength); readFAT(nds, _fatOffset); if (nds.err()) throw Common::Exception(Common::kReadError); } catch (Common::Exception &e) { e.add("Failed reading NDS file"); throw; } }
bool PCMMusicPlayer::getNextChunk() { MusicSegment *musicSegments; int32 *script, *scriptBuffer; int id; int snum; uint32 sampleOffset, sampleLength, sampleCLength; Common::File file; byte *buffer; Common::SeekableReadStream *sampleStream; switch (_state) { case S_NEW: case S_NEXT: _forcePlay = false; script = scriptBuffer = (int32 *)LockMem(_hScript); // Set parameters for this chunk of music id = _scriptNum; while (id--) script = scriptBuffer + READ_32(script); snum = FROM_32(script[_scriptIndex++]); if (snum == MUSIC_JUMP || snum == MUSIC_END) { // Let usual code sort it out! _scriptIndex--; // Undo increment _forcePlay = true; // Force a Play _state = S_END1; // 'Goto' S_END1 break; } musicSegments = (MusicSegment *) LockMem(_hSegment); assert(FROM_32(musicSegments[snum].numChannels) == 1); assert(FROM_32(musicSegments[snum].bitsPerSample) == 16); sampleOffset = FROM_32(musicSegments[snum].sampleOffset); sampleLength = FROM_32(musicSegments[snum].sampleLength); sampleCLength = (((sampleLength + 63) & ~63)*33)/64; if (!file.open(_filename)) error(CANNOT_FIND_FILE, _filename.c_str()); file.seek(sampleOffset); if (file.eos() || file.err() || (uint32)file.pos() != sampleOffset) error(FILE_IS_CORRUPT, _filename.c_str()); buffer = (byte *) malloc(sampleCLength); assert(buffer); // read all of the sample if (file.read(buffer, sampleCLength) != sampleCLength) error(FILE_IS_CORRUPT, _filename.c_str()); debugC(DEBUG_DETAILED, kTinselDebugMusic, "Creating ADPCM music chunk with size %d, " "offset %d (script %d.%d)", sampleCLength, sampleOffset, _scriptNum, _scriptIndex - 1); sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, DisposeAfterUse::YES); delete _curChunk; _curChunk = new Tinsel8_ADPCMStream(sampleStream, DisposeAfterUse::YES, sampleCLength, 22050, 1, 32); _state = S_MID; return true; case S_END1: debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END1 (script %d.%d)", _scriptNum, _scriptIndex); script = scriptBuffer = (int32 *) LockMem(_hScript); id = _scriptNum; while (id--) script = scriptBuffer + READ_32(script); snum = FROM_32(script[_scriptIndex]); if (snum == MUSIC_END) { _state = S_END2; } else { if (snum == MUSIC_JUMP) _scriptIndex = FROM_32(script[_scriptIndex+1]); _state = _forcePlay ? S_NEW : S_NEXT; _forcePlay = false; } return true; case S_END2: debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END2 (script %d.%d)", _scriptNum, _scriptIndex); _silenceSamples = 11025; // Half a second of silence return true; case S_END3: debugC(DEBUG_DETAILED, kTinselDebugMusic, "Music reached state S_END3 (script %d.%d)", _scriptNum, _scriptIndex); stop(); _state = S_IDLE; return false; case S_IDLE: return false; default: break; } return true; }
/** * Opens and inits all MIDI sequence files. */ void OpenMidiFiles() { Common::File midiStream; if (TinselV0) { // The early demo version of DW1 doesn't have MIDI } else if (TinselV2) { // DW2 uses a different music mechanism } else if (TinselV1Mac) { // open MIDI sequence file in binary mode if (!midiStream.open(MIDI_FILE)) error(CANNOT_FIND_FILE, MIDI_FILE); uint32 curTrack = 1; uint32 songLength = 0; int32 fileSize = midiStream.size(); // Init for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++) g_midiOffsets[i] = 0; midiStream.skip(4); // skip file header while (!midiStream.eos() && !midiStream.err() && midiStream.pos() != fileSize) { assert(curTrack < ARRAYSIZE(g_midiOffsets)); g_midiOffsets[curTrack] = midiStream.pos(); //debug("%d: %d", curTrack, g_midiOffsets[curTrack]); songLength = midiStream.readUint32BE(); midiStream.skip(songLength); curTrack++; } midiStream.close(); } else { if (g_midiBuffer.pDat) // already allocated return; // open MIDI sequence file in binary mode if (!midiStream.open(MIDI_FILE)) error(CANNOT_FIND_FILE, MIDI_FILE); // get length of the largest sequence g_midiBuffer.size = midiStream.readUint32LE(); if (midiStream.eos() || midiStream.err()) error(FILE_IS_CORRUPT, MIDI_FILE); if (g_midiBuffer.size) { // allocate a buffer big enough for the largest MIDI sequence if ((g_midiBuffer.pDat = (uint8 *)malloc(g_midiBuffer.size)) != NULL) { // clear out the buffer memset(g_midiBuffer.pDat, 0, g_midiBuffer.size); } } // Now scan through the contents of the MIDI file to find the offset // of each individual track, in order to create a mapping from MIDI // offset to track number, for the enhanced MIDI soundtrack. // The first song is always at position 4. The subsequent ones are // calculated dynamically. uint32 curOffset = 4; uint32 curTrack = 0; uint32 songLength = 0; // Init for (int i = 0; i < ARRAYSIZE(g_midiOffsets); i++) g_midiOffsets[i] = 0; while (!midiStream.eos() && !midiStream.err()) { if (curOffset + (4 * curTrack) >= (uint32)midiStream.size()) break; assert(curTrack < ARRAYSIZE(g_midiOffsets)); g_midiOffsets[curTrack] = curOffset + (4 * curTrack); //debug("%d: %d", curTrack, midiOffsets[curTrack]); songLength = midiStream.readUint32LE(); curOffset += songLength; midiStream.skip(songLength); curTrack++; } midiStream.close(); } }
/** * 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()); }
/** * Opens and inits all MIDI sequence files. */ void OpenMidiFiles() { Common::File midiStream; // Demo version has no midi file // Also, Discworld PSX uses still unsupported psx SEQ format for music... if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2) || TinselV1PSX) return; if (midiBuffer.pDat) // already allocated return; // open MIDI sequence file in binary mode if (!midiStream.open(MIDI_FILE)) error(CANNOT_FIND_FILE, MIDI_FILE); // gen length of the largest sequence midiBuffer.size = midiStream.readUint32LE(); if (midiStream.eos() || midiStream.err()) error(FILE_IS_CORRUPT, MIDI_FILE); if (midiBuffer.size) { // allocate a buffer big enough for the largest MIDI sequence if ((midiBuffer.pDat = (uint8 *)malloc(midiBuffer.size)) != NULL) { // clear out the buffer memset(midiBuffer.pDat, 0, midiBuffer.size); // VMM_lock(midiBuffer.pDat, midiBuffer.size); } else { //mSeqHandle = NULL; } } // Now scan through the contents of the MIDI file to find the offset // of each individual track, in order to create a mapping from MIDI // offset to track number, for the enhanced MIDI soundtrack. // The first song is always at position 4. The subsequent ones are // calculated dynamically. uint32 curOffset = 4; uint32 curTrack = 0; uint32 songLength = 0; // Init for (int i = 0; i < ARRAYSIZE(midiOffsets); i++) midiOffsets[i] = 0; while (!midiStream.eos() && !midiStream.err()) { if (curOffset + (4 * curTrack) >= (uint32)midiStream.size()) break; assert(curTrack < ARRAYSIZE(midiOffsets)); midiOffsets[curTrack] = curOffset + (4 * curTrack); //debug("%d: %d", curTrack, midiOffsets[curTrack]); songLength = midiStream.readUint32LE(); curOffset += songLength; midiStream.skip(songLength); curTrack++; } midiStream.close(); }
bool Sound::startSpeech(uint16 roomNo, uint16 localNo) { if (_cowHeader == NULL) { warning("Sound::startSpeech: COW file isn't open"); return false; } uint32 locIndex = 0xFFFFFFFF; uint32 sampleSize = 0; uint32 index = 0; if (_cowMode == CowPSX) { Common::File file; uint16 i; if (!file.open("speech.lis")) { warning ("Could not open speech.lis"); return false; } for (i = 0; !file.eos() && !file.err(); i++) if (file.readUint16LE() == roomNo) { locIndex = i; break; } file.close(); if (locIndex == 0xFFFFFFFF) { warning ("Could not find room %d in speech.lis", roomNo); return false; } if (!file.open("speech.inf")) { warning ("Could not open speech.inf"); return false; } uint16 numRooms = file.readUint16LE(); // Read number of rooms referenced in this file file.seek(locIndex * 4 + 2); // 4 bytes per room, skip first 2 bytes uint16 numLines = file.readUint16LE(); uint16 roomOffset = file.readUint16LE(); file.seek(2 + numRooms * 4 + roomOffset * 2); // The offset is in terms of uint16's, so multiply by 2. Skip the room indexes too. locIndex = 0xFFFFFFFF; for (i = 0; i < numLines; i++) if (file.readUint16LE() == localNo) { locIndex = i; break; } if (locIndex == 0xFFFFFFFF) { warning ("Could not find local number %d in room %d in speech.inf", roomNo, localNo); return false; } file.close(); index = _cowHeader[(roomOffset + locIndex) * 2]; sampleSize = _cowHeader[(roomOffset + locIndex) * 2 + 1]; } else { locIndex = _cowHeader[roomNo] >> 2; sampleSize = _cowHeader[locIndex + (localNo * 2)]; index = _cowHeader[locIndex + (localNo * 2) - 1]; } debug(6, "startSpeech(%d, %d): locIndex %d, sampleSize %d, index %d", roomNo, localNo, locIndex, sampleSize, index); Audio::AudioStream *stream = 0; if (sampleSize) { uint8 speechVol = (_speechVolR + _speechVolL) / 2; int8 speechPan = (_speechVolR - _speechVolL) / 2; if ((_cowMode == CowWave) || (_cowMode == CowDemo)) { uint32 size; int16 *data = uncompressSpeech(index + _cowHeaderSize, sampleSize, &size); if (data) { stream = Audio::makeRawStream((byte *)data, size, 11025, SPEECH_FLAGS); _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan); } } else if (_cowMode == CowPSX && sampleSize != 0xffffffff) { _cowFile.seek(index * 2048); Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize); assert(tmp); stream = Audio::makeVagStream(tmp); _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan); // with compressed audio, we can't calculate the wave volume. // so default to talking. for (int cnt = 0; cnt < 480; cnt++) _waveVolume[cnt] = true; _waveVolPos = 0; } #ifdef USE_FLAC else if (_cowMode == CowFLAC) { _cowFile.seek(index); Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize); assert(tmp); stream = Audio::makeFLACStream(tmp, DisposeAfterUse::YES); _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan); // with compressed audio, we can't calculate the wave volume. // so default to talking. for (int cnt = 0; cnt < 480; cnt++) _waveVolume[cnt] = true; _waveVolPos = 0; } #endif #ifdef USE_VORBIS else if (_cowMode == CowVorbis) { _cowFile.seek(index); Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize); assert(tmp); stream = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES); _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan); // with compressed audio, we can't calculate the wave volume. // so default to talking. for (int cnt = 0; cnt < 480; cnt++) _waveVolume[cnt] = true; _waveVolPos = 0; } #endif #ifdef USE_MAD else if (_cowMode == CowMP3) { _cowFile.seek(index); Common::SeekableReadStream *tmp = _cowFile.readStream(sampleSize); assert(tmp); stream = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES); _mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream, SOUND_SPEECH_ID, speechVol, speechPan); // with compressed audio, we can't calculate the wave volume. // so default to talking. for (int cnt = 0; cnt < 480; cnt++) _waveVolume[cnt] = true; _waveVolPos = 0; } #endif return true; } else return false; }
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; }
/** * 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; }
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; }
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; }