diff options
author | Travis Howell | 2009-08-19 01:22:43 +0000 |
---|---|---|
committer | Travis Howell | 2009-08-19 01:22:43 +0000 |
commit | 89d7fea4e619cd44d5ce16eee1e46ad417e26c9c (patch) | |
tree | a5df60123d0d6a44d824d21e95df2ef421a4700b /engines | |
parent | dfaa5acbee5766e59b35697ea1b03122aa4ea2aa (diff) | |
parent | 2bd1f51d92492fa88ba61dc65f783fc9bbfd852c (diff) | |
download | scummvm-rg350-89d7fea4e619cd44d5ce16eee1e46ad417e26c9c.tar.gz scummvm-rg350-89d7fea4e619cd44d5ce16eee1e46ad417e26c9c.tar.bz2 scummvm-rg350-89d7fea4e619cd44d5ce16eee1e46ad417e26c9c.zip |
Merged revisions 43458-43468,43470-43471,43473-43475,43477,43479-43483,43485,43497-43498,43500,43503-43504,43507,43509-43510,43512-43515,43517,43519,43521,43523 via svnmerge from
https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk
........
r43458 | thebluegr | 2009-08-17 15:55:21 +1000 (Mon, 17 Aug 2009) | 8 lines
- Simplified some functions to accept only the parts of the EngineState they need as parameters, instead of the whole EngineState
- Moved the class table in the Segment manager - it's the only class using it directly
- Removed the sci11 flag from save games (we already know this, we don't need to store it)
- Moved script_get_segment() and get_class_address() inside the segment manager class
- Removed the script_locate_by_segment wrapper
- Simplified script_lookup_export() a lot by removing some paranoia checks
- Added some WIP code for automatically determining the game id in the fallback detector (still not working)
- General cleanup
........
r43459 | lordhoto | 2009-08-17 17:36:08 +1000 (Mon, 17 Aug 2009) | 1 line
Fix compilation of the SCI engine. It seems that all of the SCI header files I touched (and probably others I luckily didn't touch) seem to assume that files including them will supply needed types. That looks like a major issue in SCI. Someone with knowledge of the SCI code should look into this and cleanup the includes of *all* SCI headers.
........
r43460 | thebluegr | 2009-08-17 19:19:53 +1000 (Mon, 17 Aug 2009) | 1 line
Cleanup
........
r43461 | strangerke | 2009-08-17 19:50:35 +1000 (Mon, 17 Aug 2009) | 1 line
Added OPCODEDRAW 0x85, with a specific workaround for Bambou hardcoded paths in scripts
........
r43462 | strangerke | 2009-08-17 19:54:49 +1000 (Mon, 17 Aug 2009) | 1 line
Suppress useless parentheses
........
r43463 | lordhoto | 2009-08-17 19:57:09 +1000 (Mon, 17 Aug 2009) | 1 line
Formatting.
........
r43464 | strangerke | 2009-08-17 20:05:02 +1000 (Mon, 17 Aug 2009) | 1 line
Add back parenthesis (I wasn't aware of this convention, sorry) in saveload, and mimic this code in oPlaytoons_openItk
........
r43465 | lordhoto | 2009-08-17 20:48:03 +1000 (Mon, 17 Aug 2009) | 2 lines
- Add note about that modifing the parent stream will mess up SeekableSubReadStream and SeekableSubReadStreamEndian.
- Link to SubReadStream documentation from SeekableSubReadStream and SeekableSubReadStreamEndian.
........
r43466 | thebluegr | 2009-08-17 21:06:27 +1000 (Mon, 17 Aug 2009) | 1 line
Made sound effect playing code consistent for compressed and uncompressed sounds. MemoryReadStream is used again instead of SeekableSubReadStream, as there will be issues when multiple sound effects or voices are played simultaneously
........
r43467 | thebluegr | 2009-08-17 21:11:38 +1000 (Mon, 17 Aug 2009) | 1 line
Ignore speech and sound effect samples with unknown compression, instead of trying to play them as raw sound. Some cleanup
........
r43468 | dreammaster | 2009-08-17 21:36:09 +1000 (Mon, 17 Aug 2009) | 1 line
Fix for some more !!HIGH STRINGS!! when talking to the palace guards
........
r43470 | thebluegr | 2009-08-17 21:49:07 +1000 (Mon, 17 Aug 2009) | 1 line
Simplified the sound playing code by removing the boolean variables for signed, stereo, endian and sample bit information - now sound flags are used instead. Some cleanup.
........
r43471 | dreammaster | 2009-08-17 21:51:42 +1000 (Mon, 17 Aug 2009) | 1 line
Switched event loop while/do to do/while, to ensure event handling occurs even when the game is under heavy load
........
r43473 | lordhoto | 2009-08-17 22:22:15 +1000 (Mon, 17 Aug 2009) | 1 line
Cleanup: don't copy the Kyrandia 1 Amiga credits data in a temporary buffer, and thus keeping it twice in memory.
........
r43474 | lordhoto | 2009-08-17 22:22:30 +1000 (Mon, 17 Aug 2009) | 1 line
Clear input queue before displaying the credits in Kyrandia 1.
........
r43475 | lordhoto | 2009-08-17 22:47:57 +1000 (Mon, 17 Aug 2009) | 1 line
Fix possible out of bounds access in Screen::drawShape.
........
r43477 | joostp | 2009-08-17 22:57:37 +1000 (Mon, 17 Aug 2009) | 2 lines
Commit (slightly) modified version of patch #2831248: Allow suspend/resume for PSP
........
r43479 | thebluegr | 2009-08-17 23:16:40 +1000 (Mon, 17 Aug 2009) | 1 line
Added looping support to LinearDiskStream, needed by SAGA and perhaps other engines. Note that the loop end parameter is still not implemented
........
r43480 | thebluegr | 2009-08-17 23:25:44 +1000 (Mon, 17 Aug 2009) | 2 lines
- Removed the custom DigitalMusicInputStream used in SAGA for the digital music in ITE CD and replaced it with the common LinearDiskStream class
- Removed leftover code which plays standalone tracks (it's not used anywhere)
........
r43481 | lordhoto | 2009-08-17 23:49:56 +1000 (Mon, 17 Aug 2009) | 1 line
Slight cleanup to makeLinearDiskStream interface.
........
r43482 | waltervn | 2009-08-18 01:49:22 +1000 (Tue, 18 Aug 2009) | 1 line
SCI: Add autodetection for DoSound. Cleanup.
........
r43483 | waltervn | 2009-08-18 02:07:47 +1000 (Tue, 18 Aug 2009) | 1 line
SCI: Build fix.
........
r43485 | thebluegr | 2009-08-18 04:25:51 +1000 (Tue, 18 Aug 2009) | 1 line
Put back the code for playing external digital music, used by the MIDI enhancement project, which was removed in rev. #43480
........
r43497 | waltervn | 2009-08-18 09:11:25 +1000 (Tue, 18 Aug 2009) | 1 line
SCI: Fix kernel table for multilingual SCI01 games. Cleanup.
........
r43498 | joostp | 2009-08-18 09:54:40 +1000 (Tue, 18 Aug 2009) | 2 lines
PSP: increase optimization level and change clock rate to 333mhz
........
r43500 | thebluegr | 2009-08-18 16:43:06 +1000 (Tue, 18 Aug 2009) | 1 line
Applied agent-q's patch to the SAGA pathfinding code for all platforms - x and y should not ever be greater than 640 and 480 respectively, so it looks safe enough to be applied
........
r43503 | thebluegr | 2009-08-18 19:12:41 +1000 (Tue, 18 Aug 2009) | 1 line
Removed the maxMemory parameter of the resource manager and replaced it with a define
........
r43504 | thebluegr | 2009-08-18 20:01:18 +1000 (Tue, 18 Aug 2009) | 2 lines
- Added game ID detection to the fallback detector. We still need to map some of Sierra's internal IDs to our own ones
- The class table is now created in the segment manager constructor
........
r43507 | dreammaster | 2009-08-18 22:25:04 +1000 (Tue, 18 Aug 2009) | 1 line
Bugfix for Castle Skorl problem reported on the list
........
r43509 | thebluegr | 2009-08-18 22:49:34 +1000 (Tue, 18 Aug 2009) | 1 line
Mapped some Sierra internal IDs to our own ones, and added a note about a hack currently used in the fallback detector
........
r43510 | thebluegr | 2009-08-19 00:10:31 +1000 (Wed, 19 Aug 2009) | 1 line
Started rewriting the SCI engine to use FSNode instead of file names. This is the proper solution for removing the hack in the fallback detector, but it still needs work. Also, reduced the things needed to be initialized a bit, so that the detection is a bit faster
........
r43512 | lordhoto | 2009-08-19 01:31:26 +1000 (Wed, 19 Aug 2009) | 1 line
Made AGOS, DRASCULA, GOB, GROOVIE, MADE, SCUMM and TINSEL properly stop CD audio playback on engine quit. (This only problem affected playback from CD, not from ripped audio files)
........
r43513 | lordhoto | 2009-08-19 01:32:26 +1000 (Wed, 19 Aug 2009) | 2 lines
- Destory AudioCDManager singleton after user quits a game, this saves a few bytes memory
- Added FIXME to audiocd.h, concering why destroying the AudioCDManager can not quit CD playback right now
........
r43514 | joostp | 2009-08-19 01:39:47 +1000 (Wed, 19 Aug 2009) | 2 lines
PSP: disable dosbox OPL
........
r43515 | lordhoto | 2009-08-19 01:41:00 +1000 (Wed, 19 Aug 2009) | 1 line
Typos.
........
r43517 | joostp | 2009-08-19 03:12:01 +1000 (Wed, 19 Aug 2009) | 2 lines
PSP: Make R-trigger act as a context sensitive modifier key, remap ENTER to triangle
........
r43519 | joostp | 2009-08-19 04:06:50 +1000 (Wed, 19 Aug 2009) | 2 lines
Implement setCursorPalette(), correct hasFeature() <-> getFeatureState() mixup.
........
r43521 | john_doe | 2009-08-19 05:42:13 +1000 (Wed, 19 Aug 2009) | 2 lines
- PMV player: Use frame count from PVM file and fix incorrect "invalid chunk type" warning
- Fix sprite drawing glitch with vertically flipped sprites (bug #2825925)
........
r43523 | buddha_ | 2009-08-19 07:37:31 +1000 (Wed, 19 Aug 2009) | 1 line
Possible fix for #2828330 (AGI: KQ1: Fast text box). If doesn't break anything else then should go to the branch-1-0-0 too, but haven't had the time to do much testing yet - thus committing to the trunk first.
........
svn-id: r43524
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; |