diff options
Diffstat (limited to 'engines')
49 files changed, 1024 insertions, 912 deletions
diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp index d212f8c2e0..2b4ef7f60a 100644 --- a/engines/agi/cycle.cpp +++ b/engines/agi/cycle.cpp @@ -266,8 +266,8 @@ process_key: } // commented out to close Sarien bug #438872 - if (key) - _game.keypress = key; + //if (key) + // _game.keypress = key; } break; case INPUT_GETSTRING: diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index 5d30eda81d..e1db04ff49 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -408,6 +408,7 @@ bool Menu::keyhandler(int key) { if (d->enabled) { debugC(6, kDebugLevelMenu | kDebugLevelInput, "event %d registered", d->event); _vm->_game.controllerOccured[d->event] = true; + _vm->_menuSelected = true; goto exit_menu; } break; diff --git a/engines/agos/agos.cpp b/engines/agos/agos.cpp index 07b5c12247..ee2ef98c42 100644 --- a/engines/agos/agos.cpp +++ b/engines/agos/agos.cpp @@ -899,7 +899,7 @@ AGOSEngine::~AGOSEngine() { if (_driver) delete _driver; - AudioCD.destroy(); + AudioCD.stop(); for (uint i = 0; i < _itemHeap.size(); i++) { delete[] _itemHeap[i]; diff --git a/engines/cruise/cruise_main.cpp b/engines/cruise/cruise_main.cpp index 94dfc95cb5..aa3283aab7 100644 --- a/engines/cruise/cruise_main.cpp +++ b/engines/cruise/cruise_main.cpp @@ -1738,7 +1738,7 @@ void CruiseEngine::mainLoop(void) { // Delay for the specified amount of time, but still respond to events bool skipEvents = false; - while (currentTick < lastTick + _gameSpeed) { + do { g_system->delayMillis(10); currentTick = g_system->getMillis(); @@ -1749,7 +1749,7 @@ void CruiseEngine::mainLoop(void) { if (_vm->getDebugger()->isAttached()) _vm->getDebugger()->onFrame(); - } + } while (currentTick < lastTick + _gameSpeed); } else { manageEvents(); diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 2e3db3478e..3920f8a56c 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -106,6 +106,7 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam DrasculaEngine::~DrasculaEngine() { delete _rnd; + stopSound(); free(_charMap); free(_itemLocations); diff --git a/engines/gob/inter.h b/engines/gob/inter.h index c3d3a26f47..057f52b360 100644 --- a/engines/gob/inter.h +++ b/engines/gob/inter.h @@ -560,6 +560,7 @@ protected: bool oPlaytoons_checkData(OpFuncParams ¶ms); void oPlaytoons_CD_20_23(); void oPlaytoons_CD_25(); + void oPlaytoons_openItk(); }; } // End of namespace Gob diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp index 285360c613..e224f29734 100644 --- a/engines/gob/inter_playtoons.cpp +++ b/engines/gob/inter_playtoons.cpp @@ -27,6 +27,7 @@ #include "gob/gob.h" #include "gob/inter.h" +#include "gob/helper.h" #include "gob/global.h" #include "gob/util.h" #include "gob/dataio.h" @@ -70,6 +71,7 @@ void Inter_Playtoons::setupOpcodesDraw() { OPCODEDRAW(0x20, oPlaytoons_CD_20_23); OPCODEDRAW(0x23, oPlaytoons_CD_20_23); OPCODEDRAW(0x25, oPlaytoons_CD_25); + OPCODEDRAW(0x85, oPlaytoons_openItk); } void Inter_Playtoons::setupOpcodesFunc() { @@ -137,5 +139,25 @@ void Inter_Playtoons::oPlaytoons_CD_25() { _vm->_game->_script->readVarIndex(); } +void Inter_Playtoons::oPlaytoons_openItk() { + char fileName[128]; + char *backSlash; + + _vm->_game->_script->evalExpr(0); + strncpy0(fileName, _vm->_game->_script->getResultStr(), 124); + + if (!strchr(fileName, '.')) + strcat(fileName, ".ITK"); + + // Workaround for Bambou : In the script, the path is hardcoded (!!) + if ((backSlash = strrchr(fileName, '\\'))) { + debugC(2, kDebugFileIO, "Opening ITK file \"%s\" instead of \"%s\"", backSlash + 1, fileName); + _vm->_dataIO->openDataFile(backSlash + 1, true); + } else + _vm->_dataIO->openDataFile(fileName, true); + // All the other checks are meant to verify (if not found at the first try) + // if the file is present on the CD or not. As everything is supposed to + // be copied, those checks are skipped +} } // End of namespace Gob diff --git a/engines/gob/sound/cdrom.cpp b/engines/gob/sound/cdrom.cpp index 4d6a7ec966..68cbb1b9e2 100644 --- a/engines/gob/sound/cdrom.cpp +++ b/engines/gob/sound/cdrom.cpp @@ -46,6 +46,7 @@ CDROM::CDROM() { } CDROM::~CDROM() { + stop(); } void CDROM::readLIC(DataStream &stream) { diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp index 797290a6f3..a92beee17e 100644 --- a/engines/groovie/music.cpp +++ b/engines/groovie/music.cpp @@ -38,6 +38,10 @@ MusicPlayer::MusicPlayer(GroovieEngine *vm) : _prevCDtrack(0), _backgroundDelay(0) { } +MusicPlayer::~MusicPlayer() { + AudioCD.stop(); +} + void MusicPlayer::playSong(uint32 fileref) { Common::StackLock lock(_mutex); diff --git a/engines/groovie/music.h b/engines/groovie/music.h index 9909c8a185..fb1ddfe9c3 100644 --- a/engines/groovie/music.h +++ b/engines/groovie/music.h @@ -37,7 +37,7 @@ namespace Groovie { class MusicPlayer { public: MusicPlayer(GroovieEngine *vm); - virtual ~MusicPlayer() {} + virtual ~MusicPlayer(); void playSong(uint32 fileref); void setBackgroundSong(uint32 fileref); diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 73a3e675e8..f5570acd72 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -1253,8 +1253,7 @@ void Screen::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int } if (flags & 0x200) { - ++_drawShapeVar1; - _drawShapeVar1 &= (_vm->gameFlags().gameID == GI_KYRA1) ? 0x7 : 0xF; + _drawShapeVar1 = (_drawShapeVar1 + 1) & 0x7; _drawShapeVar3 = drawShapeVar2[_drawShapeVar1]; _drawShapeVar4 = 0; _drawShapeVar5 = 256; diff --git a/engines/kyra/sequences_lok.cpp b/engines/kyra/sequences_lok.cpp index 35f434698b..d2ef351767 100644 --- a/engines/kyra/sequences_lok.cpp +++ b/engines/kyra/sequences_lok.cpp @@ -1134,6 +1134,11 @@ void KyraEngine_LoK::seq_playEnding() { _seqPlayerFlag = false; _screen->showMouse(); + + // To avoid any remaining input events, we remove the queue + // over here. + _eventList.clear(); + if (_flags.platform == Common::kPlatformAmiga) { _screen->_charWidth = -2; _screen->setCurPage(2); @@ -1354,15 +1359,12 @@ void KyraEngine_LoK::seq_playCreditsAmiga() { } int size = 0; - const uint8 *bufferTmp = _staticres->loadRawData(k1CreditsStrings, size); - char *buffer = new char[size]; - assert(buffer); - memcpy(buffer, bufferTmp, size); + const char *creditsData = (const char *)_staticres->loadRawData(k1CreditsStrings, size); char stringBuffer[81]; memset(stringBuffer, 0, sizeof(stringBuffer)); - char *cur = buffer; + const char *cur = creditsData; char *specialString = stringBuffer; bool fillRectFlag = false, subWidth = false, centerFlag = false; x = 0; @@ -1424,9 +1426,7 @@ void KyraEngine_LoK::seq_playCreditsAmiga() { removeInputTop(); break; } - } while (++cur != buffer + size && !shouldQuit()); - - delete[] buffer; + } while (++cur != (creditsData + size) && !shouldQuit()); } bool KyraEngine_LoK::seq_skipSequence() const { diff --git a/engines/lure/res.cpp b/engines/lure/res.cpp index 7eb76cad32..e921b93384 100644 --- a/engines/lure/res.cpp +++ b/engines/lure/res.cpp @@ -550,6 +550,7 @@ void Resources::setTalkingCharacter(uint16 id) { uint16 englishLoadOffsets[] = {0x3afe, 0x41BD, 0x7167, 0x7172, 0x8617, 0x88ac, 0}; Hotspot *Resources::activateHotspot(uint16 hotspotId) { + Resources &resources = Resources::getReference(); HotspotData *res = getHotspot(hotspotId); if (!res) return NULL; res->roomNumber &= 0x7fff; // clear any suppression bit in room # @@ -561,7 +562,6 @@ Hotspot *Resources::activateHotspot(uint16 hotspotId) { // If it's NPC with a schedule, then activate the schedule if ((res->npcScheduleId != 0) && (res->npcSchedule.isEmpty())) { - Resources &resources = Resources::getReference(); CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId); res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber); } @@ -621,9 +621,12 @@ Hotspot *Resources::activateHotspot(uint16 hotspotId) { // Special post-load handling if (res->loadOffset == 3) hotspot->setPersistant(true); if (res->loadOffset == 5) hotspot->handleTalkDialog(); - if (hotspotId == CASTLE_SKORL_ID) + if (hotspotId == CASTLE_SKORL_ID) { // The Castle skorl has a default room #99, so it needs to be adjusted dynamically - res->npcSchedule.top().setRoomNumber(res->roomNumber); + res->npcSchedule.clear(); + CharacterScheduleEntry *entry = resources.charSchedules().getEntry(res->npcScheduleId); + res->npcSchedule.addFront(DISPATCH_ACTION, entry, res->roomNumber); + } // TODO: Figure out why there's a room set in the animation decode for a range of characters, // particularly since it doesn't seem to match what happens in-game diff --git a/engines/made/made.cpp b/engines/made/made.cpp index c83f7aaf02..e826e3788a 100644 --- a/engines/made/made.cpp +++ b/engines/made/made.cpp @@ -127,6 +127,8 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng } MadeEngine::~MadeEngine() { + AudioCD.stop(); + delete _rnd; delete _pmvPlayer; delete _res; diff --git a/engines/made/pmvplayer.cpp b/engines/made/pmvplayer.cpp index 9796b01dfc..a6ee649eda 100644 --- a/engines/made/pmvplayer.cpp +++ b/engines/made/pmvplayer.cpp @@ -62,12 +62,8 @@ bool PmvPlayer::play(const char *filename) { } uint frameDelay = _fd->readUint16LE(); - int unk; _fd->skip(4); // always 0? - unk = _fd->readByte(); - debug(2, "%i", unk); - unk = _fd->readByte(); - debug(2, "%i", unk); + uint frameCount = _fd->readUint16LE(); _fd->skip(4); // always 0? uint soundFreq = _fd->readUint16LE(); @@ -80,7 +76,7 @@ bool PmvPlayer::play(const char *filename) { if (soundFreq == 22254) soundFreq = 22050; for (int i = 0; i < 22; i++) { - unk = _fd->readUint16LE(); + int unk = _fd->readUint16LE(); debug(2, "%i ", unk); } @@ -90,7 +86,7 @@ bool PmvPlayer::play(const char *filename) { _fd->read(_paletteRGB, 768); _vm->_screen->setRGBPalette(_paletteRGB); - uint32 frameCount = 0; + uint32 frameNumber = 0; uint16 chunkCount = 0; uint32 soundSize = 0; uint32 soundChunkOfs = 0, palChunkOfs = 0; @@ -108,7 +104,7 @@ bool PmvPlayer::play(const char *filename) { // get it to work well? _audioStream = Audio::makeAppendableAudioStream(soundFreq, Audio::Mixer::FLAG_UNSIGNED); - while (!_vm->shouldQuit() && !_aborted && !_fd->eos()) { + while (!_vm->shouldQuit() && !_aborted && !_fd->eos() && frameNumber < frameCount) { int32 frameTime = _vm->_system->getMillis(); @@ -117,9 +113,6 @@ bool PmvPlayer::play(const char *filename) { warning("Unknown chunk type"); } - if (_fd->eos()) - break; - // Only reallocate the frame data buffer if its size has changed if (prevChunkSize != chunkSize || !frameData) { if (frameData) @@ -192,7 +185,7 @@ bool PmvPlayer::play(const char *filename) { updateScreen(); if (skipFrames == 0) { - int32 waitTime = (frameCount * frameDelay) - + int32 waitTime = (frameNumber * frameDelay) - (g_system->getMillis() - soundStartTime) - (_vm->_system->getMillis() - frameTime); if (waitTime < 0) { @@ -204,7 +197,7 @@ bool PmvPlayer::play(const char *filename) { } else skipFrames--; - frameCount++; + frameNumber++; } diff --git a/engines/made/screen.cpp b/engines/made/screen.cpp index d55b663296..7471743ba4 100644 --- a/engines/made/screen.cpp +++ b/engines/made/screen.cpp @@ -188,7 +188,7 @@ void Screen::drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 f if (flipX) { linePtrAdd = -1; - sourceAdd = sourceSurface->w; + sourceAdd = sourceSurface->w - 1; } else { linePtrAdd = 1; sourceAdd = 0; @@ -210,6 +210,7 @@ void Screen::drawSurface(Graphics::Surface *sourceSurface, int x, int y, int16 f } linePtr += linePtrAdd; } + source += sourcePitch; dest += clipInfo.destSurface->pitch; if (_vm->getGameID() != GID_RTZ) diff --git a/engines/saga/actor.h b/engines/saga/actor.h index d998c65240..57d06e9e3a 100644 --- a/engines/saga/actor.h +++ b/engines/saga/actor.h @@ -183,8 +183,8 @@ enum DragonMoveTypes { struct PathDirectionData { int8 direction; - int x; - int y; + int16 x; + int16 y; }; struct ActorFrameRange { diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp index 27566de2c3..8ca946a127 100644 --- a/engines/saga/music.cpp +++ b/engines/saga/music.cpp @@ -41,190 +41,6 @@ namespace Saga { #define BUFFER_SIZE 4096 #define MUSIC_SUNSPOT 26 -class DigitalMusicInputStream : public Audio::AudioStream { -private: - Audio::AudioStream *_compressedStream; - ResourceContext *_context; - ResourceData * resourceData; - GameSoundTypes soundType; - Common::File *_file; - uint32 _filePos; - uint32 _startPos; - uint32 _endPos; - bool _finished; - bool _looping; - int16 _buf[BUFFER_SIZE]; - const int16 *_bufferEnd; - const int16 *_pos; - MemoryReadStream *_memoryStream; - SagaEngine *_vm; - - void refill(); - bool eosIntern() const { - return _pos >= _bufferEnd; - } - -public: - DigitalMusicInputStream(SagaEngine *vm, ResourceContext *context, uint32 resourceId, bool looping, uint32 loopStart); - ~DigitalMusicInputStream(); - - void createCompressedStream(); - - int readBuffer(int16 *buffer, const int numSamples); - - bool endOfData() const { return eosIntern(); } - bool isStereo() const { - // The digital music in the ITE Mac demo version is not stereo - return _vm->getFeatures() & GF_MONO_MUSIC ? false : true; - } - int getRate() const { return 11025; } -}; - -DigitalMusicInputStream::DigitalMusicInputStream(SagaEngine *vm, ResourceContext *context, uint32 resourceId, bool looping, uint32 loopStart) - : _vm(vm), _context(context), _finished(false), _looping(looping), _bufferEnd(_buf + BUFFER_SIZE) { - - byte compressedHeader[10]; - - resourceData = context->getResourceData(resourceId); - _file = context->getFile(resourceData); - - _compressedStream = NULL; - - if (context->isCompressed) { - // Read compressed header to determine compression type - _file->seek((long)resourceData->offset, SEEK_SET); - _file->read(compressedHeader, 9); - - if (compressedHeader[0] == char(0)) { - soundType = kSoundMP3; - } else if (compressedHeader[0] == char(1)) { - soundType = kSoundOGG; - } else if (compressedHeader[0] == char(2)) { - soundType = kSoundFLAC; - } - - createCompressedStream(); - } - - // Determine the end position - _filePos = resourceData->offset; - _endPos = _filePos + resourceData->size; - - if (_compressedStream != NULL) { - _filePos += 9; // skip compressed header - _endPos -= 9; // decrease size by the size of the compressed header - } - - _startPos = _filePos + loopStart; - if (_startPos >= _endPos) - _startPos = _filePos; - - // Read in initial data - refill(); -} - -DigitalMusicInputStream::~DigitalMusicInputStream() { - delete _compressedStream; -} - -void DigitalMusicInputStream::createCompressedStream() { -#if defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC) - uint numLoops = _looping ? 0 : 1; -#endif - _memoryStream = _file->readStream(resourceData->size - 9); - - switch (soundType) { -#ifdef USE_MAD - case kSoundMP3: - debug(1, "Playing MP3 compressed digital music"); - _compressedStream = Audio::makeMP3Stream(_memoryStream, true, 0, 0, numLoops); - break; -#endif -#ifdef USE_VORBIS - case kSoundOGG: - debug(1, "Playing OGG compressed digital music"); - _compressedStream = Audio::makeVorbisStream(_memoryStream, true, 0, 0, numLoops); - break; -#endif -#ifdef USE_FLAC - case kSoundFLAC: - debug(1, "Playing FLAC compressed digital music"); - _compressedStream = Audio::makeFlacStream(_memoryStream, true, 0, 0, numLoops); - break; -#endif - default: - // Unknown compression - error("Trying to play compressed digital music, but the compression is not known"); - break; - } -} - -int DigitalMusicInputStream::readBuffer(int16 *buffer, const int numSamples) { - if (_compressedStream != NULL) - return _compressedStream->readBuffer(buffer, numSamples); - - int samples = 0; - int len = 0; - - while (samples < numSamples && !eosIntern()) { - len = MIN(numSamples - samples, (int) (_bufferEnd - _pos)); - memcpy(buffer, _pos, len * 2); - buffer += len; - _pos += len; - samples += len; - if (_pos >= _bufferEnd) - refill(); - } - return samples; -} - -void DigitalMusicInputStream::refill() { - if (_finished) - return; - - uint32 lengthLeft; - byte *ptr = (byte *) _buf; - - _file->seek(_filePos, SEEK_SET); - - if (_looping) - lengthLeft = 2 * BUFFER_SIZE; - else - lengthLeft = MIN((uint32) (2 * BUFFER_SIZE), _endPos - _filePos); - - while (lengthLeft > 0) { - uint32 len = _file->read(ptr, MIN(lengthLeft, _endPos - _file->pos())); - - if (len & 1) - len--; - -#ifdef SCUMM_BIG_ENDIAN - if (!_context->isBigEndian) { -#else - if (_context->isBigEndian) { -#endif - uint16 *ptr16 = (uint16 *)ptr; - for (uint32 i = 0; i < (len / 2); i++) - ptr16[i] = SWAP_BYTES_16(ptr16[i]); - } - - lengthLeft -= len; - ptr += len; - - if (lengthLeft > 0) - _file->seek(_startPos); - } - - _filePos = _file->pos(); - _pos = _buf; - _bufferEnd = (int16 *)ptr; - - if (!_looping && _filePos >= _endPos) { - _finished = true; - } -} - - MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) { memset(_channel, 0, sizeof(_channel)); _masterVolume = 0; @@ -461,9 +277,7 @@ void Music::play(uint32 resourceId, MusicFlags flags) { sprintf(trackName[1], "track%02d", realTrackNumber); Audio::AudioStream *stream = 0; for (int i = 0; i < 2; ++i) { - // We multiply by 40 / 3 = 1000 / 75 to convert frames to milliseconds - // FIXME: Do we really want a duration of 10000 frames = 133 seconds, or is that just a random value? - stream = Audio::AudioStream::openStreamFile(trackName[i], 0, 10000 * 40 / 3, (flags == MUSIC_LOOP) ? 0 : 1); + stream = Audio::AudioStream::openStreamFile(trackName[i], 0, 0, (flags == MUSIC_LOOP) ? 0 : 1); if (stream) { _mixer->playInputStream(Audio::Mixer::kMusicSoundType, &_musicHandle, stream); _digitalMusic = true; @@ -474,15 +288,48 @@ void Music::play(uint32 resourceId, MusicFlags flags) { if (_vm->getGameId() == GID_ITE) { if (resourceId >= 9 && resourceId <= 34) { if (_digitalMusicContext != NULL) { - //TODO: check resource size loopStart = 0; - // fix ITE sunstatm/sunspot score - if ((_vm->getGameId() == GID_ITE) && (resourceId == MUSIC_SUNSPOT)) { + // Fix ITE sunstatm/sunspot score + if (resourceId == MUSIC_SUNSPOT) loopStart = 4 * 18727; - } - // digital music - audioStream = new DigitalMusicInputStream(_vm, _digitalMusicContext, resourceId - 9, flags == MUSIC_LOOP, loopStart); + // Digital music + ResourceData *resData = _digitalMusicContext->getResourceData(resourceId - 9); + Common::File *musicFile = _digitalMusicContext->getFile(resData); + int offs = (_digitalMusicContext->isCompressed) ? 9 : 0; + + Common::SeekableSubReadStream *musicStream = new Common::SeekableSubReadStream(musicFile, + (uint32)resData->offset + offs, (uint32)resData->offset + resData->size - offs); + + if (!_digitalMusicContext->isCompressed) { + byte musicFlags = Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_STEREO | + Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN; + if (flags == MUSIC_LOOP) + musicFlags |= Audio::Mixer::FLAG_LOOP; + + Audio::LinearDiskStreamAudioBlock audioBlocks[1]; + audioBlocks[0].pos = 0; + audioBlocks[0].len = resData->size / 2; // 16-bit sound + audioStream = Audio::makeLinearDiskStream(musicStream, audioBlocks, 1, 11025, musicFlags, false, loopStart, 0); + } else { + // Read compressed header to determine compression type + musicFile->seek((uint32)resData->offset, SEEK_SET); + byte identifier = musicFile->readByte(); + + if (identifier == 0) { // MP3 +#ifdef USE_MAD + audioStream = Audio::makeMP3Stream(musicStream, false, 0, 0, (flags == MUSIC_LOOP ? 0 : 1)); +#endif + } else if (identifier == 1) { // OGG +#ifdef USE_VORBIS + audioStream = Audio::makeVorbisStream(musicStream, false, 0, 0, (flags == MUSIC_LOOP ? 0 : 1)); +#endif + } else if (identifier == 2) { // FLAC +#ifdef USE_FLAC + audioStream = Audio::makeFlacStream(musicStream, false, 0, 0, (flags == MUSIC_LOOP ? 0 : 1)); +#endif + } + } } } } diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp index b2744482bd..eddd35300e 100644 --- a/engines/saga/sndres.cpp +++ b/engines/saga/sndres.cpp @@ -192,7 +192,6 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff GameSoundTypes resourceType = kSoundPCM; byte *data = 0; int rate = 0, size = 0; - byte flags = 0; Common::File* file; if (resourceId == (uint32)-1) { @@ -269,21 +268,17 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff } - // Default sound type is 16-bit PCM (used in ITE) - buffer.isBigEndian = context->isBigEndian; - if ((context->fileType & GAME_VOICEFILE) && (_vm->getFeatures() & GF_LE_VOICES)) - buffer.isBigEndian = false; + // Default sound type is 16-bit signed PCM, used in ITE by PCM and VOX files buffer.isCompressed = context->isCompressed; buffer.soundType = resourceType; buffer.originalSize = 0; - buffer.stereo = false; - buffer.isSigned = true; // default for PCM and VOX - buffer.frequency = 22050; // default for PCM and VOX - buffer.sampleBits = 16; // default for PCM and VOX + // Set default flags and frequency for PCM, VOC and VOX files, which got no header + buffer.flags = Audio::Mixer::FLAG_16BITS; + buffer.frequency = 22050; if (_vm->getGameId() == GID_ITE) { if (_vm->getFeatures() & GF_8BIT_UNSIGNED_PCM) { // older ITE demos - buffer.isSigned = false; - buffer.sampleBits = 8; + buffer.flags |= Audio::Mixer::FLAG_UNSIGNED; + buffer.flags &= ~Audio::Mixer::FLAG_16BITS; } else { // Voice files in newer ITE demo versions are OKI ADPCM (VOX) encoded if (!scumm_stricmp(context->fileName, "voicesd.rsc")) @@ -292,6 +287,12 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff } buffer.buffer = NULL; + // Check for LE sounds + if (!context->isBigEndian) + buffer.flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; + if ((context->fileType & GAME_VOICEFILE) && (_vm->getFeatures() & GF_LE_VOICES)) + buffer.flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; + // Older Mac versions of ITE were Macbinary packed int soundOffset = (context->fileType & GAME_MACBINARY) ? 36 : 0; @@ -321,25 +322,23 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff case kSoundShorten: case kSoundVOC: if (resourceType == kSoundWAV) { - result = Audio::loadWAVFromStream(readS, size, rate, flags); + result = Audio::loadWAVFromStream(readS, size, rate, buffer.flags); } else if (resourceType == kSoundAIFF) { - result = Audio::loadAIFFFromStream(readS, size, rate, flags); + result = Audio::loadAIFFFromStream(readS, size, rate, buffer.flags); +#ifdef ENABLE_SAGA2 + } else if (resourceType == kSoundShorten) { + result = Audio::loadShortenFromStream(readS, size, rate, buffer.flags); +#endif } else if (resourceType == kSoundVOC) { data = Audio::loadVOCFromStream(readS, size, rate); result = (data != 0); if (onlyHeader) free(data); -#ifdef ENABLE_SAGA2 - } else if (resourceType == kSoundShorten) { - result = Audio::loadShortenFromStream(readS, size, rate, flags); -#endif + buffer.flags |= Audio::Mixer::FLAG_UNSIGNED; } if (result) { buffer.frequency = rate; - buffer.sampleBits = (flags & Audio::Mixer::FLAG_16BITS) ? 16 : 8; - buffer.stereo = flags & Audio::Mixer::FLAG_STEREO; - buffer.isSigned = (resourceType == kSoundVOC) ? false : !(flags & Audio::Mixer::FLAG_UNSIGNED); buffer.size = size; if (!onlyHeader && resourceType != kSoundVOC) { @@ -360,14 +359,20 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff readS.readByte(); // Skip compression identifier byte buffer.frequency = readS.readUint16LE(); buffer.originalSize = readS.readUint32LE(); - buffer.sampleBits = readS.readByte(); - buffer.stereo = (readS.readByte() == char(0)) ? false : true; + if (readS.readByte() == 8) // read sample bits + buffer.flags &= ~Audio::Mixer::FLAG_16BITS; + if (readS.readByte() != 0) // read stereo flag + buffer.flags |= Audio::Mixer::FLAG_STEREO; buffer.size = soundResourceLength; buffer.soundType = resourceType; - buffer.soundFile = context->getFile(resourceData); buffer.fileOffset = resourceData->offset + 9; // skip compressed sfx header: byte + uint16 + uint32 + byte + byte + if (!onlyHeader) { + buffer.buffer = (byte *)malloc(buffer.size); + readS.read(buffer.buffer, buffer.size); + } + result = true; break; default: @@ -403,12 +408,12 @@ int SndRes::getVoiceLength(uint32 resourceId) { msDouble = (double)buffer.size; else msDouble = (double)buffer.originalSize; - if (buffer.sampleBits == 16) { + + if (buffer.flags & Audio::Mixer::FLAG_16BITS) msDouble /= 2.0; - } - if (buffer.stereo) { + + if (buffer.flags & Audio::Mixer::FLAG_STEREO) msDouble /= 2.0; - } msDouble = msDouble / buffer.frequency * 1000.0; return (int)msDouble; diff --git a/engines/saga/sound.cpp b/engines/saga/sound.cpp index fb7acaca8c..14e5492a48 100644 --- a/engines/saga/sound.cpp +++ b/engines/saga/sound.cpp @@ -65,84 +65,45 @@ SndHandle *Sound::getHandle() { void Sound::playSoundBuffer(Audio::SoundHandle *handle, SoundBuffer &buffer, int volume, sndHandleType handleType, bool loop) { - byte flags; - flags = Audio::Mixer::FLAG_AUTOFREE; + buffer.flags |= Audio::Mixer::FLAG_AUTOFREE; if (loop) - flags |= Audio::Mixer::FLAG_LOOP; + buffer.flags |= Audio::Mixer::FLAG_LOOP; - if (buffer.sampleBits == 16) { - flags |= Audio::Mixer::FLAG_16BITS; - - if (!buffer.isBigEndian) - flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN; - } - if (buffer.stereo) - flags |= Audio::Mixer::FLAG_STEREO; - if (!buffer.isSigned) - flags |= Audio::Mixer::FLAG_UNSIGNED; + Audio::Mixer::SoundType soundType = (handleType == kVoiceHandle) ? + Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType; if (!buffer.isCompressed) { - if (handleType == kVoiceHandle) - _mixer->playRaw(Audio::Mixer::kSpeechSoundType, handle, buffer.buffer, - buffer.size, buffer.frequency, flags, -1, volume); - else - _mixer->playRaw(Audio::Mixer::kSFXSoundType, handle, buffer.buffer, - buffer.size, buffer.frequency, flags, -1, volume); + _mixer->playRaw(soundType, handle, buffer.buffer, + buffer.size, buffer.frequency, buffer.flags, -1, volume); } else { - Audio::AudioStream *stream = NULL; -#if defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC) - MemoryReadStream *tmp = NULL; -#endif + Audio::AudioStream *stream = 0; switch (buffer.soundType) { #ifdef USE_MAD case kSoundMP3: - debug(1, "Playing MP3 compressed sound"); - buffer.soundFile->seek((long)buffer.fileOffset, SEEK_SET); - tmp = buffer.soundFile->readStream(buffer.size); - assert(tmp); - stream = Audio::makeMP3Stream(tmp, true); + stream = Audio::makeMP3Stream(new Common::MemoryReadStream(buffer.buffer, buffer.size, true), true); break; #endif #ifdef USE_VORBIS case kSoundOGG: - debug(1, "Playing OGG compressed sound"); - buffer.soundFile->seek((long)buffer.fileOffset, SEEK_SET); - tmp = buffer.soundFile->readStream(buffer.size); - assert(tmp); - stream = Audio::makeVorbisStream(tmp, true); + stream = Audio::makeVorbisStream(new Common::MemoryReadStream(buffer.buffer, buffer.size, true), true); break; #endif #ifdef USE_FLAC case kSoundFLAC: - debug(1, "Playing FLAC compressed sound"); - buffer.soundFile->seek((long)buffer.fileOffset, SEEK_SET); - tmp = buffer.soundFile->readStream(buffer.size); - assert(tmp); - stream = Audio::makeFlacStream(tmp, true); + stream = Audio::makeFlacStream(new Common::MemoryReadStream(buffer.buffer, buffer.size, true), true); break; #endif default: - // No compression, play it as raw sound - if (handleType == kVoiceHandle) - _mixer->playRaw(Audio::Mixer::kSpeechSoundType, handle, buffer.buffer, - buffer.size, buffer.frequency, flags, -1, volume); - else - _mixer->playRaw(Audio::Mixer::kSFXSoundType, handle, buffer.buffer, - buffer.size, buffer.frequency, flags, -1, volume); + // Unknown compression, ignore sample + warning("Unknown compression, ignoring sound"); break; } - if (stream != NULL) { - if (handleType == kVoiceHandle) - _mixer->playInputStream(Audio::Mixer::kSpeechSoundType, handle, stream, -1, - volume, 0, true, false); - else - _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, stream, -1, - volume, 0, true, false); - } + if (stream != NULL) + _mixer->playInputStream(soundType, handle, stream, -1, volume, 0, true, false); } } diff --git a/engines/saga/sound.h b/engines/saga/sound.h index b61cbbbe90..0aeb54a55f 100644 --- a/engines/saga/sound.h +++ b/engines/saga/sound.h @@ -44,17 +44,13 @@ enum SOUND_FLAGS { struct SoundBuffer { uint16 frequency; - int sampleBits; - bool stereo; - bool isSigned; bool isCompressed; + byte flags; byte *buffer; size_t size; size_t originalSize; - bool isBigEndian; GameSoundTypes soundType; - Common::File *soundFile; size_t fileOffset; }; diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 35ca688233..7b329864ce 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -889,10 +889,11 @@ bool Console::cmdRestartGame(int argc, const char **argv) { bool Console::cmdClassTable(int argc, const char **argv) { DebugPrintf("Available classes:\n"); - for (uint i = 0; i < _vm->_gamestate->_classtable.size(); i++) { - if (_vm->_gamestate->_classtable[i].reg.segment) { + for (uint i = 0; i < _vm->_gamestate->seg_manager->_classtable.size(); i++) { + if (_vm->_gamestate->seg_manager->_classtable[i].reg.segment) { DebugPrintf(" Class 0x%x at %04x:%04x (script 0x%x)\n", i, - PRINT_REG(_vm->_gamestate->_classtable[i].reg), _vm->_gamestate->_classtable[i].script); + PRINT_REG(_vm->_gamestate->seg_manager->_classtable[i].reg), + _vm->_gamestate->seg_manager->_classtable[i].script); } } @@ -1394,10 +1395,11 @@ bool Console::segmentInfo(int nr) { for (uint i = 0; i < scr->_objects.size(); i++) { DebugPrintf(" "); // Object header - Object *obj = obj_get(_vm->_gamestate, scr->_objects[i].pos); + Object *obj = obj_get(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, scr->_objects[i].pos); if (obj) DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(scr->_objects[i].pos), - obj_get_name(_vm->_gamestate, scr->_objects[i].pos), obj->_variables.size(), obj->methods_nr); + obj_get_name(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, + scr->_objects[i].pos), obj->_variables.size(), obj->methods_nr); } } break; @@ -1438,12 +1440,13 @@ bool Console::segmentInfo(int nr) { reg_t objpos; objpos.offset = i; objpos.segment = nr; - DebugPrintf(" [%04x] %s; copy of ", i, obj_get_name(_vm->_gamestate, objpos)); + DebugPrintf(" [%04x] %s; copy of ", i, obj_get_name(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, objpos)); // Object header - Object *obj = obj_get(_vm->_gamestate, ct->_table[i].pos); + Object *obj = obj_get(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, ct->_table[i].pos); if (obj) DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].pos), - obj_get_name(_vm->_gamestate, ct->_table[i].pos), obj->_variables.size(), obj->methods_nr); + obj_get_name(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, ct->_table[i].pos), + obj->_variables.size(), obj->methods_nr); } } break; @@ -2045,7 +2048,7 @@ bool Console::cmdBacktrace(int argc, const char **argv) { for (iter = _vm->_gamestate->_executionStack.begin(); iter != _vm->_gamestate->_executionStack.end(); ++iter, ++i) { ExecStack &call = *iter; - const char *objname = obj_get_name(_vm->_gamestate, call.sendp); + const char *objname = obj_get_name(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, call.sendp); int paramc, totalparamc; switch (call.type) { @@ -2187,7 +2190,7 @@ bool Console::cmdDissassemble(int argc, const char **argv) { return true; } - Object *obj = obj_get(_vm->_gamestate, objAddr); + Object *obj = obj_get(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, objAddr); int selector_id = _vm->getKernel()->findSelector(argv[2]); reg_t addr; @@ -2295,7 +2298,7 @@ bool Console::cmdSend(int argc, const char **argv) { return true; } - o = obj_get(_vm->_gamestate, object); + o = obj_get(_vm->_gamestate->seg_manager, _vm->_gamestate->_version, object); if (o == NULL) { DebugPrintf("Address \"%04x:%04x\" is not an object\n", PRINT_REG(object)); return true; @@ -2900,7 +2903,7 @@ int parse_reg_t(EngineState *s, const char *str, reg_t *dest) { // Returns 0 on } if (valid) { - const char *objname = obj_get_name(s, objpos); + const char *objname = obj_get_name(s->seg_manager, s->_version, objpos); if (!strcmp(objname, str_objname)) { // Found a match! if ((index < 0) && (times_found > 0)) { @@ -3042,9 +3045,10 @@ int Console::printNode(reg_t addr) { int Console::printObject(reg_t pos) { EngineState *s = _vm->_gamestate; // for the several defines in this function - Object *obj = obj_get(s, pos); + Object *obj = obj_get(s->seg_manager, s->_version, pos); Object *var_container = obj; int i; + SciVersion version = s->_version; // for the selector defines if (!obj) { DebugPrintf("[%04x:%04x]: Not an object.", PRINT_REG(pos)); @@ -3052,11 +3056,11 @@ int Console::printObject(reg_t pos) { } // Object header - DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), obj_get_name(s, pos), + DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), obj_get_name(s->seg_manager, s->_version, pos), obj->_variables.size(), obj->methods_nr); if (!(obj->_variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS)) - var_container = obj_get(s, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]); + var_container = obj_get(s->seg_manager, s->_version, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]); DebugPrintf(" -- member variables:\n"); for (i = 0; (uint)i < obj->_variables.size(); i++) { printf(" "); @@ -3068,9 +3072,9 @@ int Console::printObject(reg_t pos) { reg_t val = obj->_variables[i]; DebugPrintf("%04x:%04x", PRINT_REG(val)); - Object *ref = obj_get(s, val); + Object *ref = obj_get(s->seg_manager, s->_version, val); if (ref) - DebugPrintf(" (%s)", obj_get_name(s, val)); + DebugPrintf(" (%s)", obj_get_name(s->seg_manager, s->_version, val)); DebugPrintf("\n"); } diff --git a/engines/sci/console.h b/engines/sci/console.h index 8d1299dd1e..032a2ff865 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -29,6 +29,7 @@ #define SCI_CONSOLE_H #include "gui/debugger.h" +#include "sci/engine/vm.h" namespace Sci { diff --git a/engines/sci/debug.h b/engines/sci/debug.h index a3c4fab372..e3bca1f0c1 100644 --- a/engines/sci/debug.h +++ b/engines/sci/debug.h @@ -26,6 +26,9 @@ #ifndef SCI_DEBUG_H #define SCI_DEBUG_H +#include "sci/engine/vm_types.h" // for StackPtr +#include "sci/engine/vm.h" // for ExecStack + namespace Sci { enum DebugSeeking { diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 3df5bdb63a..ee9fd5fb18 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -27,7 +27,9 @@ #include "base/plugins.h" #include "sci/sci.h" +#include "sci/engine/kernel.h" #include "sci/exereader.h" +#include "sci/engine/seg_manager.h" namespace Sci { @@ -3031,6 +3033,76 @@ public: const ADGameDescription *fallbackDetect(const Common::FSList &fslist) const; }; +Common::String convertSierraGameId(Common::String sierraId) { + // TODO: SCI32 IDs + + // TODO: astrochicken + // TODO: The internal id of christmas1998 is "demo" + if (sierraId == "card") + return "christmas1990"; + // TODO: christmas1992 + if (sierraId == "arthur") + return "camelot"; + if (sierraId == "brain") + return "castlebrain"; + // iceman is the same + // longbow is the same + if (sierraId == "eco") + return "ecoquest"; + if (sierraId == "rain") + return "ecoquest2"; + if (sierraId == "fp") + return "freddypharkas"; + if (sierraId == "emc") + return "funseeker"; + if (sierraId == "cardgames") + return "hoyle1"; + if (sierraId == "solitare") + return "hoyle2"; + // TODO: hoyle3 + // TODO: hoyle4 + if (sierraId == "kq1") + return "kq1sci"; + if (sierraId == "kq4") + return "kq4sci"; + if (sierraId == "lsl1") + return "lsl1sci"; + // lsl2 is the same + // lsl3 is the same + // lsl5 is the same + // lsl6 is the same + // TODO: lslcasino + // TODO: fairytales + // TODO: mothergoose + // TODO: msastrochicken + if (sierraId == "cb1") + return "laurabow"; + if (sierraId == "lb2") + return "laurabow2"; + // TODO: lb2 floppy (its resources can't be read) + if (sierraId == "twisty") + return "pepper"; + // TODO: pq1sci (its resources can't be read) + if (sierraId == "pq") + return "pq2"; + // pq3 is the same + if (sierraId == "glory") + return "qfg1"; + // TODO: qfg1 VGA (its resources can't be read) + if (sierraId == "trial") + return "qfg2"; + if (sierraId == "qfg1") + return "qfg3"; + // TODO: slater + if (sierraId == "sq1") + return "sq1sci"; + // sq3 is the same + // sq4 is the same + // sq5 is the same + // TODO: islandbrain + + return sierraId; +} const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fslist) const { bool foundResMap = false; @@ -3046,8 +3118,16 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl Common::String filename = file->getName(); filename.toLowercase(); - if (filename.contains("resource.map") || filename.contains("resmap.000")) + if (filename.contains("resource.map") || filename.contains("resmap.000")) { + // HACK: resource.map is located in the same directory as the other resource files, + // therefore add the directory here, so that the game files can be opened later on + // TODO/FIXME: This should be removed, as it will cause problems with game detection: + // if we got a game A, and then try to detect another game B, adding a default + // directory here means that game A's files will be opened first. We need to rewrite + // all the functions that access game files to use FSNodes instead + Common::File::addDefaultDirectory(file->getParent().getPath()); foundResMap = true; + } if (filename.contains("resource.000") || filename.contains("resource.001") || filename.contains("ressci.000") || filename.contains("ressci.001")) @@ -3080,12 +3160,31 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl return 0; // Set some defaults - s_fallbackDesc.desc.gameid = "sci"; s_fallbackDesc.desc.extra = ""; s_fallbackDesc.desc.language = Common::UNK_LANG; s_fallbackDesc.desc.platform = exePlatform; s_fallbackDesc.desc.flags = ADGF_NO_FLAGS; + // Determine the game id + ResourceManager *resMgr = new ResourceManager(fslist); + SciVersion version = resMgr->sciVersion(); + Kernel *kernel = new Kernel(resMgr, true); + bool hasOldScriptHeader = kernel->hasOldScriptHeader(); + delete kernel; + + SegManager *segManager = new SegManager(resMgr, version, hasOldScriptHeader); + if (!script_instantiate(resMgr, segManager, version, hasOldScriptHeader, 0)) { + warning("fallbackDetect(): Could not instantiate script 0"); + return 0; + } + reg_t game_obj = script_lookup_export(segManager, 0, 0); + Common::String gameName = obj_get_name(segManager, version, game_obj); + debug(2, "Detected ID: \"%s\" at %04x:%04x", gameName.c_str(), PRINT_REG(game_obj)); + gameName.toLowercase(); + s_fallbackDesc.desc.gameid = strdup(convertSierraGameId(gameName).c_str()); + delete segManager; + delete resMgr; + printf("If this is *NOT* a fan-modified version (in particular, not a fan-made\n"); printf("translation), please, report the data above, including the following\n"); printf("version number, from the game's executable:\n"); diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp index 1fa0e03d4c..c34ac1cf00 100644 --- a/engines/sci/engine/game.cpp +++ b/engines/sci/engine/game.cpp @@ -188,159 +188,13 @@ int game_init_sound(EngineState *s, int sound_flags) { return 0; } -int create_class_table_sci11(EngineState *s) { - int scriptnr; - unsigned int seeker_offset; - char *seeker_ptr; - int classnr; - - Resource *vocab996 = s->resmgr->findResource(ResourceId(kResourceTypeVocab, 996), 1); - - if (!vocab996) - s->_classtable.resize(20); - else - s->_classtable.resize(vocab996->size >> 2); - - for (scriptnr = 0; scriptnr < 1000; scriptnr++) { - Resource *heap = s->resmgr->findResource(ResourceId(kResourceTypeHeap, scriptnr), 0); - - if (heap) { - int global_vars = READ_LE_UINT16(heap->data + 2); - - seeker_ptr = (char*)heap->data + 4 + global_vars * 2; - seeker_offset = 4 + global_vars * 2; - - while (READ_LE_UINT16((byte*)seeker_ptr) == SCRIPT_OBJECT_MAGIC_NUMBER) { - if (READ_LE_UINT16((byte*)seeker_ptr + 14) & SCRIPT_INFO_CLASS) { - classnr = READ_LE_UINT16((byte*)seeker_ptr + 10); - if (classnr >= (int)s->_classtable.size()) { - if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) { - warning("Invalid class number 0x%x in script.%d(0x%x), offset %04x", - classnr, scriptnr, scriptnr, seeker_offset); - return 1; - } - - s->_classtable.resize(classnr + 1); // Adjust maximum number of entries - } - - s->_classtable[classnr].reg.offset = seeker_offset; - s->_classtable[classnr].reg.segment = 0; - s->_classtable[classnr].script = scriptnr; - } - - seeker_ptr += READ_LE_UINT16((byte*)seeker_ptr + 2) * 2; - seeker_offset += READ_LE_UINT16((byte*)seeker_ptr + 2) * 2; - } - } - } - - s->resmgr->unlockResource(vocab996); - vocab996 = NULL; - return 0; -} - -static int create_class_table_sci0(EngineState *s) { - int scriptnr; - unsigned int seeker; - int classnr; - int magic_offset; // For strange scripts in older SCI versions - - Resource *vocab996 = s->resmgr->findResource(ResourceId(kResourceTypeVocab, 996), 1); - - if (!vocab996) - s->_classtable.resize(20); - else - s->_classtable.resize(vocab996->size >> 2); - - for (scriptnr = 0; scriptnr < 1000; scriptnr++) { - int objtype = 0; - Resource *script = s->resmgr->findResource(ResourceId(kResourceTypeScript, scriptnr), 0); - - if (script) { - if (((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader()) - magic_offset = seeker = 2; - else - magic_offset = seeker = 0; - - do { - while (seeker < script->size) { - unsigned int lastseeker = seeker; - objtype = (int16)READ_LE_UINT16(script->data + seeker); - if (objtype == SCI_OBJ_CLASS || objtype == SCI_OBJ_TERMINATOR) - break; - seeker += (int16)READ_LE_UINT16(script->data + seeker + 2); - if (seeker <= lastseeker) { - s->_classtable.clear(); - error("Script version is invalid"); - } - } - - if (objtype == SCI_OBJ_CLASS) { - int sugg_script; - - seeker -= SCRIPT_OBJECT_MAGIC_OFFSET; // Adjust position; script home is base +8 bytes - - classnr = (int16)READ_LE_UINT16(script->data + seeker + 4 + SCRIPT_SPECIES_OFFSET); - if (classnr >= (int)s->_classtable.size()) { - - if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) { - warning("Invalid class number 0x%x in script.%d(0x%x), offset %04x", - classnr, scriptnr, scriptnr, seeker); - return 1; - } - - s->_classtable.resize(classnr + 1); // Adjust maximum number of entries - } - - // Map the class ID to the script the corresponding class is contained in - // The script number is found in vocab.996, if it exists - if (!vocab996 || (uint32)classnr >= vocab996->size >> 2) - sugg_script = -1; - else - sugg_script = (int16)READ_LE_UINT16(vocab996->data + 2 + (classnr << 2)); - - // First, test whether the script hasn't been claimed, or if it's been claimed by the wrong script - - if (sugg_script == -1 || scriptnr == sugg_script /*|| !s->_classtable[classnr].reg.segment*/) { - // Now set the home script of the class - s->_classtable[classnr].reg.offset = seeker + 4 - magic_offset; - s->_classtable[classnr].reg.segment = 0; - s->_classtable[classnr].script = scriptnr; - } - - seeker += SCRIPT_OBJECT_MAGIC_OFFSET; // Re-adjust position - seeker += (int16)READ_LE_UINT16(script->data + seeker + 2); // Move to next - } - - } while (objtype != SCI_OBJ_TERMINATOR && seeker <= script->size); - - } - } - s->resmgr->unlockResource(vocab996); - vocab996 = NULL; - return 0; -} - // Architectural stuff: Init/Unintialize engine int script_init_engine(EngineState *s) { - int result; - s->kernel_opt_flags = 0; - - if (s->_version >= SCI_VERSION_1_1) - result = create_class_table_sci11(s); - else - result = create_class_table_sci0(s); - - if (result) { - debug(2, "Failed to initialize class table"); - return 1; - } - - s->seg_manager = new SegManager(s->_version >= SCI_VERSION_1_1); + s->seg_manager = new SegManager(s->resmgr, s->_version, ((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader()); s->gc_countdown = GC_INTERVAL - 1; - SegmentId script_000_segment = script_get_segment(s, 0, SCRIPT_GET_LOCK); + SegmentId script_000_segment = s->seg_manager->getSegment(0, SCRIPT_GET_LOCK); if (script_000_segment <= 0) { debug(2, "Failed to instantiate script.000"); @@ -398,7 +252,8 @@ void internal_stringfrag_strncpy(EngineState *s, reg_t *dest, reg_t *src, int le void script_free_vm_memory(EngineState *s) { debug(2, "Freeing VM memory"); - s->_classtable.clear(); + if (s->seg_manager) + s->seg_manager->_classtable.clear(); // Close all opened file handles s->_fileHandles.clear(); @@ -433,14 +288,13 @@ void script_free_breakpoints(EngineState *s) { int game_init(EngineState *s) { // FIXME Use new VM instantiation code all over the place" - reg_t game_obj; // Address of the game object DataStack *stack; stack = s->seg_manager->allocateStack(VM_STACK_SIZE, &s->stack_segment); s->stack_base = stack->entries; s->stack_top = s->stack_base + VM_STACK_SIZE; - if (!script_instantiate(s, 0)) { + if (!script_instantiate(s->resmgr, s->seg_manager, s->_version, ((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader(), 0)) { warning("game_init(): Could not instantiate script 0"); return 1; } @@ -473,20 +327,11 @@ int game_init(EngineState *s) { srand(g_system->getMillis()); // Initialize random number generator // script_dissect(0, s->_selectorNames); - game_obj = script_lookup_export(s, 0, 0); // The first entry in the export table of script 0 points to the game object + s->game_obj = script_lookup_export(s->seg_manager, 0, 0); + s->_gameName = obj_get_name(s->seg_manager, s->_version, s->game_obj); - const char *tmp = obj_get_name(s, game_obj); - - if (!tmp) { - warning("Error: script.000, export 0 (%04x:%04x) does not yield an object with a name -> sanity check failed", PRINT_REG(game_obj)); - return 1; - } - s->_gameName = tmp; - - debug(2, " \"%s\" at %04x:%04x", s->_gameName.c_str(), PRINT_REG(game_obj)); - - s->game_obj = game_obj; + debug(2, " \"%s\" at %04x:%04x", s->_gameName.c_str(), PRINT_REG(s->game_obj)); // Mark parse tree as unused s->parser_nodes[0].type = kParseTreeLeafNode; @@ -512,7 +357,9 @@ int game_exit(EngineState *s) { game_init_sound(s, SFX_STATE_FLAG_NOSOUND); } + s->seg_manager->_classtable.clear(); delete s->seg_manager; + s->seg_manager = 0; s->_synonyms.clear(); diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 193ff4cc51..223e7fc1e9 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -33,11 +33,7 @@ namespace Sci { -/** The string used to identify the "unknown" SCI0 function for each game */ -#define SCRIPT_UNKNOWN_FUNCTION_STRING "[Unknown]" - // Default kernel name table -#define SCI0_KNAMES_WELL_DEFINED 0x6e #define SCI_KNAMES_DEFAULT_ENTRIES_NR 0x89 static const char *sci_default_knames[SCI_KNAMES_DEFAULT_ENTRIES_NR] = { @@ -367,11 +363,15 @@ static const char *argtype_description[] = { "Arithmetic" }; -Kernel::Kernel(ResourceManager *resmgr) : _resmgr(resmgr) { +Kernel::Kernel(ResourceManager *resmgr, bool minimalLoad) : _resmgr(resmgr) { memset(&_selectorMap, 0, sizeof(_selectorMap)); // FIXME: Remove this once/if we C++ify selector_map_t - detectSciFeatures(); // must be called before loadSelectorNames() loadSelectorNames(); + detectSciFeatures(); + + if (minimalLoad) // If we're only asked to detect game features, stop here + return; + mapSelectors(); // Map a few special selectors for later use loadOpcodes(); loadKernelNames(); @@ -382,61 +382,30 @@ Kernel::~Kernel() { } void Kernel::detectSciFeatures() { - // FIXME Much of this is unreliable - - Resource *r = _resmgr->findResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SNAMES), 0); + SciVersion version = _resmgr->sciVersion(); - Common::StringList staticSelectorTable; - - if (!r) { // No such resource? - staticSelectorTable = checkStaticSelectorNames(); - if (staticSelectorTable.empty()) - error("Kernel: Could not retrieve selector names"); - } - - int count = staticSelectorTable.empty() ? READ_LE_UINT16(r->data) + 1 : staticSelectorTable.size(); // Counter is slightly off features = 0; // Initialize features based on SCI version - switch (_resmgr->sciVersion()) { - case SCI_VERSION_0_EARLY: - features |= kFeatureOldScriptHeader; - /* Fallthrough */ - case SCI_VERSION_0_LATE: - features |= kFeatureOldGfxFunctions; - break; - default: - break; - } - - for (int i = 0; i < count; i++) { - Common::String tmp; - - if (staticSelectorTable.empty()) { - int offset = READ_LE_UINT16(r->data + 2 + i * 2); - int len = READ_LE_UINT16(r->data + offset); - - tmp = Common::String((const char *)r->data + offset + 2, len); - } else { - tmp = staticSelectorTable[i]; - } - if (tmp == "motionCue") - features &= ~kFeatureOldGfxFunctions; + // Script header and graphics functions + if (version == SCI_VERSION_0_EARLY) { + features |= kFeatureOldScriptHeader | kFeatureOldGfxFunctions; + } else if (version == SCI_VERSION_0_LATE) { + if (findSelector("motionCue") == -1) + features |= kFeatureOldGfxFunctions; + } - if (tmp == "egoMoveSpeed" && _resmgr->sciVersion() < SCI_VERSION_1_1) + // Lofs absolute/relative + if (version >= SCI_VERSION_1_MIDDLE && version < SCI_VERSION_1_1) { + // Assume all games use absolute lofs + features |= kFeatureLofsAbsolute; + } else if (version == SCI_VERSION_1_EARLY) { + // Use heuristic + if (findSelector("egoMoveSpeed") != -1) features |= kFeatureLofsAbsolute; - - if (tmp == "setVol") - features |= kFeatureSci1Sound; - - if (tmp == "nodePtr") - features |= kFeatureSci01Sound; } - if (features & kFeatureSci1Sound) - features &= ~kFeatureSci01Sound; - printf("Kernel auto-detected features:\n"); printf("Graphics functions: "); @@ -445,19 +414,13 @@ void Kernel::detectSciFeatures() { else printf("new\n"); - printf("lofs parameters: "); - if (features & kFeatureLofsAbsolute) - printf("absolute\n"); - else - printf("relative\n"); - - printf("Sound functions: "); - if (features & kFeatureSci1Sound) - printf("SCI1\n"); - else if (features & kFeatureSci01Sound) - printf("SCI01\n"); - else - printf("SCI0\n"); + if (version < SCI_VERSION_1_1) { + printf("lofs parameters: "); + if (features & kFeatureLofsAbsolute) + printf("absolute\n"); + else + printf("relative\n"); + } } void Kernel::loadSelectorNames() { @@ -473,7 +436,7 @@ void Kernel::loadSelectorNames() { for (uint32 i = 0; i < staticSelectorTable.size(); i++) { _selectorNames.push_back(staticSelectorTable[i]); - if (features & kFeatureOldScriptHeader) + if (_resmgr->sciVersion() == SCI_VERSION_0_EARLY) _selectorNames.push_back(staticSelectorTable[i]); } @@ -492,7 +455,7 @@ void Kernel::loadSelectorNames() { // Early SCI versions used the LSB in the selector ID as a read/write // toggle. To compensate for that, we add every selector name twice. - if (features & kFeatureOldScriptHeader) + if (_resmgr->sciVersion() == SCI_VERSION_0_EARLY) _selectorNames.push_back(tmp); } } @@ -706,6 +669,8 @@ int determine_reg_type(EngineState *s, reg_t reg, bool allow_invalid) { mobj = s->seg_manager->_heap[reg.segment]; + SciVersion version = s->_version; // for the offset defines + switch (mobj->getType()) { case MEM_OBJ_SCRIPT: if (reg.offset <= (*(Script *)mobj).buf_size && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET @@ -817,32 +782,40 @@ reg_t *kernel_dereference_reg_pointer(EngineState *s, reg_t pointer, int entries } void Kernel::setDefaultKernelNames() { - bool isSci0 = (_resmgr->sciVersion() <= SCI_VERSION_0_LATE); - int offset = 0; - - _kernelNames.resize(SCI_KNAMES_DEFAULT_ENTRIES_NR + (isSci0 ? 4 : 0)); - for (int i = 0; i < SCI_KNAMES_DEFAULT_ENTRIES_NR; i++) { - // In SCI0, Platform was DoAvoider - if (!strcmp(sci_default_knames[i], "Platform") && isSci0) { - _kernelNames[i + offset] = "DoAvoider"; - continue; - } + _kernelNames = Common::StringList(sci_default_knames, SCI_KNAMES_DEFAULT_ENTRIES_NR); + + switch (_resmgr->sciVersion()) { + case SCI_VERSION_0_EARLY: + case SCI_VERSION_0_LATE: + // Insert SCI0 file functions after SetCursor (0x28) + _kernelNames.insert_at(0x29, "FOpen"); + _kernelNames.insert_at(0x2A, "FPuts"); + _kernelNames.insert_at(0x2B, "FGets"); + _kernelNames.insert_at(0x2C, "FClose"); - _kernelNames[i + offset] = sci_default_knames[i]; + // Function 0x55 is DoAvoider + _kernelNames[0x55] = "DoAvoider"; - // SCI0 has 4 extra functions between SetCursor (0x28) and Savegame - if (!strcmp(sci_default_knames[i], "SetCursor") && isSci0) { - _kernelNames[i + 1] = "FOpen"; - _kernelNames[i + 2] = "FPuts"; - _kernelNames[i + 3] = "FGets"; - _kernelNames[i + 4] = "FClose"; - offset = 4; - } - } + // Cut off unused functions + _kernelNames.resize(0x72); + break; + + case SCI_VERSION_01: + // Multilingual SCI01 games have StrSplit as function 0x78 + _kernelNames[0x78] = "StrSplit"; - if (_resmgr->sciVersion() == SCI_VERSION_1_1) { - // HACK: KQ6CD calls unimplemented function 0x26 + // Cut off unused functions + _kernelNames.resize(0x79); + break; + + case SCI_VERSION_1_1: + // KQ6CD calls unimplemented function 0x26 _kernelNames[0x26] = "Dummy"; + break; + + default: + // Use default table for the other versions + break; } } diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 2c90728fb0..997cdaea77 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -32,6 +32,8 @@ #include "sci/uinput.h" #include "sci/vocabulary.h" +#include "sci/engine/vm_types.h" // for reg_t +#include "sci/engine/vm.h" namespace Sci { @@ -58,14 +60,17 @@ struct KernelFuncWithSignature { enum AutoDetectedFeatures { kFeatureOldScriptHeader = 1 << 0, kFeatureOldGfxFunctions = 1 << 1, - kFeatureLofsAbsolute = 1 << 2, - kFeatureSci01Sound = 1 << 3, - kFeatureSci1Sound = 1 << 4 + kFeatureLofsAbsolute = 1 << 2 }; class Kernel { public: - Kernel(ResourceManager *resmgr); + /** + * Initializes the SCI kernel + * @param minimalLoad If true, only the selector names are loaded, to detect game features. + * It's set to true by the advanced game detector to speed it up + */ + Kernel(ResourceManager *resmgr, bool minimalLoad = false); ~Kernel(); uint getOpcodesSize() const { return _opcodes.size(); } @@ -117,16 +122,6 @@ public: */ bool hasLofsAbsolute() const { return (features & kFeatureLofsAbsolute); } - /** - * Determines if the game is using SCI01 sound functions - */ - bool usesSci01SoundFunctions() const { return (features & kFeatureSci01Sound); } - - /** - * Determines if the game is using SCI1 sound functions - */ - bool usesSci1SoundFunctions() const { return (features & kFeatureSci1Sound); } - // Script dissection/dumping functions void dissectScript(int scriptNumber, Vocabulary *vocab); void dumpScriptObject(char *data, int seeker, int objsize); diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index 7433324a70..58dc3b73f1 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -257,7 +257,7 @@ static void bresenham_autodetect(EngineState *s) { reg_t motion_class; if (!parse_reg_t(s, "?Motion", &motion_class)) { - Object *obj = obj_get(s, motion_class); + Object *obj = obj_get(s->seg_manager, s->_version, motion_class); reg_t fptr; byte *buf; diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index df25e11729..4d90dd68ac 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -108,7 +108,7 @@ int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvoc } bool is_object(EngineState *s, reg_t object) { - return obj_get(s, object) != NULL; + return obj_get(s->seg_manager, s->_version, object) != NULL; } // Loads arbitrary resources of type 'restype' with resource numbers 'resnrs' @@ -184,7 +184,7 @@ reg_t kResCheck(EngineState *s, int funct_nr, int argc, reg_t *argv) { reg_t kClone(EngineState *s, int funct_nr, int argc, reg_t *argv) { reg_t parent_addr = argv[0]; - Object *parent_obj = obj_get(s, parent_addr); + Object *parent_obj = obj_get(s->seg_manager, s->_version, parent_addr); reg_t clone_addr; Clone *clone_obj; // same as Object* @@ -205,6 +205,8 @@ reg_t kClone(EngineState *s, int funct_nr, int argc, reg_t *argv) { *clone_obj = *parent_obj; clone_obj->flags = 0; + SciVersion version = s->_version; // for the selector defines + // Mark as clone clone_obj->_variables[SCRIPT_INFO_SELECTOR].offset = SCRIPT_INFO_CLONE; clone_obj->_variables[SCRIPT_SPECIES_SELECTOR] = clone_obj->pos; @@ -220,7 +222,7 @@ extern void _k_view_list_mark_free(EngineState *s, reg_t off); reg_t kDisposeClone(EngineState *s, int funct_nr, int argc, reg_t *argv) { reg_t victim_addr = argv[0]; - Clone *victim_obj = obj_get(s, victim_addr); + Clone *victim_obj = obj_get(s->seg_manager, s->_version, victim_addr); uint16 underBits; if (!victim_obj) { @@ -229,6 +231,8 @@ reg_t kDisposeClone(EngineState *s, int funct_nr, int argc, reg_t *argv) { return s->r_acc; } + SciVersion version = s->_version; // for the selector defines + if (victim_obj->_variables[SCRIPT_INFO_SELECTOR].offset != SCRIPT_INFO_CLONE) { //warning("Attempt to dispose something other than a clone at %04x", offset); // SCI silently ignores this behaviour; some games actually depend on it @@ -260,7 +264,7 @@ reg_t kScriptID(EngineState *s, int funct_nr, int argc, reg_t *argv) { int script = argv[0].toUint16(); int index = (argc > 1) ? argv[1].toUint16() : 0; - SegmentId scriptid = script_get_segment(s, script, SCRIPT_GET_LOAD); + SegmentId scriptid = s->seg_manager->getSegment(script, SCRIPT_GET_LOAD); Script *scr; if (argv[0].segment) @@ -299,13 +303,13 @@ reg_t kDisposeScript(EngineState *s, int funct_nr, int argc, reg_t *argv) { scr->setLockers(1); } - script_uninstantiate(s, script); + script_uninstantiate(s->seg_manager, s->_version, script); s->_executionStackPosChanged = true; return s->r_acc; } int is_heap_object(EngineState *s, reg_t pos) { - Object *obj = obj_get(s, pos); + Object *obj = obj_get(s->seg_manager, s->_version, pos); return (obj != NULL && (!(obj->flags & OBJECT_FLAG_FREED)) && (!s->seg_manager->scriptIsMarkedAsDeleted(pos.segment))); } diff --git a/engines/sci/engine/ksound.cpp b/engines/sci/engine/ksound.cpp index 38baeafad8..44b2404e41 100644 --- a/engines/sci/engine/ksound.cpp +++ b/engines/sci/engine/ksound.cpp @@ -204,7 +204,7 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their } -reg_t kDoSound_SCI0(EngineState *s, int funct_nr, int argc, reg_t *argv) { +reg_t kDoSoundSci0(EngineState *s, int funct_nr, int argc, reg_t *argv) { reg_t obj = (argc > 1) ? argv[1] : NULL_REG; uint16 command = argv[0].toUint16(); SongHandle handle = FROBNICATE_HANDLE(obj); @@ -383,7 +383,7 @@ reg_t kDoSound_SCI0(EngineState *s, int funct_nr, int argc, reg_t *argv) { } -reg_t kDoSound_SCI01(EngineState *s, int funct_nr, int argc, reg_t *argv) { +reg_t kDoSoundSci1Early(EngineState *s, int funct_nr, int argc, reg_t *argv) { uint16 command = argv[0].toUint16(); reg_t obj = (argc > 1) ? argv[1] : NULL_REG; SongHandle handle = FROBNICATE_HANDLE(obj); @@ -673,7 +673,7 @@ reg_t kDoSound_SCI01(EngineState *s, int funct_nr, int argc, reg_t *argv) { return s->r_acc; } -reg_t kDoSound_SCI1(EngineState *s, int funct_nr, int argc, reg_t *argv) { +reg_t kDoSoundSci1Late(EngineState *s, int funct_nr, int argc, reg_t *argv) { uint16 command = argv[0].toUint16(); reg_t obj = (argc > 1) ? argv[1] : NULL_REG; SongHandle handle = FROBNICATE_HANDLE(obj); @@ -988,12 +988,17 @@ reg_t kDoSound_SCI1(EngineState *s, int funct_nr, int argc, reg_t *argv) { * Used for synthesized music playback */ reg_t kDoSound(EngineState *s, int funct_nr, int argc, reg_t *argv) { - if (((SciEngine*)g_engine)->getKernel()->usesSci1SoundFunctions()) - return kDoSound_SCI1(s, funct_nr, argc, argv); - else if (((SciEngine*)g_engine)->getKernel()->usesSci01SoundFunctions()) - return kDoSound_SCI01(s, funct_nr, argc, argv); - else - return kDoSound_SCI0(s, funct_nr, argc, argv); + switch(s->detectDoSoundType()) { + case EngineState::kDoSoundTypeSci0: + return kDoSoundSci0(s, funct_nr, argc, argv); + case EngineState::kDoSoundTypeSci1Early: + return kDoSoundSci1Early(s, funct_nr, argc, argv); + case EngineState::kDoSoundTypeSci1Late: + return kDoSoundSci1Late(s, funct_nr, argc, argv); + default: + warning("Unknown DoSound type"); + return NULL_REG; + } } /** diff --git a/engines/sci/engine/memobj.cpp b/engines/sci/engine/memobj.cpp index ab8e14efc7..34432521a0 100644 --- a/engines/sci/engine/memobj.cpp +++ b/engines/sci/engine/memobj.cpp @@ -246,6 +246,7 @@ void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback not void Script::listAllOutgoingReferences(EngineState *s, reg_t addr, void *param, NoteCallback note) { Script *script = this; + SciVersion version = s->_version; // for the offset defines if (addr.offset <= script->buf_size && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(script->buf + addr.offset)) { int idx = RAW_GET_CLASS_INDEX(script, addr); diff --git a/engines/sci/engine/memobj.h b/engines/sci/engine/memobj.h index 50c43a0e88..efe7f26f1c 100644 --- a/engines/sci/engine/memobj.h +++ b/engines/sci/engine/memobj.h @@ -27,6 +27,7 @@ #define SCI_ENGINE_MEMOBJ_H #include "common/serializer.h" +#include "sci/engine/vm.h" #include "sci/engine/vm_types.h" // for reg_t //#include "common/util.h" diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 75cd4aee54..0ddb5187ac 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -205,19 +205,21 @@ void SegManager::saveLoadWithSerializer(Common::Serializer &s) { } static void sync_SegManagerPtr(Common::Serializer &s, SegManager *&obj) { - bool sci11 = false; + SciVersion version = SCI_VERSION_AUTODETECT; + ResourceManager *resMgr = 0; if (s.isSaving()) { assert(obj); - sci11 = obj->isSci1_1; + version = obj->_version; + resMgr = obj->_resMgr; } - s.syncAsByte(sci11); + s.skip(1); // obsolete: used to be a flag indicating if we got sci11 or not if (s.isLoading()) { // FIXME: Do in-place loading at some point, instead of creating a new EngineState instance from scratch. delete obj; - obj = new SegManager(sci11); + obj = new SegManager(resMgr, version, ((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader()); } obj->saveLoadWithSerializer(s); @@ -266,7 +268,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) { sync_SegManagerPtr(s, seg_manager); - syncArray<Class>(s, _classtable); + syncArray<Class>(s, seg_manager->_classtable); sync_sfx_state_t(s, _sound); } @@ -549,7 +551,7 @@ static void load_script(EngineState *s, SegmentId seg) { heap = s->resmgr->findResource(ResourceId(kResourceTypeHeap, scr->nr), 0); memcpy(scr->buf, script->data, script->size); - if (s->seg_manager->isSci1_1) + if (s->seg_manager->_version == SCI_VERSION_1_1) memcpy(scr->buf + scr->script_size, heap->data, heap->size); } @@ -557,6 +559,8 @@ static void load_script(EngineState *s, SegmentId seg) { static void reconstruct_scripts(EngineState *s, SegManager *self) { uint i, j; MemObject *mobj; + SciVersion version = s->_version; // for the selector defines + for (i = 0; i < self->_heap.size(); i++) { if (self->_heap[i]) { mobj = self->_heap[i]; @@ -567,7 +571,7 @@ static void reconstruct_scripts(EngineState *s, SegManager *self) { // FIXME: Unify this code with script_instantiate_* load_script(s, i); scr->locals_block = (scr->locals_segment == 0) ? NULL : (LocalVariables *)(s->seg_manager->_heap[scr->locals_segment]); - if (s->seg_manager->isSci1_1) { + if (s->seg_manager->_version == SCI_VERSION_1_1) { scr->export_table = 0; scr->synonyms = 0; if (READ_LE_UINT16(scr->buf + 6) > 0) { @@ -603,7 +607,7 @@ static void reconstruct_scripts(EngineState *s, SegManager *self) { for (j = 0; j < scr->_objects.size(); j++) { byte *data = scr->buf + scr->_objects[j].pos.offset; - if (self->isSci1_1) { + if (self->_version == SCI_VERSION_1_1) { uint16 *funct_area = (uint16 *) (scr->buf + READ_LE_UINT16( data + 6 )); uint16 *prop_area = (uint16 *) (scr->buf + READ_LE_UINT16( data + 4 )); @@ -613,7 +617,7 @@ static void reconstruct_scripts(EngineState *s, SegManager *self) { int funct_area = READ_LE_UINT16( data + SCRIPT_FUNCTAREAPTR_OFFSET ); Object *base_obj; - base_obj = obj_get(s, scr->_objects[j]._variables[SCRIPT_SPECIES_SELECTOR]); + base_obj = obj_get(s->seg_manager, s->_version, scr->_objects[j]._variables[SCRIPT_SPECIES_SELECTOR]); if (!base_obj) { warning("Object without a base class: Script %d, index %d (reg address %04x:%04x", @@ -638,6 +642,8 @@ static void reconstruct_scripts(EngineState *s, SegManager *self) { // FIXME: The following should likely become a SegManager method static void reconstruct_clones(EngineState *s, SegManager *self) { + SciVersion version = s->_version; // for the selector defines + for (uint i = 0; i < self->_heap.size(); i++) { if (self->_heap[i]) { MemObject *mobj = self->_heap[i]; @@ -667,7 +673,7 @@ static void reconstruct_clones(EngineState *s, SegManager *self) { continue; } CloneTable::Entry &seeker = ct->_table[j]; - base_obj = obj_get(s, seeker._variables[SCRIPT_SPECIES_SELECTOR]); + base_obj = obj_get(s->seg_manager, s->_version, seeker._variables[SCRIPT_SPECIES_SELECTOR]); if (!base_obj) { printf("Clone entry without a base class: %d\n", j); seeker.base = seeker.base_obj = NULL; @@ -695,8 +701,7 @@ static void reconstruct_sounds(EngineState *s) { Song *seeker; SongIteratorType it_type; - if (((SciEngine *)g_engine)->getKernel()->usesSci01SoundFunctions() - || ((SciEngine *)g_engine)->getKernel()->usesSci1SoundFunctions()) + if (s->_version > SCI_VERSION_01) it_type = SCI_SONG_ITERATOR_TYPE_SCI1; else it_type = SCI_SONG_ITERATOR_TYPE_SCI0; @@ -791,7 +796,7 @@ EngineState *gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { reconstruct_scripts(retval, retval->seg_manager); reconstruct_clones(retval, retval->seg_manager); retval->game_obj = s->game_obj; - retval->script_000 = retval->seg_manager->getScript(script_get_segment(s, 0, SCRIPT_GET_DONT_LOAD)); + retval->script_000 = retval->seg_manager->getScript(retval->seg_manager->getSegment(0, SCRIPT_GET_DONT_LOAD)); retval->gc_countdown = GC_INTERVAL - 1; retval->sys_strings_segment = find_unique_seg_by_type(retval->seg_manager, MEM_OBJ_SYS_STRINGS); retval->sys_strings = (SystemStrings *)GET_SEGMENT(*retval->seg_manager, retval->sys_strings_segment, MEM_OBJ_SYS_STRINGS); @@ -831,7 +836,7 @@ EngineState *gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { retval->successor = NULL; retval->pic_priority_table = (int *)gfxop_get_pic_metainfo(retval->gfx_state); - retval->_gameName = obj_get_name(retval, retval->game_obj); + retval->_gameName = obj_get_name(retval->seg_manager, retval->_version, retval->game_obj); retval->_sound._it = NULL; retval->_sound._flags = s->_sound._flags; diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 0341ecb73d..fd7219bc85 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -37,9 +37,10 @@ extern const char *selector_name(EngineState *s, int selector); ScriptState scriptState; int propertyOffsetToId(EngineState *s, int prop_ofs, reg_t objp) { - Object *obj = obj_get(s, objp); + Object *obj = obj_get(s->seg_manager, s->_version, objp); byte *selectoroffset; int selectors; + SciVersion version = s->_version; // for the selector defines if (!obj) { warning("Applied propertyOffsetToId on non-object at %04x:%04x", PRINT_REG(objp)); @@ -52,7 +53,7 @@ int propertyOffsetToId(EngineState *s, int prop_ofs, reg_t objp) { selectoroffset = ((byte *)(obj->base_obj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2; else { if (!(obj->_variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS)) { - obj = obj_get(s, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]); + obj = obj_get(s->seg_manager, s->_version, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]); selectoroffset = (byte *)obj->base_vars; } else selectoroffset = (byte *)obj->base_vars; @@ -268,7 +269,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod selector = sb[- stackframe].offset; - name = obj_get_name(s, called_obj_addr); + name = obj_get_name(s->seg_manager, s->_version, called_obj_addr); if (!name) name = "<invalid>"; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 905cba9d94..a6f54c5bf7 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -52,7 +52,7 @@ namespace Sci { #define INVALID_SCRIPT_ID -1 -SegManager::SegManager(bool sci1_1) { +SegManager::SegManager(ResourceManager *resMgr, SciVersion version, bool oldScriptHeader) { id_seg_map = new IntMapper(); reserved_id = INVALID_SCRIPT_ID; id_seg_map->checkKey(reserved_id, true); // reserve entry 0 for INVALID_SCRIPT_ID @@ -66,7 +66,19 @@ SegManager::SegManager(bool sci1_1) { Hunks_seg_id = 0; exports_wide = 0; - isSci1_1 = sci1_1; + _version = version; + _resMgr = resMgr; + _oldScriptHeader = oldScriptHeader; + + int result = 0; + + if (version >= SCI_VERSION_1_1) + result = createSci11ClassTable(); + else + result = createSci0ClassTable(); + + if (result) + error("SegManager: Failed to initialize class table"); } // Destroy the object, free the memorys if allocated before @@ -109,7 +121,7 @@ MemObject *SegManager::allocNonscriptSegment(MemObjectType type, SegmentId *segi // Returns : 0 - allocation failure // 1 - allocated successfully // seg_id - allocated segment id -Script *SegManager::allocateScript(EngineState *s, int script_nr, SegmentId *seg_id) { +Script *SegManager::allocateScript(int script_nr, SegmentId *seg_id) { bool was_added; MemObject *mem; @@ -128,20 +140,20 @@ Script *SegManager::allocateScript(EngineState *s, int script_nr, SegmentId *seg return (Script *)mem; } -void SegManager::setScriptSize(Script &scr, EngineState *s, int script_nr) { - Resource *script = s->resmgr->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - Resource *heap = s->resmgr->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); +void SegManager::setScriptSize(Script &scr, int script_nr) { + Resource *script = _resMgr->findResource(ResourceId(kResourceTypeScript, script_nr), 0); + Resource *heap = _resMgr->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); scr.script_size = script->size; scr.heap_size = 0; // Set later - if (!script || (s->_version >= SCI_VERSION_1_1 && !heap)) { + if (!script || (_version >= SCI_VERSION_1_1 && !heap)) { error("SegManager::setScriptSize: failed to load %s", !script ? "script" : "heap"); } - if (((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader()) { + if (_oldScriptHeader) { scr.buf_size = script->size + READ_LE_UINT16(script->data) * 2; //locals_size = READ_LE_UINT16(script->data) * 2; - } else if (s->_version < SCI_VERSION_1_1) { + } else if (_version < SCI_VERSION_1_1) { scr.buf_size = script->size; } else { scr.buf_size = script->size + heap->size; @@ -163,10 +175,10 @@ void SegManager::setScriptSize(Script &scr, EngineState *s, int script_nr) { } } -int SegManager::initialiseScript(Script &scr, EngineState *s, int script_nr) { +int SegManager::initialiseScript(Script &scr, int script_nr) { // allocate the script.buf - setScriptSize(scr, s, script_nr); + setScriptSize(scr, script_nr); scr.buf = (byte *)malloc(scr.buf_size); #ifdef DEBUG_SEG_MANAGER @@ -191,7 +203,7 @@ int SegManager::initialiseScript(Script &scr, EngineState *s, int script_nr) { scr.obj_indices = new IntMapper(); - if (s->_version >= SCI_VERSION_1_1) + if (_version >= SCI_VERSION_1_1) scr.heap_start = scr.buf + scr.script_size; else scr.heap_start = scr.buf; @@ -319,7 +331,7 @@ int SegManager::relocateBlock(Common::Array<reg_t> &block, int block_location, S return 0; } block[idx].segment = segment; // Perform relocation - if (isSci1_1) + if (_version == SCI_VERSION_1_1) block[idx].offset += getScript(segment)->script_size; return 1; @@ -429,13 +441,51 @@ void SegManager::heapRelocate(reg_t block) { } } -#define INST_LOOKUP_CLASS(id) ((id == 0xffff) ? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, NULL_REG)) +SegmentId SegManager::getSegment(int script_nr, SCRIPT_GET load) { + SegmentId segment; + + if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD) + script_instantiate(_resMgr, this, _version, _oldScriptHeader, script_nr); + + segment = segGet(script_nr); + + if (segment > 0) { + if ((load & SCRIPT_GET_LOCK) == SCRIPT_GET_LOCK) + getScript(segment)->incrementLockers(); + + return segment; + } else + return 0; +} -reg_t get_class_address(EngineState *s, int classnr, SCRIPT_GET lock, reg_t caller); +#define INST_LOOKUP_CLASS(id) ((id == 0xffff) ? NULL_REG : get_class_address(id, SCRIPT_GET_LOCK, NULL_REG)) -Object *SegManager::scriptObjInit0(EngineState *s, reg_t obj_pos) { +reg_t SegManager::get_class_address(int classnr, SCRIPT_GET lock, reg_t caller) { + if (classnr < 0 || (int)_classtable.size() <= classnr || _classtable[classnr].script < 0) { + error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, _classtable.size()); + return NULL_REG; + } else { + Class *the_class = &_classtable[classnr]; + if (!the_class->reg.segment) { + getSegment(the_class->script, lock); + + if (!the_class->reg.segment) { + error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;" + " Entering debugger.", classnr, the_class->script, the_class->script); + return NULL_REG; + } + } else + if (caller.segment != the_class->reg.segment) + getScript(the_class->reg.segment)->incrementLockers(); + + return the_class->reg; + } +} + +Object *SegManager::scriptObjInit0(reg_t obj_pos) { Object *obj; int id; + SciVersion version = _version; // for the offset defines unsigned int base = obj_pos.offset - SCRIPT_OBJECT_MAGIC_OFFSET; reg_t temp; @@ -489,7 +539,7 @@ Object *SegManager::scriptObjInit0(EngineState *s, reg_t obj_pos) { return obj; } -Object *SegManager::scriptObjInit11(EngineState *s, reg_t obj_pos) { +Object *SegManager::scriptObjInit11(reg_t obj_pos) { Object *obj; int id; int base = obj_pos.offset; @@ -543,11 +593,11 @@ Object *SegManager::scriptObjInit11(EngineState *s, reg_t obj_pos) { return obj; } -Object *SegManager::scriptObjInit(EngineState *s, reg_t obj_pos) { - if (!isSci1_1) - return scriptObjInit0(s, obj_pos); +Object *SegManager::scriptObjInit(reg_t obj_pos) { + if (_version != SCI_VERSION_1_1) + return scriptObjInit0(obj_pos); else - return scriptObjInit11(s, obj_pos); + return scriptObjInit11(obj_pos); } LocalVariables *SegManager::allocLocalsSegment(Script *scr, int count) { @@ -588,7 +638,7 @@ void SegManager::scriptInitialiseLocals(reg_t location) { VERIFY(location.offset + 1 < (uint16)scr->buf_size, "Locals beyond end of script\n"); - if (isSci1_1) + if (_version == SCI_VERSION_1_1) count = READ_LE_UINT16(scr->buf + location.offset - 2); else count = (READ_LE_UINT16(scr->buf + location.offset - 2) - 4) >> 1; @@ -627,24 +677,25 @@ void SegManager::scriptRelocateExportsSci11(SegmentId seg) { } } -void SegManager::scriptInitialiseObjectsSci11(EngineState *s, SegmentId seg) { +void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { Script *scr = getScript(seg); byte *seeker = scr->heap_start + 4 + READ_LE_UINT16(scr->heap_start + 2) * 2; + SciVersion version = _version; // for the selector defines while (READ_LE_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { if (READ_LE_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { int classpos = seeker - scr->buf; int species = READ_LE_UINT16(seeker + 10); - if (species < 0 || species >= (int)s->_classtable.size()) { + if (species < 0 || species >= (int)_classtable.size()) { error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d\n", - species, species, s->_classtable.size(), scr->nr); + species, species, _classtable.size(), scr->nr); return; } - s->_classtable[species].script = scr->nr; - s->_classtable[species].reg.segment = seg; - s->_classtable[species].reg.offset = classpos; + _classtable[species].script = scr->nr; + _classtable[species].reg.segment = seg; + _classtable[species].reg.offset = classpos; } seeker += READ_LE_UINT16(seeker + 2) * 2; } @@ -656,12 +707,12 @@ void SegManager::scriptInitialiseObjectsSci11(EngineState *s, SegmentId seg) { reg.segment = seg; reg.offset = seeker - scr->buf; - obj = scriptObjInit(s, reg); + obj = scriptObjInit(reg); #if 0 if (obj->_variables[5].offset != 0xffff) { obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset); - base_obj = obj_get(s, obj->_variables[5]); + base_obj = obj_get(s->seg_manager, s->_version, obj->_variables[5]); obj->variable_names_nr = base_obj->variables_nr; obj->base_obj = base_obj->base_obj; } @@ -866,5 +917,138 @@ int SegManager::freeDynmem(reg_t addr) { return 0; // OK } +int SegManager::createSci11ClassTable() { + int scriptnr; + unsigned int seeker_offset; + char *seeker_ptr; + int classnr; + + Resource *vocab996 = _resMgr->findResource(ResourceId(kResourceTypeVocab, 996), 1); + + if (!vocab996) + _classtable.resize(20); + else + _classtable.resize(vocab996->size >> 2); + + for (scriptnr = 0; scriptnr < 1000; scriptnr++) { + Resource *heap = _resMgr->findResource(ResourceId(kResourceTypeHeap, scriptnr), 0); + + if (heap) { + int global_vars = READ_LE_UINT16(heap->data + 2); + + seeker_ptr = (char*)heap->data + 4 + global_vars * 2; + seeker_offset = 4 + global_vars * 2; + + while (READ_LE_UINT16((byte*)seeker_ptr) == SCRIPT_OBJECT_MAGIC_NUMBER) { + if (READ_LE_UINT16((byte*)seeker_ptr + 14) & SCRIPT_INFO_CLASS) { + classnr = READ_LE_UINT16((byte*)seeker_ptr + 10); + if (classnr >= (int)_classtable.size()) { + if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) { + warning("Invalid class number 0x%x in script.%d(0x%x), offset %04x", + classnr, scriptnr, scriptnr, seeker_offset); + return 1; + } + + _classtable.resize(classnr + 1); // Adjust maximum number of entries + } + + _classtable[classnr].reg.offset = seeker_offset; + _classtable[classnr].reg.segment = 0; + _classtable[classnr].script = scriptnr; + } + + seeker_ptr += READ_LE_UINT16((byte*)seeker_ptr + 2) * 2; + seeker_offset += READ_LE_UINT16((byte*)seeker_ptr + 2) * 2; + } + } + } + + _resMgr->unlockResource(vocab996); + vocab996 = NULL; + return 0; +} + +int SegManager::createSci0ClassTable() { + int scriptnr; + unsigned int seeker; + int classnr; + int magic_offset; // For strange scripts in older SCI versions + + Resource *vocab996 = _resMgr->findResource(ResourceId(kResourceTypeVocab, 996), 1); + SciVersion version = _version; // for the offset defines + + if (!vocab996) + _classtable.resize(20); + else + _classtable.resize(vocab996->size >> 2); + + for (scriptnr = 0; scriptnr < 1000; scriptnr++) { + int objtype = 0; + Resource *script = _resMgr->findResource(ResourceId(kResourceTypeScript, scriptnr), 0); + + if (script) { + if (_oldScriptHeader) + magic_offset = seeker = 2; + else + magic_offset = seeker = 0; + + do { + while (seeker < script->size) { + unsigned int lastseeker = seeker; + objtype = (int16)READ_LE_UINT16(script->data + seeker); + if (objtype == SCI_OBJ_CLASS || objtype == SCI_OBJ_TERMINATOR) + break; + seeker += (int16)READ_LE_UINT16(script->data + seeker + 2); + if (seeker <= lastseeker) { + _classtable.clear(); + error("Script version is invalid"); + } + } + + if (objtype == SCI_OBJ_CLASS) { + int sugg_script; + + seeker -= SCRIPT_OBJECT_MAGIC_OFFSET; // Adjust position; script home is base +8 bytes + + classnr = (int16)READ_LE_UINT16(script->data + seeker + 4 + SCRIPT_SPECIES_OFFSET); + if (classnr >= (int)_classtable.size()) { + + if (classnr >= SCRIPT_MAX_CLASSTABLE_SIZE) { + warning("Invalid class number 0x%x in script.%d(0x%x), offset %04x", + classnr, scriptnr, scriptnr, seeker); + return 1; + } + + _classtable.resize(classnr + 1); // Adjust maximum number of entries + } + + // Map the class ID to the script the corresponding class is contained in + // The script number is found in vocab.996, if it exists + if (!vocab996 || (uint32)classnr >= vocab996->size >> 2) + sugg_script = -1; + else + sugg_script = (int16)READ_LE_UINT16(vocab996->data + 2 + (classnr << 2)); + + // First, test whether the script hasn't been claimed, or if it's been claimed by the wrong script + + if (sugg_script == -1 || scriptnr == sugg_script /*|| !s->_classtable[classnr].reg.segment*/) { + // Now set the home script of the class + _classtable[classnr].reg.offset = seeker + 4 - magic_offset; + _classtable[classnr].reg.segment = 0; + _classtable[classnr].script = scriptnr; + } + + seeker += SCRIPT_OBJECT_MAGIC_OFFSET; // Re-adjust position + seeker += (int16)READ_LE_UINT16(script->data + seeker + 2); // Move to next + } + + } while (objtype != SCI_OBJ_TERMINATOR && seeker <= script->size); + + } + } + _resMgr->unlockResource(vocab996); + vocab996 = NULL; + return 0; +} } // End of namespace Sci diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 9d406f559f..fcf2659df3 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -43,12 +43,22 @@ namespace Sci { (((mgr)._heap[index] && ((mgr)._heap[index]->getType() == MEM_OBJ_SCRIPT || (mgr)._heap[index]->getType() == MEM_OBJ_CLONES))? (mgr)._heap[index] \ : NULL): NULL) +/** + * Parameters for getSegment() + */ +typedef enum { + SCRIPT_GET_DONT_LOAD = 0, /**< Fail if not loaded */ + SCRIPT_GET_LOAD = 1, /**< Load, if neccessary */ + SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */ +} SCRIPT_GET; + + class SegManager : public Common::Serializable { public: /** * Initialize the segment manager */ - SegManager(bool sci1_1); + SegManager(ResourceManager *resMgr, SciVersion version, bool oldScriptHeader); /** * Deallocate all memory associated with the segment manager @@ -61,14 +71,12 @@ public: /** * Allocate a script into the segment manager - * @param s The state containing resource manager - * handlers to load the script data * @param script_nr The number of the script to load * @param seg_id The segment ID of the newly allocated segment, * on success * @return 0 on failure, 1 on success */ - Script *allocateScript(EngineState *s, int script_nr, SegmentId *seg_id); + Script *allocateScript(int script_nr, SegmentId *seg_id); // The script must then be initialised; see section (1b.), below. @@ -154,7 +162,7 @@ public: * @returns A newly created Object describing the object, * stored within the relevant script */ - Object *scriptObjInit(EngineState *s, reg_t obj_pos); + Object *scriptObjInit(reg_t obj_pos); /** * Informs the segment manager that a code block must be relocated @@ -317,21 +325,31 @@ public: */ byte *dereference(reg_t reg, int *size); - + /** + * Determines the segment occupied by a certain script + * @param[in] script_id The script in question + * @param[in] load One of SCRIPT_GET_* + * @return The script's segment, or 0 on failure + */ + SegmentId getSegment(int script_nr, SCRIPT_GET load); + reg_t get_class_address(int classnr, SCRIPT_GET lock, reg_t caller); void heapRelocate(reg_t block); void scriptRelocateExportsSci11(SegmentId seg); - void scriptInitialiseObjectsSci11(EngineState *s, SegmentId seg); - int initialiseScript(Script &scr, EngineState *s, int script_nr); + void scriptInitialiseObjectsSci11(SegmentId seg); + int initialiseScript(Script &scr, int script_nr); private: IntMapper *id_seg_map; ///< id - script id; seg - index of heap + bool _oldScriptHeader; public: // TODO: make private Common::Array<MemObject *> _heap; int reserved_id; int exports_wide; - bool isSci1_1; + SciVersion _version; + ResourceManager *_resMgr; + Common::Array<Class> _classtable; /**< Table of all classes */ SegmentId Clones_seg_id; ///< ID of the (a) clones segment SegmentId Lists_seg_id; ///< ID of the (a) list segment @@ -343,6 +361,8 @@ private: LocalVariables *allocLocalsSegment(Script *scr, int count); MemObject *memObjAllocate(SegmentId segid, int hash_id, MemObjectType type); int deallocate(SegmentId seg, bool recursive); + int createSci0ClassTable(); + int createSci11ClassTable(); Hunk *alloc_Hunk(reg_t *); @@ -351,9 +371,9 @@ private: int relocateObject(Object *obj, SegmentId segment, int location); int findFreeId(int *id); - static void setScriptSize(Script &scr, EngineState *s, int script_nr); - Object *scriptObjInit0(EngineState *s, reg_t obj_pos); - Object *scriptObjInit11(EngineState *s, reg_t obj_pos); + void setScriptSize(Script &scr, int script_nr); + Object *scriptObjInit0(reg_t obj_pos); + Object *scriptObjInit11(reg_t obj_pos); /** * Check segment validity diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index fd45ef5834..baa51bcb58 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -24,6 +24,8 @@ */ #include "sci/engine/state.h" +#include "sci/engine/vm.h" +#include "sci/console.h" // For parse_reg_t namespace Sci { @@ -116,6 +118,8 @@ EngineState::EngineState(ResourceManager *res, SciVersion version, uint32 flags) successor = 0; speedThrottler = new SpeedThrottler(version); + + _doSoundType = kDoSoundTypeUnknown; } EngineState::~EngineState() { @@ -242,4 +246,75 @@ Common::String EngineState::strSplit(const char *str, const char *sep) { return retval; } +EngineState::DoSoundType EngineState::detectDoSoundType() { + if (_doSoundType == kDoSoundTypeUnknown) { + reg_t soundClass; + const uint checkBytes = 6; // Number of bytes to check + + if (!parse_reg_t(this, "?Sound", &soundClass)) { + reg_t fptr; + + Object *obj = obj_get(seg_manager, _version, soundClass); + SelectorType sel = lookup_selector(this, soundClass, ((SciEngine*)g_engine)->getKernel()->_selectorMap.play, NULL, &fptr); + + if (obj && (sel == kSelectorMethod)) { + Script *script = seg_manager->getScript(fptr.segment); + + if (fptr.offset > checkBytes) { + // Go to the last portion of Sound::init, should be right before the play function + fptr.offset -= checkBytes; + byte *buf = script->buf + fptr.offset; + + // Check the call to DoSound's INIT_HANDLE function. + // It's either subfunction 0, 5 or 6, depending on the version of DoSound. + uint sum = 0; + for (uint i = 0; i < checkBytes; i++) + sum += buf[i]; + + switch(sum) { + case 0x1B2: // SCI0 + case 0x1AE: // SCI01 + _doSoundType = kDoSoundTypeSci0; + break; + case 0x13D: + _doSoundType = kDoSoundTypeSci1Early; + break; + case 0x13E: + _doSoundType = kDoSoundTypeSci1Late; + } + } + } + } + + if (_doSoundType == kDoSoundTypeUnknown) { + warning("DoSound detection failed, taking an educated guess"); + + if (_version >= SCI_VERSION_1_MIDDLE) + _doSoundType = kDoSoundTypeSci1Late; + else if (_version > SCI_VERSION_01) + _doSoundType = kDoSoundTypeSci1Early; + else + _doSoundType = kDoSoundTypeSci0; + } + + debugCN(1, kDebugLevelSound, "Detected DoSound type: "); + + switch(_doSoundType) { + case kDoSoundTypeSci0: + debugC(1, kDebugLevelSound, "SCI0"); + break; + case kDoSoundTypeSci1Early: + debugC(1, kDebugLevelSound, "SCI1 Early"); + break; + case kDoSoundTypeSci1Late: + debugC(1, kDebugLevelSound, "SCI1 Late"); + break; + default: + break; + } + } + + return _doSoundType; +} + } // End of namespace Sci diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 39dcbb0c0b..a3983f6ae4 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -163,6 +163,14 @@ struct EngineState : public Common::Serializable { public: EngineState(ResourceManager *res, SciVersion version, uint32 flags); virtual ~EngineState(); + + enum DoSoundType { + kDoSoundTypeUnknown, + kDoSoundTypeSci0, + kDoSoundTypeSci1Early, + kDoSoundTypeSci1Late + }; + virtual void saveLoadWithSerializer(Common::Serializer &ser); kLanguage getLanguage(); @@ -272,6 +280,12 @@ public: */ Common::String strSplit(const char *str, const char *sep = "\r----------\r"); + /** + * Autodetects the DoSound type + * @return DoSound type + */ + DoSoundType detectDoSoundType(); + /* Debugger data: */ Breakpoint *bp_list; /**< List of breakpoints */ int have_bp; /**< Bit mask specifying which types of breakpoints are used in bp_list */ @@ -291,8 +305,6 @@ public: reg_t game_obj; /**< Pointer to the game object */ - Common::Array<Class> _classtable; /**< Table of all classes */ - SegManager *seg_manager; int gc_countdown; /**< Number of kernel calls until next gc */ @@ -303,6 +315,7 @@ public: EngineState *successor; /**< Successor of this state: Used for restoring */ private: + DoSoundType _doSoundType; kLanguage charToLanguage(const char c) const; Common::String getLanguageString(const char *str, kLanguage lang) const; }; diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index ae07c314d4..64ee7243fb 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -187,34 +187,6 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i #define OBJ_PROPERTY(o, p) (validate_property(o, p)) -reg_t get_class_address(EngineState *s, int classnr, SCRIPT_GET lock, reg_t caller) { - - if (NULL == s) { - warning("vm.c: get_class_address(): NULL passed for \"s\""); - return NULL_REG; - } - - if (classnr < 0 || (int)s->_classtable.size() <= classnr || s->_classtable[classnr].script < 0) { - error("[VM] Attempt to dereference class %x, which doesn't exist (max %x)", classnr, s->_classtable.size()); - return NULL_REG; - } else { - Class *the_class = &s->_classtable[classnr]; - if (!the_class->reg.segment) { - script_get_segment(s, the_class->script, lock); - - if (!the_class->reg.segment) { - error("[VM] Trying to instantiate class %x by instantiating script 0x%x (%03d) failed;" - " Entering debugger.", classnr, the_class->script, the_class->script); - return NULL_REG; - } - } else - if (caller.segment != the_class->reg.segment) - s->seg_manager->getScript(the_class->reg.segment)->incrementLockers(); - - return the_class->reg; - } -} - // Operating on the stack // 16 bit: #define PUSH(v) PUSH32(make_reg(0, v)) @@ -236,7 +208,7 @@ ExecStack *execute_method(EngineState *s, uint16 script, uint16 pubfunct, StackP Script *scr = s->seg_manager->getScriptIfLoaded(seg); if (!scr) // Script not present yet? - seg = script_instantiate(s, script); + seg = script_instantiate(s->resmgr, s->seg_manager, s->_version, ((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader(), script); else scr->unmarkDeleted(); @@ -313,7 +285,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt Breakpoint *bp; char method_name [256]; - sprintf(method_name, "%s::%s", obj_get_name(s, send_obj), ((SciEngine*)g_engine)->getKernel()->getSelectorName(selector).c_str()); + sprintf(method_name, "%s::%s", obj_get_name(s->seg_manager, s->_version, send_obj), ((SciEngine*)g_engine)->getKernel()->getSelectorName(selector).c_str()); bp = s->bp_list; while (bp) { @@ -501,10 +473,6 @@ void vm_handle_fatal_error(EngineState *s, int line, const char *file) { error("Fatal VM error in %s, L%d; aborting...", file, line); } -static Script *script_locate_by_segment(EngineState *s, SegmentId seg) { - return s->seg_manager->getScriptIfLoaded(seg); -} - static reg_t pointer_add(EngineState *s, reg_t base, int offset) { MemObject *mobj = GET_SEGMENT_ANY(*s->seg_manager, base.segment); @@ -559,8 +527,8 @@ void run_vm(EngineState *s, int restoring) { // Current execution data: scriptState.xs = &(s->_executionStack.back()); ExecStack *xs_new = NULL; - Object *obj = obj_get(s, scriptState.xs->objp); - Script *local_script = script_locate_by_segment(s, scriptState.xs->local_segment); + Object *obj = obj_get(s->seg_manager, s->_version, scriptState.xs->objp); + Script *local_script = s->seg_manager->getScriptIfLoaded(scriptState.xs->local_segment); int old_execution_stack_base = s->execution_stack_base; // Used to detect the stack bottom, for "physical" returns const byte *code_buf = NULL; // (Avoid spurious warning) @@ -606,7 +574,7 @@ void run_vm(EngineState *s, int restoring) { scriptState.xs = &(s->_executionStack.back()); s->_executionStackPosChanged = false; - scr = script_locate_by_segment(s, scriptState.xs->addr.pc.segment); + scr = s->seg_manager->getScriptIfLoaded(scriptState.xs->addr.pc.segment); if (!scr) { // No script? Implicit return via fake instruction buffer warning("Running on non-existant script in segment %x", scriptState.xs->addr.pc.segment); @@ -619,12 +587,12 @@ void run_vm(EngineState *s, int restoring) { scr = NULL; obj = NULL; } else { - obj = obj_get(s, scriptState.xs->objp); + obj = obj_get(s->seg_manager, s->_version, scriptState.xs->objp); code_buf = scr->buf; #ifndef DISABLE_VALIDATIONS code_buf_size = scr->buf_size; #endif - local_script = script_locate_by_segment(s, scriptState.xs->local_segment); + local_script = s->seg_manager->getScriptIfLoaded(scriptState.xs->local_segment); if (!local_script) { warning("Could not find local script from segment %x", scriptState.xs->local_segment); local_script = NULL; @@ -1077,7 +1045,7 @@ void run_vm(EngineState *s, int restoring) { break; case 0x28: // class - s->r_acc = get_class_address(s, (unsigned)opparams[0], SCRIPT_GET_LOCK, + s->r_acc = s->seg_manager->get_class_address((unsigned)opparams[0], SCRIPT_GET_LOCK, scriptState.xs->addr.pc); break; @@ -1097,7 +1065,7 @@ void run_vm(EngineState *s, int restoring) { break; case 0x2b: // super - r_temp = get_class_address(s, opparams[0], SCRIPT_GET_LOAD, scriptState.xs->addr.pc); + r_temp = s->seg_manager->get_class_address(opparams[0], SCRIPT_GET_LOAD, scriptState.xs->addr.pc); if (!r_temp.segment) error("[VM]: Invalid superclass in object"); @@ -1432,6 +1400,7 @@ void run_vm(EngineState *s, int restoring) { static int _obj_locate_varselector(EngineState *s, Object *obj, Selector slc) { // Determines if obj explicitly defines slc as a varselector // Returns -1 if not found + SciVersion version = s->_version; // for the selector defines if (s->_version < SCI_VERSION_1_1) { int varnum = obj->variable_names_nr; @@ -1452,7 +1421,7 @@ static int _obj_locate_varselector(EngineState *s, Object *obj, Selector slc) { int varnum = obj->_variables[1].offset; if (!(obj->_variables[SCRIPT_INFO_SELECTOR].offset & SCRIPT_INFO_CLASS)) - buf = ((byte *) obj_get(s, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR])->base_vars); + buf = ((byte *) obj_get(s->seg_manager, s->_version, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR])->base_vars); for (i = 0; i < varnum; i++) if (READ_LE_UINT16(buf + (i << 1)) == slc) // Found it? @@ -1478,6 +1447,7 @@ static int _class_locate_funcselector(EngineState *s, Object *obj, Selector slc) static SelectorType _lookup_selector_function(EngineState *s, int seg_id, Object *obj, Selector selector_id, reg_t *fptr) { int index; + SciVersion version = s->_version; // for the selector defines // "recursive" lookup @@ -1492,7 +1462,7 @@ static SelectorType _lookup_selector_function(EngineState *s, int seg_id, Object return kSelectorMethod; } else { seg_id = obj->_variables[SCRIPT_SUPERCLASS_SELECTOR].segment; - obj = obj_get(s, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]); + obj = obj_get(s->seg_manager, s->_version, obj->_variables[SCRIPT_SUPERCLASS_SELECTOR]); } } @@ -1500,9 +1470,10 @@ static SelectorType _lookup_selector_function(EngineState *s, int seg_id, Object } SelectorType lookup_selector(EngineState *s, reg_t obj_location, Selector selector_id, ObjVarRef *varp, reg_t *fptr) { - Object *obj = obj_get(s, obj_location); + Object *obj = obj_get(s->seg_manager, s->_version, obj_location); Object *species; int index; + SciVersion version = s->_version; // for the selector defines // Early SCI versions used the LSB in the selector ID as a read/write // toggle, meaning that we must remove it for selector lookup. @@ -1517,7 +1488,7 @@ SelectorType lookup_selector(EngineState *s, reg_t obj_location, Selector select if (IS_CLASS(obj)) species = obj; else - species = obj_get(s, obj->_variables[SCRIPT_SPECIES_SELECTOR]); + species = obj_get(s->seg_manager, s->_version, obj->_variables[SCRIPT_SPECIES_SELECTOR]); if (!obj) { @@ -1540,65 +1511,27 @@ SelectorType lookup_selector(EngineState *s, reg_t obj_location, Selector select return _lookup_selector_function(s, obj_location.segment, obj, selector_id, fptr); } -SegmentId script_get_segment(EngineState *s, int script_nr, SCRIPT_GET load) { - SegmentId segment; - - if ((load & SCRIPT_GET_LOAD) == SCRIPT_GET_LOAD) - script_instantiate(s, script_nr); - - segment = s->seg_manager->segGet(script_nr); - - if (segment > 0) { - if ((load & SCRIPT_GET_LOCK) == SCRIPT_GET_LOCK) - s->seg_manager->getScript(segment)->incrementLockers(); - - return segment; - } else - return 0; +reg_t script_lookup_export(SegManager *segManager, int script_nr, int export_index) { + SegmentId seg = segManager->getSegment(script_nr, SCRIPT_GET_DONT_LOAD); + Script *script = segManager->getScriptIfLoaded(seg); + return make_reg(seg, READ_LE_UINT16((byte *)(script->export_table + export_index))); } -reg_t script_lookup_export(EngineState *s, int script_nr, int export_index) { - SegmentId seg = script_get_segment(s, script_nr, SCRIPT_GET_DONT_LOAD); - Script *script = NULL; +#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segManager->get_class_address(id, SCRIPT_GET_LOCK, reg)) -#ifndef DISABLE_VALIDATIONS - if (!seg) - error("script_lookup_export(): script.%03d (0x%x) is invalid or not loaded", - script_nr, script_nr); -#endif - - script = script_locate_by_segment(s, seg); - -#ifndef DISABLE_VALIDATIONS - if (script && export_index < script->exports_nr && export_index >= 0) -#endif - return make_reg(seg, READ_LE_UINT16((byte *)(script->export_table + export_index))); -#ifndef DISABLE_VALIDATIONS - else { - if (!script) - error("script_lookup_export(): script.%03d missing", script_nr); - else - error("script_lookup_export(): script.%03d: Sought invalid export %d/%d", - script_nr, export_index, script->exports_nr); - } -#endif -} - -#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : get_class_address(s, id, SCRIPT_GET_LOCK, reg)) - -int script_instantiate_common(EngineState *s, int script_nr, Resource **script, Resource **heap, int *was_new) { +int script_instantiate_common(ResourceManager *resMgr, SegManager *segManager, SciVersion version, int script_nr, Resource **script, Resource **heap, int *was_new) { int seg_id; reg_t reg; *was_new = 1; - *script = s->resmgr->findResource(ResourceId(kResourceTypeScript, script_nr), 0); - if (s->_version >= SCI_VERSION_1_1) - *heap = s->resmgr->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); + *script = resMgr->findResource(ResourceId(kResourceTypeScript, script_nr), 0); + if (version >= SCI_VERSION_1_1) + *heap = resMgr->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - if (!*script || (s->_version >= SCI_VERSION_1_1 && !heap)) { + if (!*script || (version >= SCI_VERSION_1_1 && !heap)) { warning("Script 0x%x requested but not found", script_nr); - if (s->_version >= SCI_VERSION_1_1) { + if (version >= SCI_VERSION_1_1) { if (*heap) warning("Inconsistency: heap resource WAS found"); else if (*script) @@ -1607,13 +1540,8 @@ int script_instantiate_common(EngineState *s, int script_nr, Resource **script, return 0; } - if (NULL == s) { - warning("script_instantiate_common(): script_instantiate(): NULL passed for \"s\""); - return 0; - } - - seg_id = s->seg_manager->segGet(script_nr); - Script *scr = script_locate_by_segment(s, seg_id); + seg_id = segManager->segGet(script_nr); + Script *scr = segManager->getScriptIfLoaded(seg_id); if (scr) { if (!scr->isMarkedAsDeleted()) { scr->incrementLockers(); @@ -1622,14 +1550,14 @@ int script_instantiate_common(EngineState *s, int script_nr, Resource **script, scr->freeScript(); } } else { - scr = s->seg_manager->allocateScript(s, script_nr, &seg_id); + scr = segManager->allocateScript(script_nr, &seg_id); if (!scr) { // ALL YOUR SCRIPT BASE ARE BELONG TO US error("Not enough heap space for script size 0x%x of script 0x%x (Should this happen?)", (*script)->size, script_nr); return 0; } } - s->seg_manager->initialiseScript(*scr, s, script_nr); + segManager->initialiseScript(*scr, script_nr); reg.segment = seg_id; reg.offset = 0; @@ -1645,7 +1573,7 @@ int script_instantiate_common(EngineState *s, int script_nr, Resource **script, return seg_id; } -int script_instantiate_sci0(EngineState *s, int script_nr) { +int script_instantiate_sci0(ResourceManager *resMgr, SegManager *segManager, SciVersion version, bool oldScriptHeader, int script_nr) { int objtype; unsigned int objlength; reg_t reg; @@ -1655,7 +1583,7 @@ int script_instantiate_sci0(EngineState *s, int script_nr) { Resource *script; int was_new; - seg_id = script_instantiate_common(s, script_nr, &script, NULL, &was_new); + seg_id = script_instantiate_common(resMgr, segManager, version, script_nr, &script, NULL, &was_new); if (was_new) return seg_id; @@ -1663,9 +1591,9 @@ int script_instantiate_sci0(EngineState *s, int script_nr) { reg.segment = seg_id; reg.offset = 0; - Script *scr = s->seg_manager->getScript(seg_id); + Script *scr = segManager->getScript(seg_id); - if (((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader()) { + if (oldScriptHeader) { // int locals_nr = READ_LE_UINT16(script->data); @@ -1678,7 +1606,7 @@ int script_instantiate_sci0(EngineState *s, int script_nr) { magic_pos_adder = 2; // Step over the funny prefix if (locals_nr) - s->seg_manager->scriptInitialiseLocalsZero(reg.segment, locals_nr); + segManager->scriptInitialiseLocalsZero(reg.segment, locals_nr); } else { scr->mcpyInOut(0, script->data, script->size); @@ -1717,24 +1645,24 @@ int script_instantiate_sci0(EngineState *s, int script_nr) { break; case SCI_OBJ_LOCALVARS: - s->seg_manager->scriptInitialiseLocals(data_base); + segManager->scriptInitialiseLocals(data_base); break; case SCI_OBJ_CLASS: { int classpos = addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET; int species; species = scr->getHeap(addr.offset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET); - if (species < 0 || species >= (int)s->_classtable.size()) { + if (species < 0 || species >= (int)segManager->_classtable.size()) { error("Invalid species %d(0x%x) not in interval " "[0,%d) while instantiating script %d\n", - species, species, s->_classtable.size(), + species, species, segManager->_classtable.size(), script_nr); return 1; } - s->_classtable[species].script = script_nr; - s->_classtable[species].reg = addr; - s->_classtable[species].reg.offset = classpos; + segManager->_classtable[species].script = script_nr; + segManager->_classtable[species].reg = addr; + segManager->_classtable[species].reg.offset = classpos; // Set technical class position-- into the block allocated for it } break; @@ -1760,17 +1688,17 @@ int script_instantiate_sci0(EngineState *s, int script_nr) { switch (objtype) { case SCI_OBJ_CODE: - s->seg_manager->scriptAddCodeBlock(addr); + segManager->scriptAddCodeBlock(addr); break; case SCI_OBJ_OBJECT: case SCI_OBJ_CLASS: { // object or class? - Object *obj = s->seg_manager->scriptObjInit(s, addr); + Object *obj = segManager->scriptObjInit(addr); Object *base_obj; // Instantiate the superclass, if neccessary obj->_variables[SCRIPT_SPECIES_SELECTOR] = INST_LOOKUP_CLASS(obj->_variables[SCRIPT_SPECIES_SELECTOR].offset); - base_obj = obj_get(s, obj->_variables[SCRIPT_SPECIES_SELECTOR]); + base_obj = obj_get(segManager, version, obj->_variables[SCRIPT_SPECIES_SELECTOR]); obj->variable_names_nr = base_obj->_variables.size(); obj->base_obj = base_obj->base_obj; // Copy base from species class, as we need its selector IDs @@ -1791,24 +1719,24 @@ int script_instantiate_sci0(EngineState *s, int script_nr) { } while ((objtype != 0) && (((unsigned)reg.offset) < script->size - 2)); if (relocation >= 0) - s->seg_manager->scriptRelocate(make_reg(reg.segment, relocation)); + segManager->scriptRelocate(make_reg(reg.segment, relocation)); return reg.segment; // instantiation successful } -int script_instantiate_sci11(EngineState *s, int script_nr) { +int script_instantiate_sci11(ResourceManager *resMgr, SegManager *segManager, SciVersion version, int script_nr) { Resource *script, *heap; int seg_id; int heap_start; reg_t reg; int was_new; - seg_id = script_instantiate_common(s, script_nr, &script, &heap, &was_new); + seg_id = script_instantiate_common(resMgr, segManager, version, script_nr, &script, &heap, &was_new); if (was_new) return seg_id; - Script *scr = s->seg_manager->getScript(seg_id); + Script *scr = segManager->getScript(seg_id); heap_start = script->size; if (script->size & 2) @@ -1822,28 +1750,28 @@ int script_instantiate_sci11(EngineState *s, int script_nr) { reg.segment = seg_id; reg.offset = heap_start + 4; - s->seg_manager->scriptInitialiseLocals(reg); + segManager->scriptInitialiseLocals(reg); - s->seg_manager->scriptRelocateExportsSci11(seg_id); - s->seg_manager->scriptInitialiseObjectsSci11(s, seg_id); + segManager->scriptRelocateExportsSci11(seg_id); + segManager->scriptInitialiseObjectsSci11(seg_id); reg.offset = READ_LE_UINT16(heap->data); - s->seg_manager->heapRelocate(reg); + segManager->heapRelocate(reg); return seg_id; } -int script_instantiate(EngineState *s, int script_nr) { - if (s->_version >= SCI_VERSION_1_1) - return script_instantiate_sci11(s, script_nr); +int script_instantiate(ResourceManager *resMgr, SegManager *segManager, SciVersion version, bool oldScriptHeader, int script_nr) { + if (version >= SCI_VERSION_1_1) + return script_instantiate_sci11(resMgr, segManager, version, script_nr); else - return script_instantiate_sci0(s, script_nr); + return script_instantiate_sci0(resMgr, segManager, version, oldScriptHeader, script_nr); } -void script_uninstantiate_sci0(EngineState *s, int script_nr, SegmentId seg) { +void script_uninstantiate_sci0(SegManager *segManager, SciVersion version, int script_nr, SegmentId seg) { reg_t reg = make_reg(seg, ((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader() ? 2 : 0); int objtype, objlength; - Script *scr = s->seg_manager->getScript(seg); + Script *scr = segManager->getScript(seg); // Make a pass over the object in order uninstantiate all superclasses objlength = 0; @@ -1866,13 +1794,13 @@ void script_uninstantiate_sci0(EngineState *s, int script_nr, SegmentId seg) { superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass... if (superclass >= 0) { - int superclass_script = s->_classtable[superclass].script; + int superclass_script = segManager->_classtable[superclass].script; if (superclass_script == script_nr) { if (scr->getLockers()) scr->decrementLockers(); // Decrease lockers if this is us ourselves } else - script_uninstantiate(s, superclass_script); + script_uninstantiate(segManager, version, superclass_script); // Recurse to assure that the superclass lockers number gets decreased } @@ -1884,11 +1812,9 @@ void script_uninstantiate_sci0(EngineState *s, int script_nr, SegmentId seg) { } while (objtype != 0); } -void script_uninstantiate(EngineState *s, int script_nr) { - reg_t reg = make_reg(0, ((SciEngine*)g_engine)->getKernel()->hasOldScriptHeader() ? 2 : 0); - - reg.segment = s->seg_manager->segGet(script_nr); - Script *scr = script_locate_by_segment(s, reg.segment); +void script_uninstantiate(SegManager *segManager, SciVersion version, int script_nr) { + SegmentId segment = segManager->segGet(script_nr); + Script *scr = segManager->getScriptIfLoaded(segment); if (!scr) { // Is it already loaded? //warning("unloading script 0x%x requested although not loaded", script_nr); @@ -1902,12 +1828,12 @@ void script_uninstantiate(EngineState *s, int script_nr) { return; // Free all classtable references to this script - for (uint i = 0; i < s->_classtable.size(); i++) - if (s->_classtable[i].reg.segment == reg.segment) - s->_classtable[i].reg = NULL_REG; + for (uint i = 0; i < segManager->_classtable.size(); i++) + if (segManager->_classtable[i].reg.segment == segment) + segManager->_classtable[i].reg = NULL_REG; - if (s->_version < SCI_VERSION_1_1) - script_uninstantiate_sci0(s, script_nr, reg.segment); + if (version < SCI_VERSION_1_1) + script_uninstantiate_sci0(segManager, version, script_nr, segment); else warning("FIXME: Add proper script uninstantiation for SCI 1.1"); @@ -1999,8 +1925,8 @@ int game_run(EngineState **_s) { return 0; } -Object *obj_get(EngineState *s, reg_t offset) { - MemObject *mobj = GET_OBJECT_SEGMENT(*s->seg_manager, offset.segment); +Object *obj_get(SegManager *segManager, SciVersion version, reg_t offset) { + MemObject *mobj = GET_OBJECT_SEGMENT(*segManager, offset.segment); Object *obj = NULL; int idx; @@ -2023,8 +1949,8 @@ Object *obj_get(EngineState *s, reg_t offset) { return obj; } -const char *obj_get_name(EngineState *s, reg_t pos) { - Object *obj = obj_get(s, pos); +const char *obj_get_name(SegManager *segManager, SciVersion version, reg_t pos) { + Object *obj = obj_get(segManager, version, pos); if (!obj) return "<no such object>"; @@ -2032,7 +1958,7 @@ const char *obj_get_name(EngineState *s, reg_t pos) { if (nameReg.isNull()) return "<no name>"; - const char *name = (const char*)s->seg_manager->dereference(obj->_variables[SCRIPT_NAME_SELECTOR], NULL); + const char *name = (const char*)segManager->dereference(obj->_variables[SCRIPT_NAME_SELECTOR], NULL); if (!name) return "<invalid name>"; @@ -2056,7 +1982,7 @@ void shrink_execution_stack(EngineState *s, uint size) { } reg_t* ObjVarRef::getPointer(EngineState *s) const { - Object *o = obj_get(s, obj); + Object *o = obj_get(s->seg_manager, s->_version, obj); if (!o) return 0; return &(o->_variables[varindex]); } diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index ba225a9c00..867f732e2a 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -29,6 +29,7 @@ /* VM and kernel declarations */ #include "sci/engine/vm_types.h" // for reg_t +#include "sci/resource.h" // for SciVersion #include "common/util.h" @@ -39,6 +40,7 @@ struct EngineState; typedef int sci_version_t; struct IntMapper; struct Object; +class ResourceManager; /** Number of bytes to be allocated for the stack */ #define VM_STACK_SIZE 0x1000 @@ -67,12 +69,12 @@ struct Object; #define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8 /** Offset of the name pointer */ -#define SCRIPT_NAME_OFFSET (s->_version < SCI_VERSION_1_1 ? 14 -8 : 16) -#define SCRIPT_NAME_SELECTOR (s->_version < SCI_VERSION_1_1 ? 3 : 8) +#define SCRIPT_NAME_OFFSET (version < SCI_VERSION_1_1 ? 14 -8 : 16) +#define SCRIPT_NAME_SELECTOR (version < SCI_VERSION_1_1 ? 3 : 8) /** Object-relative offset of the -info- selector */ -#define SCRIPT_INFO_OFFSET (s->_version < SCI_VERSION_1_1 ? 12 -8 : 14) -#define SCRIPT_INFO_SELECTOR (s->_version < SCI_VERSION_1_1 ? 2 : 7) +#define SCRIPT_INFO_OFFSET (version < SCI_VERSION_1_1 ? 12 -8 : 14) +#define SCRIPT_INFO_SELECTOR (version < SCI_VERSION_1_1 ? 2 : 7) /** Flag fo the -info- selector */ #define SCRIPT_INFO_CLONE 0x0001 @@ -84,18 +86,18 @@ struct Object; /** Magical object identifier */ #define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234 /** Offset of this identifier */ -#define SCRIPT_OBJECT_MAGIC_OFFSET (s->_version < SCI_VERSION_1_1 ? -8 : 0) +#define SCRIPT_OBJECT_MAGIC_OFFSET (version < SCI_VERSION_1_1 ? -8 : 0) /** Script-relative offset of the species ID */ #define SCRIPT_SPECIES_OFFSET 8 -8 -#define SCRIPT_SUPERCLASS_OFFSET (s->_version < SCI_VERSION_1_1 ? 10 -8 : 12) +#define SCRIPT_SUPERCLASS_OFFSET (version < SCI_VERSION_1_1 ? 10 -8 : 12) /*---------------------------------*/ /* Script selector index variables */ /*---------------------------------*/ -#define SCRIPT_SPECIES_SELECTOR (s->_version < SCI_VERSION_1_1 ? 0 : 5) -#define SCRIPT_SUPERCLASS_SELECTOR (s->_version < SCI_VERSION_1_1 ? 1 : 6) +#define SCRIPT_SPECIES_SELECTOR (version < SCI_VERSION_1_1 ? 0 : 5) +#define SCRIPT_SUPERCLASS_SELECTOR (version < SCI_VERSION_1_1 ? 1 : 6) #define SCRIPT_CLASSSCRIPT_SELECTOR 4 /** Magic adjustment value for lofsa and lofss */ @@ -467,31 +469,13 @@ SelectorType lookup_selector(EngineState *s, reg_t obj, Selector selectorid, ObjVarRef *varp, reg_t *fptr); /** - * Parameters for script_get_segment() - */ -typedef enum { - SCRIPT_GET_DONT_LOAD = 0, /**< Fail if not loaded */ - SCRIPT_GET_LOAD = 1, /**< Load, if neccessary */ - SCRIPT_GET_LOCK = 3 /**< Load, if neccessary, and lock */ -} SCRIPT_GET; - -/** - * Determines the segment occupied by a certain script - * @param[in] s The state to operate on - * @param[in] script_id The script in question - * @param[in] load One of SCRIPT_GET_* - * @return The script's segment, or 0 on failure - */ -SegmentId script_get_segment(EngineState *s, int script_id, SCRIPT_GET load); - -/** * Looks up an entry of the exports table of a script - * @param[in] s The state to operate on + * @param[in] segManager The segment manager * @param[in] script_nr The script to look up in * @param[out] export_index The index of the export entry to look up * @return The handle */ -reg_t script_lookup_export(EngineState *s, int script_nr, int export_index); +reg_t script_lookup_export(SegManager *segManager, int script_nr, int export_index); /** * Makes sure that a script and its superclasses get loaded to the heap. @@ -499,21 +483,24 @@ reg_t script_lookup_export(EngineState *s, int script_nr, int export_index); * increased. All scripts containing superclasses of this script are loaded * recursively as well, unless 'recursive' is set to zero. The * complementary function is "script_uninstantiate()" below. - * @param[in] s The state to operate on - * @param[in] script_nr The script number to load - * @return The script's segment ID or 0 if out of heap + * @param[in] resMgr The resource manager + * @param[in] segManager The segment manager + * @param[in] version The SCI version to use + * @param[in] script_nr The script number to load + * @return The script's segment ID or 0 if out of heap */ -int script_instantiate(EngineState *s, int script_nr); +int script_instantiate(ResourceManager *resMgr, SegManager *segManager, SciVersion version, bool oldScriptHeader, int script_nr); /** * Decreases the numer of lockers of a script and unloads it if that number * reaches zero. * This function will recursively unload scripts containing its * superclasses, if those aren't locked by other scripts as well. - * @param[in] s The state to operate on + * @param[in] segManager The segment manager + * @param[in] version The SCI version to use * @param[in] script_nr The script number that is requestet to be unloaded */ -void script_uninstantiate(EngineState *s, int script_nr); +void script_uninstantiate(SegManager *segManager, SciVersion version, int script_nr); /** * Initializes an SCI game @@ -613,7 +600,7 @@ int kfree(EngineState *s, reg_t handle); * in a static buffer and need not be freed (neither may * it be modified). */ -const char *obj_get_name(EngineState *s, reg_t pos); +const char *obj_get_name(SegManager *segManager, SciVersion version, reg_t pos); /** * Retrieves an object from the specified location @@ -621,7 +608,7 @@ const char *obj_get_name(EngineState *s, reg_t pos); * @param[in] offset The object's offset * @return The object in question, or NULL if there is none */ -Object *obj_get(EngineState *s, reg_t offset); +Object *obj_get(SegManager *segManager, SciVersion version, reg_t offset); /** * Shrink execution stack to size. diff --git a/engines/sci/gfx/gfx_state_internal.h b/engines/sci/gfx/gfx_state_internal.h index 3beb0ea067..1bff83e713 100644 --- a/engines/sci/gfx/gfx_state_internal.h +++ b/engines/sci/gfx/gfx_state_internal.h @@ -26,6 +26,7 @@ #ifndef SCI_GFX_GFX_STATE_INTERNAL_H #define SCI_GFX_GFX_STATE_INTERNAL_H +#include "sci/engine/vm.h" #include "sci/gfx/gfx_tools.h" #include "sci/gfx/gfx_options.h" #include "sci/gfx/operations.h" diff --git a/engines/sci/gfx/gfx_widgets.h b/engines/sci/gfx/gfx_widgets.h index 80129152cb..ace13ff1b9 100644 --- a/engines/sci/gfx/gfx_widgets.h +++ b/engines/sci/gfx/gfx_widgets.h @@ -29,6 +29,7 @@ #include "common/rect.h" +#include "sci/engine/vm.h" #include "sci/gfx/gfx_system.h" #include "sci/gfx/operations.h" diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index ef31fcdd7d..9b9c9ee26c 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -112,6 +112,20 @@ ResourceSource *ResourceManager::addExternalMap(const char *file_name) { newsrc->source_type = kSourceExtMap; newsrc->location_name = file_name; + newsrc->resourceFile = 0; + newsrc->scanned = false; + newsrc->associated_map = NULL; + + _sources.push_back(newsrc); + return newsrc; +} + +ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) { + ResourceSource *newsrc = new ResourceSource(); + + newsrc->source_type = kSourceExtMap; + newsrc->location_name = mapFile->getName(); + newsrc->resourceFile = mapFile; newsrc->scanned = false; newsrc->associated_map = NULL; @@ -125,6 +139,21 @@ ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType ty newsrc->source_type = type; newsrc->scanned = false; newsrc->location_name = filename; + newsrc->resourceFile = 0; + newsrc->volume_number = number; + newsrc->associated_map = map; + + _sources.push_back(newsrc); + return newsrc; +} + +ResourceSource *ResourceManager::addSource(ResourceSource *map, ResSourceType type, const Common::FSNode *resFile, int number) { + ResourceSource *newsrc = new ResourceSource(); + + newsrc->source_type = type; + newsrc->scanned = false; + newsrc->location_name = resFile->getName(); + newsrc->resourceFile = resFile; newsrc->volume_number = number; newsrc->associated_map = map; @@ -342,6 +371,48 @@ int ResourceManager::addAppropriateSources() { return 1; } +int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { + ResourceSource *map = 0; + + // First, find resource.map + for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + if (file->isDirectory()) + continue; + + Common::String filename = file->getName(); + filename.toLowercase(); + + if (filename.contains("resource.map") || filename.contains("resmap.000")) { + map = addExternalMap(file); + break; + } + } + + if (!map) + return 0; + + // Now find all the resource.0?? files + for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { + if (file->isDirectory()) + continue; + + Common::String filename = file->getName(); + filename.toLowercase(); + + if (filename.contains("resource.0") || filename.contains("ressci.0")) { + const char *dot = strrchr(filename.c_str(), '.'); + int number = atoi(dot + 1); + + addSource(map, kSourceVolume, file, number); + } + } + + // This function is only called by the advanced detector, and we don't really need + // to add a patch directory or message.map here + + return 1; +} + int ResourceManager::addInternalSources() { Common::List<ResourceId> *resources = listResources(kResourceTypeMap); Common::List<ResourceId>::iterator itr = resources->begin(); @@ -396,16 +467,23 @@ void ResourceManager::freeResourceSources() { _sources.clear(); } -ResourceManager::ResourceManager(int maxMemory) { - _maxMemory = maxMemory; +ResourceManager::ResourceManager() { + addAppropriateSources(); + init(); +} + +ResourceManager::ResourceManager(const Common::FSList &fslist) { + addAppropriateSources(fslist); + init(); +} + +void ResourceManager::init() { _memoryLocked = 0; _memoryLRU = 0; _LRU.clear(); _resMap.clear(); _audioMapSCI1 = NULL; - addAppropriateSources(); - // FIXME: put this in an Init() function, so that we can error out if detection fails completely _mapVersion = detectMapVersion(); @@ -506,7 +584,7 @@ void ResourceManager::printLRU() { } void ResourceManager::freeOldResources() { - while (_maxMemory < _memoryLRU) { + while (MAX_MEMORY < _memoryLRU) { assert(!_LRU.empty()); Resource *goner = *_LRU.reverse_begin(); removeFromLRU(goner); @@ -602,7 +680,8 @@ const char *ResourceManager::versionDescription(ResVersion version) const { } ResourceManager::ResVersion ResourceManager::detectMapVersion() { - Common::File file; + Common::SeekableReadStream *fileStream = 0; + Common::File *file = 0; byte buff[6]; ResourceSource *rsrc= 0; @@ -610,22 +689,30 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { rsrc = *it; if (rsrc->source_type == kSourceExtMap) { - file.open(rsrc->location_name); + if (rsrc->resourceFile) { + fileStream = rsrc->resourceFile->createReadStream(); + } else { + file = new Common::File(); + file->open(rsrc->location_name); + if (file->isOpen()) + fileStream = file; + } break; } } - if (file.isOpen() == false) { + + if (!fileStream) { error("Failed to open resource map file"); return kResVersionUnknown; } // detection // SCI0 and SCI01 maps have last 6 bytes set to FF - file.seek(-4, SEEK_END); - uint32 uEnd = file.readUint32LE(); + fileStream->seek(-4, SEEK_END); + uint32 uEnd = fileStream->readUint32LE(); if (uEnd == 0xFFFFFFFF) { // check if 0 or 01 - try to read resources in SCI0 format and see if exists - file.seek(0, SEEK_SET); - while (file.read(buff, 6) == 6 && !(buff[0] == 0xFF && buff[1] == 0xFF && buff[2] == 0xFF)) { + fileStream->seek(0, SEEK_SET); + while (fileStream->read(buff, 6) == 6 && !(buff[0] == 0xFF && buff[1] == 0xFF && buff[2] == 0xFF)) { if (getVolume(rsrc, (buff[5] & 0xFC) >> 2) == NULL) return kResVersionSci1Middle; } @@ -639,14 +726,15 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { uint16 lastDirectoryOffset = 0; uint16 directorySize = 0; ResVersion mapDetected = kResVersionUnknown; - file.seek(0, SEEK_SET); - while (!file.eos()) { - directoryType = file.readByte(); - directoryOffset = file.readUint16LE(); + fileStream->seek(0, SEEK_SET); + + while (!fileStream->eos()) { + directoryType = fileStream->readByte(); + directoryOffset = fileStream->readUint16LE(); if ((directoryType < 0x80) || ((directoryType > 0xA0) && (directoryType != 0xFF))) break; // Offset is above file size? -> definitely not SCI1/SCI1.1 - if (directoryOffset > file.size()) + if (directoryOffset > fileStream->size()) break; if (lastDirectoryOffset) { directorySize = directoryOffset - lastDirectoryOffset; @@ -655,11 +743,14 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { if ((directorySize % 5 == 0) && (directorySize % 6)) mapDetected = kResVersionSci11; } - if (directoryType==0xFF) { + if (directoryType == 0xFF) { // FFh entry needs to point to EOF - if (directoryOffset != file.size()) + if (directoryOffset != fileStream->size()) break; - if (mapDetected) + + delete fileStream; + + if (mapDetected) return mapDetected; return kResVersionSci1Late; } @@ -675,29 +766,41 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { // "lastDirectoryOffset". This is probably not the correct fix, since before r43000 // the loop above could not prematurely terminate and thus this would always check the // last directory entry instead of the last checked directory entry. - file.seek(lastDirectoryOffset - 7, SEEK_SET); - if (file.readByte() == 0xFF && file.readUint16LE() == file.size()) + fileStream->seek(lastDirectoryOffset - 7, SEEK_SET); + if (fileStream->readByte() == 0xFF && fileStream->readUint16LE() == fileStream->size()) return kResVersionSci32; // TODO : check if there is a difference between these maps #endif + delete fileStream; + return kResVersionUnknown; } ResourceManager::ResVersion ResourceManager::detectVolVersion() { - Common::File file; + Common::SeekableReadStream *fileStream = 0; + Common::File *file = 0; ResourceSource *rsrc; + for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { rsrc = *it; if (rsrc->source_type == kSourceVolume) { - file.open(rsrc->location_name); + if (rsrc->resourceFile) { + fileStream = rsrc->resourceFile->createReadStream(); + } else { + file = new Common::File(); + file->open(rsrc->location_name); + if (file->isOpen()) + fileStream = file; + } break; } } - if (file.isOpen() == false) { + if (!fileStream) { error("Failed to open volume file"); return kResVersionUnknown; } + // SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes // SCI1 volume format: {bResType wResNumber wPacked+4 wUnpacked wCompression} = 9 bytes // SCI1.1 volume format: {bResType wResNumber wPacked wUnpacked wCompression} = 9 bytes @@ -710,15 +813,17 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { bool failed = false; // Check for SCI0, SCI1, SCI1.1 and SCI32 v2 (Gabriel Knight 1 CD) formats - while (!file.eos() && file.pos() < 0x100000) { + while (!fileStream->eos() && fileStream->pos() < 0x100000) { if (curVersion > kResVersionSci0Sci1Early) - file.readByte(); - resId = file.readUint16LE(); - dwPacked = (curVersion < kResVersionSci32) ? file.readUint16LE() : file.readUint32LE(); - dwUnpacked = (curVersion < kResVersionSci32) ? file.readUint16LE() : file.readUint32LE(); - wCompression = (curVersion < kResVersionSci32) ? file.readUint16LE() : file.readUint32LE(); - if (file.eos()) + fileStream->readByte(); + resId = fileStream->readUint16LE(); + dwPacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + dwUnpacked = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + wCompression = (curVersion < kResVersionSci32) ? fileStream->readUint16LE() : fileStream->readUint32LE(); + if (fileStream->eos()) { + delete fileStream; return curVersion; + } int chk = (curVersion == kResVersionSci0Sci1Early) ? 4 : 20; int offs = curVersion < kResVersionSci11 ? 4 : 0; @@ -740,18 +845,20 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { break; } - file.seek(0, SEEK_SET); + fileStream->seek(0, SEEK_SET); continue; } if (curVersion < kResVersionSci11) - file.seek(dwPacked - 4, SEEK_CUR); + fileStream->seek(dwPacked - 4, SEEK_CUR); else if (curVersion == kResVersionSci11) - file.seek((9 + dwPacked) % 2 ? dwPacked + 1 : dwPacked, SEEK_CUR); + fileStream->seek((9 + dwPacked) % 2 ? dwPacked + 1 : dwPacked, SEEK_CUR); else if (curVersion == kResVersionSci32) - file.seek(dwPacked, SEEK_CUR);//(9 + wPacked) % 2 ? wPacked + 1 : wPacked, SEEK_CUR); + fileStream->seek(dwPacked, SEEK_CUR);//(9 + wPacked) % 2 ? wPacked + 1 : wPacked, SEEK_CUR); } + delete fileStream; + if (!failed) return curVersion; @@ -1480,7 +1587,7 @@ SciVersion ResourceManager::detectSciVersion() { // If this turns out to be unreliable, we could do some pic resource checks instead. return SCI_VERSION_1_EARLY; case kResVersionSci1Middle: - return SCI_VERSION_1_LATE; + return SCI_VERSION_1_MIDDLE; case kResVersionSci1Late: if (_viewType == kViewVga11) { // SCI1.1 resources, assume SCI1.1 diff --git a/engines/sci/resource.h b/engines/sci/resource.h index 1ab49c834e..4250225ffe 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -28,12 +28,12 @@ #include "common/str.h" #include "common/file.h" +#include "common/fs.h" #include "common/archive.h" #include "sound/audiostream.h" #include "sound/mixer.h" // for SoundHandle -#include "sci/engine/vm.h" // for Object #include "sci/decompressor.h" namespace Common { @@ -53,7 +53,8 @@ enum SciVersion { SCI_VERSION_01, // KQ1 and multilingual games (S.old.*) SCI_VERSION_1_EGA, // EGA with parser, QFG2 SCI_VERSION_1_EARLY, // KQ5. (EGA/VGA) - SCI_VERSION_1_LATE, // ECO1, LSL1, LSL5. (EGA/VGA) + SCI_VERSION_1_MIDDLE, // LSL1, JONESCD. (EGA?/VGA) + SCI_VERSION_1_LATE, // ECO1, LSL5. (EGA/VGA) SCI_VERSION_1_1, // KQ6, ECO2 SCI_VERSION_32 // GK }; @@ -136,6 +137,7 @@ struct ResourceSource { ResSourceType source_type; bool scanned; Common::String location_name; // FIXME: Replace by FSNode ? + const Common::FSNode *resourceFile; int volume_number; ResourceSource *associated_map; }; @@ -245,15 +247,9 @@ public: /** * Creates a new SCI resource manager. - * @param version The SCI version to look for; use SCI_VERSION_AUTODETECT - * in the default case. - * @param maxMemory Maximum number of bytes to allow allocated for resources - * - * @note maxMemory will not be interpreted as a hard limit, only as a restriction - * for resources which are not explicitly locked. However, a warning will be - * issued whenever this limit is exceeded. */ - ResourceManager(int maxMemory); + ResourceManager(); + ResourceManager(const Common::FSList &fslist); ~ResourceManager(); /** @@ -294,8 +290,13 @@ public: void setAudioLanguage(int language); protected: + // Maximum number of bytes to allow being allocated for resources + // Note: maxMemory will not be interpreted as a hard limit, only as a restriction + // for resources which are not explicitly locked. However, a warning will be + // issued whenever this limit is exceeded. + #define MAX_MEMORY 256 * 1024 // 256KB + ViewType _viewType; // Used to determine if the game has EGA or VGA graphics - int _maxMemory; //!< Config option: Maximum total byte number allocated Common::List<ResourceSource *> _sources; int _memoryLocked; //!< Amount of resource bytes in locked memory int _memoryLRU; //!< Amount of resource bytes under LRU control @@ -308,6 +309,11 @@ protected: SciVersion _sciVersion; //!< Detected SCI version */ /** + * Initializes the resource manager + */ + void init(); + + /** * Add a path to the resource manager's list of sources. * @return a pointer to the added source structure, or NULL if an error occurred. */ @@ -324,12 +330,19 @@ protected: */ ResourceSource *addSource(ResourceSource *map, ResSourceType type, const char *filename, int number); + + ResourceSource *addSource(ResourceSource *map, ResSourceType type, + const Common::FSNode *resFile, int number); + /** * Add an external (i.e., separate file) map resource to the resource manager's list of sources. * @param file_name The name of the volume to add * @return A pointer to the added source structure, or NULL if an error occurred. */ ResourceSource *addExternalMap(const char *file_name); + + ResourceSource *addExternalMap(const Common::FSNode *mapFile); + /** * Add an internal (i.e., resource) map to the resource manager's list of sources. * @param name The name of the resource to add @@ -346,6 +359,7 @@ protected: */ void scanNewSources(); int addAppropriateSources(); + int addAppropriateSources(const Common::FSList &fslist); int addInternalSources(); void freeResourceSources(); diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 478f8645d4..596895d1cb 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -44,13 +44,14 @@ namespace Sci { class GfxDriver; // FIXME: error-prone -const char *versionNames[9] = { +const char *versionNames[10] = { "Autodetect", "SCI0 Early", "SCI0 Late", "SCI01", "SCI1 EGA", "SCI1 Early", + "SCI1 Middle", "SCI1 Late", "SCI1.1", "SCI32" @@ -140,7 +141,7 @@ Common::Error SciEngine::run() { const uint32 flags = getFlags(); - _resmgr = new ResourceManager(256 * 1024); + _resmgr = new ResourceManager(); _version = _resmgr->sciVersion(); if (!_resmgr) { diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 528cceb0cc..524dbf70ea 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -86,7 +86,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) Sound::~Sound() { stopCDTimer(); - AudioCD.destroy(); + AudioCD.stop(); delete _sfxFile; } diff --git a/engines/tinsel/pcode.cpp b/engines/tinsel/pcode.cpp index e6ed9df5c9..8646ad3267 100644 --- a/engines/tinsel/pcode.cpp +++ b/engines/tinsel/pcode.cpp @@ -126,6 +126,10 @@ const byte fragment5[] = {OP_IMM | OPSIZE16, 901 % 256, 901 / 256, OP_JUMP, 488 const int fragment5_size = 6; const byte fragment6[] = {OP_IMM | OPSIZE16, 903 % 256, 903 / 256, OP_JUMP, 516 % 256, 516 / 256}; const int fragment6_size = 6; +const byte fragment7[] = {OP_IMM | OPSIZE16, 908 % 256, 908 / 256, OP_JUMP, 616 % 256, 616 / 256}; +const int fragment7_size = 6; +const byte fragment8[] = {OP_IMM | OPSIZE16, 910 % 256, 910 / 256, OP_JUMP, 644 % 256, 644 / 256}; +const int fragment8_size = 6; const WorkaroundEntry workaroundList[] = { // DW1-SCN: Global 206 is whether Rincewind is trying to take the book back to the present. @@ -146,6 +150,8 @@ const WorkaroundEntry workaroundList[] = { {TINSEL_V1, false, 310506872, 463, fragment4_size, fragment4}, {TINSEL_V1, false, 310506872, 485, fragment5_size, fragment5}, {TINSEL_V1, false, 310506872, 513, fragment6_size, fragment6}, + {TINSEL_V1, false, 310506872, 613, fragment7_size, fragment7}, + {TINSEL_V1, false, 310506872, 641, fragment8_size, fragment8}, // DW2: In the garden, global #490 is set when the bees begin their 'out of hive' animation, and reset when done. // But if the game is saved/restored during it, the animation sequence is reset without the global being cleared. @@ -158,7 +164,7 @@ const WorkaroundEntry workaroundList[] = { {TINSEL_V0, false, 0, 0, 0, NULL} }; - +//310505453, x //----------------- LOCAL GLOBAL DATA -------------------- /** diff --git a/engines/tinsel/tinsel.cpp b/engines/tinsel/tinsel.cpp index 5f056351b6..7586c5e749 100644 --- a/engines/tinsel/tinsel.cpp +++ b/engines/tinsel/tinsel.cpp @@ -888,6 +888,7 @@ TinselEngine::~TinselEngine() { if (MoviePlaying()) FinishBMV(); + AudioCD.stop(); delete _sound; delete _midiMusic; delete _pcmMusic; |