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 | |
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
77 files changed, 1910 insertions, 955 deletions
@@ -314,6 +314,7 @@ Other contributions Andreas Karlsson - Initial port for SymbianOS Claudio Matsuoka - Daily Linux builds Thomas Mayer - PSP port contributions + Yotam Barnoy - PSP port suspend/resume support Sean Murray - ScummVM tools GUI application (GSoC 2007 task) n0p - Windows CE port aspect ratio correction scaler and right click input method @@ -205,3 +205,4 @@ Robert Wohlrab "moshroum" Xanathar "xanathar" Grant Yeager "glo_kidd" Benjamin W. Zale "junior_aepi" +Yotam Barnoy "bluddy" @@ -59,6 +59,9 @@ For a more comprehensive changelog for the latest experimental SVN code, see: - Added support for PC Speaker based music and sound effects. - Added support for 16 color dithering in Kyrandia PC-9801. + PSP port: + - Added support for sleep mode (suspend/resume). + WinCE port: - Speed optimized versions of low-res Smartphone and 2x scalers. - New aspect correction scaler for VGA (or higher) devices. diff --git a/backends/fs/psp/psp-fs-factory.cpp b/backends/fs/psp/psp-fs-factory.cpp index 27bee4de86..7ed84de034 100644 --- a/backends/fs/psp/psp-fs-factory.cpp +++ b/backends/fs/psp/psp-fs-factory.cpp @@ -34,7 +34,13 @@ AbstractFSNode *PSPFilesystemFactory::makeRootFileNode() const { AbstractFSNode *PSPFilesystemFactory::makeCurrentDirectoryFileNode() const { char buf[MAXPATHLEN]; - return getcwd(buf, MAXPATHLEN) ? new PSPFilesystemNode(buf) : NULL; + char * ret = 0; + + PowerMan.beginCriticalSection(); + ret = getcwd(buf, MAXPATHLEN); + PowerMan.endCriticalSection(); + + return (ret ? new PSPFilesystemNode(buf) : NULL); } AbstractFSNode *PSPFilesystemFactory::makeFileNodePath(const Common::String &path) const { diff --git a/backends/fs/psp/psp-fs.cpp b/backends/fs/psp/psp-fs.cpp index f5ff65c9fa..03247e86ad 100644 --- a/backends/fs/psp/psp-fs.cpp +++ b/backends/fs/psp/psp-fs.cpp @@ -26,7 +26,8 @@ #include "engines/engine.h" #include "backends/fs/abstract-fs.h" -#include "backends/fs/stdiostream.h" +#include "backends/fs/psp/psp-stream.h" +#include "backends/platform/psp/powerman.h" #include <sys/stat.h> #include <unistd.h> @@ -35,6 +36,9 @@ #define ROOT_PATH "ms0:/" +#include "backends/platform/psp/trace.h" + + /** * Implementation of the ScummVM file system API based on PSPSDK API. * @@ -61,13 +65,13 @@ public: */ PSPFilesystemNode(const Common::String &p, bool verify = true); - virtual bool exists() const { return access(_path.c_str(), F_OK) == 0; } + virtual bool exists() const; virtual Common::String getDisplayName() const { return _displayName; } virtual Common::String getName() const { return _displayName; } virtual Common::String getPath() const { return _path; } virtual bool isDirectory() const { return _isDirectory; } - virtual bool isReadable() const { return access(_path.c_str(), R_OK) == 0; } - virtual bool isWritable() const { return access(_path.c_str(), W_OK) == 0; } + virtual bool isReadable() const; + virtual bool isWritable() const; virtual AbstractFSNode *getChild(const Common::String &n) const; virtual bool getChildren(AbstractFSList &list, ListMode mode, bool hidden) const; @@ -94,11 +98,44 @@ PSPFilesystemNode::PSPFilesystemNode(const Common::String &p, bool verify) { if (verify) { struct stat st; + PowerMan.beginCriticalSection(); _isValid = (0 == stat(_path.c_str(), &st)); + PowerMan.endCriticalSection(); _isDirectory = S_ISDIR(st.st_mode); } } +bool PSPFilesystemNode::exists() const { + int ret = 0; + + PowerMan.beginCriticalSection(); // Make sure to block in case of suspend + ret = access(_path.c_str(), F_OK); + PowerMan.endCriticalSection(); + + return ret == 0; +} + +bool PSPFilesystemNode::isReadable() const { + int ret = 0; + + PowerMan.beginCriticalSection(); // Make sure to block in case of suspend + ret = access(_path.c_str(), R_OK); + PowerMan.endCriticalSection(); + + return ret == 0; +} + +bool PSPFilesystemNode::isWritable() const { + int ret = 0; + + PowerMan.beginCriticalSection(); // Make sure to block in case of suspend + ret = access(_path.c_str(), W_OK); + PowerMan.endCriticalSection(); + + return ret == 0; +} + + AbstractFSNode *PSPFilesystemNode::getChild(const Common::String &n) const { // FIXME: Pretty lame implementation! We do no error checking to speak // of, do not check if this is a special node, etc. @@ -117,6 +154,10 @@ bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool //TODO: honor the hidden flag + bool ret = true; + + PowerMan.beginCriticalSection(); // Make sure to block in case of suspend + int dfd = sceIoDopen(_path.c_str()); if (dfd > 0) { SceIoDirent dir; @@ -149,10 +190,13 @@ bool PSPFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, bool } sceIoDclose(dfd); - return true; - } else { - return false; + ret = true; + } else { // dfd <= 0 + ret = false; } + + PowerMan.endCriticalSection(); + return ret; } AbstractFSNode *PSPFilesystemNode::getParent() const { @@ -166,11 +210,11 @@ AbstractFSNode *PSPFilesystemNode::getParent() const { } Common::SeekableReadStream *PSPFilesystemNode::createReadStream() { - return StdioStream::makeFromPath(getPath().c_str(), false); + return PSPIoStream::makeFromPath(getPath(), false); } Common::WriteStream *PSPFilesystemNode::createWriteStream() { - return StdioStream::makeFromPath(getPath().c_str(), true); + return PSPIoStream::makeFromPath(getPath(), true); } #endif //#ifdef __PSP__ diff --git a/backends/fs/psp/psp-stream.cpp b/backends/fs/psp/psp-stream.cpp new file mode 100644 index 0000000000..fac4067f46 --- /dev/null +++ b/backends/fs/psp/psp-stream.cpp @@ -0,0 +1,230 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ +#ifdef __PSP__ + +#include "backends/fs/psp/psp-stream.h" +#include "backends/platform/psp/trace.h" +#include <errno.h> + +PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode) +: StdioStream((void *)1), _path(path), _writeMode(writeMode) { + + assert(!path.empty()); + + _handle = (void *)0; // Need to do this since base class asserts not 0. + + PowerMan.registerSuspend(this); // Register with the powermanager to be suspended + +} + +PSPIoStream::~PSPIoStream() { + PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended + // Must do this before fclose() or resume() will reopen. + + fclose((FILE *)_handle); +} + +// Function to open the file pointed to by the path. +// +// +void * PSPIoStream::open() { + if (PowerMan.beginCriticalSection()==PowerManager::Blocked) { + // No need to open. Just return the _handle resume() already opened. + PSPDebugTrace("Suspended in PSPIoStream::open\n"); + } else { + _handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open + } + + PowerMan.endCriticalSection(); + + return _handle; +} + +bool PSPIoStream::err() const { + bool ret; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::err()\n"); + + ret = ferror((FILE *)_handle) != 0; + + PowerMan.endCriticalSection(); + + return ret; +} + +void PSPIoStream::clearErr() { + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::clearErr()\n"); + + clearerr((FILE *)_handle); + + PowerMan.endCriticalSection(); +} + +bool PSPIoStream::eos() const { + bool ret; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::eos()\n"); + + ret = feof((FILE *)_handle) != 0; + + PowerMan.endCriticalSection(); + + return ret; +} + +int32 PSPIoStream::pos() const { + int32 ret; + + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::pos()\n"); + + ret = ftell((FILE *)_handle); + + PowerMan.endCriticalSection(); + + return ret; +} + + +int32 PSPIoStream::size() const { + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::size()\n"); + + int32 oldPos = ftell((FILE *)_handle); + fseek((FILE *)_handle, 0, SEEK_END); + int32 length = ftell((FILE *)_handle); + fseek((FILE *)_handle, oldPos, SEEK_SET); + + PowerMan.endCriticalSection(); + + return length; +} + +bool PSPIoStream::seek(int32 offs, int whence) { + int ret = 0; + + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::seek()\n"); + + ret = fseek((FILE *)_handle, offs, whence); + + PowerMan.endCriticalSection(); + + return ret == 0; +} + +uint32 PSPIoStream::read(void *ptr, uint32 len) { + int ret = 0; + + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::read()\n"); + + ret = fread((byte *)ptr, 1, len, (FILE *)_handle); + + PowerMan.endCriticalSection(); + + return ret; +} + +uint32 PSPIoStream::write(const void *ptr, uint32 len) { + int ret = 0; + + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::read()\n"); + + ret = fwrite(ptr, 1, len, (FILE *)_handle); + + PowerMan.endCriticalSection(); + + return ret; +} + +bool PSPIoStream::flush() { + int ret = 0; + + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSPDebugTrace("Suspended in PSPIoStream::read()\n"); + + ret = fflush((FILE *)_handle); + + PowerMan.endCriticalSection(); + + return ret == 0; +} + +// For the PSP, since we're building in suspend support, we moved opening +// the actual file to an open function since we need an actual PSPIoStream object to suspend. +// +PSPIoStream *PSPIoStream::makeFromPath(const Common::String &path, bool writeMode) { + PSPIoStream *stream = new PSPIoStream(path, writeMode); + + if (stream->open() > 0) { + return stream; + } else { + delete stream; + return 0; + } +} + +/* + * Function to suspend the IO stream (called by PowerManager) + */ +int PSPIoStream::suspend() { + if (_handle > 0) { + _pos = ftell((FILE *)_handle); // Save our position + fclose((FILE *)_handle); // close our file descriptor + _handle = 0; // Set handle to null + } + + return 0; +} + +/* + * Function to resume the IO stream (called by Power Manager) + */ +int PSPIoStream::resume() { + int ret = 0; + + // We reopen our file descriptor + _handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); + if (_handle <= 0) { + PSPDebugTrace("PSPIoStream::resume(): Couldn't reopen file %s\n", _path.c_str()); + ret = -1;; + } + + // Resume our previous position + if(_handle > 0) fseek((FILE *)_handle, _pos, SEEK_SET); + + return ret; +} + +#endif /* __PSP__ */ diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h new file mode 100644 index 0000000000..0363c92416 --- /dev/null +++ b/backends/fs/psp/psp-stream.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef PSPSTREAM_H_ +#define PSPSTREAM_H_ + +#include "backends/fs/stdiostream.h" +#include "backends/platform/psp/powerman.h" +#include "common/list.h" + +/* + * Class to handle special suspend/resume needs of PSP IO Streams + */ +class PSPIoStream : public StdioStream, public Suspendable { +protected: + Common::String _path; /* Need to maintain for reopening after suspend */ + bool _writeMode; /* "" */ + unsigned int _pos; /* "" */ + +public: + /** + * Given a path, invoke fopen on that path and wrap the result in a + * PSPIoStream instance. + */ + static PSPIoStream *makeFromPath(const Common::String &path, bool writeMode); + + PSPIoStream(const Common::String &path, bool writeMode); + virtual ~PSPIoStream(); + + void * open(); // open the file pointed to by the file path + + bool err() const; + void clearErr(); + bool eos() const; + + virtual uint32 write(const void *dataPtr, uint32 dataSize); + virtual bool flush(); + + virtual int32 pos() const; + virtual int32 size() const; + virtual bool seek(int32 offs, int whence = SEEK_SET); + virtual uint32 read(void *dataPtr, uint32 dataSize); + + int suspend(); /* Suspendable interface (power manager) */ + int resume(); /* " " */ +}; + +#endif /* PSPSTREAM_H_ */ diff --git a/backends/module.mk b/backends/module.mk index f3294c5dc6..42fbeb07eb 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -11,6 +11,7 @@ MODULE_OBJS := \ fs/posix/posix-fs-factory.o \ fs/ps2/ps2-fs-factory.o \ fs/psp/psp-fs-factory.o \ + fs/psp/psp-stream.o \ fs/symbian/symbian-fs-factory.o \ fs/windows/windows-fs-factory.o \ fs/wii/wii-fs-factory.o \ diff --git a/backends/platform/psp/Makefile b/backends/platform/psp/Makefile index 91a0c60bd6..d18e6ac82c 100644 --- a/backends/platform/psp/Makefile +++ b/backends/platform/psp/Makefile @@ -62,7 +62,7 @@ endif INCDIR := $(srcdir) . $(srcdir)/engines/ . $(PSPSDK)/include LIBDIR := $(LIBDIR) . $(PSPSDK)/lib -CXXFLAGS = -O2 -Wall -D__PSP__ -DNONSTANDARD_PORT -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DUSE_ZLIB -Wno-multichar `$(PSPBIN)/sdl-config --cflags` +CXXFLAGS = -O3 -Wall -D__PSP__ -DNONSTANDARD_PORT -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DUSE_ZLIB -DDISABLE_DOSBOX_OPL -Wno-multichar `$(PSPBIN)/sdl-config --cflags` CXXFLAGS:= $(addprefix -I,$(INCDIR)) $(CXXFLAGS) LDFLAGS := $(addprefix -L,$(LIBDIR)) $(LDFLAGS) LIBS = @@ -75,12 +75,13 @@ LIBS += -lmad CXXFLAGS+= -DUSE_VORBIS -DUSE_TREMOR LIBS += -lvorbisidec -LIBS += `$(PSPBIN)/sdl-config --libs` -lz -lstdc++ -lc -lpspdisplay -lpspgu -lpspctrl -lpspsdk -lpspnet -lpspnet_inet -lpsputility -lpspsdk -lpspuser +LIBS += `$(PSPBIN)/sdl-config --libs` -lz -lstdc++ -lc -lpspdisplay -lpspgu -lpspctrl -lpspsdk -lpspnet -lpspnet_inet -lpsputility -lpspuser -lpsppower CXXFLAGS := $(CXXFLAGS) -fno-exceptions -fno-rtti TARGET = scummvm-psp -OBJS := psp_main.o \ +OBJS := powerman.o \ + psp_main.o \ osys_psp.o \ osys_psp_gu.o \ kbd_ss_c.o \ diff --git a/backends/platform/psp/README.PSP.in b/backends/platform/psp/README.PSP.in index 2a2b53e721..7eeba7d391 100644 --- a/backends/platform/psp/README.PSP.in +++ b/backends/platform/psp/README.PSP.in @@ -13,15 +13,17 @@ Controls ======== Left trigger - ESC -Right trigger - Enter +Right trigger - Modifier key (see below for uses) Analog - Mouse movement +Right trigger + Analog - Fine control mouse Directionals - Mouse movement -Analog + triangle - Fine control mouse +Triangle - Enter Cross - Mouse button 1 Circle - Mouse button 2 Square - '.' (skip dialogue in some games) Select - Show/Hide Virtual Keyboard Start - F5 +Right trigger + Start - Return-To-Launcher menu Notes ===== @@ -32,9 +34,6 @@ Notes As such, it is recommended to play games in their original, uncompressed, form whenever possible. -- Sleep/Suspend mode currently isn't supported, so don't use it when - running ScummVM. - - This README may be outdated, for more up-to-date instructions and notes see the PSP Port Wiki: http://wiki.scummvm.org/index.php/PlayStation_Portable diff --git a/backends/platform/psp/module.mk b/backends/platform/psp/module.mk index afe9a23f58..8f083c5dfa 100644 --- a/backends/platform/psp/module.mk +++ b/backends/platform/psp/module.mk @@ -1,6 +1,7 @@ MODULE := backends/platform/psp MODULE_OBJS := \ + powerman.o \ psp_main.o \ osys_psp.o \ osys_psp_gu.o \ diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp index 45be0a0cd3..566db3014b 100644 --- a/backends/platform/psp/osys_psp.cpp +++ b/backends/platform/psp/osys_psp.cpp @@ -79,6 +79,8 @@ OSystem_PSP::OSystem_PSP() : _screenWidth(0), _screenHeight(0), _overlayWidth(0) memset(_palette, 0, sizeof(_palette)); + _cursorPaletteDisabled = true; + _samplesPerSec = 0; //init SDL @@ -110,14 +112,14 @@ void OSystem_PSP::initBackend() { bool OSystem_PSP::hasFeature(Feature f) { - return false; + return (f == kFeatureOverlaySupportsAlpha || f == kFeatureCursorHasPalette); } void OSystem_PSP::setFeatureState(Feature f, bool enable) { } bool OSystem_PSP::getFeatureState(Feature f) { - return (f == kFeatureOverlaySupportsAlpha); + return false; } const OSystem::GraphicsMode* OSystem_PSP::getSupportedGraphicsModes() const { @@ -411,7 +413,7 @@ bool OSystem_PSP::pollEvent(Common::Event &event) { */ uint32 buttonsChanged = pad.Buttons ^ _prevButtons; - if (buttonsChanged & (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER | PSP_CTRL_START | PSP_CTRL_SELECT | PSP_CTRL_SQUARE)) { + if (buttonsChanged & (PSP_CTRL_CROSS | PSP_CTRL_CIRCLE | PSP_CTRL_LTRIGGER | PSP_CTRL_RTRIGGER | PSP_CTRL_START | PSP_CTRL_SELECT | PSP_CTRL_SQUARE | PSP_CTRL_TRIANGLE)) { if (buttonsChanged & PSP_CTRL_CROSS) { event.type = (pad.Buttons & PSP_CTRL_CROSS) ? Common::EVENT_LBUTTONDOWN : Common::EVENT_LBUTTONUP; } else if (buttonsChanged & PSP_CTRL_CIRCLE) { @@ -419,23 +421,29 @@ bool OSystem_PSP::pollEvent(Common::Event &event) { } else { //any of the other buttons. event.type = buttonsChanged & pad.Buttons ? Common::EVENT_KEYDOWN : Common::EVENT_KEYUP; + event.kbd.ascii = 0; event.kbd.flags = 0; if (buttonsChanged & PSP_CTRL_LTRIGGER) { event.kbd.keycode = Common::KEYCODE_ESCAPE; event.kbd.ascii = 27; - } else if (buttonsChanged & PSP_CTRL_RTRIGGER) { - event.kbd.keycode = Common::KEYCODE_RETURN; - event.kbd.ascii = 13; } else if (buttonsChanged & PSP_CTRL_START) { event.kbd.keycode = Common::KEYCODE_F5; event.kbd.ascii = Common::ASCII_F5; + if (pad.Buttons & PSP_CTRL_RTRIGGER) { + event.kbd.flags = Common::KBD_CTRL; // Main menu to allow RTL + } /* } else if (buttonsChanged & PSP_CTRL_SELECT) { event.kbd.keycode = Common::KEYCODE_0; event.kbd.ascii = '0'; */ } else if (buttonsChanged & PSP_CTRL_SQUARE) { event.kbd.keycode = Common::KEYCODE_PERIOD; event.kbd.ascii = '.'; + } else if (buttonsChanged & PSP_CTRL_TRIANGLE) { + event.kbd.keycode = Common::KEYCODE_RETURN; + event.kbd.ascii = 13; + } else if (pad.Buttons & PSP_CTRL_RTRIGGER) { + event.kbd.flags |= Common::KBD_SHIFT; } } @@ -484,7 +492,7 @@ bool OSystem_PSP::pollEvent(Common::Event &event) { newY += _padAccel >> 2; // If no movement then this has no effect - if (pad.Buttons & PSP_CTRL_TRIANGLE) { + if (pad.Buttons & PSP_CTRL_RTRIGGER) { // Fine control mode for analog if (analogStepAmountX != 0) { if (analogStepAmountX > 0) diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h index 34957b293c..310efdc7d4 100644 --- a/backends/platform/psp/osys_psp.h +++ b/backends/platform/psp/osys_psp.h @@ -23,6 +23,9 @@ * */ +#ifndef OSYS_PSP_H +#define OSYS_PSP_H + #include "common/scummsys.h" #include "graphics/surface.h" #include "graphics/colormasks.h" @@ -65,6 +68,7 @@ protected: int _mouseHotspotX, _mouseHotspotY; byte _mouseKeyColour; byte *_mouseBuf; + bool _cursorPaletteDisabled; uint32 _prevButtons; uint32 _lastPadCheck; @@ -144,3 +148,5 @@ public: virtual Common::WriteStream *createConfigWriteStream(); }; + +#endif /* OSYS_PSP_H */ diff --git a/backends/platform/psp/osys_psp_gu.cpp b/backends/platform/psp/osys_psp_gu.cpp index 76f6b42e37..0fb1b4aded 100644 --- a/backends/platform/psp/osys_psp_gu.cpp +++ b/backends/platform/psp/osys_psp_gu.cpp @@ -41,6 +41,7 @@ unsigned int __attribute__((aligned(16))) list[262144]; unsigned short __attribute__((aligned(16))) clut256[256]; unsigned short __attribute__((aligned(16))) mouseClut[256]; +unsigned short __attribute__((aligned(16))) cursorPalette[256]; unsigned short __attribute__((aligned(16))) kbClut[256]; //unsigned int __attribute__((aligned(16))) offscreen256[640*480]; //unsigned int __attribute__((aligned(16))) overlayBuffer256[640*480*2]; @@ -225,6 +226,24 @@ void OSystem_PSP_GU::setPalette(const byte *colors, uint start, uint num) { sceKernelDcacheWritebackAll(); } +void OSystem_PSP_GU::setCursorPalette(const byte *colors, uint start, uint num) { + const byte *b = colors; + + for (uint i = 0; i < num; ++i) { + cursorPalette[start + i] = RGBToColour(b[0], b[1], b[2]); + b += 4; + } + + cursorPalette[0] = 0; + + _cursorPaletteDisabled = false; + + sceKernelDcacheWritebackAll(); +} + +void OSystem_PSP_GU::disableCursorPalette(bool disable) { + _cursorPaletteDisabled = disable; +} void OSystem_PSP_GU::copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) { //Clip the coordinates @@ -364,7 +383,7 @@ void OSystem_PSP_GU::updateScreen() { if (_mouseVisible) { sceGuTexMode(GU_PSM_T8, 0, 0, 0); // 8-bit image sceGuClutMode(GU_PSM_5551, 0, 0xff, 0); - sceGuClutLoad(32, mouseClut); // upload 32*8 entries (256) + sceGuClutLoad(32, _cursorPaletteDisabled ? mouseClut : cursorPalette); // upload 32*8 entries (256) sceGuAlphaFunc(GU_GREATER,0,0xff); sceGuEnable(GU_ALPHA_TEST); sceGuTexImage(0, MOUSE_SIZE, MOUSE_SIZE, MOUSE_SIZE, _mouseBuf); diff --git a/backends/platform/psp/osys_psp_gu.h b/backends/platform/psp/osys_psp_gu.h index e828a36b7d..c221971fc8 100644 --- a/backends/platform/psp/osys_psp_gu.h +++ b/backends/platform/psp/osys_psp_gu.h @@ -47,6 +47,8 @@ public: void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, byte keycolor, int cursorTargetScale); void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h) ; void setPalette(const byte *colors, uint start, uint num); + void setCursorPalette(const byte *colors, uint start, uint num); + void disableCursorPalette(bool disable); bool pollEvent(Common::Event &event); int _graphicMode; struct Vertex *_vertices; diff --git a/backends/platform/psp/powerman.cpp b/backends/platform/psp/powerman.cpp new file mode 100644 index 0000000000..c553669fc3 --- /dev/null +++ b/backends/platform/psp/powerman.cpp @@ -0,0 +1,297 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "./powerman.h" +#include "./trace.h" + +DECLARE_SINGLETON(PowerManager); + + /******************************************* +* +* Constructor +* +********************************************/ +PowerManager::PowerManager() { + _flagMutex = NULL; /* Init mutex handle */ + _listMutex = NULL; /* Init mutex handle */ + _condSuspendable = NULL; /* Init condition variable */ + _condPM = NULL; + + _condSuspendable = SDL_CreateCond(); + if (_condSuspendable <= 0) { + PSPDebugTrace("PowerManager::PowerManager(): Couldn't create condSuspendable\n"); + } + + _condPM = SDL_CreateCond(); + if (_condPM <= 0) { + PSPDebugTrace("PowerManager::PowerManager(): Couldn't create condPM\n"); + } + + _flagMutex = SDL_CreateMutex(); + if (_flagMutex <= 0) { + PSPDebugTrace("PowerManager::PowerManager(): Couldn't create flagMutex\n"); + } + + _listMutex = SDL_CreateMutex(); + if (_listMutex <= 0) { + PSPDebugTrace("PowerManager::PowerManager(): Couldn't create listMutex\n"); + } + + _suspendFlag = false; + _criticalCounter = 0; + } + +/******************************************* +* +* Function to register to be notified when suspend/resume time comes +* +********************************************/ +int PowerManager::registerSuspend(Suspendable *item) { + // Register in list + PSPDebugTrace("In registerSuspend\n"); + + if (SDL_mutexP(_listMutex) != 0) { + PSPDebugTrace("PowerManager::registerSuspend(): Couldn't lock _listMutex %d\n", _listMutex); + } + + _suspendList.push_front(item); + + if (SDL_mutexV(_listMutex) != 0) { + PSPDebugTrace("PowerManager::registerSuspend(): Couldn't unlock _listMutex %d\n", _listMutex); + } + + PSPDebugTrace("Out of registerSuspend\n"); + + return 0; +} + +/******************************************* +* +* Function to unregister to be notified when suspend/resume time comes +* +********************************************/ +int PowerManager::unregisterSuspend(Suspendable *item) { + + PSPDebugTrace("In unregisterSuspend\n"); + + // Unregister from stream list + if (SDL_mutexP(_listMutex) != 0) { + PSPDebugTrace("PowerManager::unregisterSuspend(): Couldn't unlock _listMutex %d\n", _listMutex); + } + + _suspendList.remove(item); + + if (SDL_mutexV(_listMutex) != 0) { + PSPDebugTrace("PowerManager::unregisterSuspend(): Couldn't unlock _listMutex %d\n", _listMutex); + } + + PSPDebugTrace("Out of unregisterSuspend\n"); + + return 0; + } + + /******************************************* +* +* Destructor +* +********************************************/ + PowerManager::~PowerManager() { + SDL_DestroyCond(_condSuspendable); + _condSuspendable = 0; + + SDL_DestroyCond(_condPM); + _condPM = 0; + + SDL_DestroyMutex(_flagMutex); + _flagMutex = 0; + + SDL_DestroyMutex(_listMutex); + _listMutex = 0; + } + + + /******************************************* +* +* Function to be called by threads wanting to block on the PSP entering suspend +* +********************************************/ + int PowerManager::blockOnSuspend() { + return beginCriticalSection(true); +} + + /* + * Function to block on a suspend, then start a non-suspendable critical section + */ +int PowerManager::beginCriticalSection(bool justBlock) { + int ret = PowerManager::NotBlocked; + + if (SDL_mutexP(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::blockOnSuspend(): Couldn't lock flagMutex %d\n", _flagMutex); + ret = PowerManager::Error; + } + + // Check the access flag + if (_suspendFlag == true) { + PSPDebugTrace("Blocking!!\n"); + ret = PowerManager::Blocked; + + // If it's true, we wait for a signal to continue + if( SDL_CondWait(_condSuspendable, _flagMutex) != 0) { + PSPDebugTrace("PowerManager::blockOnSuspend(): Couldn't wait on cond %d\n", _condSuspendable); + } + + PSPDebugTrace("We got blocked!!\n"); + } + + // Now put the pm to sleep + if (justBlock == false) + _criticalCounter++; + + if (SDL_mutexV(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::blockOnSuspend(): Couldn't unlock flagMutex %d\n", _flagMutex); + ret = PowerManager::Error; + } + + return ret; +} + +int PowerManager::endCriticalSection() { + int ret = 0; + + if (SDL_mutexP(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::endCriticalSection(): Couldn't lock flagMutex %d\n", _flagMutex); + ret = PowerManager::Error; + } + + // We're done with our critical section + _criticalCounter--; + + if (_criticalCounter <= 0) { + if(_suspendFlag == true) PSPDebugTrace("Waking up the PM and suspendFlag is true\n"); + + SDL_CondBroadcast(_condPM); + + if (_criticalCounter < 0) { + PSPDebugTrace("PowerManager::endCriticalSection(): Error! Critical counter is %d\n", _criticalCounter); + } + } + + if (SDL_mutexV(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::endCriticalSection(): Couldn't unlock flagMutex %d\n", _flagMutex); + ret = PowerManager::Error; + } + + return ret; +} + + /******************************************* +* +* Callback function to be called to put every Suspendable to suspend +* +********************************************/ +int PowerManager::suspend() { + int ret = 0; + + // First we set the suspend flag to true + if (SDL_mutexP(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::suspend(): Couldn't lock flagMutex %d\n", _flagMutex); + ret = -1; + } + + _suspendFlag = true; + + if (_criticalCounter > 0) + SDL_CondWait(_condPM, _flagMutex); + + if (SDL_mutexV(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::suspend(): Couldn't unlock flagMutex %d\n", _flagMutex); + ret = -1; + } + + // Loop over list, calling suspend() + if (SDL_mutexP(_listMutex) != 0) { + PSPDebugTrace("PowerManager::suspend(): Couldn't lock listMutex %d\n", _listMutex); + ret = -1; + } + + Common::List<Suspendable *>::iterator i = _suspendList.begin(); + + for (; i != _suspendList.end(); i++) { + (*i)->suspend(); + } + + if (SDL_mutexV(_listMutex) != 0) { + PSPDebugTrace("PowerManager::suspend(): Couldn't unlock listMutex %d\n", _listMutex); + ret = -1; + } + + return ret; +} + +/******************************************* +* +* Callback function to resume every Suspendable +* +********************************************/ +int PowerManager::resume() { + int ret = 0; + + // First we notify our Suspendables. Loop over list, calling resume() + if (SDL_mutexP(_listMutex) != 0) { + PSPDebugTrace("PowerManager::resume(): Couldn't lock listMutex %d\n", _listMutex); + ret = -1; + } + + Common::List<Suspendable *>::iterator i = _suspendList.begin(); + + for (; i != _suspendList.end(); i++) { + (*i)->resume(); + } + + if (SDL_mutexV(_listMutex) != 0) { + PSPDebugTrace("PowerManager::resume(): Couldn't unlock listMutex %d\n", _listMutex); + ret = -1; + } + + // Now we set the suspend flag to false + if (SDL_mutexP(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::resume(): Couldn't lock flagMutex %d\n", _flagMutex); + ret = -1; + } + _suspendFlag = false; + + // Signal the other threads to wake up + if (SDL_CondBroadcast(_condSuspendable) != 0) { + PSPDebugTrace("PowerManager::resume(): Couldn't broadcast condition %d\n", _condSuspendable); + ret = -1; + } + + if (SDL_mutexV(_flagMutex) != 0) { + PSPDebugTrace("PowerManager::resume(): Couldn't unlock flagMutex %d\n", _flagMutex); + ret = -1; + } + + return ret; +} diff --git a/backends/platform/psp/powerman.h b/backends/platform/psp/powerman.h new file mode 100644 index 0000000000..0a5f7a2361 --- /dev/null +++ b/backends/platform/psp/powerman.h @@ -0,0 +1,87 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef POWERMAN_H +#define POWERMAN_H + +#include <SDL/SDL_thread.h> +#include <SDL/SDL_mutex.h> +#include "common/singleton.h" +#include "common/list.h" + + /* + * Implement this class (interface) if you want to use PowerManager's suspend callback functionality + * + */ + class Suspendable { + public: + virtual ~Suspendable() {} + virtual int suspend() = 0; + virtual int resume() = 0; + }; + + /****************************************************************************************************** + * + * This class will call a Suspendable when the PSP goes to suspend/resumes. It also provides the ability to block + * a thread when the PSP is going to suspend/suspending, and to wake it up when the PSP is resumed. + * This ability is very useful for managing the PSPIoStream class, but may be found useful by other classes as well. + * + *******************************************************************************************************/ + class PowerManager: public Common::Singleton<PowerManager> { +private: + friend class Common::Singleton<PowerManager>; + PowerManager(); + ~PowerManager(); + + Common::List<Suspendable *> _suspendList; /* list to register in */ + + bool _suspendFlag; /* protected variable */ + SDL_mutex *_flagMutex; /* mutex to access access flag */ + SDL_mutex *_listMutex; /* mutex to access Suspendable list */ + SDL_cond *_condSuspendable; /* signal to synchronize accessing threads */ + SDL_cond *_condPM; /* signal to wake up the PM from a critical section */ + int _criticalCounter; /* Counter of how many threads are in a critical section */ + +public: + int blockOnSuspend(); /* block if suspending */ + int beginCriticalSection(bool justBlock = false); /* Use a critical section to block (if suspend was already pressed) */ + int endCriticalSection(); /* and to prevent the PSP from suspending in a particular section */ + int registerSuspend(Suspendable *item); /* register to be called to suspend/resume */ + int unregisterSuspend(Suspendable *item); /* remove from suspend/resume list */ + int suspend(); /* callback to have all items in list suspend */ + int resume(); /* callback to have all items in list resume */ + + enum { + Error = -1, + NotBlocked = 0, + Blocked = 1 + }; + + }; + + // For easy access +#define PowerMan PowerManager::instance() + + #endif /* POWERMAN_H */ diff --git a/backends/platform/psp/psp.spec b/backends/platform/psp/psp.spec index 807b8f93b7..e319b022f7 100644 --- a/backends/platform/psp/psp.spec +++ b/backends/platform/psp/psp.spec @@ -1,3 +1,3 @@ %rename lib old_lib *lib: -%(old_lib) -lpspdebug -lpspgu -lpspctrl -lpspge -lpspdisplay -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lc -lpspuser -lpsputility -lpspkernel -lpspnet_inet -lz -lstdc++ -lc -lpspdisplay -lpspgu -lpspctrl -lpspsdk -lpspnet -lpspnet_inet -lpsputility -lpspsdk -lpspuser +%(old_lib) -lpspdebug -lpspgu -lpspctrl -lpspge -lpspdisplay -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lc -lpspuser -lpsputility -lpspkernel -lpspnet_inet -lz -lstdc++ -lc -lpspdisplay -lpspgu -lpspctrl -lpspsdk -lpspnet -lpspnet_inet -lpsputility -lpspuser -lpsppower diff --git a/backends/platform/psp/psp_main.cpp b/backends/platform/psp/psp_main.cpp index 0af7ebf269..3ea6c55368 100644 --- a/backends/platform/psp/psp_main.cpp +++ b/backends/platform/psp/psp_main.cpp @@ -31,10 +31,14 @@ #include <pspdebug.h> #endif +#include <psppower.h> + #include <common/system.h> #include <engines/engine.h> #include <base/main.h> #include <base/plugins.h> +#include "backends/platform/psp/powerman.h" + #include "osys_psp_gu.h" #include "./trace.h" @@ -91,17 +95,36 @@ void loaderInit() { #endif /* Exit callback */ -SceKernelCallbackFunction exit_callback(int /*arg1*/, int /*arg2*/, void * /*common*/) { +int exit_callback(void) { sceKernelExitGame(); return 0; } +/* Function for handling suspend/resume */ +void power_callback(int , int powerinfo) { + if (powerinfo & PSP_POWER_CB_POWER_SWITCH || powerinfo & PSP_POWER_CB_SUSPENDING) { + PowerMan.suspend(); + } else if (powerinfo & PSP_POWER_CB_RESUME_COMPLETE) { + PowerMan.resume(); + } +} + /* Callback thread */ int CallbackThread(SceSize /*size*/, void *arg) { int cbid; cbid = sceKernelCreateCallback("Exit Callback", (SceKernelCallbackFunction)exit_callback, NULL); sceKernelRegisterExitCallback(cbid); + /* Set up callbacks for PSPIoStream */ + + cbid = sceKernelCreateCallback("Power Callback", (SceKernelCallbackFunction)power_callback, 0); + if (cbid >= 0) { + if(scePowerRegisterCallback(-1, cbid) < 0) { + PSPDebugTrace("SetupCallbacks(): Couldn't register callback for power_callback\n"); + } + } else { + PSPDebugTrace("SetupCallbacks(): Couldn't create a callback for power_callback\n"); + } sceKernelSleepThreadCB(); return 0; @@ -119,8 +142,13 @@ int SetupCallbacks(void) { #undef main int main(void) { + PowerManager::instance(); // Setup power manager + SetupCallbacks(); + //change clock rate to 333mhz + scePowerSetClockFrequency(333, 333, 166); + static const char *argv[] = { "scummvm", NULL }; static int argc = sizeof(argv)/sizeof(char *)-1; @@ -131,6 +159,8 @@ int main(void) { g_system->quit(); // TODO: Consider removing / replacing this! + PowerManager::destroy(); // get rid of PowerManager + sceKernelSleepThread(); return res; diff --git a/base/main.cpp b/base/main.cpp index d108512416..03db00a1d9 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -45,9 +45,12 @@ #include "common/file.h" #include "common/fs.h" #include "common/system.h" + #include "gui/GuiManager.h" #include "gui/message.h" +#include "sound/audiocd.h" + #include "backends/keymapper/keymapper.h" #if defined(_WIN32_WCE) @@ -416,6 +419,15 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { warning("Could not find any engine capable of running the selected game"); } + // We will destroy the AudioCDManager singleton here to save some memory. + // This will not make the CD audio stop, one would have to enable this: + //AudioCD.stop(); + // but the engine is responsible for stopping CD playback anyway and + // this way we catch engines not doing it properly. For some more + // information about why AudioCDManager::destroy does not stop the CD + // playback read the FIXME in sound/audiocd.h + Audio::AudioCDManager::destroy(); + // reset the graphics to default setupGraphics(system); launcherDialog(); diff --git a/common/stream.h b/common/stream.h index 9b03c20bd2..86e8e71134 100644 --- a/common/stream.h +++ b/common/stream.h @@ -400,6 +400,7 @@ public: /** * SubReadStream provides access to a ReadStream restricted to the range * [currentPosition, currentPosition+end). + * * Manipulating the parent stream directly /will/ mess up a substream. * Likewise, manipulating two substreams of a parent stream will cause them to * step on each others toes. @@ -434,6 +435,9 @@ public: * SeekableSubReadStream provides access to a SeekableReadStream restricted to * the range [begin, end). * The same caveats apply to SeekableSubReadStream as do to SeekableReadStream. + * + * Manipulating the parent stream directly /will/ mess up a substream. + * @see SubReadStream */ class SeekableSubReadStream : public SubReadStream, public SeekableReadStream { protected: @@ -451,6 +455,9 @@ public: /** * This is a wrapper around SeekableSubReadStream, but it adds non-endian * read methods whose endianness is set on the stream creation. + * + * Manipulating the parent stream directly /will/ mess up a substream. + * @see SubReadStream */ class SeekableSubReadStreamEndian : public SeekableSubReadStream { public: @@ -1181,7 +1181,7 @@ case $_host_os in # TODO nds ;; psp) - CXXFLAGS="$CXXFLAGS -O2 -G0 -I$PSPDEV/psp/sdk/include -D_PSP_FW_VERSION=150" + CXXFLAGS="$CXXFLAGS -O3 -G0 -I$PSPDEV/psp/sdk/include -D_PSP_FW_VERSION=150" ;; wince) CXXFLAGS="$CXXFLAGS -O3 -march=armv4 -mtune=xscale -D_WIN32_WCE=300 -D__ARM__ -D_ARM_ -DUNICODE -DFPM_DEFAULT -DNONSTANDARD_PORT" @@ -2085,7 +2085,7 @@ case $_backend in # TODO nds ;; psp) - DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE" + DEFINES="$DEFINES -D__PSP__ -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DDISABLE_DOSBOX_OPL" INCLUDES="$INCLUDES -I$PSPDEV/psp/include/SDL" LIBS="$LIBS -lSDL" ;; 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; diff --git a/gui/credits.h b/gui/credits.h index 5ddae4ea37..21f69d4465 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -366,6 +366,8 @@ static const char *credits[] = { "C2""Daily Linux builds", "C0""Thomas Mayer", "C2""PSP port contributions", +"C0""Yotam Barnoy", +"C2""PSP port suspend/resume support", "C0""Sean Murray", "C2""ScummVM tools GUI application (GSoC 2007 task)", "C0""n0p", diff --git a/sound/audiocd.h b/sound/audiocd.h index 4c4ff26147..3ef4a1ac09 100644 --- a/sound/audiocd.h +++ b/sound/audiocd.h @@ -67,6 +67,14 @@ private: friend class Common::Singleton<SingletonBaseType>; AudioCDManager(); + // FIXME: It might make sense to stop CD playback, when the AudioCDManager singleton + // is destroyed. Currently we can not do this, since in worst case the OSystem and + // along with it the Mixer will be destroyed before the AudioCDManager, thus + // leading to invalid memory access. If we can fix up the code to destroy the + // AudioCDManager before OSystem in *all* cases, that is including calling + // OSystem::quit, we might be able to implement it via a simple "stop()" + // call in a custom destructor of AudioCDManager. + /* used for emulated CD music */ SoundHandle _handle; bool _emulating; diff --git a/sound/audiostream.cpp b/sound/audiostream.cpp index 57d2e097bf..783a5733b3 100644 --- a/sound/audiostream.cpp +++ b/sound/audiostream.cpp @@ -211,14 +211,14 @@ protected: int _audioBlockCount; ///< Number of blocks in _audioBlock int _currentBlock; ///< Current audio block number - int _beginLoop; ///< Loop parameter, currently not implemented - int _endLoop; ///< Loop parameter, currently not implemented - + int _beginLoop; ///< Loop start parameter + int _endLoop; ///< Loop end parameter, currently not implemented + bool _loop; ///< Determines if the stream should be looped when it finishes public: - LinearDiskStream(int rate, uint beginLoop, uint endLoop, bool disposeStream, Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, uint numBlocks) + LinearDiskStream(int rate, uint beginLoop, uint endLoop, bool disposeStream, Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, uint numBlocks, bool loop) : _rate(rate), _stream(stream), _beginLoop(beginLoop), _endLoop(endLoop), _disposeAfterUse(disposeStream), - _audioBlockCount(numBlocks) { + _audioBlockCount(numBlocks), _loop(loop) { // Allocate streaming buffer if (is16Bit) { @@ -296,7 +296,6 @@ int LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffe _diskLeft = _audioBlock[_currentBlock].len; } - // Now read more data from disk if there is more to be read if ((_bufferLeft == 0) && (_diskLeft > 0)) { int32 readAmount = MIN(_diskLeft, BUFFER_SIZE); @@ -315,6 +314,14 @@ int LinearDiskStream<stereo, is16Bit, isUnsigned, isLE>::readBuffer(int16 *buffe // original position. restoreFilePosition = true; } + + // Looping + if (_diskLeft == 0 && _loop) { + // Reset the stream + _currentBlock = 0; + _filePos = _audioBlock[_currentBlock].pos + _beginLoop; + _diskLeft = _audioBlock[_currentBlock].len; + } } // In case calling code relies on the position of this stream staying @@ -399,19 +406,19 @@ AudioStream *makeLinearInputStream(const byte *ptr, uint32 len, int rate, byte f #define MAKE_LINEAR_DISK(STEREO, UNSIGNED) \ if (is16Bit) { \ if (isLE) \ - return new LinearDiskStream<STEREO, true, UNSIGNED, true>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks); \ + return new LinearDiskStream<STEREO, true, UNSIGNED, true>(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop); \ else \ - return new LinearDiskStream<STEREO, true, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks); \ + return new LinearDiskStream<STEREO, true, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop); \ } else \ - return new LinearDiskStream<STEREO, false, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, &stream, block, numBlocks) + return new LinearDiskStream<STEREO, false, UNSIGNED, false>(rate, loopStart, loopEnd, takeOwnership, stream, block, numBlocks, loop) -AudioStream *makeLinearDiskStream(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { +AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool takeOwnership, uint loopStart, uint loopEnd) { const bool isStereo = (flags & Audio::Mixer::FLAG_STEREO) != 0; const bool is16Bit = (flags & Audio::Mixer::FLAG_16BITS) != 0; const bool isUnsigned = (flags & Audio::Mixer::FLAG_UNSIGNED) != 0; const bool isLE = (flags & Audio::Mixer::FLAG_LITTLE_ENDIAN) != 0; - + const bool loop = (flags & Audio::Mixer::FLAG_LOOP) != 0; if (isStereo) { if (isUnsigned) { diff --git a/sound/audiostream.h b/sound/audiostream.h index 99e140608d..09b97374d5 100644 --- a/sound/audiostream.h +++ b/sound/audiostream.h @@ -133,7 +133,7 @@ struct LinearDiskStreamAudioBlock { * start position and length of each block of uncompressed audio in the stream. */ -AudioStream *makeLinearDiskStream(Common::SeekableReadStream& stream, LinearDiskStreamAudioBlock* block, int +AudioStream *makeLinearDiskStream(Common::SeekableReadStream *stream, LinearDiskStreamAudioBlock *block, int numBlocks, int rate, byte flags, bool disposeStream, uint loopStart, uint loopEnd); /** diff --git a/sound/voc.cpp b/sound/voc.cpp index a332477f3c..a5bab0af29 100644 --- a/sound/voc.cpp +++ b/sound/voc.cpp @@ -292,7 +292,7 @@ AudioStream *makeVOCDiskStream(Common::SeekableReadStream &stream, byte flags, b int numBlocks = parseVOCFormat(stream, block, rate, loops, begin_loop, end_loop); - AudioStream* audioStream = makeLinearDiskStream(stream, block, numBlocks, rate, flags, takeOwnership, begin_loop, end_loop); + AudioStream *audioStream = makeLinearDiskStream(&stream, block, numBlocks, rate, flags, takeOwnership, begin_loop, end_loop); delete[] block; diff --git a/tools/credits.pl b/tools/credits.pl index 2b1b93e87b..ad006b2aee 100755 --- a/tools/credits.pl +++ b/tools/credits.pl @@ -832,6 +832,7 @@ begin_credits("Credits"); add_person("Andreas Karlsson", "Sprawl", "Initial port for SymbianOS"); add_person("Claudio Matsuoka", "", "Daily Linux builds"); add_person("Thomas Mayer", "", "PSP port contributions"); + add_person("Yotam Barnoy", "bluddy", "PSP port suspend/resume support"); add_person("Sean Murray", "lightcast", "ScummVM tools GUI application (GSoC 2007 task)"); add_person("", "n0p", "Windows CE port aspect ratio correction scaler and right click input method"); add_person("Mikesch Nepomuk", "mnepomuk", "MI1 VGA floppy patches"); |