diff options
235 files changed, 7419 insertions, 8659 deletions
diff --git a/backends/fs/psp/psp-stream.cpp b/backends/fs/psp/psp-stream.cpp index 9bcbe9d7cf..8cb7dfea17 100644 --- a/backends/fs/psp/psp-stream.cpp +++ b/backends/fs/psp/psp-stream.cpp @@ -32,48 +32,28 @@ #include <errno.h> -#define MIN2(a,b) ((a < b) ? a : b) -#define MIN3(a,b,c) ( (a < b) ? (a < c ? a : c) : (b < c ? b : c) ) - -//#define __PSP_PRINT_TO_FILE__ /* For debugging suspend stuff, we have no screen output */ -//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */ +//#define __PSP_PRINT_TO_FILE__ +//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */ //#define __PSP_DEBUG_PRINT__ /* For debug printouts */ - #include "backends/platform/psp/trace.h" -//#define DEBUG_BUFFERS /* to see the contents of the buffers being read */ - -#ifdef DEBUG_BUFFERS -void printBuffer(byte *ptr, uint32 len) { - uint32 printLen = len <= 10 ? len : 10; - - for (int i = 0; i < printLen; i++) { - PSP_INFO_PRINT("%x ", ptr[i]); - } - - if (len > 10) { - PSP_INFO_PRINT("... "); - for (int i = len - 10; i < len; i++) - PSP_INFO_PRINT("%x ", ptr[i]); - } - - PSP_INFO_PRINT("\n"); -} -#endif - - PSPIoStream::PSPIoStream(const Common::String &path, bool writeMode) - : StdioStream((void *)1), _path(path), _writeMode(writeMode), - _ferror(false), _pos(0), - _physicalPos(0), _fileSize(0), _inCache(false), _eos(false), - _cacheStartOffset(-1), _cache(0), - _errorSuspend(0), _errorSource(0), - _errorPos(0), _errorHandle(0), _suspendCount(0) { + : StdioStream((void *)1), _path(path), _writeMode(writeMode) { DEBUG_ENTER_FUNC(); - // assert(!path.empty()); // do we need this? + assert(!path.empty()); _handle = (void *)0; // Need to do this since base class asserts not 0. + _ferror = false; + _feof = false; + _pos = 0; + + /* for error checking */ + _errorSuspend = 0; + _errorSource = 0; + _errorPos = 0; + _errorHandle = 0; + _suspendCount = 0; } PSPIoStream::~PSPIoStream() { @@ -83,12 +63,9 @@ PSPIoStream::~PSPIoStream() { PSP_DEBUG_PRINT_FUNC("Suspended\n"); PowerMan.unregisterSuspend(this); // Unregister with powermanager to be suspended - // Must do this before fclose() or resume() will reopen. + // Must do this before fclose() or resume() will reopen. - fclose((FILE *)_handle); // We don't need a critical section. Worst case, the handle gets closed on its own - - if (_cache) - free(_cache); + fclose((FILE *)_handle); // We don't need a critical section(?). Worst case, the handle gets closed on its own PowerMan.endCriticalSection(); } @@ -105,16 +82,6 @@ void *PSPIoStream::open() { _handle = fopen(_path.c_str(), _writeMode ? "wb" : "rb"); // open - if (_handle) { - // Get the file size - fseek((FILE *)_handle, 0, SEEK_END); // go to the end - _fileSize = ftell((FILE *)_handle); - fseek((FILE *)_handle, 0, SEEK_SET); // back to the beginning - - // Allocate the cache - _cache = (char *)memalign(64, CACHE_SIZE); - } - PowerMan.registerSuspend(this); // Register with the powermanager to be suspended PowerMan.endCriticalSection(); @@ -124,183 +91,100 @@ void *PSPIoStream::open() { bool PSPIoStream::err() const { DEBUG_ENTER_FUNC(); - - if (_ferror) // We dump since no printing to screen with suspend - PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], \ - _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n", - _ferror, _errorSource, _errorSuspend, _pos, - _errorPos, _errorHandle, _suspendCount); + if (_ferror) + PSP_ERROR("mem_ferror[%d], source[%d], suspend error[%d], pos[%d], _errorPos[%d], _errorHandle[%p], suspendCount[%d]\n", + _ferror, _errorSource, _errorSuspend, _pos, _errorPos, _errorHandle, _suspendCount); return _ferror; } void PSPIoStream::clearErr() { - _ferror = false; + _ferror = false; // Remove regular error bit } bool PSPIoStream::eos() const { - return _eos; + return _feof; } int32 PSPIoStream::pos() const { return _pos; } + int32 PSPIoStream::size() const { - return _fileSize; + DEBUG_ENTER_FUNC(); + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSP_DEBUG_PRINT_FUNC("Suspended\n"); + + fseek((FILE *)_handle, 0, SEEK_END); + int32 length = ftell((FILE *)_handle); + fseek((FILE *)_handle, _pos, SEEK_SET); + + if (_pos < 0 || length < 0) { // Check for errors + _errorSource = 2; + PSP_ERROR("pos[%d] or length[%d] < 0!\n", _pos, length); + _ferror = true; + length = -1; // If our oldPos is bad, we want length to be bad too to signal + clearerr((FILE *)_handle); + } + + PowerMan.endCriticalSection(); + + return length; } bool PSPIoStream::seek(int32 offs, int whence) { DEBUG_ENTER_FUNC(); - PSP_DEBUG_PRINT_FUNC("offset[0x%x], whence[%d], _pos[0x%x], _physPos[0x%x]\n", offs, whence, _pos, _physicalPos); - _eos = false; - - int32 posToSearchFor = 0; - switch (whence) { - case SEEK_CUR: - posToSearchFor = _pos; - break; - case SEEK_END: - posToSearchFor = _fileSize; // unsure. Does it take us here or to EOS - 1? - break; - } - posToSearchFor += offs; - - // Check for bad values - if (posToSearchFor < 0) { - _ferror = true; - return false; - } - - if (posToSearchFor > _fileSize) { + + // Check if we can access the file + if (PowerMan.beginCriticalSection() == PowerManager::Blocked) + PSP_DEBUG_PRINT_FUNC("Suspended\n"); + + int ret = fseek((FILE *)_handle, offs, whence); + + if (ret != 0) { _ferror = true; - _eos = true; - return false; + PSP_ERROR("fseek returned with [%d], non-zero\n", ret); + clearerr((FILE *)_handle); + _feof = feof((FILE *)_handle); + _errorSource = 3; + } else { // everything ok + _feof = false; // Reset eof flag since we know it was ok } - - // See if we can find it in cache - if (isOffsetInCache(posToSearchFor)) { - PSP_DEBUG_PRINT("seek offset[0x%x] found in cache. Cache starts[0x%x]\n", posToSearchFor, _cacheStartOffset); - _inCache = true; - } else { // not in cache - _inCache = false; - } - _pos = posToSearchFor; - return true; + + _pos = ftell((FILE *)_handle); // update pos + + PowerMan.endCriticalSection(); + + return (ret == 0); } uint32 PSPIoStream::read(void *ptr, uint32 len) { DEBUG_ENTER_FUNC(); - PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x], ptr[%p]\n", _path.c_str(), len, ptr); - - if (_ferror || _eos) - return 0; - - byte *destPtr = (byte *)ptr; - uint32 lenFromFile = len; // how much we read from the actual file - uint32 lenFromCache = 0; // how much we read from cache - uint32 lenRemainingInFile = _fileSize - _pos; - - if (lenFromFile > lenRemainingInFile) { - lenFromFile = lenRemainingInFile; - _eos = true; - } - - // Are we in cache? - if (_inCache && isCacheValid()) { - uint32 offsetInCache = _pos - _cacheStartOffset; - // We can read at most what's in the cache or the remaining size of the file - lenFromCache = MIN2(lenFromFile, CACHE_SIZE - offsetInCache); // unsure - - PSP_DEBUG_PRINT("reading 0x%x bytes from cache to %p. pos[0x%x] physPos[0x%x] cacheStart[0x%x]\n", lenFromCache, destPtr, _pos, _physicalPos, _cacheStartOffset); - - memcpy(destPtr, &_cache[offsetInCache], lenFromCache); - _pos += lenFromCache; - - if (lenFromCache < lenFromFile) { // there's more to copy from the file - lenFromFile -= lenFromCache; - lenRemainingInFile -= lenFromCache; // since we moved pos - destPtr += lenFromCache; - } else { // we're done -#ifdef DEBUG_BUFFERS - printBuffer((byte *)ptr, len); -#endif - - return lenFromCache; // how much we actually read - } - } - + // Check if we can access the file if (PowerMan.beginCriticalSection() == PowerManager::Blocked) PSP_DEBUG_PRINT_FUNC("Suspended\n"); - - - synchronizePhysicalPos(); // we need to update our physical position - - if (lenFromFile <= MIN_READ_SIZE) { // We load the cache in case the read is small enough - // This optimization is based on the principle that reading 1 byte is as expensive as 1000 bytes - uint32 lenToCopyToCache = MIN2((uint32)MIN_READ_SIZE, lenRemainingInFile); // at most remaining file size - - PSP_DEBUG_PRINT("filling cache with 0x%x bytes from physicalPos[0x%x]. cacheStart[0x%x], pos[0x%x], fileSize[0x%x]\n", lenToCopyToCache, _physicalPos, _cacheStartOffset, _pos, _fileSize); - - size_t ret = fread(_cache, 1, lenToCopyToCache, (FILE *)_handle); - if (ret != lenToCopyToCache) { - PSP_ERROR("in filling cache, failed to get 0x%x bytes. Only got 0x%x\n", lenToCopyToCache, ret); - _ferror = true; - clearerr((FILE *)_handle); - } - _cacheStartOffset = _physicalPos; - _inCache = true; - - _physicalPos += ret; - - PSP_DEBUG_PRINT("copying 0x%x bytes from cache to %p\n", lenFromFile, destPtr); - - // Copy to the destination buffer from cache - memcpy(destPtr, _cache, lenFromFile); - _pos += lenFromFile; - - } else { // Too big for cache. No caching - PSP_DEBUG_PRINT("reading 0x%x bytes from file to %p. Pos[0x%x], physPos[0x%x]\n", lenFromFile, destPtr, _pos, _physicalPos); - size_t ret = fread(destPtr, 1, lenFromFile, (FILE *)_handle); - - _physicalPos += ret; // Update pos - _pos = _physicalPos; - - if (ret != lenFromFile) { // error - PSP_ERROR("fread returned [0x%x] instead of len[0x%x]\n", ret, lenFromFile); + + PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len); + + size_t ret = fread((byte *)ptr, 1, len, (FILE *)_handle); + + _pos += ret; // Update pos + + if (ret != len) { // Check for eof + _feof = feof((FILE *)_handle); + if (!_feof) { // It wasn't an eof. Must be an error _ferror = true; clearerr((FILE *)_handle); - _errorSource = 4; + _pos = ftell((FILE *)_handle); // Update our position + _errorSource = 4; + PSP_ERROR("fread returned ret[%d] instead of len[%d]\n", ret, len); } - _inCache = false; } PowerMan.endCriticalSection(); -#ifdef DEBUG_BUFFERS - printBuffer((byte *)ptr, len); -#endif - - return lenFromCache + lenFromFile; // total of what was copied -} - -// TODO: Test if seeking backwards/forwards has any effect on performance -inline bool PSPIoStream::synchronizePhysicalPos() { - if (_pos != _physicalPos) { - if (fseek((FILE *)_handle, _pos - _physicalPos, SEEK_CUR) != 0) - return false; - _physicalPos = _pos; - } - - return true; -} - -inline bool PSPIoStream::isOffsetInCache(uint32 offset) { - if (_cacheStartOffset != -1 && - offset >= (uint32)_cacheStartOffset && - offset < (uint32)(_cacheStartOffset + CACHE_SIZE)) - return true; - return false; + return ret; } uint32 PSPIoStream::write(const void *ptr, uint32 len) { @@ -309,30 +193,18 @@ uint32 PSPIoStream::write(const void *ptr, uint32 len) { if (PowerMan.beginCriticalSection() == PowerManager::Blocked) PSP_DEBUG_PRINT_FUNC("Suspended\n"); - PSP_DEBUG_PRINT_FUNC("filename[%s], len[0x%x]\n", _path.c_str(), len); + PSP_DEBUG_PRINT_FUNC("filename[%s], len[%d]\n", _path.c_str(), len); - if (_ferror) - return 0; - - _eos = false; // we can't have eos with write - synchronizePhysicalPos(); - size_t ret = fwrite(ptr, 1, len, (FILE *)_handle); - // If we're making the file bigger, adjust the size - if (_physicalPos + (int)ret > _fileSize) - _fileSize = _physicalPos + ret; - _physicalPos += ret; - _pos = _physicalPos; - _inCache = false; - _cacheStartOffset = -1; // invalidate cache + _pos += ret; if (ret != len) { // Set error _ferror = true; clearerr((FILE *)_handle); _pos = ftell((FILE *)_handle); // Update pos _errorSource = 5; - PSP_ERROR("fwrite returned[0x%x] instead of len[0x%x]\n", ret, len); + PSP_ERROR("fwrite returned[%d] instead of len[%d]\n", ret, len); } PowerMan.endCriticalSection(); @@ -352,7 +224,7 @@ bool PSPIoStream::flush() { _ferror = true; clearerr((FILE *)_handle); _errorSource = 6; - PSP_ERROR("fflush returned ret[%d]\n", ret); + PSP_ERROR("fflush returned ret[%u]\n", ret); } PowerMan.endCriticalSection(); @@ -414,9 +286,6 @@ int PSPIoStream::resume() { // Resume our previous position if (_handle > 0 && _pos > 0) { ret = fseek((FILE *)_handle, _pos, SEEK_SET); - - _physicalPos = _pos; - _inCache = false; if (ret != 0) { // Check for problem _errorSuspend = ResumeError; diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h index 9fd1ad0470..673630b685 100644 --- a/backends/fs/psp/psp-stream.h +++ b/backends/fs/psp/psp-stream.h @@ -35,39 +35,25 @@ */ class PSPIoStream : public StdioStream, public Suspendable { protected: - Common::String _path; - int _fileSize; - bool _writeMode; // for resuming in the right mode - int _physicalPos; // position in the real file - int _pos; // position. Sometimes virtual - bool _inCache; // whether we're in cache (virtual) mode - bool _eos; // EOS flag - + Common::String _path; /* Need to maintain for reopening after suspend */ + bool _writeMode; /* "" */ + int _pos; /* "" */ + mutable int _ferror; /* Save file ferror */ + mutable bool _feof; /* and eof */ + enum { SuspendError = 2, ResumeError = 3 }; - enum { - CACHE_SIZE = 1024, - MIN_READ_SIZE = 1024 // reading less than 1024 takes exactly the same time as 1024 - }; - - // For caching - char *_cache; - int _cacheStartOffset; // starting offset of the cache. -1 when cache is invalid - - mutable int _ferror; // file error state - int _errorSuspend; // for debugging + int _errorSuspend; mutable int _errorSource; + + // Error checking int _errorPos; void * _errorHandle; int _suspendCount; - bool synchronizePhysicalPos(); // synchronize the physical and virtual positions - bool isOffsetInCache(uint32 pos); // check if an offset is found in cache - bool isCacheValid() { return _cacheStartOffset != -1; } - public: /** diff --git a/backends/module.mk b/backends/module.mk index 59df56b468..46c9e166a6 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -35,6 +35,7 @@ MODULE_OBJS := \ plugins/sdl/sdl-provider.o \ plugins/win32/win32-provider.o \ plugins/psp/psp-provider.o \ + plugins/ps2/ps2-provider.o \ saves/savefile.o \ saves/default/default-saves.o \ saves/posix/posix-saves.o \ diff --git a/backends/platform/ds/arm7/source/main.cpp b/backends/platform/ds/arm7/source/main.cpp index a4cde02ba6..7029d96405 100644 --- a/backends/platform/ds/arm7/source/main.cpp +++ b/backends/platform/ds/arm7/source/main.cpp @@ -38,7 +38,7 @@ #include <system.h> #include <stdlib.h> #include <string.h> -#include <registers_alt.h> // Needed for SOUND_CR +//#include <registers_alt.h> // not needed in current libnds #include <NDS/scummvm_ipc.h> ////////////////////////////////////////////////////////////////////// #ifdef USE_DEBUGGER @@ -590,7 +590,7 @@ int main(int argc, char ** argv) { IPC->reset = false; - //fifoInit(); + fifoInit(); for (int r = 0; r < 8; r++) { IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512); diff --git a/backends/platform/ds/arm9/makefile b/backends/platform/ds/arm9/makefile index 7f03f4c310..eca170ef96 100644 --- a/backends/platform/ds/arm9/makefile +++ b/backends/platform/ds/arm9/makefile @@ -75,7 +75,7 @@ else ifdef DS_BUILD_K else - # USE_MAD = 1 + USE_MAD = 1 endif endif endif diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp index 5a63e5f08f..7eb02f9070 100644 --- a/backends/platform/ds/arm9/source/dsmain.cpp +++ b/backends/platform/ds/arm9/source/dsmain.cpp @@ -702,7 +702,7 @@ void displayMode8Bit() { - consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 2, 0, true, true); + consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 2, 0, true); // Set this again because consoleinit resets it videoSetMode(MODE_5_2D | (consoleEnable? DISPLAY_BG0_ACTIVE: 0) | DISPLAY_BG3_ACTIVE | DISPLAY_SPR_ACTIVE | DISPLAY_SPR_1D | DISPLAY_SPR_1D_BMP); @@ -940,7 +940,7 @@ void displayMode16Bit() { SUB_BG0_CR = BG_MAP_BASE(4) | BG_TILE_BASE(0); SUB_BG0_Y0 = 0; - consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false, true); + consoleInit(NULL, 0, BgType_Text4bpp, BgSize_T_256x256, 4, 0, false); // consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(4), (u16*)CHAR_BASE_BLOCK_SUB(0), 16); for (int r = 0; r < 32 * 32; r++) { diff --git a/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h index f41548f400..9344be68f9 100644 --- a/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h +++ b/backends/platform/ds/commoninclude/NDS/scummvm_ipc.h @@ -33,18 +33,6 @@ ////////////////////////////////////////////////////////////////////// -typedef struct sTransferSoundData { -//--------------------------------------------------------------------------------- - const void *data; - u32 len; - u32 rate; - u8 vol; - u8 pan; - u8 format; - u8 PADDING; -} TransferSoundData, * pTransferSoundData; - - //--------------------------------------------------------------------------------- diff --git a/backends/platform/gp2x/graphics.cpp b/backends/platform/gp2x/graphics.cpp index 4a3c668c52..1888cbe47c 100644 --- a/backends/platform/gp2x/graphics.cpp +++ b/backends/platform/gp2x/graphics.cpp @@ -1405,6 +1405,7 @@ void OSystem_GP2X::drawMouse() { SDL_Rect zoomdst; SDL_Rect dst; int scale; + int width, height; int hotX, hotY; int tmpScreenWidth, tmpScreenHeight; @@ -1425,12 +1426,16 @@ void OSystem_GP2X::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp b/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp index 6abddd52f3..ff3bd725be 100644 --- a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp +++ b/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp @@ -149,6 +149,7 @@ void OSystem_GP2XWIZ::drawMouse() { SDL_Rect dst; int scale; + int width, height; int hotX, hotY; if (_videoMode.mode == GFX_HALF && !_overlayVisible){ @@ -161,12 +162,16 @@ void OSystem_GP2XWIZ::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/linuxmoto/linuxmoto-graphics.cpp b/backends/platform/linuxmoto/linuxmoto-graphics.cpp index a39416ebc4..d66d41dfab 100644 --- a/backends/platform/linuxmoto/linuxmoto-graphics.cpp +++ b/backends/platform/linuxmoto/linuxmoto-graphics.cpp @@ -168,6 +168,7 @@ void OSystem_LINUXMOTO::drawMouse() { SDL_Rect dst; int scale; + int width, height; int hotX, hotY; if (_videoMode.mode == GFX_HALF && !_overlayVisible) { @@ -180,12 +181,16 @@ void OSystem_LINUXMOTO::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/ps2/Makefile.ps2 b/backends/platform/ps2/Makefile.ps2 index bf7ac0aca0..d6ebc9bfc8 100644 --- a/backends/platform/ps2/Makefile.ps2 +++ b/backends/platform/ps2/Makefile.ps2 @@ -1,39 +1,52 @@ # $Header: Exp $ include $(PS2SDK)/Defs.make -PS2_EXTRA = /works/devel/ps2/sdk-extra +PS2_EXTRA = /home/tony/GSOC/ps2/sdk-extra PS2_EXTRA_INCS = /zlib/include /libmad/ee/include /SjPcm/ee/src /tremor -PS2_EXTRA_LIBS = /zlib/lib /libmad/ee/lib /SjPcm/ee/lib /tremor/tremor +PS2_EXTRA_LIBS = /zlib/lib /libmad/ee/lib /SjPcm/ee/lib /vorbis /tremor/tremor -ENABLED=STATIC_PLUGIN +# Set to 1 to enable, 0 to disable dynamic modules +DYNAMIC_MODULES = 1 + +VERBOSE_BUILD=0 + +# Test for dynamic plugins +ifeq ($(DYNAMIC_MODULES),1) +ENABLED = DYNAMIC_PLUGIN +DEFINES += -DDYNAMIC_MODULES +PRE_OBJS_FLAGS = -Wl,--whole-archive +POST_OBJS_FLAGS = -Wl,--no-whole-archive +else +ENABLED = STATIC_PLUGIN +endif ENABLE_SCUMM = $(ENABLED) -ENABLE_SCUMM_7_8 = $(ENABLED) -ENABLE_HE = $(ENABLED) +#ENABLE_SCUMM_7_8 = $(ENABLED) +#ENABLE_HE = $(ENABLED) ENABLE_AGI = $(ENABLED) -ENABLE_AGOS = $(ENABLED) -ENABLE_CINE = $(ENABLED) -ENABLE_CRUISE = $(ENABLED) -ENABLE_DRASCULA = $(ENABLED) -ENABLE_GOB = $(ENABLED) -ENABLE_KYRA = $(ENABLED) -ENABLE_LURE = $(ENABLED) +#ENABLE_AGOS = $(ENABLED) +#ENABLE_CINE = $(ENABLED) +#ENABLE_CRUISE = $(ENABLED) +#ENABLE_DRASCULA = $(ENABLED) +#ENABLE_GOB = $(ENABLED) +#ENABLE_KYRA = $(ENABLED) +#ENABLE_LURE = $(ENABLED) # ENABLE_M4 = $(ENABLED) -ENABLE_MADE = $(ENABLED) -ENABLE_PARALLACTION = $(ENABLED) -ENABLE_QUEEN = $(ENABLED) -ENABLE_SAGA = $(ENABLED) -ENABLE_SAGA2 = $(ENABLED) -ENABLE_IHNM = $(ENABLED) -ENABLE_SKY = $(ENABLED) -ENABLE_SWORD1 = $(ENABLED) -ENABLE_SWORD2 = $(ENABLED) +#ENABLE_MADE = $(ENABLED) +#ENABLE_PARALLACTION = $(ENABLED) +#ENABLE_QUEEN = $(ENABLED) +#ENABLE_SAGA = $(ENABLED) +#ENABLE_SAGA2 = $(ENABLED) +#ENABLE_IHNM = $(ENABLED) +#ENABLE_SKY = $(ENABLED) +#ENABLE_SWORD1 = $(ENABLED) +#ENABLE_SWORD2 = $(ENABLED) # ENABLE_TINSEL = $(ENABLED) -ENABLE_TOUCHE = $(ENABLED) +#ENABLE_TOUCHE = $(ENABLED) HAVE_GCC3 = true -CC = ee-gcc +CC = ee-gcc CXX = ee-g++ AS = ee-gcc LD = ee-gcc @@ -46,15 +59,21 @@ RM = rm -f srcdir = ../../.. VPATH = $(srcdir) INCDIR = ../../../ -# DEPDIR = .deps +DEPDIR = .deps +CXX_UPDATE_DEP_FLAG = -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP -DEFINES = -DUSE_VORBIS -DUSE_TREMOR -DUSE_MAD -DUSE_ZLIB -DFORCE_RTL -D_EE -D__PLAYSTATION2__ -O2 -Wall -Wno-multichar +# Variables for dynamic plugin building +PLUGIN_PREFIX = +PLUGIN_SUFFIX = .plg +PLUGIN_EXTRA_DEPS = plugin.syms elf/scummvm.elf +PLUGIN_LDFLAGS = -nostartfiles -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -Wl,-q,--just-symbols=elf/scummvm.org.elf,-Tlinkfile,--retain-symbols-file,plugin.syms -lstdc++ -lc +DEFINES = -DUSE_VORBIS -DUSE_MAD -DUSE_TREMOR -DUSE_ZLIB -DFORCE_RTL -D_EE -D__PLAYSTATION2__ -O2 -Wall -Wno-multichar -fno-exceptions -fno-rtti -INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS)) +INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS)) INCLUDES += -I $(PS2SDK)/ee/include -I $(PS2SDK)/common/include -I ./common -I . -I $(srcdir) -I $(srcdir)/engines -TARGET = elf/scummvm.elf +TARGET = elf/scummvm OBJS := backends/platform/ps2/DmaPipe.o \ backends/platform/ps2/Gs2dScreen.o \ @@ -71,20 +90,20 @@ OBJS := backends/platform/ps2/DmaPipe.o \ backends/platform/ps2/systemps2.o \ backends/platform/ps2/ps2mutex.o \ backends/platform/ps2/ps2time.o \ - backends/platform/ps2/ps2debug.o + backends/platform/ps2/ps2debug.o \ + backends/platform/ps2/ps2loader.o MODULE_DIRS += . include $(srcdir)/Makefile.common -LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(PS2SDK)/ee/startup/linkfile -LDFLAGS += -L $(PS2SDK)/ee/lib -L . +LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T main_prog.ld #$(PS2SDK)/ee/startup/linkfile +LDFLAGS += -G 0 -L $(PS2SDK)/ee/lib -L . LDFLAGS += $(addprefix -L$(PS2_EXTRA),$(PS2_EXTRA_LIBS)) -LDFLAGS += -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lmad -ltremor -lz -lm -lc -lfileXio -lkernel -lstdc++ -LDFLAGS += -s - -all: $(TARGET) +LDFLAGS += -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lmad -ltremor -lz -lm -lc -lfileXio -lkernel -lstdc++ +LDFLAGS += -$(TARGET): $(OBJS) - $(LD) $^ $(LDFLAGS) -o $@ +all: $(TARGET).elf +$(TARGET).elf: $(OBJS) + $(LD) $(PRE_OBJS_FLAGS) $(OBJS) $(POST_OBJS_FLAGS) $(LDFLAGS) -o $@ diff --git a/backends/platform/ps2/elf32.h b/backends/platform/ps2/elf32.h new file mode 100644 index 0000000000..616cc4b4d2 --- /dev/null +++ b/backends/platform/ps2/elf32.h @@ -0,0 +1,209 @@ +/* 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 BACKENDS_ELF_H +#define BACKENDS_ELF_H + +/* ELF stuff */ + +typedef unsigned short Elf32_Half, Elf32_Section; +typedef unsigned int Elf32_Word, Elf32_Addr, Elf32_Off; +typedef signed int Elf32_Sword; +typedef Elf32_Half Elf32_Versym; + +#define EI_NIDENT (16) +#define SELFMAG 6 + +/* ELF File format structures. Look up ELF structure for more details */ + +// ELF header (contains info about the file) +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +// Should be in e_ident +#define ELFMAG "\177ELF\1\1" /* ELF Magic number */ + +// e_type values +#define ET_NONE 0 /* no file type */ +#define ET_REL 1 /* relocatable */ +#define ET_EXEC 2 /* executable */ +#define ET_DYN 3 /* shared object */ +#define ET_CORE 4 /* core file */ + +// e_machine values +#define EM_MIPS 8 + + +// Program header (contains info about segment) +typedef struct { + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +// p_type values +#define PT_NULL 0 /* ignored */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking info */ +#define PT_INTERP 3 /* info about interpreter */ +#define PT_NOTE 4 /* note segment */ +#define PT_SHLIB 5 /* reserved */ +#define PT_PHDR 6 /* Program header table */ +#define PT_MIPS_REGINFO 0x70000000 /* register usage info */ + +// p_flags value +#define PF_X 1 /* execute */ +#define PF_W 2 /* write */ +#define PF_R 4 /* read */ + +// Section header (contains info about section) +typedef struct { + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +// sh_type values +#define SHT_NULL 0 /* Inactive section */ +#define SHT_PROGBITS 1 /* Proprietary */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addend */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Info for dynamic linking */ +#define SHT_NOTE 7 /* Note section */ +#define SHT_NOBITS 8 /* Occupies no space */ +#define SHT_REL 9 /* Relocation entries without addend */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Minimal set of dynamic linking symbols */ +#define SHT_MIPS_LIBLSIT 0x70000000 /* Info about dynamic shared object libs */ +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicts btw executables and shared objects */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global pointer table */ + +// sh_flags values +#define SHF_WRITE 0 /* writable section */ +#define SHF_ALLOC 2 /* section occupies memory */ +#define SHF_EXECINSTR 4 /* machine instructions */ +#define SHF_MIPS_GPREL 0x10000000 /* Must be made part of global data area */ + + +// Symbol entry (contain info about a symbol) +typedef struct { + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +// Extract from the st_info +#define SYM_TYPE(x) ((x)&0xF) +#define SYM_BIND(x) ((x)>>4) + + +// Symbol binding values from st_info +#define STB_LOCAL 0 /* Symbol not visible outside object */ +#define STB_GLOBAL 1 /* Symbol visible to all object files */ +#define STB_WEAK 2 /* Similar to STB_GLOBAL */ + +// Symbol type values from st_info +#define STT_NOTYPE 0 /* Not specified */ +#define STT_OBJECT 1 /* Data object e.g. variable */ +#define STT_FUNC 2 /* Function */ +#define STT_SECTION 3 /* Section */ +#define STT_FILE 4 /* Source file associated with object file */ + +// Special section header index values from st_shndex +#define SHN_UNDEF 0 +#define SHN_LOPROC 0xFF00 /* Extended values */ +#define SHN_ABS 0xFFF1 /* Absolute value: don't relocate */ +#define SHN_COMMON 0xFFF2 /* Common block. Not allocated yet */ +#define SHN_HIPROC 0xFF1F +#define SHN_HIRESERVE 0xFFFF + +// Relocation entry (info about how to relocate) +typedef struct { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +// Access macros for the relocation info +#define REL_TYPE(x) ((x)&0xFF) /* Extract relocation type */ +#define REL_INDEX(x) ((x)>>8) /* Extract relocation index into symbol table */ + +// MIPS relocation types +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 +#define R_MIPS_GOTHI16 13 +#define R_MIPS_GOTLO16 14 +#define R_MIPS_CALLHI16 15 +#define R_MIPS_CALLLO16 16 + +// Mock function to get value of global pointer +#define getGP() ({ \ + unsigned int __valgp; \ + __asm__ ("add %0, $gp, $0" : "=r"(__valgp) : ); \ + __valgp; \ +}) + +#endif /* BACKENDS_ELF_H */ diff --git a/backends/platform/ps2/main_prog.ld b/backends/platform/ps2/main_prog.ld new file mode 100644 index 0000000000..de4b534135 --- /dev/null +++ b/backends/platform/ps2/main_prog.ld @@ -0,0 +1,97 @@ +ENTRY(_start); + +SECTIONS { + .text 0x00100000: { + _ftext = . ; + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + KEEP(*(.init)) + KEEP(*(.fini)) + QUAD(0) + } + + PROVIDE(_etext = .); + PROVIDE(etext = .); + + .reginfo : { *(.reginfo) } + + /* Global/static constructors and deconstructors. */ + .ctors ALIGN(16): { + KEEP(*crtbegin*.o(.ctors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + } + .dtors ALIGN(16): { + KEEP(*crtbegin*.o(.dtors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + } + + /* Static data. */ + .rodata ALIGN(128): { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r*) + } + + .data ALIGN(128): { + _fdata = . ; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + SORT(CONSTRUCTORS) + } + + .rdata ALIGN(128): { *(.rdata) } + .gcc_except_table ALIGN(128): { *(.gcc_except_table) } + + _gp = ALIGN(128) + 0x7ff0; + .lit4 ALIGN(128): { *(.lit4) } + .lit8 ALIGN(128): { *(.lit8) } + + .sdata ALIGN(128): { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s*) + } + + _edata = .; + PROVIDE(edata = .); + + /* Uninitialized data. */ + .sbss ALIGN(128) : { + _fbss = . ; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb*) + *(.scommon) + } + + __plugin_hole_start = .; + . = _gp + 0x7ff0; + __plugin_hole_end = .; + + COMMON : + { + *(COMMON) + } + . = ALIGN(128); + + .bss ALIGN(128) : { + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b*) + } + _end_bss = .; + + _end = . ; + PROVIDE(end = .); + + /* Symbols needed by crt0.s. */ + PROVIDE(_heap_size = -1); + PROVIDE(_stack = -1); + PROVIDE(_stack_size = 128 * 1024); +} diff --git a/backends/platform/ps2/module.mk b/backends/platform/ps2/module.mk index 86b12cb668..69a28b93c4 100644 --- a/backends/platform/ps2/module.mk +++ b/backends/platform/ps2/module.mk @@ -16,7 +16,8 @@ MODULE_OBJS := \ systemps2.o \ ps2mutex.o \ ps2time.o \ - ps2debug.o + ps2debug.o \ + ps2loader.o MODULE_DIRS += \ backends/platform/ps2/ diff --git a/backends/platform/ps2/plugin.ld b/backends/platform/ps2/plugin.ld new file mode 100644 index 0000000000..d6ca6dcfe6 --- /dev/null +++ b/backends/platform/ps2/plugin.ld @@ -0,0 +1,214 @@ +/* Script for -z combreloc: combine and sort reloc sections */ +OUTPUT_FORMAT("elf32-littlemips", "elf32-bigmips", + "elf32-littlemips") +OUTPUT_ARCH(mips:5900) +SEARCH_DIR("/home/tony/GSOC/ps2/tools/ee/ee/lib"); +PHDRS +{ + plugin PT_LOAD ; + shorts PT_LOAD ; +} +/* Do we need any of these for elf? + __DYNAMIC = 0; +_DYNAMIC_LINK = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0x100000; + .interp : { *(.interp) } : plugin + .reginfo : { *(.reginfo) } : plugin + .dynamic : { *(.dynamic) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) + *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) + *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) + *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) + *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) + *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) + *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : + { + KEEP (*(.init)) + } =0 + .plt : { *(.plt) } + .text : + { + _ftext = . ; + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.mips16.fn.*) *(.mips16.call.*) + } =0 + .fini : + { + KEEP (*(.fini)) + } =0 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .sdata2 : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) } + .sbss2 : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) } + .eh_frame_hdr : { *(.eh_frame_hdr) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(128) + (. & (128 - 1)); + /* Ensure the __preinit_array_start label is properly aligned. We + could instead move the label definition inside the section, but + the linker would then create the section even if it turns out to + be empty, which isn't pretty. */ + . = ALIGN(32 / 8); + PROVIDE (__preinit_array_start = .); + .preinit_array : { *(.preinit_array) } + PROVIDE (__preinit_array_end = .); + PROVIDE (__init_array_start = .); + .init_array : { *(.init_array) } + PROVIDE (__init_array_end = .); + PROVIDE (__fini_array_start = .); + .fini_array : { *(.fini_array) } + PROVIDE (__fini_array_end = .); + .data : + { + _fdata = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .eh_frame : { KEEP (*(.eh_frame)) } + .gcc_except_table : { *(.gcc_except_table) } + .dynamic : { *(.dynamic) } + .ctors : + { + ___plugin_ctors = .; + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __plugin_ctors_end = .; + } + .dtors : + { + ___plugin_dtors = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + ___plugin_dtors_end = .; + } + .jcr : { KEEP (*(.jcr)) } + + . = __plugin_hole_start; + + .got : { *(.got.plt) *(.got) } : shorts + .lit8 : { *(.lit8) } + .lit4 : { *(.lit4) } + /* We want the small data sections together, so single-instruction offsets + can access them all, and initialized data all before uninitialized, so + we can shorten the on-disk segment size. */ + .sdata : + { + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + _fbss = .; + .sbss : + { + PROVIDE (__sbss_start = .); + PROVIDE (___sbss_start = .); + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.scommon) + PROVIDE (__sbss_end = .); + PROVIDE (___sbss_end = .); + } + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. */ + . = ALIGN(32 / 8); + } + . = ALIGN(32 / 8); + _end = .; + PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) } + .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) } + + /* Symbols needed by crt0.s */ + PROVIDE(_heap_size = -1); + PROVIDE(_stack = -1); + PROVIDE(_stack_size = 128 * 1024); +} diff --git a/backends/platform/ps2/plugin.syms b/backends/platform/ps2/plugin.syms new file mode 100644 index 0000000000..24ee1a19dc --- /dev/null +++ b/backends/platform/ps2/plugin.syms @@ -0,0 +1,8 @@ +PLUGIN_getVersion +PLUGIN_getType +PLUGIN_getTypeVersion +PLUGIN_getObject +___plugin_ctors +___plugin_ctors_end +___plugin_dtors +___plugin_dtors_end diff --git a/backends/platform/ps2/ps2loader.cpp b/backends/platform/ps2/ps2loader.cpp new file mode 100644 index 0000000000..bea697bf07 --- /dev/null +++ b/backends/platform/ps2/ps2loader.cpp @@ -0,0 +1,721 @@ +/* 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$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(__PLAYSTATION2__) + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <malloc.h> +#include <unistd.h> +#include <sys/_default_fcntl.h> + +#include <ps2utils.h> + +#include "backends/platform/ps2/ps2loader.h" +//#include "backends/platform/ps2/powerman.h" //TODO + +//#define __PS2_DEBUG_PLUGINS__ + +#ifdef __PS2_DEBUG_PLUGINS__ +#define DBG(x,...) fprintf(stderr,x, ## __VA_ARGS__) +#else +#define DBG(x,...) +#endif + +#define seterror(x,...) fprintf(stderr,x, ## __VA_ARGS__) + +extern char __plugin_hole_start; // Indicates start of hole in program file for shorts +extern char __plugin_hole_end; // Indicates end of hole in program file +extern char _gp[]; // Value of gp register + +DECLARE_SINGLETON(ShortSegmentManager) // For singleton + +// Get rid of symbol table in memory +void DLObject::discard_symtab() { + free(_symtab); + free(_strtab); + _symtab = NULL; + _strtab = NULL; + _symbol_cnt = 0; +} + +// Unload all objects from memory +void DLObject::unload() { + discard_symtab(); + free(_segment); + _segment = NULL; + + if (_shortsSegment) { + ShortsMan.deleteSegment(_shortsSegment); + _shortsSegment = NULL; + } +} + +/** + * Follow the instruction of a relocation section. + * + * @param fd File Descriptor + * @param offset Offset into the File + * @param size Size of relocation section + * @param relSegment Base address of relocated segment in memory (memory offset) + * + */ +bool DLObject::relocate(int fd, unsigned long offset, unsigned long size, void *relSegment) { + Elf32_Rel *rel = NULL; // relocation entry + + // Allocate memory for relocation table + if (!(rel = (Elf32_Rel *)malloc(size))) { + seterror("Out of memory."); + return false; + } + + // Read in our relocation table + if (lseek(fd, offset, SEEK_SET) < 0 || + read(fd, rel, size) != (ssize_t)size) { + seterror("Relocation table load failed."); + free(rel); + return false; + } + + // Treat each relocation entry. Loop over all of them + int cnt = size / sizeof(*rel); + + DBG("Loaded relocation table. %d entries. base address=%p\n", cnt, relSegment); + + bool seenHi16 = false; // For treating HI/LO16 commands + int firstHi16 = -1; // Mark the point of the first hi16 seen + Elf32_Addr ahl = 0; // Calculated addend + int a = 0; // Addend: taken from the target + + unsigned int *lastTarget = 0; // For processing hi16 when lo16 arrives + unsigned int relocation = 0; + int debugRelocs[10] = {0}; // For debugging + int extendedHi16 = 0; // Count extended hi16 treatments + Elf32_Addr lastHiSymVal = 0; + bool hi16InShorts = false; + +#define DEBUG_NUM 2 + + // Loop over relocation entries + for (int i = 0; i < cnt; i++) { + // Get the symbol this relocation entry is referring to + Elf32_Sym *sym = (Elf32_Sym *)(_symtab) + (REL_INDEX(rel[i].r_info)); + + // Get the target instruction in the code + unsigned int *target = (unsigned int *)((char *)relSegment + rel[i].r_offset); + + unsigned int origTarget = *target; // Save for debugging + + // Act differently based on the type of relocation + switch (REL_TYPE(rel[i].r_info)) { + + case R_MIPS_HI16: // Absolute addressing. + if (sym->st_shndx < SHN_LOPROC && // Only shift for plugin section (ie. has a real section index) + firstHi16 < 0) { // Only process first in block of HI16s + firstHi16 = i; // Keep the first Hi16 we saw + seenHi16 = true; + ahl = (*target & 0xffff) << 16; // Take lower 16 bits shifted up + + lastHiSymVal = sym->st_value; + hi16InShorts = (ShortsMan.inGeneralSegment((char *)sym->st_value)); // Fix for problem with switching btw segments + if (debugRelocs[0]++ < DEBUG_NUM) // Print only a set number + DBG("R_MIPS_HI16: i=%d, offset=%x, ahl = %x, target = %x\n", + i, rel[i].r_offset, ahl, *target); + } + break; + + case R_MIPS_LO16: // Absolute addressing. Needs a HI16 to come before it + if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. (ie. has a real section index) + if (!seenHi16) { // We MUST have seen HI16 first + seterror("R_MIPS_LO16 w/o preceding R_MIPS_HI16 at relocation %d!\n", i); + free(rel); + return false; + } + + // Fix: bug in gcc makes LO16s connect to wrong HI16s sometimes (shorts and regular segment) + // Note that we can check the entire shorts segment because the executable's shorts don't belong to this plugin section + // and will be screened out above + bool lo16InShorts = ShortsMan.inGeneralSegment((char *)sym->st_value); + + // Correct the bug by getting the proper value in ahl (taken from the current symbol) + if ((hi16InShorts && !lo16InShorts) || (!hi16InShorts && lo16InShorts)) { + ahl -= (lastHiSymVal & 0xffff0000); // We assume gcc meant the same offset + ahl += (sym->st_value & 0xffff0000); + } + + ahl &= 0xffff0000; // Clean lower 16 bits for repeated LO16s + a = *target & 0xffff; // Take lower 16 bits of the target + a = (a << 16) >> 16; // Sign extend them + ahl += a; // Add lower 16 bits. AHL is now complete + + // Fix: we can have LO16 access to the short segment sometimes + if (lo16InShorts) { + relocation = ahl + _shortsSegment->getOffset(); // Add in the short segment offset + } else // It's in the regular segment + relocation = ahl + (Elf32_Addr)_segment; // Add in the new offset for the segment + + if (firstHi16 >= 0) { // We haven't treated the HI16s yet so do it now + for (int j = firstHi16; j < i; j++) { + if (REL_TYPE(rel[j].r_info) != R_MIPS_HI16) continue; // Skip over non-Hi16s + + lastTarget = (unsigned int *)((char *)relSegment + rel[j].r_offset); // get hi16 target + *lastTarget &= 0xffff0000; // Clear the lower 16 bits of the last target + *lastTarget |= (relocation >> 16) & 0xffff; // Take the upper 16 bits of the relocation + if (relocation & 0x8000)(*lastTarget)++; // Subtle: we need to add 1 to the HI16 in this case + } + firstHi16 = -1; // Reset so we'll know we treated it + } else { + extendedHi16++; + } + + *target &= 0xffff0000; // Clear the lower 16 bits of current target + *target |= relocation & 0xffff; // Take the lower 16 bits of the relocation + + if (debugRelocs[1]++ < DEBUG_NUM) + DBG("R_MIPS_LO16: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n", + i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target); + if (lo16InShorts && debugRelocs[2]++ < DEBUG_NUM) + DBG("R_MIPS_LO16s: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n", + i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target); + } + break; + + case R_MIPS_26: // Absolute addressing (for jumps and branches only) + if (sym->st_shndx < SHN_LOPROC) { // Only relocate for main segment + a = *target & 0x03ffffff; // Get 26 bits' worth of the addend + a = (a << 6) >> 6; // Sign extend a + relocation = ((a << 2) + (Elf32_Addr)_segment) >> 2; // a already points to the target. Subtract our offset + *target &= 0xfc000000; // Clean lower 26 target bits + *target |= (relocation & 0x03ffffff); + + if (debugRelocs[3]++ < DEBUG_NUM) + DBG("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n", + i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target); + } else { + if (debugRelocs[4]++ < DEBUG_NUM) + DBG("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n", + i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target); + } + break; + + case R_MIPS_GPREL16: // GP Relative addressing + if (_shortsSegment->getOffset() != 0 && // Only relocate if we shift the shorts section + ShortsMan.inGeneralSegment((char *)sym->st_value)) { // Only relocate things in the plugin hole + a = *target & 0xffff; // Get 16 bits' worth of the addend + a = (a << 16) >> 16; // Sign extend it + + relocation = a + _shortsSegment->getOffset(); + + *target &= 0xffff0000; // Clear the lower 16 bits of the target + *target |= relocation & 0xffff; + + if (debugRelocs[5]++ < DEBUG_NUM) + DBG("R_MIPS_GPREL16: i=%d, a=%x, gpVal=%x, origTarget=%x, target=%x, offset=%x\n", + i, a, _gpVal, origTarget, *target, _shortsSegment->getOffset()); + } + + break; + + case R_MIPS_32: // Absolute addressing + if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. + a = *target; // Get full 32 bits of addend + + if (ShortsMan.inGeneralSegment((char *)sym->st_value)) // Check if we're in the shorts segment + relocation = a + _shortsSegment->getOffset(); // Shift by shorts offset + else // We're in the main section + relocation = a + (Elf32_Addr)_segment; // Shift by main offset + *target = relocation; + + if (debugRelocs[6]++ < DEBUG_NUM) + DBG("R_MIPS_32: i=%d, a=%x, origTarget=%x, target=%x\n", i, a, origTarget, *target); + } + break; + + default: + seterror("Unknown relocation type %x at relocation %d.\n", REL_TYPE(rel[i].r_info), i); + free(rel); + return false; + } + } + + DBG("Done with relocation. extendedHi16=%d\n\n", extendedHi16); + + free(rel); + return true; +} + +bool DLObject::readElfHeader(int fd, Elf32_Ehdr *ehdr) { + // Start reading the elf header. Check for errors + if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr) || + memcmp(ehdr->e_ident, ELFMAG, SELFMAG) || // Check MAGIC + ehdr->e_type != ET_EXEC || // Check for executable + ehdr->e_machine != EM_MIPS || // Check for MIPS machine type + ehdr->e_phentsize < sizeof(Elf32_Phdr) || // Check for size of program header + ehdr->e_shentsize != sizeof(Elf32_Shdr)) { // Check for size of section header + seterror("Invalid file type."); + return false; + } + + DBG("phoff = %d, phentsz = %d, phnum = %d\n", + ehdr->e_phoff, ehdr->e_phentsize, ehdr->e_phnum); + + return true; +} + +bool DLObject::readProgramHeaders(int fd, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num) { + // Read program header + if (lseek(fd, ehdr->e_phoff + sizeof(*phdr)*num, SEEK_SET) < 0 || + read(fd, phdr, sizeof(*phdr)) != sizeof(*phdr)) { + seterror("Program header load failed."); + return false; + } + + // Check program header values + if (phdr->p_type != PT_LOAD || phdr->p_filesz > phdr->p_memsz) { + seterror("Invalid program header."); + return false; + } + + DBG("offs = %x, filesz = %x, memsz = %x, align = %x\n", + phdr->p_offset, phdr->p_filesz, phdr->p_memsz, phdr->p_align); + + return true; + +} + +bool DLObject::loadSegment(int fd, Elf32_Phdr *phdr) { + + char *baseAddress = 0; + + // We need to take account of non-allocated segment for shorts + if (phdr->p_flags & PF_X) { // This is a relocated segment + + // Attempt to allocate memory for segment + int extra = phdr->p_vaddr % phdr->p_align; // Get extra length TODO: check logic here + DBG("extra mem is %x\n", extra); + + if (phdr->p_align < 0x10000) phdr->p_align = 0x10000; // Fix for wrong alignment on e.g. AGI + + if (!(_segment = (char *)memalign(phdr->p_align, phdr->p_memsz + extra))) { + seterror("Out of memory.\n"); + return false; + } + DBG("allocated segment @ %p\n", _segment); + + // Get offset to load segment into + baseAddress = (char *)_segment + phdr->p_vaddr; + _segmentSize = phdr->p_memsz + extra; + } else { // This is a shorts section. + _shortsSegment = ShortsMan.newSegment(phdr->p_memsz, (char *)phdr->p_vaddr); + + baseAddress = _shortsSegment->getStart(); + DBG("shorts segment @ %p to %p. Segment wants to be at %x. Offset=%x\n", + _shortsSegment->getStart(), _shortsSegment->getEnd(), phdr->p_vaddr, _shortsSegment->getOffset()); + + } + + // Set bss segment to 0 if necessary (assumes bss is at the end) + if (phdr->p_memsz > phdr->p_filesz) { + DBG("Setting %p to %p to 0 for bss\n", baseAddress + phdr->p_filesz, baseAddress + phdr->p_memsz); + memset(baseAddress + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); + } + // Read the segment into memory + if (lseek(fd, phdr->p_offset, SEEK_SET) < 0 || + read(fd, baseAddress, phdr->p_filesz) != (ssize_t)phdr->p_filesz) { + seterror("Segment load failed."); + return false; + } + + return true; +} + + +Elf32_Shdr * DLObject::loadSectionHeaders(int fd, Elf32_Ehdr *ehdr) { + + Elf32_Shdr *shdr = NULL; + + // Allocate memory for section headers + if (!(shdr = (Elf32_Shdr *)malloc(ehdr->e_shnum * sizeof(*shdr)))) { + seterror("Out of memory."); + return NULL; + } + + // Read from file into section headers + if (lseek(fd, ehdr->e_shoff, SEEK_SET) < 0 || + read(fd, shdr, ehdr->e_shnum * sizeof(*shdr)) != + (ssize_t)(ehdr->e_shnum * sizeof(*shdr))) { + seterror("Section headers load failed."); + return NULL; + } + + return shdr; +} + +int DLObject::loadSymbolTable(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { + + // Loop over sections, looking for symbol table linked to a string table + for (int i = 0; i < ehdr->e_shnum; i++) { + //DBG("Section %d: type = %x, size = %x, entsize = %x, link = %x\n", + // i, shdr[i].sh_type, shdr[i].sh_size, shdr[i].sh_entsize, shdr[i].sh_link); + + if (shdr[i].sh_type == SHT_SYMTAB && + shdr[i].sh_entsize == sizeof(Elf32_Sym) && + shdr[i].sh_link < ehdr->e_shnum && + shdr[shdr[i].sh_link].sh_type == SHT_STRTAB && + _symtab_sect < 0) { + _symtab_sect = i; + } + } + + // Check for no symbol table + if (_symtab_sect < 0) { + seterror("No symbol table."); + return -1; + } + + DBG("Symbol section at section %d, size %x\n", _symtab_sect, shdr[_symtab_sect].sh_size); + + // Allocate memory for symbol table + if (!(_symtab = malloc(shdr[_symtab_sect].sh_size))) { + seterror("Out of memory."); + return -1; + } + + // Read symbol table into memory + if (lseek(fd, shdr[_symtab_sect].sh_offset, SEEK_SET) < 0 || + read(fd, _symtab, shdr[_symtab_sect].sh_size) != + (ssize_t)shdr[_symtab_sect].sh_size) { + seterror("Symbol table load failed."); + return -1; + } + + // Set number of symbols + _symbol_cnt = shdr[_symtab_sect].sh_size / sizeof(Elf32_Sym); + DBG("Loaded %d symbols.\n", _symbol_cnt); + + return _symtab_sect; + +} + +bool DLObject::loadStringTable(int fd, Elf32_Shdr *shdr) { + + int string_sect = shdr[_symtab_sect].sh_link; + + // Allocate memory for string table + if (!(_strtab = (char *)malloc(shdr[string_sect].sh_size))) { + seterror("Out of memory."); + return false; + } + + // Read string table into memory + if (lseek(fd, shdr[string_sect].sh_offset, SEEK_SET) < 0 || + read(fd, _strtab, shdr[string_sect].sh_size) != + (ssize_t)shdr[string_sect].sh_size) { + seterror("Symbol table strings load failed."); + return false; + } + return true; +} + +void DLObject::relocateSymbols(Elf32_Addr offset, Elf32_Addr shortsOffset) { + + int shortsCount = 0, othersCount = 0; + DBG("Relocating symbols by %x. Shorts offset=%x\n", offset, shortsOffset); + + // Loop over symbols, add relocation offset + Elf32_Sym *s = (Elf32_Sym *)_symtab; + for (int c = _symbol_cnt; c--; s++) { + // Make sure we don't relocate special valued symbols + if (s->st_shndx < SHN_LOPROC) { + if (!ShortsMan.inGeneralSegment((char *)s->st_value)) { + othersCount++; + s->st_value += offset; + if (s->st_value < (Elf32_Addr)_segment || s->st_value > (Elf32_Addr)_segment + _segmentSize) + seterror("Symbol out of bounds! st_value = %x\n", s->st_value); + } else { // shorts section + shortsCount++; + s->st_value += shortsOffset; + if (!_shortsSegment->inSegment((char *)s->st_value)) + seterror("Symbol out of bounds! st_value = %x\n", s->st_value); + } + + } + + } + + DBG("Relocated %d short symbols, %d others.\n", shortsCount, othersCount); +} + +bool DLObject::relocateRels(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { + + // Loop over sections, finding relocation sections + for (int i = 0; i < ehdr->e_shnum; i++) { + + Elf32_Shdr *curShdr = &(shdr[i]); + //Elf32_Shdr *linkShdr = &(shdr[curShdr->sh_info]); + + if (curShdr->sh_type == SHT_REL && // Check for a relocation section + curShdr->sh_entsize == sizeof(Elf32_Rel) && // Check for proper relocation size + (int)curShdr->sh_link == _symtab_sect && // Check that the sh_link connects to our symbol table + curShdr->sh_info < ehdr->e_shnum && // Check that the relocated section exists + (shdr[curShdr->sh_info].sh_flags & SHF_ALLOC)) { // Check if relocated section resides in memory + if (!ShortsMan.inGeneralSegment((char *)shdr[curShdr->sh_info].sh_addr)) { // regular segment + if (!relocate(fd, curShdr->sh_offset, curShdr->sh_size, _segment)) { + return false; + } + } else { // In Shorts segment + if (!relocate(fd, curShdr->sh_offset, curShdr->sh_size, (void *)_shortsSegment->getOffset())) { + return false; + } + } + + } + } + + return true; +} + + +bool DLObject::load(int fd) { + fprintf(stderr, "In DLObject::load\n"); + + Elf32_Ehdr ehdr; // ELF header + Elf32_Phdr phdr; // Program header + Elf32_Shdr *shdr; // Section header + bool ret = true; + + if (readElfHeader(fd, &ehdr) == false) { + return false; + } + + for (int i = 0; i < ehdr.e_phnum; i++) { // Load our 2 segments + + fprintf(stderr, "Loading segment %d\n", i); + + if (readProgramHeaders(fd, &ehdr, &phdr, i) == false) + return false; + + if (!loadSegment(fd, &phdr)) + return false; + } + + if ((shdr = loadSectionHeaders(fd, &ehdr)) == NULL) + ret = false; + + if (ret && ((_symtab_sect = loadSymbolTable(fd, &ehdr, shdr)) < 0)) + ret = false; + + if (ret && (loadStringTable(fd, shdr) == false)) + ret = false; + + if (ret) + relocateSymbols((Elf32_Addr)_segment, _shortsSegment->getOffset()); // Offset by our segment allocated address + + if (ret && (relocateRels(fd, &ehdr, shdr) == false)) + ret = false; + + free(shdr); + + return ret; +} + +bool DLObject::open(const char *path) { + int fd; + void *ctors_start, *ctors_end; + + DBG("open(\"%s\")\n", path); + + // Get the address of the global pointer + _gpVal = (unsigned int) & _gp; + DBG("_gpVal is %x\n", _gpVal); + + PowerMan.beginCriticalSection(); + + if ((fd = ::open(path, O_RDONLY)) < 0) { + seterror("%s not found.", path); + return false; + } + + // Try to load and relocate + if (!load(fd)) { + ::close(fd); + unload(); + return false; + } + + ::close(fd); + + PowerMan.endCriticalSection(); + + // flush data cache + sceKernelDcacheWritebackAll(); + + // Get the symbols for the global constructors and destructors + ctors_start = symbol("___plugin_ctors"); + ctors_end = symbol("___plugin_ctors_end"); + _dtors_start = symbol("___plugin_dtors"); + _dtors_end = symbol("___plugin_dtors_end"); + + if (ctors_start == NULL || ctors_end == NULL || _dtors_start == NULL || + _dtors_end == NULL) { + seterror("Missing ctors/dtors."); + _dtors_start = _dtors_end = NULL; + unload(); + return false; + } + + DBG("Calling constructors.\n"); + for (void (**f)(void) = (void (**)(void))ctors_start; f != ctors_end; f++) + (**f)(); + + DBG("%s opened ok.\n", path); + return true; +} + +bool DLObject::close() { + if (_dtors_start != NULL && _dtors_end != NULL) + for (void (**f)(void) = (void (**)(void))_dtors_start; f != _dtors_end; f++) + (**f)(); + _dtors_start = _dtors_end = NULL; + unload(); + return true; +} + +void *DLObject::symbol(const char *name) { + DBG("symbol(\"%s\")\n", name); + + if (_symtab == NULL || _strtab == NULL || _symbol_cnt < 1) { + seterror("No symbol table loaded."); + return NULL; + } + + Elf32_Sym *s = (Elf32_Sym *)_symtab; + for (int c = _symbol_cnt; c--; s++) { + + // We can only import symbols that are global or weak in the plugin + if ((SYM_BIND(s->st_info) == STB_GLOBAL || SYM_BIND(s->st_info) == STB_WEAK) && + /*_strtab[s->st_name] == '_' && */ // Try to make this more efficient + !strcmp(name, _strtab + s->st_name)) { + + // We found the symbol + DBG("=> %p\n", (void*)s->st_value); + return (void*)s->st_value; + } + } + + seterror("Symbol \"%s\" not found.", name); + return NULL; +} + + + +ShortSegmentManager::ShortSegmentManager() { + _shortsStart = &__plugin_hole_start ; + _shortsEnd = &__plugin_hole_end; +} + +ShortSegmentManager::Segment *ShortSegmentManager::newSegment(int size, char *origAddr) { + char *lastAddress = origAddr; + Common::List<Segment *>::iterator i; + + // Find a block that fits, starting from the beginning + for (i = _list.begin(); i != _list.end(); ++i) { + char *currAddress = (*i)->getStart(); + + if ((int)(currAddress - lastAddress) >= size) break; + + lastAddress = (*i)->getEnd(); + } + + if ((Elf32_Addr)lastAddress & 3) + lastAddress += 4 - ((Elf32_Addr)lastAddress & 3); // Round up to multiple of 4 + + if (lastAddress + size > _shortsEnd) { + seterror("Error. No space in shorts segment for %x bytes. Last address is %p, max address is %p.\n", + size, lastAddress, _shortsEnd); + return NULL; + } + + Segment *seg = new Segment(lastAddress, size, origAddr); // Create a new segment + + if (lastAddress + size > _highestAddress) _highestAddress = lastAddress + size; // Keep track of maximum + + _list.insert(i, seg); + + DBG("Shorts segment size %x allocated. End = %p. Remaining space = %x. Highest so far is %p.\n", + size, lastAddress + size, _shortsEnd - _list.back()->getEnd(), _highestAddress); + + return seg; +} + +void ShortSegmentManager::deleteSegment(ShortSegmentManager::Segment *seg) { + DBG("Deleting shorts segment from %p to %p.\n\n", seg->getStart(), seg->getEnd()); + _list.remove(seg); + delete seg; +} + +static char dlerr[MAXDLERRLEN]; + +void *dlopen(const char *filename, int flags) { + DLObject *obj = new DLObject(dlerr); + if (obj->open(filename)) + return (void *)obj; + delete obj; + return NULL; +} + +int dlclose(void *handle) { + DLObject *obj = (DLObject *)handle; + if (obj == NULL) { + strcpy(dlerr, "Handle is NULL."); + return -1; + } + if (obj->close()) { + delete obj; + return 0; + } + return -1; +} + +void *dlsym(void *handle, const char *symbol) { + if (handle == NULL) { + strcpy(dlerr, "Handle is NULL."); + return NULL; + } + return ((DLObject *)handle)->symbol(symbol); +} + +const char *dlerror() { + return dlerr; +} + +void dlforgetsyms(void *handle) { + if (handle != NULL) + ((DLObject *)handle)->discard_symtab(); +} + + +#endif /* DYNAMIC_MODULES && __PLAYSTATION2__ */ diff --git a/backends/platform/ps2/ps2loader.h b/backends/platform/ps2/ps2loader.h new file mode 100644 index 0000000000..7c412e7b05 --- /dev/null +++ b/backends/platform/ps2/ps2loader.h @@ -0,0 +1,137 @@ +/* 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 PS2LOADER_H +#define PS2LOADER_H + +#include "elf32.h" +#include "common/list.h" +#include "common/singleton.h" + +#define MAXDLERRLEN 80 + +#define ShortsMan ShortSegmentManager::instance() + +class ShortSegmentManager : public Common::Singleton<ShortSegmentManager> { +private: + char *_shortsStart; + char *_shortsEnd; + +public: + char *getShortsStart() { + return _shortsStart; + } + bool inGeneralSegment(char *addr) { + return ((char *)addr >= _shortsStart && (char *)addr < _shortsEnd); + } + + class Segment { + private: + friend class ShortSegmentManager; + Segment(char *start, int size, char *origAddr) : _startAddress(start), _size(size), _origAddress(origAddr) {} + ~Segment() {} + char *_startAddress; // Start of shorts segment in memory + int _size; // Size of shorts segment + char *_origAddress; // Original address this segment was supposed to be at + public: + char *getStart() { + return _startAddress; + } + char *getEnd() { + return (_startAddress + _size); + } + Elf32_Addr getOffset() { + return (Elf32_Addr)(_startAddress - _origAddress); + } + bool inSegment(char *addr) { + return ((char *)addr >= _startAddress && (char *)addr <= _startAddress + _size); + } + }; + + Segment *newSegment(int size, char *origAddr); + void deleteSegment(Segment *); + +private: + ShortSegmentManager(); + friend class Common::Singleton<ShortSegmentManager>; + Common::List<Segment *> _list; + char *_highestAddress; +}; + + + + +class DLObject { +protected: + char *_errbuf; /* For error messages, at least MAXDLERRLEN in size */ + + ShortSegmentManager::Segment *_shortsSegment; // For assigning shorts ranges + void *_segment, *_symtab; + char *_strtab; + int _symbol_cnt; + int _symtab_sect; + void *_dtors_start, *_dtors_end; + + unsigned int _gpVal; // Value of Global Pointer + int _segmentSize; + + void seterror(const char *fmt, ...); + void unload(); + bool relocate(int fd, unsigned long offset, unsigned long size, void *); + bool load(int fd); + + bool readElfHeader(int fd, Elf32_Ehdr *ehdr); + bool readProgramHeaders(int fd, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num); + bool loadSegment(int fd, Elf32_Phdr *phdr); + Elf32_Shdr *loadSectionHeaders(int fd, Elf32_Ehdr *ehdr); + int loadSymbolTable(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); + bool loadStringTable(int fd, Elf32_Shdr *shdr); + void relocateSymbols(Elf32_Addr offset, Elf32_Addr shortsOffset); + bool relocateRels(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); + +public: + bool open(const char *path); + bool close(); + void *symbol(const char *name); + void discard_symtab(); + + DLObject(char *errbuf = NULL) : _errbuf(_errbuf), _shortsSegment(NULL), _segment(NULL), _symtab(NULL), + _strtab(NULL), _symbol_cnt(0), _symtab_sect(-1), _dtors_start(NULL), _dtors_end(NULL), _gpVal(0) , + _segmentSize(0) {} +}; + + + +#define RTLD_LAZY 0 + +extern "C" { + void *dlopen(const char *filename, int flags); + int dlclose(void *handle); + void *dlsym(void *handle, const char *symbol); + const char *dlerror(); + void dlforgetsyms(void *handle); +} + +#endif /* PS2LOADER_H */ diff --git a/backends/platform/ps2/systemps2.cpp b/backends/platform/ps2/systemps2.cpp index 7659d5194d..120f6ee157 100644 --- a/backends/platform/ps2/systemps2.cpp +++ b/backends/platform/ps2/systemps2.cpp @@ -59,6 +59,8 @@ #include "backends/platform/ps2/ps2debug.h" #include "backends/fs/ps2/ps2-fs-factory.h" +#include "backends/plugins/ps2/ps2-provider.h" + #include "backends/saves/default/default-saves.h" #include "common/config-manager.h" @@ -132,6 +134,10 @@ extern "C" int main(int argc, char *argv[]) { g_systemPs2->init(); +#ifdef DYNAMIC_MODULES + PluginManager::instance().addPluginProvider(new PS2PluginProvider()); +#endif + sioprintf("init done. starting ScummVM.\n"); int res = scummvm_main(argc, argv); sioprintf("scummvm_main terminated: %d\n", res); diff --git a/backends/platform/psp/Makefile b/backends/platform/psp/Makefile index 7f8bb63b0a..8e83563d10 100644 --- a/backends/platform/psp/Makefile +++ b/backends/platform/psp/Makefile @@ -129,8 +129,7 @@ SDLFLAGS := $(shell $(PSPBIN)/sdl-config --cflags) SDLLIBS := $(shell $(PSPBIN)/sdl-config --libs) # PSP LIBS PSPLIBS = -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk \ - -lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspaudiocodec \ - -lpspkernel + -lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspkernel # Add in PSPSDK includes and libraries. CXXFLAGS += $(SDLFLAGS) @@ -148,10 +147,8 @@ OBJS := powerman.o \ cursor.o \ trace.o \ psploader.o \ - pspkeyboard.o \ - audio.o \ - thread.o \ - mp3.o + pspkeyboard.o + # Include common Scummvm makefile include $(srcdir)/Makefile.common diff --git a/backends/platform/psp/audio.cpp b/backends/platform/psp/audio.cpp deleted file mode 100644 index bf1fb9ab41..0000000000 --- a/backends/platform/psp/audio.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ - * - */ - -#include <pspthreadman.h> -#include <pspaudio.h> - -#include "common/scummsys.h" -#include "backends/platform/psp/audio.h" -#include "backends/platform/psp/thread.h" - -//#define __PSP_DEBUG_FUNCS__ /* For debugging function calls */ -//#define __PSP_DEBUG_PRINT__ /* For debug printouts */ - -#include "backends/platform/psp/trace.h" - -bool PspAudio::open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData) { - DEBUG_ENTER_FUNC(); - if (_init) { - PSP_ERROR("audio device already initialized\n"); - return true; - } - - PSP_DEBUG_PRINT("freq[%d], numOfChannels[%d], numOfSamples[%d], callback[%p], userData[%x]\n", - freq, numOfChannels, numOfSamples, callback, (uint32)userData); - - numOfSamples = PSP_AUDIO_SAMPLE_ALIGN(numOfSamples); - uint32 bufLen = numOfSamples * numOfChannels * NUM_BUFFERS * sizeof(uint16); - - PSP_DEBUG_PRINT("total buffer size[%d]\n", bufLen); - - _buffers[0] = (byte *)memalign(64, bufLen); - if (!_buffers[0]) { - PSP_ERROR("failed to allocate memory for audio buffers\n"); - return false; - } - memset(_buffers[0], 0, bufLen); // clean the buffer - - // Fill in the rest of the buffer pointers - byte *pBuffer = _buffers[0]; - for (int i = 1; i < NUM_BUFFERS; i++) { - pBuffer += numOfSamples * numOfChannels * sizeof(uint16); - _buffers[i] = pBuffer; - } - - // Reserve a HW channel for our audio - _pspChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, numOfSamples, numOfChannels == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO); - if (_pspChannel < 0) { - PSP_ERROR("failed to reserve audio channel\n"); - return false; - } - - PSP_DEBUG_PRINT("reserved channel[%d] for audio\n", _pspChannel); - - // Save our data - _numOfChannels = numOfChannels; - _numOfSamples = numOfSamples; - _bufferSize = numOfSamples * numOfChannels * sizeof(uint16); // should be the right size to send the app - _callback = callback; - _userData = userData; - _bufferToFill = 0; - _bufferToPlay = 0; - - _init = true; - _paused = true; // start in paused mode - - createThread(); - - return true; -} - -bool PspAudio::createThread() { - DEBUG_ENTER_FUNC(); - int threadId = sceKernelCreateThread("audioThread", thread, PRIORITY_AUDIO_THREAD, STACK_AUDIO_THREAD, THREAD_ATTR_USER, 0); - - if (threadId < 0) { // error - PSP_ERROR("failed to create audio thread. Error code %d\n", threadId); - return false; - } - - PspAudio *_this = this; // trick to get into context when the thread starts - - if (sceKernelStartThread(threadId, sizeof(uint32 *), &_this) < 0) { - PSP_ERROR("failed to start thread %d\n", threadId); - return false; - } - - PSP_DEBUG_PRINT("created audio thread[%x]\n", threadId); - - return true; -} - -// Static function to be called upon thread startup. Will call a non-static function -int PspAudio::thread(SceSize, void *__this) { - DEBUG_ENTER_FUNC(); - PspAudio *_this = *(PspAudio **)__this; // get our this for the context - - _this->audioThread(); - return 0; -}; - -// The real thread function -void PspAudio::audioThread() { - assert(_callback); - PSP_DEBUG_PRINT_FUNC("audio thread started\n"); - - while (_init) { // Keep looping so long as we haven't been told to stop - if (_paused) - PSP_DEBUG_PRINT("audio thread paused\n"); - while (_paused) { // delay until we stop pausing - sceKernelDelayThread(100000); // 100ms - if (!_paused) - PSP_DEBUG_PRINT("audio thread unpaused\n"); - } - - PSP_DEBUG_PRINT("remaining samples[%d]\n", remainingSamples); - - PSP_DEBUG_PRINT("filling buffer[%d]\n", _bufferToFill); - _callback(_userData, _buffers[_bufferToFill], _bufferSize); // ask mixer to fill in - nextBuffer(_bufferToFill); - - PSP_DEBUG_PRINT("playing buffer[%d].\n", _bufferToPlay); - playBuffer(); - nextBuffer(_bufferToPlay); - } // while _init - - // destroy everything - free(_buffers[0]); - sceAudioChRelease(_pspChannel); - PSP_DEBUG_PRINT("audio thread exiting. ****************************\n"); -} - -// Much faster than using % -inline void PspAudio::nextBuffer(int &bufferIdx) { - DEBUG_ENTER_FUNC(); - bufferIdx++; - if (bufferIdx >= NUM_BUFFERS) - bufferIdx = 0; -} - -// Don't do it with blocking -inline bool PspAudio::playBuffer() { - DEBUG_ENTER_FUNC(); - int ret; - if (_numOfChannels == 1) - ret = sceAudioOutputBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]); - else - ret = sceAudioOutputPannedBlocking(_pspChannel, PSP_AUDIO_VOLUME_MAX, PSP_AUDIO_VOLUME_MAX, _buffers[_bufferToPlay]); - - if (ret < 0) { - PSP_ERROR("failed to output audio. Error[%d]\n", ret); - return false; - } - return true; -} - -void PspAudio::close() { - PSP_DEBUG_PRINT("close had been called ***************\n"); - _init = false; -} diff --git a/backends/platform/psp/audio.h b/backends/platform/psp/audio.h deleted file mode 100644 index 603f8f6bfc..0000000000 --- a/backends/platform/psp/audio.h +++ /dev/null @@ -1,69 +0,0 @@ -/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ - * - */ - -#ifndef PSP_AUDIO_H -#define PSP_AUDIO_H - -class PspAudio { -public: - enum { - NUM_BUFFERS = 2, - FREQUENCY = 44100 /* only frequency we allow */ - }; - typedef void (* callbackFunc)(void *userData, byte *samples, int len); - PspAudio() : _pspChannel(0), - _numOfChannels(0), _numOfSamples(0), _callback(0), - _bufferToPlay(0), _bufferToFill(0), - _init(false), _paused(true) { - for (int i=0; i<NUM_BUFFERS; i++) - _buffers[i] = 0; - } - ~PspAudio() { close(); } - bool playBuffer(); - void nextBuffer(int &bufferIdx); - static int thread(SceSize, void *); - void audioThread(); - bool open(uint32 freq, uint32 numOfChannels, uint32 numOfSamples, callbackFunc callback, void *userData); - bool createThread(); - void close(); - uint32 getFrequency() { return FREQUENCY; } - void pause() { _paused = true; } - void unpause() { _paused = false; } - -private: - int _pspChannel; // chosen hardware output channel - uint32 _numOfChannels; // 1 for mono; 2 for stereo - uint32 _numOfSamples; - callbackFunc _callback; // the callback to call between outputting audio - void *_userData; // userData to send with callback - byte *_buffers[NUM_BUFFERS]; - int _bufferToPlay; // the next buffer to output - int _bufferToFill; - int _bufferSize; - bool _init; // flag for initialization - bool _paused; -}; - -#endif /* PSP_AUDIO_H */ diff --git a/backends/platform/psp/display_client.cpp b/backends/platform/psp/display_client.cpp index c5a6250188..90c41e796d 100644 --- a/backends/platform/psp/display_client.cpp +++ b/backends/platform/psp/display_client.cpp @@ -686,18 +686,17 @@ void GuRenderer::fillVertices(Vertex *vertices) { uint32 gapX = _useGlobalScaler ? (PSP_SCREEN_WIDTH - outputWidth) >> 1 : 0; uint32 gapY = _useGlobalScaler ? (PSP_SCREEN_HEIGHT - outputHeight) >> 1 : 0; - // Save scaled offset on screen - float scaledOffsetOnScreenX = scaleSourceToOutputX(_offsetOnScreen.x); - float scaledOffsetOnScreenY = scaleSourceToOutputY(_offsetOnScreen.y); - float imageStartX, imageStartY, imageEndX, imageEndY; - imageStartX = gapX + scaledOffsetOnScreenX + (scaleSourceToOutputX(_maxTextureOffset.x)); - imageStartY = gapY + scaledOffsetOnScreenY; + imageStartX = gapX + (scaleSourceToOutputX(_maxTextureOffset.x)); + imageStartY = gapY; + + imageStartX += scaleSourceToOutputX(_offsetOnScreen.x); + imageStartY += scaleSourceToOutputY(_offsetOnScreen.y); if (_fullScreen) { // shortcut - imageEndX = PSP_SCREEN_WIDTH - gapX + scaledOffsetOnScreenX; - imageEndY = PSP_SCREEN_HEIGHT - gapY + scaledOffsetOnScreenY; // needed for screen shake + imageEndX = PSP_SCREEN_WIDTH - gapX; + imageEndY = PSP_SCREEN_HEIGHT - gapY; } else { /* !fullScreen */ imageEndX = imageStartX + scaleSourceToOutputX(_drawSize.width); imageEndY = imageStartY + scaleSourceToOutputY(_drawSize.height); diff --git a/backends/platform/psp/display_manager.cpp b/backends/platform/psp/display_manager.cpp index c2f21e084b..0982512a86 100644 --- a/backends/platform/psp/display_manager.cpp +++ b/backends/platform/psp/display_manager.cpp @@ -34,7 +34,6 @@ #include "backends/platform/psp/default_display_client.h" #include "backends/platform/psp/cursor.h" #include "backends/platform/psp/pspkeyboard.h" -#include "backends/platform/psp/thread.h" #define USE_DISPLAY_CALLBACK // to use callback for finishing the render #include "backends/platform/psp/display_manager.h" @@ -65,7 +64,7 @@ const OSystem::GraphicsMode DisplayManager::_supportedModes[] = { void MasterGuRenderer::setupCallbackThread() { DEBUG_ENTER_FUNC(); - int thid = sceKernelCreateThread("displayCbThread", guCallbackThread, PRIORITY_DISPLAY_THREAD, STACK_DISPLAY_THREAD, THREAD_ATTR_USER, 0); + int thid = sceKernelCreateThread("displayCbThread", guCallbackThread, 0x11, 4*1024, THREAD_ATTR_USER, 0); PSP_DEBUG_PRINT("Display CB thread id is %x\n", thid); diff --git a/backends/platform/psp/module.mk b/backends/platform/psp/module.mk index 99170ce7fb..f7191fe14f 100644 --- a/backends/platform/psp/module.mk +++ b/backends/platform/psp/module.mk @@ -12,10 +12,7 @@ MODULE_OBJS := powerman.o \ cursor.o \ trace.o \ psploader.o \ - pspkeyboard.o \ - audio.o \ - thread.o \ - mp3.o + pspkeyboard.o MODULE_DIRS += \ backends/platform/psp/ diff --git a/backends/platform/psp/mp3.cpp b/backends/platform/psp/mp3.cpp deleted file mode 100644 index 972c5a8ba8..0000000000 --- a/backends/platform/psp/mp3.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/* 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 "common/debug.h" -#include "common/stream.h" -#include "common/util.h" -#include "common/singleton.h" -#include "common/mutex.h" - -#include "sound/audiostream.h" - -#include <pspaudiocodec.h> -#include <psputility_modules.h> -#include <pspthreadman.h> -#include <pspsysmem.h> -#include <pspmodulemgr.h> -#include <psputility_avmodules.h> -#include <mad.h> -#include "backends/platform/psp/mp3.h" - -//#define DISABLE_PSP_MP3 // to make us use the regular MAD decoder instead - -//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */ -//#define __PSP_DEBUG_PRINT__ -#include "backends/platform/psp/trace.h" - -//#define PRINT_BUFFERS /* to debug MP3 buffers */ - -namespace Audio { - -class Mp3PspStream; - -bool Mp3PspStream::_decoderInit = false; // has the decoder been initialized -#ifdef DISABLE_PSP_MP3 -bool Mp3PspStream::_decoderFail = true; // pretend the decoder failed -#else -bool Mp3PspStream::_decoderFail = false; // has the decoder failed to load -#endif - -bool Mp3PspStream::initDecoder() { - DEBUG_ENTER_FUNC(); - - if (_decoderInit) { - PSP_ERROR("Already initialized!"); - return true; - } - - // Based on PSP firmware version, we need to do different things to do Media Engine processing - uint32 firmware = sceKernelDevkitVersion(); - PSP_DEBUG_PRINT("Firmware version 0x%x\n", firmware); - if (firmware == 0x01050001){ - if (!loadStartAudioModule((char *)(void *)"flash0:/kd/me_for_vsh.prx", - PSP_MEMORY_PARTITION_KERNEL)) { - PSP_ERROR("failed to load me_for_vsh.prx. ME cannot start.\n"); - _decoderFail = true; - return false; - } - if (!loadStartAudioModule((char *)(void *)"flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL)) { - PSP_ERROR("failed to load audiocodec.prx. ME cannot start.\n"); - _decoderFail = true; - return false; - } - } else { - if (sceUtilityLoadAvModule(PSP_AV_MODULE_AVCODEC) < 0) { - PSP_ERROR("failed to load AVCODEC module.\n"); - _decoderFail = true; - return false; - } - } - - PSP_INFO_PRINT("Using PSP's ME for MP3\n"); // important to know this is happening - - _decoderInit = true; - return true; -} - -bool Mp3PspStream::stopDecoder() { - DEBUG_ENTER_FUNC(); - - if (!_decoderInit) - return true; - - // Based on PSP firmware version, we need to do different things to do Media Engine processing - if (sceKernelDevkitVersion() == 0x01050001){ -/* if (!unloadAudioModule("flash0:/kd/me_for_vsh.prx", PSP_MEMORY_PARTITION_KERNEL) || - !unloadAudioModule("flash0:/kd/audiocodec.prx", PSP_MEMORY_PARTITION_KERNEL) { - PSP_ERROR("failed to unload audio module\n"); - return false; - } -*/ - }else{ - if (sceUtilityUnloadModule(PSP_MODULE_AV_AVCODEC) < 0) { - PSP_ERROR("failed to unload avcodec module\n"); - return false; - } - } - - _decoderInit = false; - return true; -} - -//Load a PSP audio module -bool Mp3PspStream::loadStartAudioModule(const char *modname, int partition){ - DEBUG_ENTER_FUNC(); - - SceKernelLMOption option; - SceUID modid; - - memset(&option, 0, sizeof(option)); - option.size = sizeof(option); - option.mpidtext = partition; - option.mpiddata = partition; - option.position = 0; - option.access = 1; - - modid = sceKernelLoadModule(modname, 0, &option); - if (modid < 0) { - PSP_ERROR("Failed to load module %s. Got error 0x%x\n", modname, modid); - return false; - } - - int ret = sceKernelStartModule(modid, 0, NULL, NULL, NULL); - if (ret < 0) { - PSP_ERROR("Failed to start module %s. Got error 0x%x\n", modname, ret); - return false; - } - return true; -} - -// TODO: make parallel function for unloading the 1.50 modules - -Mp3PspStream::Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) : - _inStream(inStream), - _disposeAfterUse(dispose), - _pcmLength(0), - _posInFrame(0), - _state(MP3_STATE_INIT), - _length(0, 1000), - _sampleRate(0), - _totalTime(mad_timer_zero) { - - DEBUG_ENTER_FUNC(); - - assert(_decoderInit); // must be initialized by now - - // let's leave the buffer guard -- who knows, it may be good? - memset(_buf, 0, sizeof(_buf)); - memset(_codecInBuffer, 0, sizeof(_codecInBuffer)); - - initStream(); // init needed stuff for the stream - - while (_state != MP3_STATE_EOS) - findValidHeader(); // get a first header so we can read basic stuff - - _sampleRate = _header.samplerate; // copy it before it gets destroyed - - _length = Timestamp(mad_timer_count(_totalTime, MAD_UNITS_MILLISECONDS), getRate()); - - //initStreamME(); // init the stuff needed for the ME to work - - deinitStream(); - //releaseStreamME(); - - _state = MP3_STATE_INIT; -} - -int Mp3PspStream::initStream() { - DEBUG_ENTER_FUNC(); - - if (_state != MP3_STATE_INIT) - deinitStream(); - - // Init MAD - mad_stream_init(&_stream); - mad_header_init(&_header); - - // Reset the stream data - _inStream->seek(0, SEEK_SET); - _totalTime = mad_timer_zero; - _posInFrame = 0; - - // Update state - _state = MP3_STATE_READY; - - // Read the first few sample bytes into the buffer - readMP3DataIntoBuffer(); - - return true; -} - -bool Mp3PspStream::initStreamME() { - // The following will eventually go into the thread - sceAudiocodecReleaseEDRAM(_codecParams); // do we need this? - - memset(_codecParams, 0, sizeof(_codecParams)); - - // Init the MP3 hardware - int ret = 0; - ret = sceAudiocodecCheckNeedMem(_codecParams, 0x1002); - if (ret < 0) { - PSP_ERROR("failed to init MP3 ME module. sceAudiocodecCheckNeedMem returned 0x%x.\n", ret); - return false; - } - PSP_DEBUG_PRINT("sceAudiocodecCheckNeedMem returned %d\n", ret); - ret = sceAudiocodecGetEDRAM(_codecParams, 0x1002); - if (ret < 0) { - PSP_ERROR("failed to init MP3 ME module. sceAudiocodecGetEDRAM returned 0x%x.\n", ret); - return false; - } - PSP_DEBUG_PRINT("sceAudioCodecGetEDRAM returned %d\n", ret); - - PSP_DEBUG_PRINT("samplerate[%d]\n", _sampleRate); - _codecParams[10] = _sampleRate; - - ret = sceAudiocodecInit(_codecParams, 0x1002); - if (ret < 0) { - PSP_ERROR("failed to init MP3 ME module. sceAudiocodecInit returned 0x%x.\n", ret); - return false; - } - - return true; -} - -Mp3PspStream::~Mp3PspStream() { - DEBUG_ENTER_FUNC(); - - deinitStream(); - releaseStreamME(); // free the memory used for this stream - - if (_disposeAfterUse == DisposeAfterUse::YES) - delete _inStream; -} - -void Mp3PspStream::deinitStream() { - DEBUG_ENTER_FUNC(); - - if (_state == MP3_STATE_INIT) - return; - - // Deinit MAD - mad_header_finish(&_header); - mad_stream_finish(&_stream); - - _state = MP3_STATE_EOS; -} - -void Mp3PspStream::releaseStreamME() { - sceAudiocodecReleaseEDRAM(_codecParams); -} - -void Mp3PspStream::decodeMP3Data() { - DEBUG_ENTER_FUNC(); - - do { - if (_state == MP3_STATE_INIT) { - initStream(); - initStreamME(); - } - - if (_state == MP3_STATE_EOS) - return; - - findValidHeader(); // seach for next valid header - - while (_state == MP3_STATE_READY) { - _stream.error = MAD_ERROR_NONE; - - uint32 frame_size = _stream.next_frame - _stream.this_frame; - uint32 samplesPerFrame = _header.layer == MAD_LAYER_III ? 576 : 1152; // Varies by layer - // calculate frame size -- try - //uint32 calc_frame_size = ((144 * _header.bitrate) / 22050) + (_header.flags & MAD_FLAG_PADDING ? 1 : 0); - - // Get stereo/mono - uint32 multFactor = 1; - if (_header.mode != MAD_MODE_SINGLE_CHANNEL) // mono - x2 for 16bit - multFactor *= 2; // stereo - x4 for 16bit - - PSP_DEBUG_PRINT("MP3 frame size[%d]. Samples[%d]. Multfactor[%d] pad[%d]\n", frame_size, samplesPerFrame, multFactor, _header.flags & MAD_FLAG_PADDING); - memcpy(_codecInBuffer, _stream.this_frame, frame_size); // we need it aligned - - // set up parameters for ME - _codecParams[6] = (unsigned long)_codecInBuffer; - _codecParams[8] = (unsigned long)_pcmSamples; - _codecParams[7] = frame_size; - _codecParams[9] = samplesPerFrame * multFactor; // x2 for stereo - - // debug -#ifdef PRINT_BUFFERS - PSP_DEBUG_PRINT("mp3 frame:\n"); - for (int i=0; i < (int)frame_size; i++) { - PSP_DEBUG_PRINT_SAMELN("%x ", _codecInBuffer[i]); - } - PSP_DEBUG_PRINT("\n"); -#endif - // Decode the next frame - // This function blocks. We'll want to put it in a thread - int ret = sceAudiocodecDecode(_codecParams, 0x1002); - if (ret < 0) { - PSP_ERROR("failed to decode MP3 data in ME. sceAudiocodecDecode returned 0x%x\n", ret); - // handle error here - } - -#ifdef PRINT_BUFFERS - PSP_DEBUG_PRINT("PCM frame:\n"); - for (int i=0; i < (int)_codecParams[9]; i+=2) { // changed from i+=2 - PSP_DEBUG_PRINT_SAMELN("%d ", (int16)_pcmSamples[i]); - } - PSP_DEBUG_PRINT("\n"); -#endif - _pcmLength = samplesPerFrame; - _posInFrame = 0; - break; - } - } while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN); - - if (_stream.error != MAD_ERROR_NONE) // catch EOS - _state = MP3_STATE_EOS; -} - -void Mp3PspStream::readMP3DataIntoBuffer() { - DEBUG_ENTER_FUNC(); - - uint32 remaining = 0; - - // Give up immediately if we already used up all data in the stream - if (_inStream->eos()) { - _state = MP3_STATE_EOS; - return; - } - - if (_stream.next_frame) { - // If there is still data in the MAD stream, we need to preserve it. - // Note that we use memmove, as we are reusing the same buffer, - // and hence the data regions we copy from and to may overlap. - remaining = _stream.bufend - _stream.next_frame; - assert(remaining < BUFFER_SIZE); // Paranoia check - memmove(_buf, _stream.next_frame, remaining); // TODO: may want another buffer - } - - // Try to read the next block - uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining); - if (size <= 0) { - _state = MP3_STATE_EOS; - return; - } - - // Feed the data we just read into the stream decoder - _stream.error = MAD_ERROR_NONE; - mad_stream_buffer(&_stream, _buf, size + remaining); // just setup the pointers -} - -bool Mp3PspStream::seek(const Timestamp &where) { - DEBUG_ENTER_FUNC(); - - if (where == _length) { - _state = MP3_STATE_EOS; - return true; - } else if (where > _length) { - return false; - } - - const uint32 time = where.msecs(); - - mad_timer_t destination; - mad_timer_set(&destination, time / 1000, time % 1000, 1000); - - // Check if we need to rewind - if (_state != MP3_STATE_READY || mad_timer_compare(destination, _totalTime) < 0) { - initStream(); - initStreamME(); - } - - // The ME will need clear data no matter what once we seek? - //if (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS) - // initStreamME(); - - // Skip ahead - while (mad_timer_compare(destination, _totalTime) > 0 && _state != MP3_STATE_EOS) - findValidHeader(); - - return (_state != MP3_STATE_EOS); -} - -// Seek in the stream, finding the next valid header -void Mp3PspStream::findValidHeader() { - DEBUG_ENTER_FUNC(); - - if (_state != MP3_STATE_READY) - return; - - // If necessary, load more data into the stream decoder - if (_stream.error == MAD_ERROR_BUFLEN) - readMP3DataIntoBuffer(); - - while (_state != MP3_STATE_EOS) { - _stream.error = MAD_ERROR_NONE; - - // Decode the next header. - if (mad_header_decode(&_header, &_stream) == -1) { - if (_stream.error == MAD_ERROR_BUFLEN) { - readMP3DataIntoBuffer(); // Read more data - continue; - } else if (MAD_RECOVERABLE(_stream.error)) { - debug(6, "MP3PSPStream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - continue; - } else { - warning("MP3PSPStream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); - break; - } - } - - // Sum up the total playback time so far - mad_timer_add(&_totalTime, _header.duration); - break; - } - - if (_stream.error != MAD_ERROR_NONE) - _state = MP3_STATE_EOS; -} - -int Mp3PspStream::readBuffer(int16 *buffer, const int numSamples) { - DEBUG_ENTER_FUNC(); - - int samples = 0; -#ifdef PRINT_BUFFERS - int16 *debugBuffer = buffer; -#endif - - // Keep going as long as we have input available - while (samples < numSamples && _state != MP3_STATE_EOS) { - const int len = MIN(numSamples, samples + (int)(_pcmLength - _posInFrame) * MAD_NCHANNELS(&_header)); - - while (samples < len) { - *buffer++ = _pcmSamples[_posInFrame << 1]; - samples++; - if (MAD_NCHANNELS(&_header) == 2) { - *buffer++ = _pcmSamples[(_posInFrame << 1) + 1]; - samples++; - } - _posInFrame++; // always skip an extra sample since ME always outputs stereo - } - - //memcpy(buffer, &_pcmSamples[_posInFrame], len << 1); // 16 bits - //_posInFrame += len; // next time we start from the middle - - if (_posInFrame >= _pcmLength) { - // We used up all PCM data in the current frame -- read & decode more - decodeMP3Data(); - } - } - -#ifdef PRINT_BUFFERS - PSP_INFO_PRINT("buffer:\n"); - for (int i = 0; i<numSamples; i++) - PSP_INFO_PRINT("%d ", debugBuffer[i]); - PSP_INFO_PRINT("\n\n"); -#endif - - return samples; -} - -} // End of namespace Audio - - diff --git a/backends/platform/psp/mp3.h b/backends/platform/psp/mp3.h deleted file mode 100644 index f8802f930c..0000000000 --- a/backends/platform/psp/mp3.h +++ /dev/null @@ -1,121 +0,0 @@ -/* 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 SOUND_MP3_PSP_H -#define SOUND_MP3_PSP_H - -#include "common/types.h" -#include "common/scummsys.h" - -namespace Common { - class SeekableReadStream; -} - -namespace Audio { - -class AudioStream; -class SeekableAudioStream; - -class Mp3PspStream : public SeekableAudioStream { -protected: - enum State { - MP3_STATE_INIT, // Need to init the decoder - MP3_STATE_READY, // ready for processing data - MP3_STATE_EOS // end of data reached (may need to loop) - }; - - #define MAX_SAMPLES_PER_FRAME 2048 * 2 - int16 _pcmSamples[MAX_SAMPLES_PER_FRAME] __attribute__((aligned(64))); // samples to output PCM data into - byte _codecInBuffer[3072] __attribute__((aligned(64))); // the codec always needs alignment - unsigned long _codecParams[65]__attribute__((aligned(64))); // TODO: change to struct - - Common::SeekableReadStream *_inStream; - DisposeAfterUse::Flag _disposeAfterUse; - - uint32 _pcmLength; // how many pcm samples we have (/2 for mono) - - uint _posInFrame; // position in frame - State _state; // what state the stream is in - - Timestamp _length; - uint32 _sampleRate; - - mad_timer_t _totalTime; - mad_stream _stream; // - mad_header _header; // This is all we need from libmad - - static bool _decoderInit; // has the decoder been initialized - static bool _decoderFail; // has the decoder failed to load - - enum { - BUFFER_SIZE = 5 * 8192 - }; - - // This buffer contains a slab of input data - byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD]; - - void decodeMP3Data(); - void readMP3DataIntoBuffer(); - - static bool loadStartAudioModule(const char *modname, int partition); - int initStream(); - void findValidHeader(); - void deinitStream(); - - // to init and uninit ME decoder - static bool initDecoder(); - static bool stopDecoder(); - - // ME functions for stream - bool initStreamME(); - void releaseStreamME(); - -public: - Mp3PspStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose); - ~Mp3PspStream(); - - // This function avoids having to create streams when it's not possible - static inline bool isOkToCreateStream() { - if (_decoderFail) // fatal failure - return false; - if (!_decoderInit) // if we're not initialized - if (!initDecoder()) // check if we failed init - return false; - return true; - } - - int readBuffer(int16 *buffer, const int numSamples); - - bool endOfData() const { return _state == MP3_STATE_EOS; } - bool isStereo() const { return MAD_NCHANNELS(&_header) == 2; } - int getRate() const { return _header.samplerate; } - - bool seek(const Timestamp &where); - Timestamp getLength() const { return _length; } -}; - -} // End of namespace Audio - -#endif // #ifndef SOUND_MP3_PSP_H diff --git a/backends/platform/psp/osys_psp.cpp b/backends/platform/psp/osys_psp.cpp index a36ae1847f..58d98bc219 100644 --- a/backends/platform/psp/osys_psp.cpp +++ b/backends/platform/psp/osys_psp.cpp @@ -37,7 +37,6 @@ #include "backends/platform/psp/psppixelformat.h" #include "backends/platform/psp/osys_psp.h" #include "backends/platform/psp/powerman.h" -#include "backends/platform/psp/thread.h" #include "backends/saves/psp/psp-saves.h" #include "backends/timer/default/default-timer.h" @@ -49,7 +48,6 @@ #include "backends/platform/psp/trace.h" -#define USE_PSP_AUDIO #define SAMPLES_PER_SEC 44100 @@ -60,11 +58,7 @@ static int timer_handler(int t) { } void OSystem_PSP::initSDL() { -#ifdef USE_PSP_AUDIO - SDL_Init(0); -#else - SDL_Init(SDL_INIT_AUDIO); -#endif + SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER); } OSystem_PSP::~OSystem_PSP() {} @@ -91,7 +85,7 @@ void OSystem_PSP::initBackend() { _inputHandler.init(); initSDL(); - + _savefile = new PSPSaveFileManager; _timer = new DefaultTimerManager(); @@ -299,18 +293,17 @@ bool OSystem_PSP::pollEvent(Common::Event &event) { return _inputHandler.getAllInputs(event); } + uint32 OSystem_PSP::getMillis() { - return PspThread::getMillis(); + return SDL_GetTicks(); } void OSystem_PSP::delayMillis(uint msecs) { - PspThread::delayMillis(msecs); + SDL_Delay(msecs); } void OSystem_PSP::setTimerCallback(TimerProc callback, int interval) { - _pspTimer.setCallback((PspTimer::CallbackFunc)callback); - _pspTimer.setIntervalMs(interval); - _pspTimer.start(); + SDL_SetTimer(interval, (SDL_TimerCallback)callback); } OSystem::MutexRef OSystem_PSP::createMutex(void) { @@ -338,6 +331,8 @@ void OSystem_PSP::mixCallback(void *sys, byte *samples, int len) { } void OSystem_PSP::setupMixer(void) { + SDL_AudioSpec desired; + SDL_AudioSpec obtained; // Determine the desired output sampling frequency. uint32 samplesPerSec = 0; @@ -354,22 +349,6 @@ void OSystem_PSP::setupMixer(void) { while (samples * 16 > samplesPerSec * 2) samples >>= 1; - assert(!_mixer); - -#ifdef USE_PSP_AUDIO - if (!_audio.open(samplesPerSec, 2, samples, mixCallback, this)) { - PSP_ERROR("failed to open audio\n"); - return; - } - samplesPerSec = _audio.getFrequency(); // may have been changed by audio system - _mixer = new Audio::MixerImpl(this, samplesPerSec); - assert(_mixer); - _mixer->setReady(true); - _audio.unpause(); -#else - SDL_AudioSpec obtained; - SDL_AudioSpec desired; - memset(&desired, 0, sizeof(desired)); desired.freq = samplesPerSec; desired.format = AUDIO_S16SYS; @@ -377,7 +356,8 @@ void OSystem_PSP::setupMixer(void) { desired.samples = samples; desired.callback = mixCallback; desired.userdata = this; - + + assert(!_mixer); if (SDL_OpenAudio(&desired, &obtained) != 0) { warning("Could not open audio: %s", SDL_GetError()); _mixer = new Audio::MixerImpl(this, samplesPerSec); @@ -396,15 +376,10 @@ void OSystem_PSP::setupMixer(void) { SDL_PauseAudio(0); } -#endif /* USE_PSP_AUDIO */ } void OSystem_PSP::quit() { -#ifdef USE_PSP_AUDIO - _audio.close(); -#else SDL_CloseAudio(); -#endif SDL_Quit(); sceKernelExitGame(); } diff --git a/backends/platform/psp/osys_psp.h b/backends/platform/psp/osys_psp.h index 3f075d0139..8c5b40dcdf 100644 --- a/backends/platform/psp/osys_psp.h +++ b/backends/platform/psp/osys_psp.h @@ -38,8 +38,6 @@ #include "backends/platform/psp/pspkeyboard.h" #include "backends/platform/psp/display_manager.h" #include "backends/platform/psp/input.h" -#include "backends/platform/psp/audio.h" -#include "backends/timer/psp/timer.h" #include <SDL.h> @@ -57,8 +55,6 @@ private: DisplayManager _displayManager; PSPKeyboard _keyboard; InputHandler _inputHandler; - PspAudio _audio; - PspTimer _pspTimer; void initSDL(); diff --git a/backends/platform/psp/psp.spec b/backends/platform/psp/psp.spec index ac325b7fd6..debdab3208 100644 --- a/backends/platform/psp/psp.spec +++ b/backends/platform/psp/psp.spec @@ -1,3 +1,3 @@ %rename lib old_lib *lib: -%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspaudiocodec -lpspkernel +%(old_lib) -lz -lstdc++ -lc -lm -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk -lpsputility -lpspuser -lpsppower -lpsphprm -lpsprtc -lpspaudio -lpspkernel diff --git a/backends/platform/psp/psp_main.cpp b/backends/platform/psp/psp_main.cpp index e6940eba13..e568184990 100644 --- a/backends/platform/psp/psp_main.cpp +++ b/backends/platform/psp/psp_main.cpp @@ -39,7 +39,6 @@ #include <base/main.h> #include <base/plugins.h> #include "backends/platform/psp/powerman.h" -#include "backends/platform/psp/thread.h" #include "backends/plugins/psp/psp-provider.h" #include "backends/platform/psp/psppixelformat.h" @@ -141,7 +140,7 @@ int CallbackThread(SceSize /*size*/, void *arg) { /* Sets up the callback thread and returns its thread id */ int SetupCallbacks(void) { - int thid = sceKernelCreateThread("power_thread", CallbackThread, PRIORITY_POWER_THREAD, STACK_POWER_THREAD, THREAD_ATTR_USER, 0); + int thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0); if (thid >= 0) { sceKernelStartThread(thid, 0, 0); } diff --git a/backends/platform/psp/thread.cpp b/backends/platform/psp/thread.cpp deleted file mode 100644 index 88e7b6fe38..0000000000 --- a/backends/platform/psp/thread.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.h $ - * $Id: osys_psp.h 49173 2010-05-24 03:05:17Z bluddy $ - * - */ - -#include <time.h> -#include <psptypes.h> -#include <psprtc.h> -#include <pspthreadman.h> - -#include "backends/platform/psp/thread.h" - -void PspThread::delayMillis(uint32 ms) { - sceKernelDelayThread(ms * 1000); -} - -void PspThread::delayMicros(uint32 us) { - sceKernelDelayThread(us); -} - -uint32 PspThread::getMillis() { - uint32 ticks[2]; - sceRtcGetCurrentTick((u64 *)ticks); - return (ticks[0]/1000); -} - -uint32 PspThread::getMicros() { - uint32 ticks[2]; - sceRtcGetCurrentTick((u64 *)ticks); - return ticks[0]; -} - diff --git a/backends/platform/psp/thread.h b/backends/platform/psp/thread.h deleted file mode 100644 index e83eead68e..0000000000 --- a/backends/platform/psp/thread.h +++ /dev/null @@ -1,56 +0,0 @@ -/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/portdefs.h $ - * $Id: portdefs.h 38687 2009-02-21 12:08:52Z joostp $ - * - */ - -#ifndef PSP_THREAD_H -#define PSP_THREAD_H - -#include "common/scummsys.h" - -class PspThread { -public: - static void delayMillis(uint32 ms); - static void delayMicros(uint32 us); - static uint32 getMillis(); - static uint32 getMicros(); -}; - -enum ThreadPriority { - PRIORITY_MAIN_THREAD = 36, - PRIORITY_TIMER_THREAD = 30, - PRIORITY_AUDIO_THREAD = 25, // must be higher than timer or we get stuttering - PRIORITY_POWER_THREAD = 20, // quite a light thread - PRIORITY_DISPLAY_THREAD = 17 // very light thread for callbacks only -}; - -enum StackSizes { - STACK_AUDIO_THREAD = 16 * 1024, - STACK_TIMER_THREAD = 32 * 1024, - STACK_DISPLAY_THREAD = 2 * 1024, - STACK_POWER_THREAD = 4 * 1024 -}; - -#endif /* PSP_THREADS_H */ - - diff --git a/backends/platform/samsungtv/main.cpp b/backends/platform/samsungtv/main.cpp index 2c025b750c..a6b8376912 100644 --- a/backends/platform/samsungtv/main.cpp +++ b/backends/platform/samsungtv/main.cpp @@ -43,7 +43,7 @@ extern "C" int Game_Main(char *path, char *) { // Invoke the actual ScummVM main entry point: int res = scummvm_main(0, 0); - ((OSystem_SDL *)g_system)->deinit(); + g_system->quit(); // TODO: Consider removing / replacing this! return res; } diff --git a/backends/platform/sdl/graphics.cpp b/backends/platform/sdl/graphics.cpp index 82670cfcb7..9d0d872c3d 100644 --- a/backends/platform/sdl/graphics.cpp +++ b/backends/platform/sdl/graphics.cpp @@ -1673,6 +1673,7 @@ void OSystem_SDL::drawMouse() { SDL_Rect dst; int scale; + int width, height; int hotX, hotY; dst.x = _mouseCurState.x; @@ -1680,12 +1681,16 @@ void OSystem_SDL::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp index 13e614a4a2..b38b815438 100644 --- a/backends/platform/sdl/main.cpp +++ b/backends/platform/sdl/main.cpp @@ -63,7 +63,7 @@ int main(int argc, char *argv[]) { // Invoke the actual ScummVM main entry point: int res = scummvm_main(argc, argv); - ((OSystem_SDL *)g_system)->deinit(); + g_system->quit(); // TODO: Consider removing / replacing this! return res; } diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index f8ae824acf..e54d70e8b2 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -482,7 +482,7 @@ bool OSystem_SDL::getFeatureState(Feature f) { } } -void OSystem_SDL::deinit() { +void OSystem_SDL::quit() { if (_cdrom) { SDL_CDStop(_cdrom); SDL_CDClose(_cdrom); @@ -504,14 +504,10 @@ void OSystem_SDL::deinit() { SDL_Quit(); - // Event Manager requires save manager for storing + // Even Manager requires save manager for storing // recorded events delete getEventManager(); delete _savefile; -} - -void OSystem_SDL::quit() { - deinit(); #if !defined(SAMSUNGTV) exit(0); diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 341a59c8cf..b6baa52391 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -202,8 +202,6 @@ public: // Quit virtual void quit(); // overloaded by CE backend - void deinit(); - virtual void getTimeAndDate(TimeDate &t) const; virtual Common::TimerManager *getTimerManager(); diff --git a/backends/plugins/ps2/ps2-provider.cpp b/backends/plugins/ps2/ps2-provider.cpp new file mode 100644 index 0000000000..3e966bbd08 --- /dev/null +++ b/backends/plugins/ps2/ps2-provider.cpp @@ -0,0 +1,108 @@ +/* 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$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(__PLAYSTATION2__) + +#include "backends/plugins/ps2/ps2-provider.h" +#include "backends/plugins/dynamic-plugin.h" +#include "common/fs.h" + +#include "backends/platform/ps2/ps2loader.h" + + +class PS2Plugin : public DynamicPlugin { +protected: + void *_dlHandle; + Common::String _filename; + + virtual VoidFunc findSymbol(const char *symbol) { + void *func = dlsym(_dlHandle, symbol); + if (!func) + warning("Failed loading symbol '%s' from plugin '%s' (%s)", symbol, _filename.c_str(), dlerror()); + + // FIXME HACK: This is a HACK to circumvent a clash between the ISO C++ + // standard and POSIX: ISO C++ disallows casting between function pointers + // and data pointers, but dlsym always returns a void pointer. For details, + // see e.g. <http://www.trilithium.com/johan/2004/12/problem-with-dlsym/>. + assert(sizeof(VoidFunc) == sizeof(func)); + VoidFunc tmp; + memcpy(&tmp, &func, sizeof(VoidFunc)); + return tmp; + } + +public: + PS2Plugin(const Common::String &filename) + : _dlHandle(0), _filename(filename) {} + + ~PS2Plugin() { + if (_dlHandle) unloadPlugin(); + } + + bool loadPlugin() { + assert(!_dlHandle); + _dlHandle = dlopen(_filename.c_str(), RTLD_LAZY); + + if (!_dlHandle) { + warning("Failed loading plugin '%s' (%s)", _filename.c_str(), dlerror()); + return false; + } + + bool ret = DynamicPlugin::loadPlugin(); + + if (ret) + dlforgetsyms(_dlHandle); + + return ret; + } + + void unloadPlugin() { + DynamicPlugin::unloadPlugin(); + if (_dlHandle) { + if (dlclose(_dlHandle) != 0) + warning("Failed unloading plugin '%s' (%s)", _filename.c_str(), dlerror()); + _dlHandle = 0; + } + } +}; + + +Plugin* PS2PluginProvider::createPlugin(const Common::FSNode &node) const { + return new PS2Plugin(node.getPath()); +} + +bool PS2PluginProvider::isPluginFilename(const Common::FSNode &node) const { + // Check the plugin suffix + Common::String filename = node.getName(); + fprintf(stderr, "Testing name %s", filename.c_str()); + if (!filename.hasSuffix(".PLG") && !filename.hasSuffix(".plg")) { + fprintf(stderr," fail.\n"); + return false; + } + + fprintf(stderr," success!\n"); + return true; +} + +#endif // defined(DYNAMIC_MODULES) && defined(__PLAYSTATION2__) diff --git a/engines/sci/graphics/maciconbar.h b/backends/plugins/ps2/ps2-provider.h index 71e65fcb40..6a357db63d 100644 --- a/engines/sci/graphics/maciconbar.h +++ b/backends/plugins/ps2/ps2-provider.h @@ -23,33 +23,21 @@ * */ -#ifndef SCI_GRAPHICS_MACICONBAR_H -#define SCI_GRAPHICS_MACICONBAR_H +#ifndef BACKENDS_PLUGINS_PS2_PS2_PROVIDER_H +#define BACKENDS_PLUGINS_PS2_PS2_PROVIDER_H -#include "common/array.h" +#include "base/plugins.h" -#include "sci/engine/vm.h" +#if defined(DYNAMIC_MODULES) && defined(__PLAYSTATION2__) -namespace Graphics { - struct Surface; -} +class PS2PluginProvider : public FilePluginProvider { +protected: + Plugin* createPlugin(const Common::FSNode &node) const; -namespace Sci { + bool isPluginFilename(const Common::FSNode &node) const; -class GfxMacIconBar { -public: - GfxMacIconBar() {} - ~GfxMacIconBar() {} - - void addIcon(reg_t obj); - void drawIcons(); - -private: - Common::Array<reg_t> _iconBarObjects; - - void remapColors(Graphics::Surface *surf, byte *palette); }; -} // End of namespace Sci +#endif // defined(DYNAMIC_MODULES) && defined(__PLAYSTATION2__) -#endif +#endif /* BACKENDS_PLUGINS_PS2_PS2_PROVIDER_H */ diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index 1ab898d2d6..6a91f2cbe1 100644 --- a/backends/saves/default/default-saves.cpp +++ b/backends/saves/default/default-saves.cpp @@ -131,11 +131,11 @@ bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) { // There is a nicely portable workaround, too: Make this method overloadable. if (remove(file.getPath().c_str()) != 0) { #ifndef _WIN32_WCE - if (errno == EACCES) + /* if (errno == EACCES) setError(Common::kWritePermissionDenied, "Search or write permission denied: "+file.getName()); if (errno == ENOENT) - setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid"); + setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid"); */ #endif return false; } else { diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 207ff79c4c..8fa2f54b03 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -602,9 +602,7 @@ static void listTargets() { } /** List all saves states for the given target. */ -static Common::Error listSaves(const char *target) { - Common::Error result = Common::kNoError; - +static void listSaves(const char *target) { // FIXME HACK g_system->initBackend(); @@ -629,14 +627,13 @@ static Common::Error listSaves(const char *target) { GameDescriptor game = EngineMan.findGame(gameid, &plugin); if (!plugin) { - warning("Could not find any plugin to handle target '%s' (gameid '%s')", target, gameid.c_str()); - return Common::kPluginNotFound; + error("Could not find any plugin to handle target '%s' (gameid '%s')", target, gameid.c_str()); + return; } if (!(*plugin)->hasFeature(MetaEngine::kSupportsListSaves)) { // TODO: Include more info about the target (desc, engine name, ...) ??? printf("ScummVM does not support listing save states for target '%s' (gameid '%s') .\n", target, gameid.c_str()); - result = Common::kPluginNotSupportSaves; } else { // Query the plugin for a list of savegames SaveStateList saveList = (*plugin)->listSaves(target); @@ -646,9 +643,6 @@ static Common::Error listSaves(const char *target) { printf(" Slot Description \n" " ---- ------------------------------------------------------\n"); - if (saveList.size() == 0) - result = Common::kNoSavesError; - for (SaveStateList::const_iterator x = saveList.begin(); x != saveList.end(); ++x) { printf(" %-4s %s\n", x->save_slot().c_str(), x->description().c_str()); // TODO: Could also iterate over the full hashmap, printing all key-value pairs @@ -657,8 +651,6 @@ static Common::Error listSaves(const char *target) { // Revert to the old active domain ConfMan.setActiveDomain(oldDomain); - - return result; } /** Lists all usable themes */ @@ -871,7 +863,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha #endif // DISABLE_COMMAND_LINE -Common::Error processSettings(Common::String &command, Common::StringMap &settings) { +bool processSettings(Common::String &command, Common::StringMap &settings) { #ifndef DISABLE_COMMAND_LINE @@ -880,33 +872,34 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin // have been loaded. if (command == "list-targets") { listTargets(); - return Common::kNoError; + return false; } else if (command == "list-games") { listGames(); - return Common::kNoError; + return false; } else if (command == "list-saves") { - return listSaves(settings["list-saves"].c_str()); + listSaves(settings["list-saves"].c_str()); + return false; } else if (command == "list-themes") { listThemes(); - return Common::kNoError; + return false; } else if (command == "version") { printf("%s\n", gScummVMFullVersion); printf("Features compiled in: %s\n", gScummVMFeatures); - return Common::kNoError; + return false; } else if (command == "help") { printf(HELP_STRING, s_appName); - return Common::kNoError; + return false; } #ifdef DETECTOR_TESTING_HACK else if (command == "test-detector") { runDetectorTest(); - return Common::kNoError; + return false; } #endif #ifdef UPGRADE_ALL_TARGETS_HACK else if (command == "upgrade-targets") { upgradeTargets(); - return Common::kNoError; + return false; } #endif @@ -978,7 +971,7 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin ConfMan.set(key, value, Common::ConfigManager::kTransientDomain); } - return Common::kArgumentNotProcessed; + return true; } } // End of namespace Base diff --git a/base/commandLine.h b/base/commandLine.h index c7e8d8b0d0..8801ed17d8 100644 --- a/base/commandLine.h +++ b/base/commandLine.h @@ -33,7 +33,7 @@ namespace Base { void registerDefaults(); Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv); -Common::Error processSettings(Common::String &command, Common::StringMap &settings); +bool processSettings(Common::String &command, Common::StringMap &settings); } // End of namespace Base diff --git a/base/main.cpp b/base/main.cpp index bfb6611a91..2658d1dc67 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -340,10 +340,8 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // Process the remaining command line settings. Must be done after the // config file and the plugins have been loaded. - Common::Error res; - - if ((res = Base::processSettings(command, settings)) != Common::kArgumentNotProcessed) - return res; + if (!Base::processSettings(command, settings)) + return 0; // Init the backend. Must take place after all config data (including // the command line params) was read. diff --git a/common/error.h b/common/error.h index 7aff8d40b9..c4d383e508 100644 --- a/common/error.h +++ b/common/error.h @@ -59,16 +59,10 @@ enum Error { kPathNotDirectory, ///< The specified path does not point to a directory kPathNotFile, ///< The specified path does not point to a file - kCreatingFileFailed, ///< Failed creating a (savestate) file - kReadingFailed, ///< Failed to read a file (permission denied?) + kCreatingFileFailed, + kReadingFailed, ///< Failed creating a (savestate) file kWritingFailed, ///< Failure to write data -- disk full? - // The following are used by --list-saves - kPluginNotFound, ///< Failed to find plugin to handle tager - kPluginNotSupportSaves, ///< Failed if plugin does not support saves - kNoSavesError, ///< There are no saves to show - - kArgumentNotProcessed, ///< Used in command line parsing kUnknownError ///< Catch-all error, used if no other error code matches }; diff --git a/common/macresman.cpp b/common/macresman.cpp index 6a6a818083..de78cedf61 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -439,11 +439,6 @@ Common::SeekableReadStream *MacResManager::getResource(uint32 typeID, uint16 res _stream->seek(_dataOffset + _resLists[typeNum][resNum].dataOffset); uint32 len = _stream->readUint32BE(); - - // Ignore resources with 0 length - if (!len) - return 0; - return _stream->readStream(len); } @@ -453,11 +448,6 @@ Common::SeekableReadStream *MacResManager::getResource(const Common::String &fil if (_resLists[i][j].nameOffset != -1 && filename.equalsIgnoreCase(_resLists[i][j].name)) { _stream->seek(_dataOffset + _resLists[i][j].dataOffset); uint32 len = _stream->readUint32BE(); - - // Ignore resources with 0 length - if (!len) - return 0; - return _stream->readStream(len); } } diff --git a/common/str.h b/common/str.h index 189c37adb4..12e2b0d2d3 100644 --- a/common/str.h +++ b/common/str.h @@ -222,12 +222,6 @@ public: typedef const char * const_iterator; iterator begin() { - // Since the user could potentially - // change the string via the returned - // iterator we have to assure we are - // pointing to a unique storage. - makeUnique(); - return _str; } diff --git a/common/stream.h b/common/stream.h index 11041fa3ce..b6afcd85a9 100644 --- a/common/stream.h +++ b/common/stream.h @@ -156,7 +156,7 @@ public: class ReadStream : virtual public Stream { public: /** - * Returns true if a read failed because the stream end has been reached. + * Returns true if a read failed because the stream has been reached. * This flag is cleared by clearErr(). * For a SeekableReadStream, it is also cleared by a successful seek. */ diff --git a/common/unzip.cpp b/common/unzip.cpp index e46106025e..a83f70d671 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1433,11 +1433,11 @@ Common::SeekableReadStream *ZipArchive::createReadStreamForMember(const Common:: unz_file_info fileInfo; unzOpenCurrentFile(_zipFile); unzGetCurrentFileInfo(_zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0); - byte *buffer = (byte *)malloc(fileInfo.uncompressed_size); + byte *buffer = (byte *)calloc(fileInfo.uncompressed_size+1, 1); assert(buffer); unzReadCurrentFile(_zipFile, buffer, fileInfo.uncompressed_size); unzCloseCurrentFile(_zipFile); - return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size, DisposeAfterUse::YES); + return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, DisposeAfterUse::YES); // FIXME: instead of reading all into a memory stream, we could // instead create a new ZipStream class. But then we have to be @@ -1506,10 +1506,6 @@ if test -n "$_host"; then _ar="$_host-ar cru" _ranlib=$_host-ranlib ;; - mips-sgi*) - _endian=big - _need_memalign=yes - ;; motoezx) DEFINES="$DEFINES -DUNIX -DMOTOEZX" ASFLAGS="$ASFLAGS -mfpu=vfp" diff --git a/engines/dialogs.cpp b/engines/dialogs.cpp index 954bc81470..73ba591b4b 100644 --- a/engines/dialogs.cpp +++ b/engines/dialogs.cpp @@ -35,9 +35,8 @@ #include "gui/GuiManager.h" #include "gui/launcher.h" #include "gui/ListWidget.h" -#include "gui/options.h" -#include "gui/saveload.h" #include "gui/ThemeEval.h" +#include "gui/saveload.h" #include "engines/dialogs.h" #include "engines/engine.h" @@ -50,17 +49,16 @@ using GUI::CommandSender; using GUI::StaticTextWidget; -class ConfigDialog : public GUI::OptionsDialog { -protected: -#ifdef SMALL_SCREEN_DEVICE - GUI::Dialog *_keysDialog; -#endif - -public: - ConfigDialog(bool subtitleControls); - ~ConfigDialog(); - - virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +enum { + kSaveCmd = 'SAVE', + kLoadCmd = 'LOAD', + kPlayCmd = 'PLAY', + kOptionsCmd = 'OPTN', + kHelpCmd = 'HELP', + kAboutCmd = 'ABOU', + kQuitCmd = 'QUIT', + kRTLCmd = 'RTL ', + kChooseCmd = 'CHOS' }; MainMenuDialog::MainMenuDialog(Engine *engine) @@ -97,12 +95,6 @@ MainMenuDialog::MainMenuDialog(Engine *engine) new GUI::ButtonWidget(this, "GlobalMenu.Options", "Options", kOptionsCmd, 'O'); - // The help button is disabled by default. - // To enable "Help", an engine needs to use a subclass of MainMenuDialog - // (at least for now, we might change how this works in the future). - _helpButton = new GUI::ButtonWidget(this, "GlobalMenu.Help", "Help", kHelpCmd, 'H'); - _helpButton->setEnabled(false); - new GUI::ButtonWidget(this, "GlobalMenu.About", "About", kAboutCmd, 'A'); _rtlButton = new GUI::ButtonWidget(this, "GlobalMenu.RTL", "Return to Launcher", kRTLCmd, 'R'); @@ -143,9 +135,6 @@ void MainMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat case kAboutCmd: _aboutDialog->runModal(); break; - case kHelpCmd: - // Not handled here -- needs to be handled by a subclass (for now) - break; case kRTLCmd: { Common::Event eventRTL; eventRTL.type = Common::EVENT_RTL; @@ -274,13 +263,13 @@ enum { // "" as value for the domain, and in fact provide a somewhat better user // experience at the same time. ConfigDialog::ConfigDialog(bool subtitleControls) - : GUI::OptionsDialog("", "GlobalConfig") { + : GUI::OptionsDialog("", "ScummConfig") { // // Sound controllers // - addVolumeControls(this, "GlobalConfig."); + addVolumeControls(this, "ScummConfig."); setVolumeSettingsState(true); // could disable controls by GUI options // @@ -289,7 +278,7 @@ ConfigDialog::ConfigDialog(bool subtitleControls) if (subtitleControls) { // Global talkspeed range of 0-255 - addSubtitleControls(this, "GlobalConfig.", 255); + addSubtitleControls(this, "ScummConfig.", 255); setSubtitleSettingsState(true); // could disable controls by GUI options } @@ -297,11 +286,11 @@ ConfigDialog::ConfigDialog(bool subtitleControls) // Add the buttons // - new GUI::ButtonWidget(this, "GlobalConfig.Ok", "OK", GUI::kOKCmd, 'O'); - new GUI::ButtonWidget(this, "GlobalConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); + new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O'); + new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); #ifdef SMALL_SCREEN_DEVICE - new GUI::ButtonWidget(this, "GlobalConfig.Keys", "Keys", kKeysCmd, 'K'); + new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K'); _keysDialog = NULL; #endif } diff --git a/engines/dialogs.h b/engines/dialogs.h index 6e5338b317..6bee7c5fb1 100644 --- a/engines/dialogs.h +++ b/engines/dialogs.h @@ -27,6 +27,7 @@ #include "common/str.h" #include "gui/dialog.h" +#include "gui/options.h" class Engine; @@ -38,19 +39,6 @@ namespace GUI { class MainMenuDialog : public GUI::Dialog { public: - enum { - kSaveCmd = 'SAVE', - kLoadCmd = 'LOAD', - kPlayCmd = 'PLAY', - kOptionsCmd = 'OPTN', - kHelpCmd = 'HELP', - kAboutCmd = 'ABOU', - kQuitCmd = 'QUIT', - kRTLCmd = 'RTL ', - kChooseCmd = 'CHOS' - }; - -public: MainMenuDialog(Engine *engine); ~MainMenuDialog(); @@ -63,20 +51,29 @@ protected: void load(); protected: - Engine *_engine; + Engine *_engine; - GUI::GraphicsWidget *_logo; + GUI::GraphicsWidget *_logo; + GUI::ButtonWidget *_rtlButton; + GUI::ButtonWidget *_loadButton; + GUI::ButtonWidget *_saveButton; + GUI::Dialog *_aboutDialog; + GUI::Dialog *_optionsDialog; + GUI::SaveLoadChooser *_loadDialog; + GUI::SaveLoadChooser *_saveDialog; +}; - GUI::ButtonWidget *_rtlButton; - GUI::ButtonWidget *_loadButton; - GUI::ButtonWidget *_saveButton; - GUI::ButtonWidget *_helpButton; +class ConfigDialog : public GUI::OptionsDialog { +protected: +#ifdef SMALL_SCREEN_DEVICE + GUI::Dialog *_keysDialog; +#endif - GUI::Dialog *_aboutDialog; - GUI::Dialog *_optionsDialog; +public: + ConfigDialog(bool subtitleControls); + ~ConfigDialog(); - GUI::SaveLoadChooser *_loadDialog; - GUI::SaveLoadChooser *_saveDialog; + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); }; #endif diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp index e4bd844d75..4a0b82d746 100644 --- a/engines/drascula/animation.cpp +++ b/engines/drascula/animation.cpp @@ -1602,7 +1602,7 @@ void DrasculaEngine::animation_9_6() { // We set the room number to -1 for the same purpose. // Also check animation_2_1(), where the same hack was used // by the original - roomNumber = -2; + roomNumber = -1; loadPic("nota2.alg", bgSurface, HALF_PAL); black(); trackProtagonist = 1; diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 2c3ca63600..276554a24c 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -87,7 +87,6 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam _textverbs = 0; _textmisc = 0; _textd1 = 0; - _talkSequences = 0; _color = 0; blinking = 0; diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp index 13c8a742ca..c4dc3df1f6 100644 --- a/engines/drascula/objects.cpp +++ b/engines/drascula/objects.cpp @@ -89,8 +89,7 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) { updateRoom(); updateScreen(); - // roomNumber -2 is end credits. Do not show cursor there - if (cursorVisible && roomNumber != -2) + if (cursorVisible) showCursor(); } diff --git a/engines/m4/animation.cpp b/engines/m4/animation.cpp index 55566aad7e..fe46e121f0 100644 --- a/engines/m4/animation.cpp +++ b/engines/m4/animation.cpp @@ -26,448 +26,181 @@ #include "m4/assets.h" #include "m4/animation.h" #include "m4/compression.h" -#include "m4/mads_scene.h" namespace M4 { // TODO: this code needs cleanup -MadsAnimation::MadsAnimation(MadsM4Engine *vm, MadsView *view): Animation(vm), _view(view) { - _font = NULL; - _freeFlag = false; - _skipLoad = false; - _unkIndex = -1; - _messageCtr= 0; +Animation::Animation(MadsM4Engine *vm) { + _vm = vm; + _playing = false; } -MadsAnimation::~MadsAnimation() { - for (uint i = 0; i < _messages.size(); ++i) { - if (_messages[i].kernelMsgIndex >= 0) - _view->_kernelMessages.remove(_messages[i].kernelMsgIndex); - } - - // Further deletion logic - if (_field12) { - _view->_spriteSlots.deleteSprites(_spriteListIndexes[_spriteListIndex]); - } - - delete _font; +void Animation::loadFullScreen(const char *filename) { + _vm->_palette->deleteAllRanges(); + load(filename); } -/** - * Initialises and loads the data of an animation - */ -void MadsAnimation::initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface) { - MadsPack anim(filename.c_str(), _vm); - bool madsRes = filename[0] == '*'; +void Animation::load(const char *filename) { + MadsPack anim(filename, _vm); char buffer[20]; - int streamIndex = 1; // Chunk 1: header // header - + // TODO: there are some unknown fields here, plus we don't read + // the entire chunk Common::SeekableReadStream *animStream = anim.getItemStream(0); + Common::SeekableReadStream *spriteSeriesStream; + //printf("Chunk 0, size %i\n", animStream->size()); + _seriesCount = animStream->readUint16LE(); + _frameCount = animStream->readUint16LE(); + _frameEntryCount = animStream->readUint16LE(); - int spriteListCount = animStream->readUint16LE(); - int miscEntriesCount = animStream->readUint16LE(); - int frameEntryCount = animStream->readUint16LE(); - int messagesCount = animStream->readUint16LE(); - animStream->skip(1); - _flags = animStream->readByte(); - - animStream->skip(2); - _animMode = animStream->readUint16LE(); - _roomNumber = animStream->readUint16LE(); - animStream->skip(2); - _field12 = animStream->readUint16LE() != 0; - _spriteListIndex = animStream->readUint16LE(); - _scrollX = animStream->readUint16LE(); - _scrollY = animStream->readSint16LE(); - animStream->skip(10); - - animStream->read(buffer, 13); - _interfaceFile = Common::String(buffer, 13); - - for (int i = 0; i < 10; ++i) { - animStream->read(buffer, 13); - _spriteSetNames[i] = Common::String(buffer, 13); - } - - animStream->skip(81); - animStream->read(buffer, 13); - _lbmFilename = Common::String(buffer, 13); - animStream->read(buffer, 13); - _spritesFilename = Common::String(buffer, 13); - animStream->skip(48); - animStream->read(buffer, 13); - _soundName = Common::String(buffer, 13); - animStream->skip(26); - animStream->read(buffer, 13); - Common::String fontResource(buffer, 13); - - if (_animMode == 4) - flags |= 0x4000; - if (flags & 0x100) - loadInterface(interfaceSurface, sceneSurface); - - // Initialise the reference list - for (int i = 0; i < spriteListCount; ++i) - _spriteListIndexes.push_back(-1); - - delete animStream; - - if (messagesCount > 0) { - // Chunk 2 - // Following is a list of any messages for the animation - - animStream = anim.getItemStream(streamIndex++); - - for (int i = 0; i < messagesCount; ++i) { - AnimMessage rec; - animStream->read(rec.msg, 70); - rec.pos.x = animStream->readUint16LE(); - rec.pos.y = animStream->readUint16LE(); - animStream->readUint16LE(); - rec.rgb1.r = animStream->readByte(); - rec.rgb1.g = animStream->readByte(); - rec.rgb1.b = animStream->readByte(); - rec.rgb2.r = animStream->readByte(); - rec.rgb2.g = animStream->readByte(); - rec.rgb2.b = animStream->readByte(); - rec.kernelMsgIndex = animStream->readUint16LE(); - animStream->skip(6); - rec.startFrame = animStream->readUint16LE(); - rec.endFrame = animStream->readUint16LE(); - animStream->readUint16LE(); - - _messages.push_back(rec); - } - - delete animStream; - } - - if (frameEntryCount > 0) { - // Chunk 3: animation frame info - animStream = anim.getItemStream(streamIndex++); - - for (int i = 0; i < frameEntryCount; i++) { - AnimFrameEntry rec; - rec.frameNumber = animStream->readUint16LE(); - rec.seqIndex = animStream->readByte(); - rec.spriteSlot.spriteListIndex = animStream->readByte(); - rec.spriteSlot.frameNumber = animStream->readUint16LE(); - rec.spriteSlot.xp = animStream->readUint16LE(); - rec.spriteSlot.yp = animStream->readUint16LE(); - rec.spriteSlot.depth = animStream->readByte(); - rec.spriteSlot.scale = animStream->readByte(); - - _frameEntries.push_back(rec); - } - - delete animStream; - } + // Unknown + for (int i = 0; i < 43; i++) + animStream->readByte(); - if (miscEntriesCount > 0) { - // Chunk 4: Misc Data - animStream = anim.getItemStream(streamIndex); + _spriteSeriesNames = new Common::String[_seriesCount]; + printf("%i sprite series\n", _seriesCount); - for (int i = 0; i < miscEntriesCount; ++i) { - AnimMiscEntry rec; - rec.soundNum = animStream->readByte(); - animStream->skip(1); - rec.numTicks = animStream->readUint16LE(); - rec.posAdjust.x = animStream->readUint16LE(); - rec.posAdjust.y = animStream->readUint16LE(); - animStream->readUint16LE(); + // TODO: for now, we only load the first sprite series + if (_seriesCount > 1) + printf("TODO: Anim has %i sprite series, for now, we only load the first one\n", _seriesCount); + _seriesCount = 1; // TODO - _miscEntries.push_back(rec); + for (int i = 0; i < _seriesCount; i++) { + animStream->read(buffer, 13); + _spriteSeriesNames[i] = Common::String(buffer); + //printf("%03d: %s\n", i, _spriteSeriesNames[i].c_str()); + + spriteSeriesStream = _vm->res()->get(_spriteSeriesNames[i].c_str()); + _spriteSeries = new SpriteAsset(_vm, spriteSeriesStream, + spriteSeriesStream->size(), _spriteSeriesNames[i].c_str()); + _vm->res()->toss(_spriteSeriesNames[i].c_str()); + + // Adjust the palette of the sprites in the sprite series + // so that they can be displayed on screen correctly + RGBList *palData = new RGBList(_spriteSeries->getColorCount(), _spriteSeries->getPalette(), true); + _vm->_palette->addRange(palData); + + for (int k = 0; k < _spriteSeries->getCount(); k++) { + M4Sprite *spr = _spriteSeries->getFrame(k); + spr->translate(palData); // sprite pixel translation } - - delete animStream; } - // If the animation specifies a font, then load it for access - if (_flags & ANIM_CUSTOM_FONT) { - Common::String fontName; - if (madsRes) - fontName += "*"; - fontName += fontResource; - - _font = _vm->_font->getFont(fontName); - } - - // Load all the sprite sets for the animation - for (int i = 0; i < spriteListCount; ++i) { - _spriteListIndexes[i] = _view->_spriteSlots.addSprites(_spriteSetNames[i].c_str()); - } + //printf("End pos: %i\n", animStream->pos()); + delete animStream; - if (_field12) { - Common::String resName; - if (madsRes) - resName += "*"; - resName += _spriteSetNames[_spriteListIndex]; - - _spriteListIndexes[_spriteListIndex] = _view->_spriteSlots.addSprites(resName.c_str()); - } + // ------------------ + + // Chunk 2: anim info + AnimationFrame frame; + animStream = anim.getItemStream(1); + //printf("Chunk 1, size %i\n", animStream->size()); + + _frameEntries = new AnimationFrame[_frameEntryCount]; + + for (int i = 0; i < _frameEntryCount; i++) { + + frame.animFrameIndex = animStream->readUint16LE(); + frame.u = animStream->readByte(); + frame.seriesIndex = animStream->readByte(); + frame.seriesFrameIndex = animStream->readUint16LE(); + frame.x = animStream->readUint16LE(); + frame.y = animStream->readUint16LE(); + frame.v = animStream->readByte(); + frame.w = animStream->readByte(); + + _frameEntries[i] = frame; + + /* + printf( + "animFrameIndex = %4d, " + "u = %3d, " + "seriesIndex = %3d, " + "seriesFrameIndex = %6d, " + "x = %3d, " + "y = %3d, " + "v = %3d, " + "w = %3d\n", + + frame.animFrameIndex, + frame.u, + frame.seriesIndex, + frame.seriesFrameIndex, + frame.x, + frame.y, + frame.v, + frame.w + ); + */ + } + //printf("End pos: %i\n", animStream->pos()); - // TODO: Unknown section about handling palette entries - I think it's adjusting sprite sets - // to the palette of the game screen + delete animStream; - // Process the sprite list indexes to remap them to the actual sprite list indexes - + // Chunk 3: unknown (seems to be sound data?) + // TODO } -/** - * Loads an animation file for display - */ -void MadsAnimation::load(const Common::String &filename, int abortTimers) { - initialise(filename, 0, NULL, NULL); - _messageCtr = 0; - _skipLoad = true; - - if (_field12) { - _unkIndex = -1; - int listIndex = _spriteListIndexes[_spriteListIndex]; - SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex); -warning("%d", spriteSet.getCount()); - } - - // Initialise miscellaneous fields - _currentFrame = 0; - _oldFrameEntry = 0; - _nextFrameTimer = _madsVm->_currentTimer; - _abortTimers = abortTimers; - _abortMode = _madsVm->scene()->_abortTimersMode2; - - for (int i = 0; i < 3; ++i) - _actionNouns[i] = _madsVm->scene()->actionNouns[i]; +Animation::~Animation() { + //delete[] _spriteSeriesNames; + //delete[] _spriteSeries; + //delete[] _frameEntries; +} - // Initialise kernel message list - for (uint i = 0; i < _messages.size(); ++i) - _messages[i].kernelMsgIndex = -1; +void Animation::start() { + _curFrame = 0; + _curFrameEntry = 0; + //for (int i = 0; i < _seriesCount; i++) { + //_spriteSeries[i] = new SpriteSeries((char*)_spriteSeriesNames[i].c_str()); + //} + _playing = true; + updateAnim(); } -void MadsAnimation::update() { - if (_field12) { - int spriteListIndex = _spriteListIndexes[_spriteListIndex]; - int newIndex = -1; - - for (uint idx = _oldFrameEntry; idx < _frameEntries.size(); ++idx) { - if (_frameEntries[idx].frameNumber > _currentFrame) - break; - if (_frameEntries[idx].spriteSlot.spriteListIndex == spriteListIndex) - newIndex = _frameEntries[idx].spriteSlot.frameNumber; - } +bool Animation::updateAnim() { + if (!_playing) + return true; - if (newIndex >= 0) - load1(newIndex); - } + // Get the scene background surface + M4Surface *bg = _vm->_scene->getBackgroundSurface(); - // If it's not time for the next frame, then exit - if (_madsVm->_currentTimer < _nextFrameTimer) - return; + while (_frameEntries[_curFrameEntry].animFrameIndex == _curFrame) { + AnimationFrame *frame = &_frameEntries[_curFrameEntry]; + int seriesFrameIndex = (frame->seriesFrameIndex & 0x7FFF) - 1; - // Loop checks for any prior animation sprite slots to be expired - for (int slotIndex = 0; slotIndex < _view->_spriteSlots.startIndex; ++slotIndex) { - if ((_view->_spriteSlots[slotIndex].seqIndex >= 0x80) && - (_view->_spriteSlots[slotIndex].seqIndex <= 0xFD)) { - // Flag the frame as animation sprite slot - _view->_spriteSlots[slotIndex].spriteType = EXPIRED_SPRITE; - } - } + // Write the sprite onto the screen + M4Sprite *spr = _spriteSeries->getFrame(seriesFrameIndex); - // Validate the current frame - if (_currentFrame > (int)_miscEntries.size()) { - // Is the animation allowed to be repeated? - if (_resetFlag) { - _currentFrame = 0; - _oldFrameEntry = 0; - } else { - _freeFlag = true; - return; - } - } - - // Handle starting any sound for this frame - AnimMiscEntry &misc = _miscEntries[_currentFrame]; - if (misc.soundNum) - _vm->_sound->playSound(misc.soundNum); + // FIXME: correct x, y + spr->copyTo(bg, frame->x, frame->y, (int)spr->getTransparentColor()); - bool screenChanged = false; + // HACK: wait a bit + g_system->delayMillis(100); - // Handle any scrolling of the screen surface - if ((_scrollX != 0) || (_scrollY != 0)) { - _view->_bgSurface->scrollX(_scrollX); - _view->_bgSurface->scrollY(_scrollY); - - screenChanged = true; + //printf("_curFrameEntry = %d\n", _curFrameEntry); + _curFrameEntry++; } - // Handle any offset adjustment for sprites as of this frame - if (_view->_posAdjust.x != misc.posAdjust.x) { - misc.posAdjust.x = _view->_posAdjust.x; - screenChanged = true; - } - if (_view->_posAdjust.y != misc.posAdjust.y) { - misc.posAdjust.y = _view->_posAdjust.y; - screenChanged = true; - } - if (screenChanged) { - // Signal the entire screen needs refreshing - _view->_spriteSlots.fullRefresh(); - } + //printf("_curFrame = %d\n", _curFrame); - int spriteSlotsMax = _view->_spriteSlots.startIndex; - - // Main frame animation loop - frames get animated by being placed, as necessary, into the - // main sprite slot array - while ((uint)_oldFrameEntry < _frameEntries.size()) { - if (_frameEntries[_oldFrameEntry].frameNumber > _currentFrame) - break; - else if (_frameEntries[_oldFrameEntry].frameNumber == _currentFrame) { - // Found the correct frame - int spriteSlotIndex = 0; - int index = 0; - - for (;;) { - if ((spriteSlotIndex == 0) && (index < spriteSlotsMax)) { - int seqIndex = _frameEntries[_oldFrameEntry].seqIndex - _view->_spriteSlots[index].seqIndex; - if (seqIndex == 0x80) { - if (_view->_spriteSlots[index] == _frameEntries[_oldFrameEntry].spriteSlot) - _view->_spriteSlots[index].spriteType = SPRITE_ZERO; - } - ++index; - continue; - } - - if (spriteSlotIndex == 0) { - int slotIndex = _view->_spriteSlots.getIndex(); - _view->_spriteSlots[slotIndex].copy(_frameEntries[_oldFrameEntry].spriteSlot); - _view->_spriteSlots[slotIndex].seqIndex += 0x80; - - SpriteAsset &spriteSet = _view->_spriteSlots.getSprite( - _view->_spriteSlots[slotIndex].spriteListIndex); - - _view->_spriteSlots[slotIndex].spriteType = (spriteSet.getAssetType() == 0) ? - SPRITE_FOUR : SPRITE_ZERO; - } - break; - } - } - - ++_oldFrameEntry; - } - - // Handle the display of any messages - for (uint idx = 0; idx < _messages.size(); ++idx) { - if (_messages[idx].kernelMsgIndex >= 0) { - // Handle currently active message - if ((_currentFrame < _messages[idx].startFrame) || (_currentFrame > _messages[idx].endFrame)) { - _view->_kernelMessages.remove(_messages[idx].kernelMsgIndex); - _messages[idx].kernelMsgIndex = -1; - --_messageCtr; - } - } else if ((_currentFrame >= _messages[idx].startFrame) && (_currentFrame <= _messages[idx].endFrame)) { - // Start displaying the message - AnimMessage &me = _messages[idx]; - - // The colour index to use is dependant on how many messages are currently on-screen - uint8 colIndex; - switch (_messageCtr) { - case 1: - colIndex = 252; - break; - case 2: - colIndex = 16; - break; - default: - colIndex = 250; - break; - } - - _vm->_palette->setEntry(colIndex, me.rgb1.r, me.rgb1.g, me.rgb1.b); - _vm->_palette->setEntry(colIndex + 1, me.rgb2.r, me.rgb2.g, me.rgb2.b); - - // Add a kernel message to display the given text - me.kernelMsgIndex = _view->_kernelMessages.add(me.pos, colIndex * 101, 0, 0, INDEFINITE_TIMEOUT, me.msg); - ++_messageCtr; - } - } - - // Move to the next frame - _currentFrame++; - if (_currentFrame >= (int)_miscEntries.size()) { - // Animation is complete - if (_abortTimers != 0) { - _view->_abortTimers = _abortTimers; - _view->_abortTimersMode = _abortMode; - - if (_abortMode != ABORTMODE_1) { - // Copy the noun list - for (int i = 0; i < 3; ++i) - _madsVm->scene()->actionNouns[i] = _actionNouns[i]; - } - } - } + _curFrame++; + if (_curFrame >= _frameCount) // anim done + stop(); - int frameNum = MIN(_currentFrame, (int)_miscEntries.size() - 1); - _nextFrameTimer = _madsVm->_currentTimer + _miscEntries[frameNum].numTicks; + return _curFrame >= _frameCount; } -void MadsAnimation::setCurrentFrame(int frameNumber) { - _currentFrame = frameNumber; - _oldFrameEntry = 0; - _freeFlag = false; -} - -void MadsAnimation::load1(int frameNumber) { - if (_skipLoad) - return; - - Common::Point pt; - int listIndex = _spriteListIndexes[_spriteListIndex]; - SpriteAsset &spriteSet = _view->_spriteSlots.getSprite(listIndex); - - if (_unkIndex < 0) { - M4Surface *frame = spriteSet.getFrame(0); - pt.x = frame->bounds().left; - pt.y = frame->bounds().top; - } else { - pt.x = _unkList[_unkIndex].x; - pt.y = _unkList[_unkIndex].y; - _unkIndex = 1 - _unkIndex; - } - - if (proc1(spriteSet, pt, frameNumber)) - error("proc1 failure"); -} - -bool MadsAnimation::proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber) { - return 0; -} +void Animation::stop() { + _playing = false; -void MadsAnimation::loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface) { - if (_animMode <= 2) { - MadsSceneResources sceneResources; - sceneResources.load(_roomNumber, _interfaceFile.c_str(), 0, depthSurface, interfaceSurface); - - // Rex only supports a single dialog draw style - assert(sceneResources.drawStyle == 2); - - } else if (_animMode == 4) { - // Load a scene interface - interfaceSurface->madsLoadInterface(_interfaceFile); - } else { - // This mode allocates two large surfaces for the animation - // TODO: Are these ever properly freed? -error("Anim mode %d - need to check free logic", _animMode); - assert(!interfaceSurface); - assert(!depthSurface); - depthSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT); - interfaceSurface = new M4Surface(MADS_SURFACE_WIDTH, MADS_SCREEN_HEIGHT); - depthSurface->clear(); - interfaceSurface->clear(); + for (int i = 0; i < _seriesCount; i++) { + // TODO: cleanup + //delete _spriteSeries[i]; + //_spriteSeries[i] = NULL; } } diff --git a/engines/m4/animation.h b/engines/m4/animation.h index 883d3f2de6..2dfe0d887e 100644 --- a/engines/m4/animation.h +++ b/engines/m4/animation.h @@ -29,89 +29,39 @@ #include "m4/m4.h" #include "m4/graphics.h" #include "m4/assets.h" -#include "m4/mads_views.h" -#include "common/array.h" namespace M4 { -class MadsView; -class SpriteSlotSubset; - -class AnimMessage { -public: - char msg[70]; - Common::Point pos; - RGB8 rgb1, rgb2; - int kernelMsgIndex; - - int startFrame, endFrame; -}; - -class AnimFrameEntry { -public: - int frameNumber; - int seqIndex; - SpriteSlotSubset spriteSlot; +struct AnimationFrame { + uint16 animFrameIndex; + byte u; + byte seriesIndex; + uint16 seriesFrameIndex; + uint16 x, y; + byte v, w; }; -class AnimMiscEntry { -public: - int soundNum; - int numTicks; - Common::Point posAdjust; -}; - -#define ANIM_SPRITE_SET_SIZE 50 - -enum MadsAnimationFlags {ANIM_CUSTOM_FONT = 0x20}; - -class MadsAnimation: public Animation { -private: - MadsView *_view; - - int _spriteListCount; - Common::Array<AnimMessage> _messages; - Common::Array<AnimFrameEntry> _frameEntries; - Common::Array<AnimMiscEntry> _miscEntries; - Font *_font; - - uint8 _flags; - int _animMode; - int _roomNumber; - bool _field12; - int _spriteListIndex; - int _scrollX; - int _scrollY; - Common::String _interfaceFile; - Common::String _spriteSetNames[10]; - Common::String _lbmFilename; - Common::String _spritesFilename; - Common::String _soundName; - Common::Array<int> _spriteListIndexes; - - int _currentFrame, _oldFrameEntry; - bool _resetFlag; - bool _freeFlag; - bool _skipLoad; - int _unkIndex; - Common::Point _unkList[2]; - uint32 _nextFrameTimer; - int _messageCtr; - int _abortTimers; - AbortTimerMode _abortMode; - uint16 _actionNouns[3]; - - void load1(int frameNumber); - bool proc1(SpriteAsset &spriteSet, const Common::Point &pt, int frameNumber); - void loadInterface(M4Surface *&interfaceSurface, M4Surface *&depthSurface); -public: - MadsAnimation(MadsM4Engine *vm, MadsView *view); - virtual ~MadsAnimation(); - - virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *interfaceSurface, M4Surface *sceneSurface); - virtual void load(const Common::String &filename, int abortTimers); - virtual void update(); - virtual void setCurrentFrame(int frameNumber); +class Animation { + public: + Animation(MadsM4Engine *vm); + ~Animation(); + + void load(const char *filename); + void loadFullScreen(const char *filename); + void start(); + bool updateAnim(); + void stop(); + + private: + bool _playing; + MadsM4Engine *_vm; + int _seriesCount; + int _frameCount; + int _frameEntryCount; + AnimationFrame *_frameEntries; + Common::String *_spriteSeriesNames; + SpriteAsset *_spriteSeries; + int _curFrame, _curFrameEntry; bool freeFlag() const { return _freeFlag; } }; diff --git a/engines/m4/assets.cpp b/engines/m4/assets.cpp index 91c371dec5..e604019901 100644 --- a/engines/m4/assets.cpp +++ b/engines/m4/assets.cpp @@ -30,13 +30,13 @@ namespace M4 { -BaseAsset::BaseAsset(MadsM4Engine *vm) : _vm(vm) { +BaseAsset::BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : _vm(vm) { } BaseAsset::~BaseAsset() { } -MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) { +MachineAsset::MachineAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) { uint32 stateCount = stream->readUint32LE(); for (uint32 curState = 0; curState < stateCount; curState++) { uint32 stateOffset = stream->readUint32LE(); @@ -61,7 +61,7 @@ uint32 MachineAsset::getStateOffset(uint32 state) { return _stateTable[state]; } -SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) { +SequenceAsset::SequenceAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) { _localVarCount = stream->readUint32LE(); _codeSize = size - 4; _code = new byte[_codeSize]; @@ -78,7 +78,7 @@ void SequenceAsset::getCode(byte *&code, uint32 &codeSize) { } -DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm) { +DataAsset::DataAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name) : BaseAsset(vm, stream, size, name) { _recCount = stream->readUint32LE(); _recSize = stream->readUint32LE(); @@ -98,8 +98,7 @@ long *DataAsset::getRow(int index) { return &_data[_recSize * index]; } -SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : - BaseAsset(vm) { +SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream) : BaseAsset(vm, stream, size, name) { _stream = stream; _palInterface = NULL; _paletteData = NULL; @@ -111,20 +110,6 @@ SpriteAsset::SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, i } } -SpriteAsset::SpriteAsset(MadsM4Engine *vm, const char *name): BaseAsset(vm) { - _stream = vm->res()->get(name); - _palInterface = NULL; - _paletteData = NULL; - - if (_vm->isM4()) { - loadM4SpriteAsset(vm, _stream, true); - } else { - loadMadsSpriteAsset(vm, _stream); - } - - vm->res()->toss(name); -} - SpriteAsset::~SpriteAsset() { if (_palInterface) { // Internally stored palette translation data, so release it @@ -210,9 +195,7 @@ void SpriteAsset::loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStre _maxHeight = 0; Common::SeekableReadStream *spriteStream = sprite.getItemStream(0); - - _assetType = spriteStream->readUint16LE(); - for (int i = 0; i < 18; i++) { + for (int i = 0; i < 19; i++) { spriteStream->readUint16LE(); } _frameCount = spriteStream->readUint16LE(); diff --git a/engines/m4/assets.h b/engines/m4/assets.h index 816a8dcff0..7b0ce24dc4 100644 --- a/engines/m4/assets.h +++ b/engines/m4/assets.h @@ -49,7 +49,7 @@ class Palette; class BaseAsset { public: - BaseAsset(MadsM4Engine *vm); + BaseAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name); ~BaseAsset(); const Common::String getName() const { return _name; } protected: @@ -103,7 +103,6 @@ struct SpriteAssetFrame { class SpriteAsset : public BaseAsset { public: SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, int size, const char *name, bool asStream = false); - SpriteAsset(MadsM4Engine *vm, const char *name); ~SpriteAsset(); void loadM4SpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream, bool asStream); void loadMadsSpriteAsset(MadsM4Engine *vm, Common::SeekableReadStream* stream); @@ -114,7 +113,6 @@ public: int32 getFrameHeight(int index); int32 getMaxFrameWidth() const { return _maxWidth; } int32 getMaxFrameHeight() const { return _maxHeight; } - uint16 getAssetType() const { return _assetType; } M4Sprite *getFrame(int frameIndex); void loadStreamingFrame(M4Sprite *frame, int frameIndex, int destX, int destY); RGB8* getPalette() { return _palette; } @@ -125,7 +123,6 @@ public: int32 getFrameSize(int index); M4Sprite *operator[](int index) { return getFrame(index); } protected: - Common::SeekableReadStream *_stream; RGB8 _palette[256]; uint32 _colorCount; uint32 _srcSize; @@ -135,10 +132,7 @@ protected: Common::Array<uint32> _frameOffsets; Common::Array<SpriteAssetFrame> _frames; uint32 _frameStartOffset; - - // MADS sprite set fields - uint16 _assetType; - + Common::SeekableReadStream *_stream; int32 parseSprite(bool isBigEndian = false); void loadFrameHeader(SpriteAssetFrame &frameHeader, bool isBigEndian = false); private: diff --git a/engines/m4/compression.h b/engines/m4/compression.h index 00e3d1f927..74fed357ff 100644 --- a/engines/m4/compression.h +++ b/engines/m4/compression.h @@ -66,8 +66,8 @@ public: class FabDecompressor { private: - int _bitsLeft; - uint32 _bitBuffer; + int _bitsLeft; + uint32 _bitBuffer; const byte *_srcData, *_srcP; int _srcSize; diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp index 4e14afdfaf..0c2e80df0e 100644 --- a/engines/m4/console.cpp +++ b/engines/m4/console.cpp @@ -47,6 +47,7 @@ Console::Console(MadsM4Engine *vm) : GUI::Debugger() { DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation)); DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview)); DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview)); + DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation)); } Console::~Console() { @@ -246,6 +247,33 @@ bool Console::cmdShowAnimview(int argc, const char **argv) { return false; } +bool Console::cmdPlayAnimation(int argc, const char **argv) { + View *view = _vm->_viewManager->getView(VIEWID_SCENE); + if (view == NULL) { + DebugPrintf("The scene view isn't currently active\n"); + } else if (argc != 2 && argc != 3) { + DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]); + DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n"); + } else { + char resourceName[20]; + strncpy(resourceName, argv[1], 15); + resourceName[15] = '\0'; + if (!strchr(resourceName, '.')) + strcat(resourceName, ".AA"); + + _vm->_viewManager->moveToFront(view); + if (argc == 3 && atoi(argv[2]) == 1) + _vm->_animation->loadFullScreen(resourceName); + else + _vm->_animation->load(resourceName); + _vm->_animation->start(); + view->restore(0, 0, view->width(), view->height()); + return false; + } + + return true; +} + /*--------------------------------------------------------------------------*/ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) { @@ -254,7 +282,6 @@ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) { DCmd_Register("object", WRAP_METHOD(MadsConsole, cmdObject)); DCmd_Register("message", WRAP_METHOD(MadsConsole, cmdMessage)); DCmd_Register("scene_info", WRAP_METHOD(MadsConsole, cmdSceneInfo)); - DCmd_Register("anim", WRAP_METHOD(MadsConsole, cmdPlayAnimation)); } bool MadsConsole::cmdObject(int argc, const char **argv) { @@ -359,33 +386,6 @@ bool MadsConsole::cmdSceneInfo(int argc, const char **argv) { return true; } -bool MadsConsole::cmdPlayAnimation(int argc, const char **argv) { - View *view = _vm->_viewManager->getView(VIEWID_SCENE); - if (view == NULL) { - DebugPrintf("The scene view isn't currently active\n"); - } else if (argc != 2 && argc != 3) { - DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]); - DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n"); - } else { - char resourceName[20]; - strncpy(resourceName, argv[1], 15); - resourceName[15] = '\0'; - if (!strchr(resourceName, '.')) - strcat(resourceName, ".AA"); - - _vm->_viewManager->moveToFront(view); - if (argc == 3 && atoi(argv[2]) == 1) - _madsVm->_palette->deleteAllRanges(); - - _madsVm->scene()->_sceneAnimation->load(resourceName, 0); - - view->restore(0, 0, view->width(), view->height()); - return false; - } - - return true; -} - /*--------------------------------------------------------------------------*/ M4Console::M4Console(M4Engine *vm): Console(vm) { diff --git a/engines/m4/console.h b/engines/m4/console.h index 53a47dada9..b592f041cf 100644 --- a/engines/m4/console.h +++ b/engines/m4/console.h @@ -50,6 +50,7 @@ private: bool cmdStartConversation(int argc, const char **argv); bool cmdShowTextview(int argc, const char **argv); bool cmdShowAnimview(int argc, const char **argv); + bool cmdPlayAnimation(int argc, const char **argv); public: Console(MadsM4Engine *vm); @@ -63,8 +64,6 @@ private: bool cmdObject(int argc, const char **argv); bool cmdMessage(int argc, const char **argv); bool cmdSceneInfo(int argc, const char **argv); - bool cmdPlayAnimation(int argc, const char **argv); - public: MadsConsole(MadsEngine *vm); virtual ~MadsConsole() {} diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp index 11bc165811..746ced5d11 100644 --- a/engines/m4/converse.cpp +++ b/engines/m4/converse.cpp @@ -96,7 +96,7 @@ void ConversationView::setNode(int32 nodeIndex) { _vm->_font->setFont(FONT_CONVERSATION); // TODO: Conversation styles and colors - _vm->_font->current()->setColours(2, 1, 3); + _vm->_font->setColors(2, 1, 3); _currentNodeIndex = nodeIndex; @@ -124,7 +124,7 @@ void ConversationView::setNode(int32 nodeIndex) { } // Figure out the longest string to determine where option highlighting ends - int tempX = _vm->_font->current()->getWidth(node->entries[i]->text, 0) + + int tempX = _vm->_font->getWidth(node->entries[i]->text, 0) + CONV_ENTRIES_X_OFFSET + 10; _xEnd = MAX(_xEnd, tempX); } @@ -163,10 +163,10 @@ void ConversationView::onRefresh(RectList *rects, M4Surface *destSurface) { if (i > CONV_MAX_SHOWN_ENTRIES - 1) break; - _vm->_font->current()->setColour((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED : + _vm->_font->setColor((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED : CONVERSATION_ENTRY_NORMAL); - _vm->_font->current()->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET, + _vm->_font->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET, CONV_ENTRIES_Y_OFFSET + CONV_ENTRIES_HEIGHT * i, 0, 0); } } diff --git a/engines/m4/dialogs.cpp b/engines/m4/dialogs.cpp index a7104537f5..3af94af262 100644 --- a/engines/m4/dialogs.cpp +++ b/engines/m4/dialogs.cpp @@ -127,7 +127,7 @@ void Dialog::writeChars(const char *srcLine) { strcat(line, wordStr); lineLen = strlen(line); - lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING); + lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING); if (((_lineX + lineLen) > _widthChars) || ((_widthX + lineWidth) > _dialogWidth)) { incLine(); @@ -146,7 +146,7 @@ void Dialog::writeChars(const char *srcLine) { */ void Dialog::appendText(const char *line) { _lineX += strlen(line); - _widthX += _vm->_font->current()->getWidth(line, DIALOG_SPACING); + _widthX += _vm->_font->getWidth(line, DIALOG_SPACING); strcat(_lines[_lines.size() - 1].data, line); } @@ -158,7 +158,7 @@ void Dialog::addLine(const char *line, bool underlineP) { if ((_widthX > 0) || (_lineX > 0)) incLine(); - int lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING); + int lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING); int lineLen = strlen(line); if ((lineWidth > _dialogWidth) || (lineLen >= _widthChars)) @@ -383,7 +383,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v if (id > 0) { // Suffix provided - specifies the dialog width in number of chars _widthChars = id * 2; - _dialogWidth = id * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10; + _dialogWidth = id * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10; } } else if (matchCommand(cmdText, "UNDER")) { @@ -416,7 +416,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v Dialog::Dialog(MadsM4Engine *vm, int widthChars): View(vm, Common::Rect(0, 0, 0, 0)) { _vm->_font->setFont(FONT_INTERFACE_MADS); _widthChars = widthChars * 2; - _dialogWidth = widthChars * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10; + _dialogWidth = widthChars * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10; _screenType = LAYER_DIALOG; _lineX = 0; _widthX = 0; @@ -439,7 +439,7 @@ void Dialog::draw() { // Calculate bounds int dlgWidth = _dialogWidth; - int dlgHeight = _lines.size() * (_vm->_font->current()->getHeight() + 1) + 10; + int dlgHeight = _lines.size() * (_vm->_font->getHeight() + 1) + 10; int dialogX = (_vm->_screen->width() - dlgWidth) / 2; int dialogY = (_vm->_screen->height() - dlgHeight) / 2; @@ -480,26 +480,26 @@ void Dialog::draw() { } // Handle drawing the text contents - _vm->_font->current()->setColours(7, 7, 7); + _vm->_font->setColours(7, 7, 7); setColour(7); - for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->current()->getHeight() + 1) { + for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->getHeight() + 1) { if (_lines[lineCtr].barLine) { // Bar separation line - hLine(5, width() - 6, ((_vm->_font->current()->getHeight() + 1) >> 1) + yp); + hLine(5, width() - 6, ((_vm->_font->getHeight() + 1) >> 1) + yp); } else { // Standard line Common::Point pt(_lines[lineCtr].xp + 5, yp); if (_lines[lineCtr].xp & 0x40) ++pt.y; - _vm->_font->current()->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING); + _vm->_font->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING); if (_lines[lineCtr].underline) // Underline needed - hLine(pt.x, pt.x + _vm->_font->current()->getWidth(_lines[lineCtr].data, DIALOG_SPACING), - pt.y + _vm->_font->current()->getHeight()); + hLine(pt.x, pt.x + _vm->_font->getWidth(_lines[lineCtr].data, DIALOG_SPACING), + pt.y + _vm->_font->getHeight()); } } @@ -528,7 +528,7 @@ void Dialog::display(MadsM4Engine *vm, int widthChars, const char **descEntries) dlg->incLine(); dlg->writeChars(*descEntries); - int lineWidth = vm->_font->current()->getWidth(*descEntries, DIALOG_SPACING); + int lineWidth = vm->_font->getWidth(*descEntries, DIALOG_SPACING); dlg->_lines[dlg->_lines.size() - 1].xp = (dlg->_dialogWidth - 10 - lineWidth) / 2; ++descEntries; } diff --git a/engines/m4/font.cpp b/engines/m4/font.cpp index 4afa158976..f8dec65412 100644 --- a/engines/m4/font.cpp +++ b/engines/m4/font.cpp @@ -29,46 +29,28 @@ namespace M4 { -FontManager::~FontManager() { - for (uint i = 0; i < _entries.size(); ++i) - delete _entries[i]; - _entries.clear(); -} - -Font *FontManager::getFont(const Common::String &filename) { - // Check if the font is already loaded - for (uint i = 0; i < _entries.size(); ++i) - { - if (_entries[i]->_filename.equals(filename)) - return _entries[i]; - } - - Font *f = new Font(_vm, filename); - _entries.push_back(f); - return f; -} - -void FontManager::setFont(const Common::String &filename) { - _currentFont = getFont(filename); -} - -//-------------------------------------------------------------------------- - -Font::Font(MadsM4Engine *vm, const Common::String &filename) : _vm(vm), _filename(filename) { +Font::Font(MadsM4Engine *vm) : _vm(vm) { _sysFont = true; - + _filename = NULL; //TODO: System font _fontColors[0] = _vm->_palette->BLACK; _fontColors[1] = _vm->_palette->WHITE; _fontColors[2] = _vm->_palette->BLACK; _fontColors[3] = _vm->_palette->DARK_GRAY; +} + +void Font::setFont(const char *filename) { + if ((_filename != NULL) && (strcmp(filename, _filename) == 0)) + // Already using specified font, so don't bother reloading + return; _sysFont = false; + _filename = filename; if (_vm->isM4()) - setFontM4(filename.c_str()); + setFontM4(filename); else - setFontMads(filename.c_str()); + setFontMads(filename); } void Font::setFontM4(const char *filename) { @@ -152,21 +134,20 @@ Font::~Font() { } } -void Font::setColour(uint8 colour) { +void Font::setColor(uint8 color) { if (_sysFont) - _fontColors[1] = colour; + _fontColors[1] = color; else - _fontColors[3] = colour; + _fontColors[3] = color; } -void Font::setColours(uint8 col1, uint8 col2, uint8 col3) { +void Font::setColors(uint8 alt1, uint8 alt2, uint8 foreground) { if (_sysFont) - _fontColors[1] = col3; + _fontColors[1] = foreground; else { - _fontColors[0] = 0xFF; - _fontColors[1] = col1; - _fontColors[2] = col2; - _fontColors[3] = col3; + _fontColors[1] = alt1; + _fontColors[2] = alt2; + _fontColors[3] = foreground; } } diff --git a/engines/m4/font.h b/engines/m4/font.h index ca47848c61..e64f80b70d 100644 --- a/engines/m4/font.h +++ b/engines/m4/font.h @@ -59,11 +59,19 @@ namespace M4 { class Font { public: - Font(MadsM4Engine *vm, const Common::String &filename); + Font(MadsM4Engine *vm); ~Font(); - void setColour(uint8 colour); - void setColours(uint8 col1, uint8 col2, uint8 col3); + Font *getFont(const char *filename) { + // TODO: Proper separation of font instances + setFont(filename); + return this; + } + void setFont(const char *filename); + void setColor(uint8 color); + void setColors(uint8 alt1, uint8 alt2, uint8 foreground); + void setColour(uint8 colour) { setColor(colour); } + void setColours(uint8 alt1, uint8 alt2, uint8 foreground) { setColors(alt1, alt2, foreground); } int32 getWidth(const char *text, int spaceWidth = -1); int32 getHeight() const { return _maxHeight; } @@ -72,8 +80,7 @@ public: int32 writeString(M4Surface *surface, const char *text, int x, int y, int width = 0, int spaceWidth = -1) { return write(surface, text, x, y, width, spaceWidth, _fontColors); } -public: - const Common::String _filename; + private: void setFontM4(const char *filename); void setFontMads(const char *filename); @@ -84,39 +91,10 @@ private: uint16 *_charOffs; uint8 *_charData; bool _sysFont; + const char *_filename; uint8 _fontColors[4]; }; -class FontEntry { -public: - Font *_font; - - FontEntry() { - _font = NULL; - } - ~FontEntry() { - delete _font; - } -}; - -class FontManager { -private: - MadsM4Engine *_vm; - Common::Array<Font *> _entries; - Font *_currentFont; -public: - FontManager(MadsM4Engine *vm): _vm(vm) { _currentFont = NULL; } - ~FontManager(); - - Font *getFont(const Common::String &filename); - void setFont(const Common::String &filename); - - Font *current() { - assert(_currentFont); - return _currentFont; - } -}; - } // End of namespace M4 #endif diff --git a/engines/m4/globals.h b/engines/m4/globals.h index de6e716ece..a052a4c868 100644 --- a/engines/m4/globals.h +++ b/engines/m4/globals.h @@ -264,7 +264,6 @@ public: // DEPRECATED: ScummVM re-implementation keeps all the quotes loaded, so the methods below are stubs void clearQuotes() {} void loadQuoteRange(int startNum, int endNum) {} - void loadQuoteSet(...) {} void loadQuote(int quoteNum) {} void loadMadsMessagesInfo(); diff --git a/engines/m4/graphics.cpp b/engines/m4/graphics.cpp index 29aaa184a3..893c415576 100644 --- a/engines/m4/graphics.cpp +++ b/engines/m4/graphics.cpp @@ -69,13 +69,6 @@ void RGBList::setRange(int start, int count, const RGB8 *src) { #define VGA_COLOR_TRANS(x) (x == 0x3f ? 255 : x << 2) -M4Surface::~M4Surface() { - if (_rgbList) { - _madsVm->_palette->deleteRange(_rgbList); - delete _rgbList; - } -} - void M4Surface::loadCodesM4(Common::SeekableReadStream *source) { if (!source) { free(); @@ -340,7 +333,7 @@ void M4Surface::fillRect(const Common::Rect &r, uint8 color) { } void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY, - int transparentColour) { + int transparentColor) { // Validation of the rectangle and position if ((destX >= w) || (destY >= h)) return; @@ -369,13 +362,13 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest byte *destPtr = (byte *)pixels + (destY * width()) + destX; for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { - if (transparentColour == -1) + if (transparentColor == -1) // No transparency, so copy line over Common::copy(srcPtr, srcPtr + copyRect.width(), destPtr); else { // Copy each byte one at a time checking for the transparency color for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) - if (srcPtr[xCtr] != transparentColour) destPtr[xCtr] = srcPtr[xCtr]; + if (srcPtr[xCtr] != transparentColor) destPtr[xCtr] = srcPtr[xCtr]; } srcPtr += src->width(); @@ -385,81 +378,6 @@ void M4Surface::copyFrom(M4Surface *src, const Common::Rect &srcBounds, int dest src->freeData(); } -/** - * Copies a given image onto a destination surface with scaling, transferring only pixels that meet - * the specified depth requirement on a secondary surface contain depth information - */ -void M4Surface::copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthsSurface, - int scale, int transparentColour) { - /* TODO: This isn't a straight re-implementation of the original draw routine. Double check in future - * whether this implementation provides equivalent functionality - */ - Common::Rect copyRect(0, 0, src->width(), src->height()); - - if (destX < 0) { - copyRect.left += -destX; - destX = 0; - } else if (destX + copyRect.width() > w) { - copyRect.right -= destX + copyRect.width() - w; - } - if (destY < 0) { - copyRect.top += -destY; - destY = 0; - } else if (destY + copyRect.height() > h) { - copyRect.bottom -= destY + copyRect.height() - h; - } - - if (!copyRect.isValidRect()) - return; - - // Copy the specified area - - byte *data = src->getBasePtr(); - byte *srcPtr = data + (src->width() * copyRect.top + copyRect.left); - byte *depthsData = depthsSurface->getBasePtr(); - byte *depthsPtr = depthsData + (src->width() * copyRect.top + copyRect.left); - byte *destPtr = (byte *)pixels + (destY * width()) + destX; - - if (scale == 100) { - // 100% scaling variation - for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { - // Copy each byte one at a time checking against the depth - for (int xCtr = 0; xCtr < copyRect.width(); ++xCtr) { - if ((depthsPtr[xCtr] > depth) && (srcPtr[xCtr] != transparentColour)) - destPtr[xCtr] = srcPtr[xCtr]; - } - - srcPtr += src->width(); - depthsPtr += depthsSurface->width(); - destPtr += width(); - } - } else { - // Scaled variation - for (int rowCtr = 0; rowCtr < copyRect.height(); ++rowCtr) { - int currX = -1; - - // Loop through the source pixels - for (int xCtr = 0, xTotal = 0; xCtr < copyRect.width(); ++xCtr, xTotal += (100 - scale)) { - int srcX = xTotal / 100; - - if (srcX != currX) { - currX = srcX; - - if ((depthsPtr[currX] > depth) && (srcPtr[xCtr] != transparentColour)) - destPtr[currX] = srcPtr[xCtr]; - } - } - - srcPtr += src->width(); - depthsPtr += depthsSurface->width(); - destPtr += width(); - } - } - - src->freeData(); - depthsSurface->freeData(); -} - void M4Surface::loadBackgroundRiddle(const char *sceneName) { char resourceName[20]; Common::SeekableReadStream *stream; @@ -479,18 +397,9 @@ void M4Surface::loadBackground(int sceneNumber, RGBList **palData) { if (_vm->getGameType() == GType_RexNebular) { // Load Rex Nebular screen - bool hasPalette = palData != NULL; - if (!hasPalette) - palData = &_rgbList; - sprintf(resourceName, "rm%d.art", sceneNumber); stream = _vm->_resourceManager->get(resourceName); rexLoadBackground(stream, palData); - - if (!hasPalette) { - _vm->_palette->addRange(_rgbList); - this->translate(_rgbList); - } } else { // Loads M4 game scene if (palData) @@ -727,8 +636,10 @@ void M4Surface::m4LoadBackground(Common::SeekableReadStream *source) { delete tileBuffer; } -void M4Surface::madsLoadInterface(const Common::String &filename) { - MadsPack intFile(filename.c_str(), _vm); +void M4Surface::madsloadInterface(int index, RGBList **palData) { + char resourceName[20]; + sprintf(resourceName, "i%d.int", index); + MadsPack intFile(resourceName, _vm); RGB8 *palette = new RGB8[16]; // Chunk 0, palette @@ -742,7 +653,7 @@ void M4Surface::madsLoadInterface(const Common::String &filename) { intStream->readByte(); intStream->readByte(); } - _rgbList = new RGBList(16, palette, true); + *palData = new RGBList(16, palette, true); delete intStream; // Chunk 1, data @@ -750,77 +661,8 @@ void M4Surface::madsLoadInterface(const Common::String &filename) { create(320, 44, 1); intStream->read(pixels, 320 * 44); delete intStream; - - // Translate the interface palette - _vm->_palette->addRange(_rgbList); - this->translate(_rgbList); } -void M4Surface::scrollX(int xAmount) { - if (xAmount == 0) - return; - - byte buffer[80]; - int direction = (xAmount > 0) ? 1 : -1; - int xSize = ABS(xAmount); - assert(xSize <= 80); - - byte *srcP = (byte *)getBasePtr(0, 0); - - for (int y = 0; y < height(); ++y, srcP += pitch) { - if (direction < 0) { - // Copy area to be overwritten - Common::copy(srcP, srcP + xSize, &buffer[0]); - // Shift the remainder of the line over the given area - Common::copy(srcP + xSize, srcP + width(), srcP); - // Move buffered area to the end of the line - Common::copy(&buffer[0], &buffer[xSize], srcP + width() - xSize); - } else { - // Copy area to be overwritten - Common::copy_backward(srcP + width() - xSize, srcP + width(), &buffer[80]); - // Shift the remainder of the line over the given area - Common::copy_backward(srcP, srcP + width() - xSize, srcP + width()); - // Move buffered area to the start of the line - Common::copy_backward(&buffer[80 - xSize], &buffer[80], srcP + xSize); - } - } -} - -void M4Surface::scrollY(int yAmount) { - if (yAmount == 0) - return; - - int direction = (yAmount > 0) ? 1 : -1; - int ySize = ABS(yAmount); - assert(ySize < (height() / 2)); - assert(width() == pitch); - - int blockSize = ySize * width(); - byte *tempData = (byte *)malloc(blockSize); - byte *pixelsP = (byte *)getBasePtr(0, 0); - - if (direction > 0) { - // Buffer the lines to be overwritten - byte *srcP = (byte *)getBasePtr(0, height() - ySize); - Common::copy(srcP, srcP + (pitch * ySize), tempData); - // Vertically shift all the lines - Common::copy_backward(pixelsP, pixelsP + (pitch * (height() - ySize)), - pixelsP + (pitch * height())); - // Transfer the buffered lines top the top of the screen - Common::copy(tempData, tempData + blockSize, pixelsP); - } else { - // Buffer the lines to be overwritten - Common::copy(pixelsP, pixelsP + (pitch * ySize), tempData); - // Vertically shift all the lines - Common::copy(pixelsP + (pitch * ySize), pixelsP + (pitch * height()), pixelsP); - // Transfer the buffered lines to the bottom of the screen - Common::copy(tempData, tempData + blockSize, pixelsP + (pitch * (height() - ySize))); - } - - ::free(tempData); -} - - void M4Surface::translate(RGBList *list, bool isTransparent) { byte *p = getBasePtr(0, 0); byte *palIndexes = list->palIndexes(); diff --git a/engines/m4/graphics.h b/engines/m4/graphics.h index 8c4b9ac072..4c89c50b8a 100644 --- a/engines/m4/graphics.h +++ b/engines/m4/graphics.h @@ -35,12 +35,6 @@ namespace M4 { -#define MADS_SURFACE_WIDTH 320 -#define MADS_SURFACE_HEIGHT 156 -#define MADS_SCREEN_HEIGHT 200 -#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2) - - struct BGR8 { uint8 b, g, r; }; @@ -95,23 +89,19 @@ class M4Surface : protected Graphics::Surface { private: byte _color; bool _isScreen; - RGBList *_rgbList; void rexLoadBackground(Common::SeekableReadStream *source, RGBList **palData = NULL); void madsLoadBackground(int roomNumber, RGBList **palData = NULL); void m4LoadBackground(Common::SeekableReadStream *source); public: M4Surface(bool isScreen = false) { - create(g_system->getWidth(), isScreen ? g_system->getHeight() : MADS_SURFACE_HEIGHT, 1); + create(g_system->getWidth(), g_system->getHeight(), 1); _isScreen = isScreen; - _rgbList = NULL; } M4Surface(int width_, int height_) { create(width_, height_, 1); _isScreen = false; - _rgbList = NULL; } - virtual ~M4Surface(); // loads a .COD file into the M4Surface // TODO: maybe move this to the rail system? check where it makes sense @@ -122,8 +112,7 @@ public: // loads the specified background void loadBackground(int sceneNumber, RGBList **palData = NULL); void loadBackgroundRiddle(const char *sceneName); - void madsLoadInterface(int index, RGBList **palData = NULL); - void madsLoadInterface(const Common::String &filename); + void madsloadInterface(int index, RGBList **palData); void setColor(byte value) { _color = value; } void setColour(byte value) { _color = value; } @@ -156,10 +145,8 @@ public: void clear(); void frameRect(const Common::Rect &r, uint8 color); void fillRect(const Common::Rect &r, uint8 color); - void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY, - int transparentColour = -1); - void copyFrom(M4Surface *src, int destX, int destY, int depth, M4Surface *depthSurface, int scale, - int transparentColour = -1); + void copyFrom(M4Surface *src, const Common::Rect &srcBounds, int destX, int destY, + int transparentColor = -1); void update() { if (_isScreen) { @@ -169,23 +156,16 @@ public: } // copyTo methods - inline void copyTo(M4Surface *dest, int transparentColour = -1) { - dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColour); + inline void copyTo(M4Surface *dest, int transparentColor = -1) { + dest->copyFrom(this, Common::Rect(width(), height()), 0, 0, transparentColor); } - inline void copyTo(M4Surface *dest, int x, int y, int transparentColour = -1) { - dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColour); + inline void copyTo(M4Surface *dest, int x, int y, int transparentColor = -1) { + dest->copyFrom(this, Common::Rect(width(), height()), x, y, transparentColor); } inline void copyTo(M4Surface *dest, const Common::Rect &srcBounds, int destX, int destY, - int transparentColour = -1) { - dest->copyFrom(this, srcBounds, destX, destY, transparentColour); + int transparentColor = -1) { + dest->copyFrom(this, srcBounds, destX, destY, transparentColor); } - inline void copyTo(M4Surface *dest, int destX, int destY, int depth, M4Surface *depthsSurface, int scale, - int transparentColour = -1) { - dest->copyFrom(this, destX, destY, depth, depthsSurface, scale, transparentColour); - } - - void scrollX(int xAmount); - void scrollY(int yAmount); void translate(RGBList *list, bool isTransparent = false); }; diff --git a/engines/m4/gui.cpp b/engines/m4/gui.cpp index 8665b4e767..8f949de9c5 100644 --- a/engines/m4/gui.cpp +++ b/engines/m4/gui.cpp @@ -290,26 +290,26 @@ void MenuButton::onRefresh() { case OBJTYPE_SL_TEXT: switch (_objectState) { case OS_MOUSEOVER: - _vm->_font->current()->setColours(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND, TEXT_COLOR_MOUSEOVER_HILIGHT); sprite = sprites[SL_LINE_MOUSEOVER]; break; case OS_PRESSED: - _vm->_font->current()->setColours(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND, TEXT_COLOR_PRESSED_HILIGHT); sprite = sprites[SL_LINE_PRESSED]; break; case OS_GREYED: - _vm->_font->current()->setColours(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND, TEXT_COLOR_GREYED_HILIGHT); sprite = sprites[SL_LINE_NORMAL]; break; default: case OS_NORMAL: - _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, TEXT_COLOR_NORMAL_HILIGHT); sprite = sprites[SL_LINE_NORMAL]; break; @@ -849,11 +849,11 @@ void MenuSaveLoadText::onRefresh() { if (_displayValue != 0) { char tempBuffer[5]; sprintf(tempBuffer, "%02d", _displayValue); - _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); xp = _bounds.left + 26; } - _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); } } @@ -955,18 +955,18 @@ void MenuTextField::onRefresh() { // Draw the text _vm->_font->setFont(FONT_MENU); - _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, TEXT_COLOR_NORMAL_HILIGHT); int xp = _bounds.left + 4; if (_displayValue != 0) { char tempBuffer[5]; sprintf(tempBuffer, "%02d", _displayValue); - _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); xp = _bounds.left + 26; } - _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); if (focused) { // Draw in the cursor @@ -975,7 +975,7 @@ void MenuTextField::onRefresh() { // Get the width of the string up to the cursor position char tempCh = *_cursor; *_cursor = '\0'; - int stringWidth = _vm->_font->current()->getWidth(_displayText); + int stringWidth = _vm->_font->getWidth(_displayText); *_cursor = tempCh; parent()->setColor(TEXT_COLOR_MOUSEOVER_FOREGROUND); @@ -1015,10 +1015,10 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb tempP = &tempStr[tempLen]; _vm->_font->setFont(FONT_MENU); - tempLen = _vm->_font->current()->getWidth(tempStr); + tempLen = _vm->_font->getWidth(tempStr); while ((tempP != &tempStr[0]) && (tempLen > x - _bounds.left - 26)) { *--tempP = '\0'; - tempLen = _vm->_font->current()->getWidth(tempStr); + tempLen = _vm->_font->getWidth(tempStr); } _cursor = &_displayText[tempP - &tempStr[0]]; @@ -1098,7 +1098,7 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb parent()->_deleteSaveDesc = false; _vm->_font->setFont(FONT_MENU); - tempLen = _vm->_font->current()->getWidth(_displayText); + tempLen = _vm->_font->getWidth(_displayText); if ((strlen(_displayText) < MAX_SAVEGAME_NAME - 1) && (tempLen < _pixelWidth - 12) && (param >= 32) && (param <= 127)) { @@ -1140,9 +1140,9 @@ GUITextField::GUITextField(View *owner, const Common::Rect &bounds): GUIRect(own void GUITextField::onRefresh() { _parent->fillRect(_bounds, _vm->_palette->BLACK); - _vm->_font->current()->setColours(3, 3, 3); + _vm->_font->setColors(3, 3, 3); _vm->_font->setFont(FONT_INTERFACE); - _vm->_font->current()->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1); + _vm->_font->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1); } //-------------------------------------------------------------------------- diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp index da271b10c9..897fb468cd 100644 --- a/engines/m4/m4.cpp +++ b/engines/m4/m4.cpp @@ -145,6 +145,7 @@ MadsM4Engine::~MadsM4Engine() { delete _script; delete _ws; delete _random; + delete _animation; delete _palette; } @@ -169,7 +170,7 @@ Common::Error MadsM4Engine::run() { _events = new Events(this); _kernel = new Kernel(this); _player = new Player(this); - _font = new FontManager(this); + _font = new Font(this); if (getGameType() == GType_Burger) { _actor = new Actor(this); _conversationView = new ConversationView(this); @@ -183,6 +184,7 @@ Common::Error MadsM4Engine::run() { _sound = new Sound(this, _mixer, 255); _script = new ScriptInterpreter(this); _ws = new WoodScript(this); + _animation = new Animation(this); //_callbacks = new Callbacks(this); _random = new Common::RandomSource(); g_eventRec.registerRandomSource(*_random, "m4"); @@ -552,9 +554,9 @@ Common::Error MadsEngine::run() { _scene->show(); _font->setFont(FONT_MAIN_MADS); - _font->current()->setColours(2, 1, 3); - _font->current()->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2); - _font->current()->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2); + _font->setColors(2, 1, 3); + _font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2); + _font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2); if (getGameType() == GType_DragonSphere) { //_scene->showMADSV2TextBox("Test", 10, 10, NULL); @@ -579,6 +581,8 @@ Common::Error MadsEngine::run() { while (!_events->quitFlag) { eventHandler(); + _animation->updateAnim(); + if (g_system->getMillis() >= nextFrame) { nextFrame = g_system->getMillis() + GAME_FRAME_DELAY; ++_currentTimer; diff --git a/engines/m4/m4.h b/engines/m4/m4.h index 9937107668..1f34bd3685 100644 --- a/engines/m4/m4.h +++ b/engines/m4/m4.h @@ -189,7 +189,7 @@ public: Player *_player; Mouse *_mouse; Events *_events; - FontManager *_font; + Font *_font; Actor *_actor; Scene *_scene; Dialogs *_dialogs; @@ -200,6 +200,7 @@ public: Rails *_rails; ScriptInterpreter *_script; WoodScript *_ws; + Animation *_animation; Common::RandomSource *_random; Scene *scene() { return _scene; } diff --git a/engines/m4/m4_views.cpp b/engines/m4/m4_views.cpp index f4345787df..3d633cef0d 100644 --- a/engines/m4/m4_views.cpp +++ b/engines/m4/m4_views.cpp @@ -34,7 +34,7 @@ namespace M4 { GUIInventory::GUIInventory(View *owner, MadsM4Engine *vm, const Common::Rect &bounds, int horizCells, int vertCells, int cellWidth, int cellHeight, int tag): GUIRect(owner, bounds, tag) { - _vm = vm; + _vm = vm; _cellCount.x = horizCells; _cellCount.y = vertCells; _cellSize.x = cellWidth; diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp index 24a041e04d..954916700c 100644 --- a/engines/m4/mads_anim.cpp +++ b/engines/m4/mads_anim.cpp @@ -37,7 +37,7 @@ namespace M4 { TextviewView::TextviewView(MadsM4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())), _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT), - _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->current()->getHeight() + + _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() + TEXTVIEW_LINE_SPACING) { _screenType = VIEWID_TEXTVIEW; @@ -60,7 +60,7 @@ TextviewView::TextviewView(MadsM4Engine *vm): _vm->_palette->setPalette(&palData[0], 4, 3); _vm->_palette->blockRange(4, 3); - _vm->_font->current()->setColours(5, 6, 4); + _vm->_font->setColors(5, 6, 4); clear(); _bgSurface.clear(); @@ -222,7 +222,7 @@ void TextviewView::updateState() { } } else { // Handling a text row - if (++_lineY == (_vm->_font->current()->getHeight() + TEXTVIEW_LINE_SPACING)) + if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING)) processLines(); } @@ -404,7 +404,7 @@ void TextviewView::processText() { if (!strcmp(_currentLine, "***")) { // Special signifier for end of script - _scrollCount = _vm->_font->current()->getHeight() * 13; + _scrollCount = _vm->_font->getHeight() * 13; _lineY = -1; return; } @@ -416,7 +416,7 @@ void TextviewView::processText() { char *centerP = strchr(_currentLine, '@'); if (centerP) { *centerP = '\0'; - xStart = (width() / 2) - _vm->_font->current()->getWidth(_currentLine); + xStart = (width() / 2) - _vm->_font->getWidth(_currentLine); // Delete the @ character and shift back the remainder of the string char *p = centerP + 1; @@ -424,16 +424,16 @@ void TextviewView::processText() { strcpy(centerP, p); } else { - lineWidth = _vm->_font->current()->getWidth(_currentLine); + lineWidth = _vm->_font->getWidth(_currentLine); xStart = (width() - lineWidth) / 2; } // Copy the text line onto the bottom of the textSurface surface, which will allow it // to gradually scroll onto the screen - int yp = _textSurface.height() - _vm->_font->current()->getHeight() - TEXTVIEW_LINE_SPACING; + int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING; _textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()), _vm->_palette->BLACK); - _vm->_font->current()->writeString(&_textSurface, _currentLine, xStart, yp); + _vm->_font->writeString(&_textSurface, _currentLine, xStart, yp); } diff --git a/engines/m4/mads_logic.cpp b/engines/m4/mads_logic.cpp index 9cb053a876..d0b7021f38 100644 --- a/engines/m4/mads_logic.cpp +++ b/engines/m4/mads_logic.cpp @@ -59,8 +59,8 @@ void MadsSceneLogic::getSceneSpriteSet() { // if ((_sceneNumber == 105) ((_sceneNumber == 109) && (word_84800 != 0))) // _madsVm->globals()->playerSpriteChanged = true; - _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF); - _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4); +// _vm->_palette->setEntry(16, 0x38, 0xFF, 0xFF); +// _vm->_palette->setEntry(17, 0x38, 0xb4, 0xb4); } void MadsSceneLogic::getAnimName() { @@ -78,30 +78,31 @@ uint16 MadsSceneLogic::loadSpriteSet(uint16 suffixNum, uint16 sepChar) { } uint16 MadsSceneLogic::startReversibleSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { - M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0); + M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1); uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2), spriteFrame->y + (spriteFrame->height() / 2))); return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, - true, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0); + -1, 100, depth - 1, 1, ANIMTYPE_REVERSIBLE, 0, 0); } uint16 MadsSceneLogic::startCycledSpriteSequence(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { - M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0); + M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1); +warning("%d %dx%d %d/%d", srcSpriteIdx, spriteFrame->x, spriteFrame->y, spriteFrame->width(), spriteFrame->height()); uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2), spriteFrame->y + (spriteFrame->height() / 2))); return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, - true, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0); + -1, 100, depth - 1, 1, ANIMTYPE_CYCLED, 0, 0); } uint16 MadsSceneLogic::startSpriteSequence3(uint16 srcSpriteIdx, int v0, int numTicks, int triggerCountdown, int timeoutTicks, int extraTicks) { - M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(0); + M4Sprite *spriteFrame = _madsVm->scene()->_spriteSlots.getSprite(srcSpriteIdx).getFrame(1); uint8 depth = _madsVm->_rails->getDepth(Common::Point(spriteFrame->x + (spriteFrame->width() / 2), spriteFrame->y + (spriteFrame->height() / 2))); return _madsVm->scene()->_sequenceList.add(srcSpriteIdx, v0, 1, triggerCountdown, timeoutTicks, extraTicks, numTicks, 0, 0, - true, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0); + -1, 100, depth - 1, -1, ANIMTYPE_CYCLED, 0, 0); } void MadsSceneLogic::activateHotspot(int idx, bool active) { @@ -146,7 +147,6 @@ void MadsSceneLogic::lowRoomsEntrySound() { } } - /*--------------------------------------------------------------------------*/ /** @@ -160,7 +160,7 @@ void MadsSceneLogic::selectScene(int sceneNum) { assert(sceneNum == 101); _sceneNumber = sceneNum; - Common::set_to(&_spriteIndexes[0], &_spriteIndexes[50], 0); + } void MadsSceneLogic::setupScene() { @@ -170,9 +170,7 @@ void MadsSceneLogic::setupScene() { // sub_1e754(animName, 3); - if ((_sceneNumber >= 101) && (_sceneNumber <= 112)) - getSceneSpriteSet(); - + getSceneSpriteSet(); getAnimName(); } @@ -208,39 +206,10 @@ void MadsSceneLogic::enterScene() { if (_madsVm->globals()->previousScene != -1) _madsVm->globals()->_globals[10] = 0; if (_madsVm->globals()->previousScene != -2) { - _madsVm->scene()->getSceneResources().playerPos = Common::Point(100, 152); + //playerPos = (100, 152); } - if ((_madsVm->globals()->previousScene == 112) || - ((_madsVm->globals()->previousScene != -2) && (_spriteIndexes[29] != 0))) { - // Returning from probe cutscene? - _spriteIndexes[29] = -1; - _madsVm->scene()->getSceneResources().playerPos = Common::Point(161, 123); - _madsVm->scene()->getSceneResources().playerDir = 9; - - // TODO: Extra flags setting - _spriteIndexes[25] = startCycledSpriteSequence(_spriteIndexes[10], 0, 3, 0, 0, 0); - _madsVm->scene()->_sequenceList.setAnimRange(_spriteIndexes[25], 17, 17); - activateHotspot(0x47, false); // CHAIR - /*timer_unk1 = */_madsVm->scene()->_dynamicHotspots.add(0x47, 0x13F /*SIT_IN*/, -1, - Common::Rect(159, 84, 159+33, 84+36)); - - //if (_madsVm->globals()->previousScene == 112) - // room101Check(); - } else { - _spriteIndexes[26] = startCycledSpriteSequence(_spriteIndexes[11], 0, 6, 0, 0, 0); - } - - _madsVm->globals()->loadQuoteSet(0x31, 0x32, 0x37, 0x38, 0x39, -1); - - if (_madsVm->globals()->_globals[10]) { - const char *animName = MADSResourceManager::getResourceName('S', 'e', EXTTYPE_AA, NULL, -1); - _madsVm->scene()->loadAnimation(animName, 0x47); - - _madsVm->scene()->getSceneResources().playerPos = Common::Point(68, 140); - _madsVm->scene()->getSceneResources().playerDir = 4; - // TODO: Flags setting - } + // TODO: EXTRA STUFF lowRoomsEntrySound(); } @@ -249,15 +218,4 @@ void MadsSceneLogic::doAction() { } -void MadsSceneLogic::sceneStep() { - // FIXME: Temporary code to display a message on-screen - static bool tempBool = false; - if (!tempBool) { - tempBool = true; - - _madsVm->scene()->_kernelMessages.add(Common::Point(63, 100), 0x1110, 0, 0, 240, - _madsVm->globals()->getQuote(49)); - } -} - } diff --git a/engines/m4/mads_logic.h b/engines/m4/mads_logic.h index 8c3f41d08b..a589556a21 100644 --- a/engines/m4/mads_logic.h +++ b/engines/m4/mads_logic.h @@ -29,8 +29,6 @@ #ifndef M4_MADS_LOGIC_H #define M4_MADS_LOGIC_H -#include "m4/mads_views.h" - namespace M4 { class MadsSceneLogic { @@ -56,7 +54,6 @@ public: void setupScene(); void enterScene(); void doAction(); - void sceneStep(); }; } diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp index d00272d31e..64f18fa11d 100644 --- a/engines/m4/mads_menus.cpp +++ b/engines/m4/mads_menus.cpp @@ -616,10 +616,10 @@ void RexDialogView::initialiseLines() { } _totalTextEntries = 0; - // Set up a default sprite slot entry for a full screen refresh + // Set up a default sprite slot entry _spriteSlots.startIndex = 1; - _spriteSlots[0].spriteType = FULL_SCREEN_REFRESH; - _spriteSlots[0].seqIndex = -1; + _spriteSlots[0].spriteId = -2; + _spriteSlots[0].timerIndex = -1; } void RexDialogView::initialiseGraphics() { @@ -795,8 +795,8 @@ bool RexDialogView::onEvent(M4EventType eventType, int32 param1, int x, int y, b void RexDialogView::setFrame(int frameNumber, int depth) { int slotIndex = _spriteSlots.getIndex(); - _spriteSlots[slotIndex].spriteType = FOREGROUND_SPRITE; - _spriteSlots[slotIndex].seqIndex = 1; + _spriteSlots[slotIndex].spriteId = 1; + _spriteSlots[slotIndex].timerIndex = 1; _spriteSlots[slotIndex].spriteListIndex = 0; //_menuSpritesIndex; _spriteSlots[slotIndex].frameNumber = frameNumber; @@ -985,15 +985,15 @@ RexGameMenuDialog::RexGameMenuDialog(): RexDialogView() { void RexGameMenuDialog::addLines() { // Add the title - int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 2) * 6) >> 1) - 78); + int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 2) * 6) >> 1) - 78); - addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 10); + addQuote(_vm->_font, ALIGN_CENTER, 0, top, 10); // Loop for adding the option lines of the dialog top += 6; for (int idx = 0; idx < 5; ++idx) { - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 11 + idx); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CENTER, 0, top, 11 + idx); } } @@ -1069,42 +1069,42 @@ void RexOptionsDialog::reload() { void RexOptionsDialog::addLines() { // Add the title - int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 1) * 9 + 12) >> 1) - 78); + int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 1) * 9 + 12) >> 1) - 78); - addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 16); + addQuote(_vm->_font, ALIGN_CENTER, 0, top, 16); // Music state line - top += _vm->_font->current()->getHeight() + 1 + 6; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25); + top += _vm->_font->getHeight() + 1 + 6; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25); // Sound state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27); // Interface easy state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28); // Inventory sppinng state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30); // Text window state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32); // Screen fade state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34); // Storyline mode line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38); // Add Done and Cancel button texts - top += _vm->_font->current()->getHeight() + 1 + 6; - addQuote(_vm->_font->current(), ALIGN_CENTER, -54, top, 1, 0); - addQuote(_vm->_font->current(), ALIGN_CENTER, 54, top, 2, 0); + top += _vm->_font->getHeight() + 1 + 6; + addQuote(_vm->_font, ALIGN_CENTER, -54, top, 1, 0); + addQuote(_vm->_font, ALIGN_CENTER, 54, top, 2, 0); } bool RexOptionsDialog::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { diff --git a/engines/m4/mads_scene.cpp b/engines/m4/mads_scene.cpp index a65224c722..8073db32db 100644 --- a/engines/m4/mads_scene.cpp +++ b/engines/m4/mads_scene.cpp @@ -37,31 +37,18 @@ #include "m4/mads_views.h" #include "m4/compression.h" #include "m4/staticres.h" -#include "m4/animation.h" namespace M4 { -static const int INV_ANIM_FRAME_SPEED = 2; -static const int INVENTORY_X = 160; -static const int INVENTORY_Y = 159; -static const int SCROLLER_DELAY = 200; - -//-------------------------------------------------------------------------- - MadsScene::MadsScene(MadsEngine *vm): _sceneResources(), Scene(vm, &_sceneResources), MadsView(this) { _vm = vm; - _activeAnimation = NULL; - MadsView::_bgSurface = Scene::_backgroundSurface; - MadsView::_depthSurface = Scene::_walkSurface; _interfaceSurface = new MadsInterfaceView(vm); for (int i = 0; i < 3; ++i) actionNouns[i] = 0; } MadsScene::~MadsScene() { - delete _activeAnimation; - _activeAnimation = NULL; leaveScene(); _vm->_viewManager->deleteView(_interfaceSurface); } @@ -81,13 +68,6 @@ void MadsScene::loadScene2(const char *aaName) { // Load scene walk paths loadSceneCodes(_currentScene); - - // Initialise the scene animation - uint16 flags = 0x4100; - if (_madsVm->globals()->_config.textWindowStill) - flags |= 0x200; - - _sceneAnimation->initialise(aaName, flags, _interfaceSurface, NULL); } /** @@ -96,12 +76,27 @@ void MadsScene::loadScene2(const char *aaName) { void MadsScene::loadSceneTemporary() { /* Existing code that eventually needs to be replaced with the proper MADS code */ // Set system palette entries - _vm->_palette->blockRange(0, 18); + _vm->_palette->blockRange(0, 7); RGB8 sysColors[3] = { {0x1f<<2, 0x2d<<2, 0x31<<2, 0}, {0x24<<2, 0x37<<2, 0x3a<<2, 0}, {0x00<<2, 0x10<<2, 0x16<<2, 0}}; _vm->_palette->setPalette(&sysColors[0], 4, 3); - _interfaceSurface->initialise(); + _backgroundSurface->loadBackground(_currentScene, &_palData); + _vm->_palette->addRange(_palData); + _backgroundSurface->translate(_palData); + + if (_currentScene < 900) { + _interfaceSurface->madsloadInterface(0, &_interfacePal); + _vm->_palette->addRange(_interfacePal); + _interfaceSurface->translate(_interfacePal); + _backgroundSurface->copyFrom(_interfaceSurface, Common::Rect(0, 0, 320, 44), 0, 200 - 44); + + _interfaceSurface->initialise(); + } + + // Don't load other screen resources for system screens + if (_currentScene >= 900) + return; loadSceneHotspots(_currentScene); @@ -170,11 +165,6 @@ void MadsScene::leaveScene() { delete _sceneResources.props; delete _walkSurface; - if (_activeAnimation) { - delete _activeAnimation; - _activeAnimation = NULL; - } - Scene::leaveScene(); } @@ -193,6 +183,15 @@ void MadsScene::loadSceneCodes(int sceneNumber, int index) { sceneS = walkData.getItemStream(0); _walkSurface->loadCodesMads(sceneS); _vm->res()->toss(filename); + } else if (_vm->getGameType() == GType_RexNebular) { + // For Rex Nebular, the walk areas are part of the scene info + byte *destP = _walkSurface->getBasePtr(0, 0); + const byte *srcP = _sceneResources.walkData; + byte runLength; + while ((runLength = *srcP++) != 0) { + Common::set_to(destP, destP + runLength, *srcP++); + destP += runLength; + } } } @@ -278,6 +277,9 @@ void MadsScene::drawElements() { void MadsScene::update() { + // Copy the bare scene in + _backgroundSurface->copyTo(this); + // Draw all the various elements drawElements(); @@ -288,17 +290,19 @@ void MadsScene::update() { if (sStatusText[0]) { // Text colors are inverted in Dragonsphere if (_vm->getGameType() == GType_DragonSphere) - _vm->_font->current()->setColours(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK); + _vm->_font->setColors(_vm->_palette->BLACK, _vm->_palette->WHITE, _vm->_palette->BLACK); else - _vm->_font->current()->setColours(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK); + _vm->_font->setColors(_vm->_palette->WHITE, _vm->_palette->BLACK, _vm->_palette->BLACK); _vm->_font->setFont(FONT_MAIN_MADS); - _vm->_font->current()->writeString(this, sStatusText, (width() - _vm->_font->current()->getWidth(sStatusText)) / 2, 142, 0); + _vm->_font->writeString(this, sStatusText, (width() - _vm->_font->getWidth(sStatusText)) / 2, 142, 0); } + + //***DEBUG*** + _spriteSlots.getSprite(0).getFrame(1)->copyTo(this, 120, 90, 0); } void MadsScene::updateState() { - _sceneLogic.sceneStep(); _sequenceList.tick(); if ((_activeAnimation) && !_abortTimers) { @@ -442,15 +446,6 @@ void MadsScene::showMADSV2TextBox(char *text, int x, int y, char *faceName) { boxSprites->getFrame(bottomRight)->copyTo(_backgroundSurface, curX, curY + 1); } -void MadsScene::loadAnimation(const Common::String &animName, int v0) { - if (_activeAnimation) - error("Multiple active animations are not allowed"); - - MadsAnimation *anim = new MadsAnimation(_vm, this); - anim->load(animName.c_str(), 0); - _activeAnimation = anim; -} - /*--------------------------------------------------------------------------*/ MadsAction::MadsAction() { @@ -628,531 +623,97 @@ void MadsAction::set() { /*--------------------------------------------------------------------------*/ -void MadsSceneResources::load(int sceneNumber, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface) { - char buffer1[80]; - const char *sceneName; - - // TODO: Initialise spriteSet / xp_list - - if (sceneNumber > 0) { - sceneName = MADSResourceManager::getResourceName(RESPREFIX_RM, sceneNumber, ".DAT"); - } else { - strcat(buffer1, "*"); - strcat(buffer1, resName); - sceneName = buffer1; // TODO: Check whether this needs to be converted to 'HAG form' - } - - Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneName); +void MadsSceneResources::load(int sId) { + const char *sceneInfoStr = MADSResourceManager::getResourceName(RESPREFIX_RM, sId, ".DAT"); + Common::SeekableReadStream *rawStream = _vm->_resourceManager->get(sceneInfoStr); MadsPack sceneInfo(rawStream); - // Chunk 0: // Basic scene info Common::SeekableReadStream *stream = sceneInfo.getItemStream(0); int resSceneId = stream->readUint16LE(); - assert(resSceneId == sceneNumber); + assert(resSceneId == sId); + artFileNum = stream->readUint16LE(); - drawStyle = stream->readUint16LE(); + field_4 = stream->readUint16LE(); width = stream->readUint16LE(); height = stream->readUint16LE(); assert((width == 320) && (height == 156)); stream->skip(24); - int objectCount = stream->readUint16LE(); + objectCount = stream->readUint16LE(); stream->skip(40); - // Load in any scene objects for (int i = 0; i < objectCount; ++i) { - MadsObject rec; - rec.load(stream); - objects.push_back(rec); - } - for (int i = 0; i < 20 - objectCount; ++i) - stream->skip(48); - - int setCount = stream->readUint16LE(); - stream->readUint16LE(); - for (int i = 0; i < setCount; ++i) { - char buffer2[64]; - Common::String s(buffer2, 64); - setNames.push_back(s); - } - - // Initialise a copy of the surfaces if they weren't provided - bool dsFlag = false, ssFlag = false; - int gfxSize = width * height; - if (!surface) { - surface = new M4Surface(width, height); - ssFlag = true; - } - int walkSize = gfxSize; - if (drawStyle == 2) { - width >>= 2; - walkSize = width * height; - } - if (!depthSurface) { - depthSurface = new M4Surface(width, height); - dsFlag = true; + objects[i].load(stream); } // For Rex Nebular, read in the scene's compressed walk surface information if (_vm->getGameType() == GType_RexNebular) { - assert(depthSurface); + delete walkData; + stream = sceneInfo.getItemStream(1); - byte *walkData = (byte *)malloc(stream->size()); + walkData = (byte *)malloc(stream->size()); stream->read(walkData, stream->size()); - - // For Rex Nebular, the walk areas are part of the scene info - byte *destP = depthSurface->getBasePtr(0, 0); - const byte *srcP = walkData; - byte runLength; - while ((runLength = *srcP++) != 0) { - Common::set_to(destP, destP + runLength, *srcP++); - destP += runLength; - } - - delete walkData; - delete stream; } - _vm->_resourceManager->toss(sceneName); - - // Load the surface artwork - surface->loadBackground(sceneNumber); - - // Final cleanup - if (ssFlag) - delete surface; - if (dsFlag) - delete depthSurface; + _vm->_resourceManager->toss(sceneInfoStr); } - /*--------------------------------------------------------------------------*/ -/*-------------------------------------------------------------------------- - * MadsInterfaceView handles the user interface section at the bottom of - * game screens in MADS games - *-------------------------------------------------------------------------- +/** + * Adds a new entry to the timed on-screen text display list */ - -MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm, - Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) { - _screenType = VIEWID_INTERFACE; - _highlightedElement = -1; - _topIndex = 0; - _selectedObject = -1; - _cheatKeyCtr = 0; - - _objectSprites = NULL; - _objectPalData = NULL; - - /* Set up the rect list for screen elements */ - // Actions - for (int i = 0; i < 10; ++i) - _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2, - ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2); - - // Scroller elements (up arrow, scroller, down arrow) - _screenObjects.addRect(73, 160, 82, 167); - _screenObjects.addRect(73, 168, 82, 190); - _screenObjects.addRect(73, 191, 82, 198); - - // Inventory object names - for (int i = 0; i < 5; ++i) - _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8); - - // Full rectangle area for all vocab actions - for (int i = 0; i < 5; ++i) - _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8); -} - -MadsInterfaceView::~MadsInterfaceView() { - delete _objectSprites; -} - -void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) { - switch (newMode) { - case ITEM_NORMAL: - _vm->_font->current()->setColours(4, 4, 0xff); - break; - case ITEM_HIGHLIGHTED: - _vm->_font->current()->setColours(5, 5, 0xff); - break; - case ITEM_SELECTED: - _vm->_font->current()->setColours(6, 6, 0xff); - break; - } +/* +void MadsScreenText::draw(M4Surface *surface) { } -void MadsInterfaceView::initialise() { - // Build up the inventory list - _inventoryList.clear(); - - for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) { - MadsObject *obj = _madsVm->globals()->getObject(i); - if (obj->roomNumber == PLAYER_INVENTORY) - _inventoryList.push_back(i); +void MadsScreenText::timedDisplay() { + for (int idx = 0; !_abortTimedText && (idx < OLD_TEXT_DISPLAY_SIZE); ++idx) { + if (((_timedText[idx].flags & TEXTFLAG_ACTIVE) != 0) && + (_timedText[idx].frameTimer <= g_system->getMillis())) + // Add the specified entry + addTimedText(&_timedText[idx]); } - - // If the inventory has at least one object, select it - if (_inventoryList.size() > 0) - setSelectedObject(_inventoryList[0]); } -void MadsInterfaceView::setSelectedObject(int objectNumber) { - char resName[80]; - - // Load inventory resource - if (_objectSprites) { - _vm->_palette->deleteRange(_objectPalData); - delete _objectSprites; - } - - // Check to make sure the object is in the inventory, and also visible on-screen - int idx = _inventoryList.indexOf(objectNumber); - if (idx == -1) { - // Object wasn't found, so return - _selectedObject = -1; +void MadsScreenText::addTimedText(TimedText *entry) { + if ((entry->flags & TEXTFLAG_40) != 0) { + this->setActive2(entry->textDisplayIndex); + entry->flags &= 0x7F; return; } - // Found the object - if (idx < _topIndex) - _topIndex = idx; - else if (idx >= (_topIndex + 5)) - _topIndex = MAX(0, idx - 4); - - _selectedObject = objectNumber; - sprintf(resName, "*OB%.3dI.SS", objectNumber); - - Common::SeekableReadStream *data = _vm->res()->get(resName); - _objectSprites = new SpriteAsset(_vm, data, data->size(), resName); - _vm->res()->toss(resName); - - // Slot it into available palette space - _objectPalData = _objectSprites->getRgbList(); - _vm->_palette->addRange(_objectPalData); - _objectSprites->translate(_objectPalData, true); - - _objectFrameNumber = 0; -} - -void MadsInterfaceView::addObjectToInventory(int objectNumber) { - if (_inventoryList.indexOf(objectNumber) == -1) { - _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY; - _inventoryList.push_back(objectNumber); - } - - setSelectedObject(objectNumber); -} - -void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { - _vm->_font->setFont(FONT_INTERFACE_MADS); - char buffer[100]; - - // Check to see if any dialog is currently active - bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL; - - // Highlighting logic for action list - int actionIndex = 0; - for (int x = 0; x < 2; ++x) { - for (int y = 0; y < 5; ++y, ++actionIndex) { - // Determine the font colour depending on whether an item is selected. Note that the first action, - // 'Look', is always 'selected', even when another action is clicked on - setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED : - ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL)); - - // Get the verb action and capitalise it - const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex); - strcpy(buffer, verbStr); - if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; - - // Display the verb - const Common::Rect r(_screenObjects[actionIndex]); - _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); - } - } + if ((entry->flags & TEXTFLAG_8) == 0) + // FIXME: Adjust timeouts for ScumVM's milli counter + entry->timeout -= 3; - // Check for highlighting of the scrollbar controls - if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) { - // Highlight the control's borders - const Common::Rect r(_screenObjects[_highlightedElement]); - destSurface->frameRect(r, 5); + if ((entry->flags & TEXTFLAG_4) != 0) { + Text4A &rec = _text4A[entry->unk4AIndex]; + if ((rec.field25 != 0) || (rec.active == 0)) + entry->timeout = 0; } - // Draw the horizontal line in the scroller representing the current top selected - const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]); - int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1); - destSurface->setColor(4); - destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP); - - // List inventory items - for (uint i = 0; i < 5; ++i) { - if ((_topIndex + i) >= _inventoryList.size()) - break; + if ((entry->timeout == 0) && !_abortTimedText) { + entry->flags |= TEXTFLAG_40; - const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject( - _inventoryList[_topIndex + i])->descId); - strcpy(buffer, descStr); - if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; - - const Common::Rect r(_screenObjects[INVLIST_START + i]); - - // Set the highlighting of the inventory item - if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED); - else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED); - else setFontMode(ITEM_NORMAL); - - // Write out it's description - _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); - } - - // Handle the display of any currently selected object - if (_objectSprites) { - // Display object sprite. Note that the frame number isn't used directly, because it would result - // in too fast an animation - M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED); - spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0); - - if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) { - // If objects need to be animated, move to the next frame - if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED)) - _objectFrameNumber = 0; - } - - // List the vocab actions for the currently selected object - MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); - int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); - - for (int i = 0; i < obj->vocabCount; ++i) { - const Common::Rect r(_screenObjects[VOCAB_START + i]); - - // Get the vocab description and capitalise it - const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId); - strcpy(buffer, descStr); - if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; - - // Set the highlighting and display the entry - setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL); - _vm->_font->current()->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); - } - } -} - -bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { - MadsAction &act = _madsVm->scene()->getAction(); - - // If the mouse isn't being held down, then reset the repeated scroll timer - if (eventType != MEVENT_LEFT_HOLD) - _nextScrollerTicks = 0; - - // Handle various event types - switch (eventType) { - case MEVENT_MOVE: - // If the cursor isn't in "wait mode", don't do any processing - if (_vm->_mouse->getCursorNum() == CURSOR_WAIT) - return true; - - // Ensure the cursor is the standard arrow - _vm->_mouse->setCursorNum(CURSOR_ARROW); - - // Check if any interface element is currently highlighted - _highlightedElement = _screenObjects.find(Common::Point(x, y)); - - return true; - - case MEVENT_LEFT_CLICK: - // Left mouse click - { - // Check if an inventory object was selected - if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) { - // Ensure there is an inventory item listed in that cell - uint idx = _highlightedElement - INVLIST_START; - if ((_topIndex + idx) < _inventoryList.size()) { - // Set the selected object - setSelectedObject(_inventoryList[_topIndex + idx]); - } - } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) { - // A standard action was selected - int verbId = kVerbLook + (_highlightedElement - ACTIONS_START); - warning("Selected action #%d", verbId); - - } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) { - // A vocab action was selected - MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); - int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); - if (vocabIndex >= 0) { - act._actionMode = ACTMODE_OBJECT; - act._actionMode2 = ACTMODE2_2; - act._flags1 = obj->vocabList[1].flags1; - act._flags2 = obj->vocabList[1].flags2; - - act._currentHotspot = _selectedObject; - act._articleNumber = act._flags2; - } - } - } - return true; - - case MEVENT_LEFT_HOLD: - // Left mouse hold - // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down - if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) { - if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) { - // Handle scroll up/down action - _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY; - - if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0)) - --_topIndex; - if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1))) - ++_topIndex; + if (entry->field_1C) { + _abortTimedText = entry->field_1C; + //word_84208 = entry->field_1D; + + if (entry->field_1D != 1) { + // Restore the action list + for (int i = 0; i < 3; ++i) + _madsVm->scene()->actionNouns[i] = entry->actionNouns[i]; } } - return true; - - case MEVENT_LEFT_DRAG: - // Left mouse drag - // Handle the the the scroller area that can be dragged to adjust the top displayed index - if (_highlightedElement == SCROLL_SCROLLER) { - // Calculate the new top index based on the Y position - const Common::Rect r(_screenObjects[SCROLL_SCROLLER]); - _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5), - 0, (int)_inventoryList.size() - 1); - } - return true; - - case KEVENT_KEY: - if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) - handleCheatKey(param1); - handleKeypress(param1); - return true; - - default: - break; - } - - return false; -} - -bool MadsInterfaceView::handleCheatKey(int32 keycode) { - switch (keycode) { - case Common::KEYCODE_SPACE: - // TODO: Move player to current destination - return true; - - case Common::KEYCODE_t | (Common::KEYCODE_LALT): - case Common::KEYCODE_t | (Common::KEYCODE_RALT): - { - // Teleport to room - //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE); - - - return true; - } - - default: - break; - } - - return false; -} - -const char *CHEAT_SEQUENCE = "widepipe"; - -bool MadsInterfaceView::handleKeypress(int32 keycode) { - int flags = keycode >> 24; - int kc = keycode & 0xffff; - - // Capitalise the letter if necessary - if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) { - if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) { - ++_cheatKeyCtr; - if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) - Dialog::display(_vm, 22, cheatingEnabledDesc); - return true; - } else { - _cheatKeyCtr = 0; - } } - // Handle the various keys - if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) { - // Game menu - _madsVm->globals()->dialogType = DIALOG_GAME_MENU; - leaveScene(); - return false; - } else if (flags & Common::KBD_CTRL) { - // Handling of the different control key combinations - switch (kc) { - case Common::KEYCODE_i: - // Mouse to inventory - warning("TODO: Mouse to inventory"); - break; - - case Common::KEYCODE_k: - // Toggle hotspots - warning("TODO: Toggle hotspots"); - break; - - case Common::KEYCODE_p: - // Player stats - warning("TODO: Player stats"); - break; - - case Common::KEYCODE_q: - // Quit game - break; - - case Common::KEYCODE_s: - // Activate sound - warning("TODO: Activate sound"); - break; - - case Common::KEYCODE_u: - // Rotate player - warning("TODO: Rotate player"); - break; - - case Common::KEYCODE_v: { - // Release version - Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr); - _vm->_viewManager->addView(dlg); - _vm->_viewManager->moveToFront(dlg); - return false; - } - - default: - break; - } - } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) { - // Quit Game - - } else { - // Standard keypresses - switch (kc) { - case Common::KEYCODE_F2: - // Save game - _madsVm->globals()->dialogType = DIALOG_SAVE; - leaveScene(); - break; - case Common::KEYCODE_F3: - // Restore game - _madsVm->globals()->dialogType = DIALOG_RESTORE; - leaveScene(); - break; - } - } -//DIALOG_OPTIONS - return false; -} - -void MadsInterfaceView::leaveScene() { - // Close the scene - View *view = _madsVm->_viewManager->getView(VIEWID_SCENE); - _madsVm->_viewManager->deleteView(view); + // TODO: code from 'loc_244ec' onwards } +*/ } // End of namespace M4 diff --git a/engines/m4/mads_scene.h b/engines/m4/mads_scene.h index 0269de75c8..f7625bb761 100644 --- a/engines/m4/mads_scene.h +++ b/engines/m4/mads_scene.h @@ -33,24 +33,37 @@ namespace M4 { #define INTERFACE_HEIGHT 106 -class MadsInterfaceView; + +struct SpriteSlot { + int16 spriteId; + int16 scale; + uint16 spriteListIndex; +}; + +struct DirtyArea { + bool active; + bool active2; + Common::Rect bounds; +}; + class MadsSceneResources: public SceneResources { public: int sceneId; int artFileNum; - int drawStyle; + int field_4; int width; int height; - Common::Array<MadsObject> objects; - Common::Array<Common::String> setNames; + + int objectCount; + MadsObject objects[32]; - Common::Point playerPos; - int playerDir; + int walkSize; + byte *walkData; - MadsSceneResources() { playerDir = 0; } - ~MadsSceneResources() {} - void load(int sceneId, const char *resName, int v0, M4Surface *depthSurface, M4Surface *surface); + MadsSceneResources() { walkSize = 0; walkData = NULL; } + ~MadsSceneResources() { delete walkData; } + void load(int sceneId); }; enum MadsActionMode {ACTMODE_NONE = 0, ACTMODE_VERB = 1, ACTMODE_OBJECT = 3, ACTMODE_TALK = 6}; @@ -88,15 +101,17 @@ public: const char *statusText() const { return _statusText; } }; +#define DIRTY_AREA_SIZE 90 + class MadsScene : public Scene, public MadsView { private: MadsEngine *_vm; MadsSceneResources _sceneResources; MadsAction _action; - Animation *_activeAnimation; MadsSceneLogic _sceneLogic; SpriteAsset *_playerSprites; + DirtyArea _dirtyAreas[DIRTY_AREA_SIZE]; void drawElements(); void loadScene2(const char *aaName); @@ -128,7 +143,6 @@ public: int loadSceneSpriteSet(const char *setName); void loadPlayerSprites(const char *prefix); void showMADSV2TextBox(char *text, int x, int y, char *faceName); - void loadAnimation(const Common::String &animName, int v0); MadsInterfaceView *getInterface() { return (MadsInterfaceView *)_interfaceSurface; } MadsSceneResources &getSceneResources() { return _sceneResources; } @@ -136,56 +150,6 @@ public: void setStatusText(const char *text) {}//***DEPRECATED*** }; -#define CHEAT_SEQUENCE_MAX 8 - -class IntegerList : public Common::Array<int> { -public: - int indexOf(int v) { - for (uint i = 0; i < size(); ++i) - if (operator [](i) == v) - return i; - return -1; - } -}; - -enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED}; - -enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12, - INVLIST_START = 13, VOCAB_START = 18}; - -class MadsInterfaceView : public GameInterfaceView { -private: - IntegerList _inventoryList; - RectList _screenObjects; - int _highlightedElement; - int _topIndex; - uint32 _nextScrollerTicks; - int _cheatKeyCtr; - - // Object display fields - int _selectedObject; - SpriteAsset *_objectSprites; - RGBList *_objectPalData; - int _objectFrameNumber; - - void setFontMode(InterfaceFontMode newMode); - bool handleCheatKey(int32 keycode); - bool handleKeypress(int32 keycode); - void leaveScene(); -public: - MadsInterfaceView(MadsM4Engine *vm); - ~MadsInterfaceView(); - - virtual void initialise(); - virtual void setSelectedObject(int objectNumber); - virtual void addObjectToInventory(int objectNumber); - int getSelectedObject() { return _selectedObject; } - int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; } - - void onRefresh(RectList *rects, M4Surface *destSurface); - bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents); -}; - } // End of namespace M4 #endif diff --git a/engines/m4/mads_views.cpp b/engines/m4/mads_views.cpp index c656db83f1..e50c35bc04 100644 --- a/engines/m4/mads_views.cpp +++ b/engines/m4/mads_views.cpp @@ -24,7 +24,6 @@ */ #include "m4/m4_views.h" -#include "m4/animation.h" #include "m4/dialogs.h" #include "m4/events.h" #include "m4/font.h" @@ -37,23 +36,14 @@ namespace M4 { -bool MadsSpriteSlot::operator==(const SpriteSlotSubset &other) const { - return (spriteListIndex == other.spriteListIndex) && (frameNumber == other.frameNumber) && - (xp == other.xp) && (yp == other.yp) && (depth == other.depth) && (scale == other.scale); -} - -void MadsSpriteSlot::copy(const SpriteSlotSubset &other) { - spriteListIndex = other.spriteListIndex; - frameNumber = other.frameNumber; - xp = other.xp; - yp = other.yp; - depth = other.depth; - scale = other.scale; -} +static const int INV_ANIM_FRAME_SPEED = 2; +static const int INVENTORY_X = 160; +static const int INVENTORY_Y = 159; +static const int SCROLLER_DELAY = 200; //-------------------------------------------------------------------------- -MadsSpriteSlots::MadsSpriteSlots(MadsView &owner): _owner(owner) { +MadsSpriteSlots::MadsSpriteSlots() { for (int i = 0; i < SPRITE_SLOTS_SIZE; ++i) { MadsSpriteSlot rec; _entries.push_back(rec); @@ -62,23 +52,6 @@ MadsSpriteSlots::MadsSpriteSlots(MadsView &owner): _owner(owner) { startIndex = 0; } -MadsSpriteSlots::~MadsSpriteSlots() { - for (uint i = 0; i < _sprites.size(); ++i) - delete _sprites[i]; -} - -void MadsSpriteSlots::clear() { - _owner._textDisplay.clear(); - for (uint i = 0; i < _sprites.size(); ++i) - delete _sprites[i]; - _sprites.clear(); - - // Reset the sprite slots list back to a single entry for a full screen refresh - startIndex = 1; - _entries[0].spriteType = FULL_SCREEN_REFRESH; - _entries[0].seqIndex = -1; -} - int MadsSpriteSlots::getIndex() { if (startIndex == SPRITE_SLOTS_SIZE) error("Run out of sprite slots"); @@ -91,31 +64,20 @@ int MadsSpriteSlots::addSprites(const char *resName) { Common::SeekableReadStream *data = _vm->res()->get(resName); SpriteAsset *spriteSet = new SpriteAsset(_vm, data, data->size(), resName); spriteSet->translate(_madsVm->_palette); - assert(spriteSet != NULL); - _sprites.push_back(spriteSet); + _sprites.push_back(SpriteList::value_type(spriteSet)); _vm->res()->toss(resName); return _sprites.size() - 1; } -void MadsSpriteSlots::deleteSprites(int listIndex) { - if (listIndex < 0) - return; - - delete _sprites[listIndex]; - _sprites[listIndex] = NULL; - if (listIndex == ((int)_sprites.size() - 1)) - _sprites.remove_at(listIndex); -} - /* * Deletes the sprite slot with the given timer entry */ -void MadsSpriteSlots::deleteTimer(int seqIndex) { +void MadsSpriteSlots::deleteTimer(int timerIndex) { for (int idx = 0; idx < startIndex; ++idx) { - if (_entries[idx].seqIndex == seqIndex) - _entries[idx].spriteType = EXPIRED_SPRITE; + if (_entries[idx].timerIndex == timerIndex) + _entries[idx].spriteId = -1; } } @@ -133,49 +95,12 @@ bool sortHelper(const DepthEntry &entry1, const DepthEntry &entry2) { typedef Common::List<DepthEntry> DepthList; -void MadsSpriteSlots::drawBackground() { - // Draw all active sprites onto the background surface - for (int i = 0; i < startIndex; ++i) { - if (_entries[i].spriteType >= 0) { - _owner._dirtyAreas[i].active = false; - } else { - _owner._dirtyAreas[i].textActive = true; - _owner._dirtyAreas.setSpriteSlot(i, _entries[i]); - - if (_entries[i].spriteType == BACKGROUND_SPRITE) { - SpriteAsset &spriteSet = getSprite(_entries[i].spriteListIndex); - M4Sprite *frame = spriteSet.getFrame((_entries[i].frameNumber & 0x7fff) - 1); - int xp = _entries[i].xp; - int yp = _entries[i].yp; - - if (_entries[i].scale != -1) { - // Adjust position based on frame size - xp -= frame->width() / 2; - yp -= frame->height() / 2; - } - - if (_entries[i].depth <= 1) { - // No depth, so simply copy the frame onto the background - frame->copyTo(_owner._bgSurface, xp, yp); - } else { - // Depth was specified, so draw frame using scene's depth information - frame->copyTo(_owner._bgSurface, xp, yp, _entries[i].depth, _owner._depthSurface, 100); - } - } - } - } - - // Flag any remaining dirty areas as inactive - for (uint i = startIndex; i < DIRTY_AREAS_TEXT_DISPLAY_IDX; ++i) - _owner._dirtyAreas[i].active = false; -} - -void MadsSpriteSlots::drawForeground(View *view) { +void MadsSpriteSlots::draw(View *view) { DepthList depthList; // Get a list of sprite object depths for active objects for (int i = 0; i < startIndex; ++i) { - if (_entries[i].spriteType >= 0) { + if (_entries[i].spriteId >= 0) { DepthEntry rec(_entries[i].depth, i); depthList.push_back(rec); } @@ -190,57 +115,34 @@ void MadsSpriteSlots::drawForeground(View *view) { DepthEntry &de = *i; MadsSpriteSlot &slot = _entries[de.index]; assert(slot.spriteListIndex < (int)_sprites.size()); - SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex]; + SpriteAsset &spriteSet = *_sprites[slot.spriteListIndex].get(); if (slot.scale < 100) { // Minimalised drawing assert(slot.spriteListIndex < (int)_sprites.size()); M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1); - spr->copyTo(view, slot.xp, slot.yp, slot.depth, _owner._depthSurface, slot.scale, 0); + spr->draw1(view, slot.scale, slot.depth, slot.xp, slot.yp); } else { int xp, yp; M4Sprite *spr = spriteSet.getFrame(slot.frameNumber - 1); if (slot.scale == -1) { - xp = slot.xp - _owner._posAdjust.x; - yp = slot.yp - _owner._posAdjust.y; + xp = slot.xp; // - widthAdjust; + yp = slot.yp; // - heightAdjust; } else { - xp = slot.xp - (spr->width() / 2) - _owner._posAdjust.x; - yp = slot.yp - spr->height() - _owner._posAdjust.y + 1; + xp = slot.xp - (spr->width() / 2); // - widthAdjust; + yp = slot.yp - spr->height() + 1; // - heightAdjust; } if (slot.depth > 1) { - // Draw the frame with depth processing - spr->copyTo(view, xp, yp, slot.depth, _owner._depthSurface, 100, 0); + spr->draw2(view, slot.depth, xp, yp); } else { - // No depth, so simply draw the image - spr->copyTo(view, xp, yp, 0); + spr->draw3(view, xp, yp); } } } } -void MadsSpriteSlots::setDirtyAreas() { - for (int i = 0; i < startIndex; ++i) { - if (_entries[i].spriteType >= 0) { - _owner._dirtyAreas.setSpriteSlot(i, _entries[i]); - - _owner._dirtyAreas[i].textActive = (_entries[i].spriteType <= 0) ? 0 : 1; - _entries[i].spriteType = 0; - } - } -} - -/** - * Flags the entire screen to be redrawn during the next drawing cycle - */ -void MadsSpriteSlots::fullRefresh() { - int idx = getIndex(); - - _entries[idx].spriteType = FULL_SCREEN_REFRESH; - _entries[idx].seqIndex = -1; -} - /** * Removes any sprite slots that are no longer needed */ @@ -248,7 +150,7 @@ void MadsSpriteSlots::cleanUp() { // Delete any entries that aren't needed int idx = 0; while (idx < startIndex) { - if (_entries[idx].spriteType < 0) { + if (_entries[idx].spriteId >= 0) { _entries.remove_at(idx); --startIndex; } else { @@ -266,7 +168,7 @@ void MadsSpriteSlots::cleanUp() { //-------------------------------------------------------------------------- -MadsTextDisplay::MadsTextDisplay(MadsView &owner): _owner(owner) { +MadsTextDisplay::MadsTextDisplay() { for (int i = 0; i < TEXT_DISPLAY_SIZE; ++i) { MadsTextDisplayEntry rec; rec.active = false; @@ -304,32 +206,11 @@ int MadsTextDisplay::add(int xp, int yp, uint fontColour, int charSpacing, const return usedSlot; } -void MadsTextDisplay::setDirtyAreas() { - // Determine dirty areas for active text areas - for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) { - if ((_entries[idx].expire < 0) || !_entries[idx].active) - _owner._dirtyAreas[dirtyIdx].active = false; - else { - _owner._dirtyAreas[dirtyIdx].textActive = true; - _owner._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]); - } - } -} - -void MadsTextDisplay::setDirtyAreas2() { - // Determine dirty areas for active text areas - for (uint idx = 0, dirtyIdx = DIRTY_AREAS_TEXT_DISPLAY_IDX; dirtyIdx < DIRTY_AREAS_SIZE; ++idx, ++dirtyIdx) { - if (_entries[idx].active && (_entries[idx].expire >= 0)) { - _owner._dirtyAreas.setTextDisplay(dirtyIdx, _entries[idx]); - _owner._dirtyAreas[dirtyIdx].textActive = (_entries[idx].expire <= 0) ? 0 : 1; - } - } -} - void MadsTextDisplay::draw(View *view) { for (uint idx = 0; idx < _entries.size(); ++idx) { if (_entries[idx].active && (_entries[idx].expire >= 0)) { - _entries[idx].font->setColours(_entries[idx].colour1, _entries[idx].colour2, 0); + _entries[idx].font->setColours(_entries[idx].colour1, + (_entries[idx].colour2 == 0) ? _entries[idx].colour1 : _entries[idx].colour2, 0xff); _entries[idx].font->writeString(view, _entries[idx].msg, _entries[idx].bounds.left, _entries[idx].bounds.top, _entries[idx].bounds.width(), _entries[idx].spacing); @@ -361,13 +242,12 @@ void MadsTextDisplay::cleanUp() { MadsKernelMessageList::MadsKernelMessageList(MadsView &owner): _owner(owner) { for (int i = 0; i < TIMED_TEXT_SIZE; ++i) { - MadsKernelMessageEntry rec; + MadsKernelMessageListEntry rec; _entries.push_back(rec); } _owner._textSpacing = -1; _talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS); - word_8469E = 0; } void MadsKernelMessageList::clear() { @@ -378,20 +258,20 @@ void MadsKernelMessageList::clear() { _talkFont = _vm->_font->getFont(FONT_CONVERSATION_MADS); } -int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg) { +int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg) { // Find a free slot uint idx = 0; while ((idx < _entries.size()) && ((_entries[idx].flags & KMSG_ACTIVE) != 0)) ++idx; if (idx == _entries.size()) { - if (abortTimers == 0) + if (v2 == 0) return -1; error("MadsKernelList overflow"); } - MadsKernelMessageEntry &rec = _entries[idx]; - strcpy(rec.msg, msg); + MadsKernelMessageListEntry &rec = _entries[idx]; + rec.msg = msg; rec.flags = flags | KMSG_ACTIVE; rec.colour1 = fontColour & 0xff; rec.colour2 = fontColour >> 8; @@ -399,37 +279,37 @@ int MadsKernelMessageList::add(const Common::Point &pt, uint fontColour, uint8 f rec.textDisplayIndex = -1; rec.timeout = timeout; rec.frameTimer = _madsVm->_currentTimer; - rec.abortTimers = abortTimers; + rec.field_1C = v2; rec.abortMode = _owner._abortTimersMode2; for (int i = 0; i < 3; ++i) rec.actionNouns[i] = _madsVm->scene()->actionNouns[i]; - if (flags & KMSG_OWNER_TIMEOUT) + if (flags & KMSG_2) rec.frameTimer = _owner._ticksAmount + _owner._newTimeout; return idx; } -int MadsKernelMessageList::addQuote(int quoteId, int abortTimers, uint32 timeout) { +int MadsKernelMessageList::addQuote(int quoteId, int v2, uint32 timeout) { const char *quoteStr = _madsVm->globals()->getQuote(quoteId); - return add(Common::Point(0, 0), 0x1110, KMSG_OWNER_TIMEOUT | KMSG_CENTER_ALIGN, abortTimers, timeout, quoteStr); + return add(Common::Point(0, 0), 0x1110, KMSG_2 | KMSG_20, v2, timeout, quoteStr); } -void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quoted) { +void MadsKernelMessageList::unk1(int msgIndex, int v1, int v2) { if (msgIndex < 0) return; - _entries[msgIndex].flags |= quoted ? (KMSG_SCROLL | KMSG_QUOTED) : KMSG_SCROLL; + _entries[msgIndex].flags |= (v2 == 0) ? KMSG_8 : (KMSG_8 | KMSG_1); _entries[msgIndex].msgOffset = 0; - _entries[msgIndex].numTicks = numTicks; + _entries[msgIndex].field_E = v1; _entries[msgIndex].frameTimer2 = _madsVm->_currentTimer; const char *msgP = _entries[msgIndex].msg; _entries[msgIndex].asciiChar = *msgP; _entries[msgIndex].asciiChar2 = *(msgP + 1); - if (_entries[msgIndex].flags & KMSG_OWNER_TIMEOUT) + if (_entries[msgIndex].flags & KMSG_2) _entries[msgIndex].frameTimer2 = _owner._ticksAmount + _owner._newTimeout; _entries[msgIndex].frameTimer = _entries[msgIndex].frameTimer2; @@ -437,18 +317,18 @@ void MadsKernelMessageList::scrollMessage(int msgIndex, int numTicks, bool quote void MadsKernelMessageList::setSeqIndex(int msgIndex, int seqIndex) { if (msgIndex >= 0) { - _entries[msgIndex].flags |= KMSG_SEQ_ENTRY; + _entries[msgIndex].flags |= KMSG_4; _entries[msgIndex].sequenceIndex = seqIndex; } } void MadsKernelMessageList::remove(int msgIndex) { - MadsKernelMessageEntry &rec = _entries[msgIndex]; + MadsKernelMessageListEntry &rec = _entries[msgIndex]; if (rec.flags & KMSG_ACTIVE) { - if (rec.flags & KMSG_SCROLL) { - *(rec.msg + rec.msgOffset) = rec.asciiChar; - *(rec.msg + rec.msgOffset + 1) = rec.asciiChar2; + if (rec.flags & KMSG_8) { + //*(rec.msg + rec.msgOffset) = rec.asciiChar; + //*(rec.msg + rec.msgOffset + 1) = rec.asciiChar2; } if (rec.textDisplayIndex >= 0) @@ -465,136 +345,6 @@ void MadsKernelMessageList::reset() { // sub_20454 } -void MadsKernelMessageList::update() { - uint32 currentTimer = _madsVm->_currentTimer; - - for (uint i = 0; i < _entries.size(); ++i) { - if (((_entries[i].flags & KMSG_ACTIVE) != 0) && (currentTimer >= _entries[i].frameTimer)) - processText(i); - } -} - -void MadsKernelMessageList::processText(int msgIndex) { - MadsKernelMessageEntry &msg = _entries[msgIndex]; - uint32 currentTimer = _madsVm->_currentTimer; - bool flag = false; - - if ((msg.flags & KMSG_EXPIRE) != 0) { - _owner._textDisplay.expire(msg.textDisplayIndex); - msg.flags &= !KMSG_ACTIVE; - return; - } - - if ((msg.flags & KMSG_SCROLL) == 0) { - msg.timeout -= 3; - } - - if (msg.flags & KMSG_SEQ_ENTRY) { - MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex]; - if (seqEntry.doneFlag || !seqEntry.active) - msg.timeout = 0; - } - - if ((msg.timeout <= 0) && (_owner._abortTimers == 0)) { - msg.flags |= KMSG_EXPIRE; - if (msg.abortTimers != 0) { - _owner._abortTimers = msg.abortTimers; - _owner._abortTimersMode = msg.abortMode; - - if (_owner._abortTimersMode != ABORTMODE_1) { - for (int i = 0; i < 3; ++i) - _madsVm->scene()->actionNouns[i] = msg.actionNouns[i]; - } - } - } - - msg.frameTimer = currentTimer + 3; - int x1 = 0, y1 = 0; - - if (msg.flags & KMSG_SEQ_ENTRY) { - MadsSequenceEntry &seqEntry = _owner._sequenceList[msg.sequenceIndex]; - if (!seqEntry.nonFixed) { - SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex); - M4Sprite *frame = spriteSet.getFrame(seqEntry.frameIndex - 1); - x1 = frame->bounds().left; - y1 = frame->bounds().top; - } else { - x1 = seqEntry.msgPos.x; - y1 = seqEntry.msgPos.y; - } - } - - if (msg.flags & KMSG_OWNER_TIMEOUT) { - if (word_8469E != 0) { - // TODO: Figure out various flags - } else { - x1 = 160; - y1 = 78; - } - } - - x1 += msg.position.x; - y1 += msg.position.y; - - if ((msg.flags & KMSG_SCROLL) && (msg.frameTimer >= currentTimer)) { - msg.msg[msg.msgOffset] = msg.asciiChar; - char *msgP = &msg.msg[++msg.msgOffset]; - *msgP = msg.asciiChar2; - - msg.asciiChar = *msgP; - msg.asciiChar2 = *(msgP + 1); - - if (!msg.asciiChar) { - // End of message - *msgP = '\0'; - msg.flags &= ~KMSG_SCROLL; - } else if (msg.flags & KMSG_QUOTED) { - *msgP = '"'; - *(msgP + 1) = '\0'; - } - - msg.frameTimer = msg.frameTimer2 = currentTimer + msg.numTicks; - flag = true; - } - - int strWidth = _talkFont->getWidth(msg.msg, _owner._textSpacing); - - if (msg.flags & (KMSG_RIGHT_ALIGN | KMSG_CENTER_ALIGN)) { - x1 -= (msg.flags & KMSG_CENTER_ALIGN) ? strWidth / 2 : strWidth; - } - - // Make sure text appears entirely on-screen - int x2 = x1 + strWidth; - if (x2 > MADS_SURFACE_WIDTH) - x1 -= x2 - MADS_SURFACE_WIDTH; - if (x1 > (MADS_SURFACE_WIDTH - 1)) - x1 = MADS_SURFACE_WIDTH - 1; - if (x1 < 0) - x1 = 0; - - if (y1 > (MADS_SURFACE_HEIGHT - 1)) - y1 = MADS_SURFACE_HEIGHT - 1; - if (y1 < 0) - y1 = 0; - - if (msg.textDisplayIndex >= 0) { - MadsTextDisplayEntry textEntry = _owner._textDisplay[msg.textDisplayIndex]; - - if (flag || (textEntry.bounds.left != x1) || (textEntry.bounds.top != y1)) { - // Mark the associated text entry as deleted, so it can be re-created - _owner._textDisplay.expire(msg.textDisplayIndex); - msg.textDisplayIndex = -1; - } - } - - if (msg.textDisplayIndex < 0) { - // Need to create a new text display entry for this message - int idx = _owner._textDisplay.add(x1, y1, msg.colour1 | (msg.colour2 << 8), _owner._textSpacing, msg.msg, _talkFont); - if (idx >= 0) - msg.textDisplayIndex = idx; - } -} - //-------------------------------------------------------------------------- /** @@ -655,23 +405,21 @@ MadsDynamicHotspots::MadsDynamicHotspots(MadsView &owner): _owner(owner) { for (int i = 0; i < DYNAMIC_HOTSPOTS_SIZE; ++i) { DynamicHotspot rec; rec.active = false; - _entries.push_back(rec); } _flag = true; _count = 0; } -int MadsDynamicHotspots::add(int descId, int field14, int seqIndex, const Common::Rect &bounds) { +int MadsDynamicHotspots::add(int descId, int field14, int timerIndex, const Common::Rect &bounds) { // Find a free slot uint idx = 0; - while ((idx < _entries.size()) && _entries[idx].active) + while ((idx < _entries.size()) && !_entries[idx].active) ++idx; if (idx == _entries.size()) error("MadsDynamicHotspots overflow"); _entries[idx].active = true; _entries[idx].descId = descId; - _entries[idx].seqIndex = seqIndex; _entries[idx].bounds = bounds; _entries[idx].pos.x = -3; _entries[idx].pos.y = 0; @@ -682,9 +430,7 @@ int MadsDynamicHotspots::add(int descId, int field14, int seqIndex, const Common ++_count; _flag = true; - - if (seqIndex >= 0) - _owner._sequenceList[seqIndex].dynamicHotspotIndex = idx; + _owner._sequenceList[timerIndex].dynamicHotspotIndex = idx; return idx; } @@ -708,8 +454,8 @@ int MadsDynamicHotspots::set17(int index, int v) { void MadsDynamicHotspots::remove(int index) { if (_entries[index].active) { - if (_entries[index].seqIndex >= 0) - _owner._sequenceList[_entries[index].seqIndex].dynamicHotspotIndex = -1; + if (_entries[index].timerIndex >= 0) + _owner._sequenceList[_entries[index].timerIndex].dynamicHotspotIndex = -1; _entries[index].active = false; --_count; @@ -727,143 +473,6 @@ void MadsDynamicHotspots::reset() { /*--------------------------------------------------------------------------*/ -void MadsDirtyArea::setArea(int width, int height, int maxWidth, int maxHeight) { - if (bounds.left % 2) { - --bounds.left; - ++width; - } - int right = bounds.left + width; - if (bounds.left < 0) - bounds.left = 0; - if (right < 0) - right = 0; - if (right > maxWidth) - right = maxWidth; - - bounds.right = right; - bounds2.left = bounds.width() / 2; - bounds2.right = bounds.left + (bounds.width() + 1) / 2 - 1; - - if (bounds.top < 0) - bounds.top = 0; - int bottom = bounds.top + height; - if (bottom < 0) - bottom = 0; - if (bottom > maxHeight) - bottom = maxHeight; - - bounds.bottom = bottom; - bounds2.top = bounds.height() / 2; - bounds2.bottom = bounds.top + (bounds.height() + 1) / 2 - 1; - - active = true; -} - -/*--------------------------------------------------------------------------*/ - -MadsDirtyAreas::MadsDirtyAreas(MadsView &owner): _owner(owner) { - for (int i = 0; i < DIRTY_AREAS_SIZE; ++i) { - MadsDirtyArea rec; - rec.active = false; - _entries.push_back(rec); - } -} - -void MadsDirtyAreas::setSpriteSlot(int dirtyIdx, const MadsSpriteSlot &spriteSlot) { - int width, height; - MadsDirtyArea &dirtyArea = _entries[dirtyIdx]; - - if (spriteSlot.spriteType == FULL_SCREEN_REFRESH) { - // Special entry to refresh the entire screen - dirtyArea.bounds.left = 0; - dirtyArea.bounds.top = 0; - width = MADS_SURFACE_WIDTH; - height = MADS_SURFACE_HEIGHT; - } else { - // Standard sprite slots - dirtyArea.bounds.left = spriteSlot.xp - _owner._posAdjust.x; - dirtyArea.bounds.top = spriteSlot.yp - _owner._posAdjust.y; - - SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(spriteSlot.spriteListIndex); - M4Sprite *frame = spriteSet.getFrame(((spriteSlot.frameNumber & 0x7fff) - 1) & 0x7f); - - if (spriteSlot.scale == -1) { - width = frame->width(); - height = frame->height(); - } else { - width = frame->width() * spriteSlot.scale / 100; - height = frame->height() * spriteSlot.scale / 100; - - dirtyArea.bounds.left -= width / 2; - dirtyArea.bounds.top += -(height - 1); - } - } - - dirtyArea.setArea(width, height, MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT); -} - -void MadsDirtyAreas::setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &textDisplay) { - MadsDirtyArea &dirtyArea = _entries[dirtyIdx]; - dirtyArea.bounds.left = textDisplay.bounds.left; - dirtyArea.bounds.top = textDisplay.bounds.top; - - dirtyArea.setArea(textDisplay.bounds.width(), textDisplay.bounds.height(), MADS_SURFACE_WIDTH, MADS_SURFACE_HEIGHT); -} - -/** - * Merge together any designated dirty areas that overlap - * @param startIndex 1-based starting dirty area starting index - * @param count Number of entries to process - */ -void MadsDirtyAreas::merge(int startIndex, int count) { - if (startIndex >= count) - return; - - for (int outerCtr = startIndex - 1, idx = 0; idx < count; ++outerCtr, ++idx) { - if (!_entries[outerCtr].active) - continue; - - for (int innerCtr = outerCtr + 1; innerCtr < count; ++innerCtr) { - if (!_entries[innerCtr].active || !intersects(outerCtr, innerCtr)) - continue; - - if (_entries[outerCtr].textActive && _entries[innerCtr].textActive) - mergeAreas(outerCtr, innerCtr); - } - } -} - -/** - * Returns true if two dirty areas intersect - */ -bool MadsDirtyAreas::intersects(int idx1, int idx2) { - return _entries[idx1].bounds2.intersects(_entries[idx2].bounds2); -} - -void MadsDirtyAreas::mergeAreas(int idx1, int idx2) { - MadsDirtyArea &da1 = _entries[idx1]; - MadsDirtyArea &da2 = _entries[idx2]; - - da1.bounds.extend(da2.bounds); - - da1.bounds2.left = da1.bounds.width() / 2; - da1.bounds2.right = da1.bounds.left + (da1.bounds.width() + 1) / 2 - 1; - da1.bounds2.top = da1.bounds.height() / 2; - da1.bounds2.bottom = da1.bounds.top + (da1.bounds.height() + 1) / 2 - 1; - - da2.active = false; - da1.textActive = true; -} - -void MadsDirtyAreas::copy(M4Surface *dest, M4Surface *src) { - for (uint i = 0; i < _entries.size(); ++i) { - if (_entries[i].active && _entries[i].bounds.isValidRect()) - src->copyTo(dest, _entries[i].bounds, _entries[i].bounds.left, _entries[i].bounds.top); - } -} - -/*--------------------------------------------------------------------------*/ - MadsSequenceList::MadsSequenceList(MadsView &owner): _owner(owner) { for (int i = 0; i < TIMER_LIST_SIZE; ++i) { MadsSequenceEntry rec; @@ -893,14 +502,14 @@ bool MadsSequenceList::addSubEntry(int index, SequenceSubEntryMode mode, int fra } int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, - int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, + int height, int width, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, int frameStart) { // Find a free slot - uint seqIndex = 0; - while ((seqIndex < _entries.size()) && (_entries[seqIndex].active)) - ++seqIndex; - if (seqIndex == _entries.size()) + uint timerIndex = 0; + while ((timerIndex < _entries.size()) && (_entries[timerIndex].active)) + ++timerIndex; + if (timerIndex == _entries.size()) error("TimerList full"); if (frameStart <= 0) @@ -911,76 +520,77 @@ int MadsSequenceList::add(int spriteListIndex, int v0, int frameIndex, int trigg frameInc = 0; // Set the list entry fields - _entries[seqIndex].active = true; - _entries[seqIndex].spriteListIndex = spriteListIndex; - _entries[seqIndex].field_2 = v0; - _entries[seqIndex].frameIndex = frameIndex; - _entries[seqIndex].frameStart = frameStart; - _entries[seqIndex].numSprites = numSprites; - _entries[seqIndex].animType = animType; - _entries[seqIndex].frameInc = frameInc; - _entries[seqIndex].depth = depth; - _entries[seqIndex].scale = scale; - _entries[seqIndex].nonFixed = nonFixed; - _entries[seqIndex].msgPos.x = msgX; - _entries[seqIndex].msgPos.y = msgY; - _entries[seqIndex].numTicks = numTicks; - _entries[seqIndex].extraTicks = extraTicks; - - _entries[seqIndex].timeout = _madsVm->_currentTimer + delayTicks; - - _entries[seqIndex].triggerCountdown = triggerCountdown; - _entries[seqIndex].doneFlag = false; - _entries[seqIndex].field_13 = 0; - _entries[seqIndex].dynamicHotspotIndex = -1; - _entries[seqIndex].entries.count = 0; - _entries[seqIndex].abortMode = _owner._abortTimersMode2; + _entries[timerIndex].active = true; + _entries[timerIndex].spriteListIndex = spriteListIndex; + _entries[timerIndex].field_2 = v0; + _entries[timerIndex].frameIndex = frameIndex; + _entries[timerIndex].frameStart = frameStart; + _entries[timerIndex].numSprites = numSprites; + _entries[timerIndex].animType = animType; + _entries[timerIndex].frameInc = frameInc; + _entries[timerIndex].depth = depth; + _entries[timerIndex].scale = scale; + _entries[timerIndex].field_12 = field_12; + _entries[timerIndex].width = width; + _entries[timerIndex].height = height; + _entries[timerIndex].numTicks = numTicks; + _entries[timerIndex].extraTicks = extraTicks; + + _entries[timerIndex].timeout = _madsVm->_currentTimer + delayTicks; + + _entries[timerIndex].triggerCountdown = triggerCountdown; + _entries[timerIndex].doneFlag = false; + _entries[timerIndex].field_13 = 0; + _entries[timerIndex].dynamicHotspotIndex = -1; + _entries[timerIndex].entries.count = 0; + _entries[timerIndex].abortMode = _owner._abortTimersMode2; for (int i = 0; i < 3; ++i) - _entries[seqIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i]; + _entries[timerIndex].actionNouns[i] = _madsVm->scene()->actionNouns[i]; - return seqIndex; + return timerIndex; } -void MadsSequenceList::remove(int seqIndex) { - if (_entries[seqIndex].active) { - if (_entries[seqIndex].dynamicHotspotIndex >= 0) - _owner._dynamicHotspots.remove(_entries[seqIndex].dynamicHotspotIndex); +void MadsSequenceList::remove(int timerIndex) { + if (_entries[timerIndex].active) { + if (_entries[timerIndex].dynamicHotspotIndex >= 0) + _owner._dynamicHotspots.remove(_entries[timerIndex].dynamicHotspotIndex); } - _entries[seqIndex].active = false; - _owner._spriteSlots.deleteTimer(seqIndex); + _entries[timerIndex].active = false; + _owner._spriteSlots.deleteTimer(timerIndex); } -void MadsSequenceList::setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot) { - MadsSequenceEntry &timerEntry = _entries[seqIndex]; +void MadsSequenceList::setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot) { + MadsSequenceEntry &timerEntry = _entries[timerIndex]; SpriteAsset &sprite = _owner._spriteSlots.getSprite(timerEntry.spriteListIndex); - spriteSlot.spriteType = sprite.getAssetType() == 1 ? BACKGROUND_SPRITE : FOREGROUND_SPRITE; - spriteSlot.seqIndex = seqIndex; + // TODO: Figure out logic for spriteId value based on SPRITE_SLOT.field_0 + spriteSlot.spriteId = (0 /*field 0*/ == 1) ? -4 : 1; + spriteSlot.timerIndex = timerIndex; spriteSlot.spriteListIndex = timerEntry.spriteListIndex; spriteSlot.frameNumber = ((timerEntry.field_2 == 1) ? 0x8000 : 0) | timerEntry.frameIndex; spriteSlot.depth = timerEntry.depth; spriteSlot.scale = timerEntry.scale; - if (!timerEntry.nonFixed) { - spriteSlot.xp = timerEntry.msgPos.x; - spriteSlot.yp = timerEntry.msgPos.y; + if (timerEntry.field_12 == 0) { + spriteSlot.xp = timerEntry.width; + spriteSlot.yp = timerEntry.height; } else { spriteSlot.xp = sprite.getFrame(timerEntry.frameIndex - 1)->x; spriteSlot.yp = sprite.getFrame(timerEntry.frameIndex - 1)->y; } } -bool MadsSequenceList::loadSprites(int seqIndex) { - MadsSequenceEntry &seqEntry = _entries[seqIndex]; +bool MadsSequenceList::loadSprites(int timerIndex) { + MadsSequenceEntry &seqEntry = _entries[timerIndex]; int slotIndex; bool result = false; int idx = -1; - _owner._spriteSlots.deleteTimer(seqIndex); + _owner._spriteSlots.deleteTimer(timerIndex); if (seqEntry.doneFlag) { - remove(seqIndex); + remove(timerIndex); return false; } @@ -989,7 +599,7 @@ bool MadsSequenceList::loadSprites(int seqIndex) { seqEntry.doneFlag = true; } else if ((slotIndex = _owner._spriteSlots.getIndex()) >= 0) { MadsSpriteSlot &spriteSlot = _owner._spriteSlots[slotIndex]; - setSpriteSlot(seqIndex, spriteSlot); + setSpriteSlot(timerIndex, spriteSlot); int x2 = 0, y2 = 0; @@ -1117,108 +727,454 @@ void MadsSequenceList::delay(uint32 v1, uint32 v2) { } } -void MadsSequenceList::setAnimRange(int seqIndex, int startVal, int endVal) { - MadsSequenceEntry &seqEntry = _entries[seqIndex]; - SpriteAsset &spriteSet = _owner._spriteSlots.getSprite(seqEntry.spriteListIndex); - int numSprites = spriteSet.getCount(); - int tempStart = startVal, tempEnd = endVal; +//-------------------------------------------------------------------------- - switch (startVal) { - case -2: - tempStart = numSprites; - break; - case -1: - tempStart = 1; - break; - } - - switch (endVal) { - case -2: - case 0: - tempEnd = numSprites; +MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this), + _kernelMessages(*this) { + _textSpacing = -1; + _ticksAmount = 3; + _newTimeout = 0; + _abortTimers = 0; + _abortTimers2 = 0; + _abortTimersMode = ABORTMODE_0; + _abortTimersMode2 = ABORTMODE_0; +} + +void MadsView::refresh() { + // Draw any sprites + _spriteSlots.draw(_view); + + // Draw text elements onto the view + _textDisplay.draw(_view); + + // Remove any sprite slots that are no longer needed + _spriteSlots.cleanUp(); + + // Deactivate any text display entries that are no longer needed + _textDisplay.cleanUp(); +} + +/*-------------------------------------------------------------------------- + * MadsInterfaceView handles the user interface section at the bottom of + * game screens in MADS games + *-------------------------------------------------------------------------- + */ + +MadsInterfaceView::MadsInterfaceView(MadsM4Engine *vm): GameInterfaceView(vm, + Common::Rect(0, MADS_SURFACE_HEIGHT, vm->_screen->width(), vm->_screen->height())) { + _screenType = VIEWID_INTERFACE; + _highlightedElement = -1; + _topIndex = 0; + _selectedObject = -1; + _cheatKeyCtr = 0; + + _objectSprites = NULL; + _objectPalData = NULL; + + /* Set up the rect list for screen elements */ + // Actions + for (int i = 0; i < 10; ++i) + _screenObjects.addRect((i / 5) * 32 + 1, (i % 5) * 8 + MADS_SURFACE_HEIGHT + 2, + ((i / 5) + 1) * 32 + 3, ((i % 5) + 1) * 8 + MADS_SURFACE_HEIGHT + 2); + + // Scroller elements (up arrow, scroller, down arrow) + _screenObjects.addRect(73, 160, 82, 167); + _screenObjects.addRect(73, 168, 82, 190); + _screenObjects.addRect(73, 191, 82, 198); + + // Inventory object names + for (int i = 0; i < 5; ++i) + _screenObjects.addRect(89, 158 + i * 8, 160, 166 + i * 8); + + // Full rectangle area for all vocab actions + for (int i = 0; i < 5; ++i) + _screenObjects.addRect(239, 158 + i * 8, 320, 166 + i * 8); +} + +MadsInterfaceView::~MadsInterfaceView() { + delete _objectSprites; +} + +void MadsInterfaceView::setFontMode(InterfaceFontMode newMode) { + switch (newMode) { + case ITEM_NORMAL: + _vm->_font->setColors(4, 4, 0xff); break; - case -1: - tempEnd = 1; + case ITEM_HIGHLIGHTED: + _vm->_font->setColors(5, 5, 0xff); break; - default: - tempEnd = numSprites; + case ITEM_SELECTED: + _vm->_font->setColors(6, 6, 0xff); break; } +} - seqEntry.frameStart = tempStart; - seqEntry.numSprites = tempEnd; +void MadsInterfaceView::initialise() { + // Build up the inventory list + _inventoryList.clear(); + + for (uint i = 0; i < _madsVm->globals()->getObjectsSize(); ++i) { + MadsObject *obj = _madsVm->globals()->getObject(i); + if (obj->roomNumber == PLAYER_INVENTORY) + _inventoryList.push_back(i); + } - seqEntry.frameIndex = (seqEntry.frameInc < 0) ? tempStart : tempEnd; + // If the inventory has at least one object, select it + if (_inventoryList.size() > 0) + setSelectedObject(_inventoryList[0]); } -void MadsSequenceList::scan() { - for (uint i = 0; i < _entries.size(); ++i) { - if (!_entries[i].active && (_entries[i].spriteListIndex != -1)) { - int idx = _owner._spriteSlots.getIndex(); - setSpriteSlot(i, _owner._spriteSlots[idx]); - } +void MadsInterfaceView::setSelectedObject(int objectNumber) { + char resName[80]; + + // Load inventory resource + if (_objectSprites) { + _vm->_palette->deleteRange(_objectPalData); + delete _objectSprites; } + + // Check to make sure the object is in the inventory, and also visible on-screen + int idx = _inventoryList.indexOf(objectNumber); + if (idx == -1) { + // Object wasn't found, so return + _selectedObject = -1; + return; + } + + // Found the object + if (idx < _topIndex) + _topIndex = idx; + else if (idx >= (_topIndex + 5)) + _topIndex = MAX(0, idx - 4); + + _selectedObject = objectNumber; + sprintf(resName, "*OB%.3dI.SS", objectNumber); + + Common::SeekableReadStream *data = _vm->res()->get(resName); + _objectSprites = new SpriteAsset(_vm, data, data->size(), resName); + _vm->res()->toss(resName); + + // Slot it into available palette space + _objectPalData = _objectSprites->getRgbList(); + _vm->_palette->addRange(_objectPalData); + _objectSprites->translate(_objectPalData, true); + + _objectFrameNumber = 0; } -//-------------------------------------------------------------------------- +void MadsInterfaceView::addObjectToInventory(int objectNumber) { + if (_inventoryList.indexOf(objectNumber) == -1) { + _madsVm->globals()->getObject(objectNumber)->roomNumber = PLAYER_INVENTORY; + _inventoryList.push_back(objectNumber); + } -Animation::Animation(MadsM4Engine *vm): _vm(vm) { + setSelectedObject(objectNumber); } -Animation::~Animation() { +void MadsInterfaceView::onRefresh(RectList *rects, M4Surface *destSurface) { + _vm->_font->setFont(FONT_INTERFACE_MADS); + char buffer[100]; + + // Check to see if any dialog is currently active + bool dialogVisible = _vm->_viewManager->getView(LAYER_DIALOG) != NULL; + + // Highlighting logic for action list + int actionIndex = 0; + for (int x = 0; x < 2; ++x) { + for (int y = 0; y < 5; ++y, ++actionIndex) { + // Determine the font colour depending on whether an item is selected. Note that the first action, + // 'Look', is always 'selected', even when another action is clicked on + setFontMode((_highlightedElement == actionIndex) ? ITEM_HIGHLIGHTED : + ((actionIndex == 0) ? ITEM_SELECTED : ITEM_NORMAL)); + + // Get the verb action and capitalise it + const char *verbStr = _madsVm->globals()->getVocab(kVerbLook + actionIndex); + strcpy(buffer, verbStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + // Display the verb + const Common::Rect r(_screenObjects[actionIndex]); + _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + } + + // Check for highlighting of the scrollbar controls + if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_SCROLLER) || (_highlightedElement == SCROLL_DOWN)) { + // Highlight the control's borders + const Common::Rect r(_screenObjects[_highlightedElement]); + destSurface->frameRect(r, 5); + } + + // Draw the horizontal line in the scroller representing the current top selected + const Common::Rect scroller(_screenObjects[SCROLL_SCROLLER]); + int yP = (_inventoryList.size() < 2) ? 0 : (scroller.height() - 5) * _topIndex / (_inventoryList.size() - 1); + destSurface->setColor(4); + destSurface->hLine(scroller.left + 2, scroller.right - 3, scroller.top + 2 + yP); + + // List inventory items + for (uint i = 0; i < 5; ++i) { + if ((_topIndex + i) >= _inventoryList.size()) + break; + + const char *descStr = _madsVm->globals()->getVocab(_madsVm->globals()->getObject( + _inventoryList[_topIndex + i])->descId); + strcpy(buffer, descStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + const Common::Rect r(_screenObjects[INVLIST_START + i]); + + // Set the highlighting of the inventory item + if (_highlightedElement == (int)(INVLIST_START + i)) setFontMode(ITEM_HIGHLIGHTED); + else if (_selectedObject == _inventoryList[_topIndex + i]) setFontMode(ITEM_SELECTED); + else setFontMode(ITEM_NORMAL); + + // Write out it's description + _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + + // Handle the display of any currently selected object + if (_objectSprites) { + // Display object sprite. Note that the frame number isn't used directly, because it would result + // in too fast an animation + M4Sprite *spr = _objectSprites->getFrame(_objectFrameNumber / INV_ANIM_FRAME_SPEED); + spr->copyTo(destSurface, INVENTORY_X, INVENTORY_Y, 0); + + if (!_madsVm->globals()->_config.invObjectsStill && !dialogVisible) { + // If objects need to be animated, move to the next frame + if (++_objectFrameNumber >= (_objectSprites->getCount() * INV_ANIM_FRAME_SPEED)) + _objectFrameNumber = 0; + } + + // List the vocab actions for the currently selected object + MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); + int yIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); + + for (int i = 0; i < obj->vocabCount; ++i) { + const Common::Rect r(_screenObjects[VOCAB_START + i]); + + // Get the vocab description and capitalise it + const char *descStr = _madsVm->globals()->getVocab(obj->vocabList[i].vocabId); + strcpy(buffer, descStr); + if ((buffer[0] >= 'a') && (buffer[0] <= 'z')) buffer[0] -= 'a' - 'A'; + + // Set the highlighting and display the entry + setFontMode((i == yIndex) ? ITEM_HIGHLIGHTED : ITEM_NORMAL); + _vm->_font->writeString(destSurface, buffer, r.left, r.top, r.width(), 0); + } + } } -//-------------------------------------------------------------------------- +bool MadsInterfaceView::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { + MadsAction &act = _madsVm->scene()->getAction(); -MadsView::MadsView(View *view): _view(view), _dynamicHotspots(*this), _sequenceList(*this), - _kernelMessages(*this), _spriteSlots(*this), _dirtyAreas(*this), _textDisplay(*this) { - - _textSpacing = -1; - _ticksAmount = 3; - _newTimeout = 0; - _abortTimers = 0; - _abortTimers2 = 0; - _abortTimersMode = ABORTMODE_0; - _abortTimersMode2 = ABORTMODE_0; - - _depthSurface = NULL; - _bgSurface = NULL; - _sceneAnimation = new MadsAnimation(_vm, this); + // If the mouse isn't being held down, then reset the repeated scroll timer + if (eventType != MEVENT_LEFT_HOLD) + _nextScrollerTicks = 0; + + // Handle various event types + switch (eventType) { + case MEVENT_MOVE: + // If the cursor isn't in "wait mode", don't do any processing + if (_vm->_mouse->getCursorNum() == CURSOR_WAIT) + return true; + + // Ensure the cursor is the standard arrow + _vm->_mouse->setCursorNum(CURSOR_ARROW); + + // Check if any interface element is currently highlighted + _highlightedElement = _screenObjects.find(Common::Point(x, y)); + + return true; + + case MEVENT_LEFT_CLICK: + // Left mouse click + { + // Check if an inventory object was selected + if ((_highlightedElement >= INVLIST_START) && (_highlightedElement < (INVLIST_START + 5))) { + // Ensure there is an inventory item listed in that cell + uint idx = _highlightedElement - INVLIST_START; + if ((_topIndex + idx) < _inventoryList.size()) { + // Set the selected object + setSelectedObject(_inventoryList[_topIndex + idx]); + } + } else if ((_highlightedElement >= ACTIONS_START) && (_highlightedElement < (ACTIONS_START + 10))) { + // A standard action was selected + int verbId = kVerbLook + (_highlightedElement - ACTIONS_START); + warning("Selected action #%d", verbId); + + } else if ((_highlightedElement >= VOCAB_START) && (_highlightedElement < (VOCAB_START + 5))) { + // A vocab action was selected + MadsObject *obj = _madsVm->globals()->getObject(_selectedObject); + int vocabIndex = MIN(_highlightedElement - VOCAB_START, obj->vocabCount - 1); + if (vocabIndex >= 0) { + act._actionMode = ACTMODE_OBJECT; + act._actionMode2 = ACTMODE2_2; + act._flags1 = obj->vocabList[1].flags1; + act._flags2 = obj->vocabList[1].flags2; + + act._currentHotspot = _selectedObject; + act._articleNumber = act._flags2; + } + } + } + return true; + + case MEVENT_LEFT_HOLD: + // Left mouse hold + // Handle the scroller - the up/down buttons allow for multiple actions whilst the mouse is held down + if ((_highlightedElement == SCROLL_UP) || (_highlightedElement == SCROLL_DOWN)) { + if ((_nextScrollerTicks == 0) || (g_system->getMillis() >= _nextScrollerTicks)) { + // Handle scroll up/down action + _nextScrollerTicks = g_system->getMillis() + SCROLLER_DELAY; + + if ((_highlightedElement == SCROLL_UP) && (_topIndex > 0)) + --_topIndex; + if ((_highlightedElement == SCROLL_DOWN) && (_topIndex < (int)(_inventoryList.size() - 1))) + ++_topIndex; + } + } + return true; + + case MEVENT_LEFT_DRAG: + // Left mouse drag + // Handle the the the scroller area that can be dragged to adjust the top displayed index + if (_highlightedElement == SCROLL_SCROLLER) { + // Calculate the new top index based on the Y position + const Common::Rect r(_screenObjects[SCROLL_SCROLLER]); + _topIndex = CLIP((int)(_inventoryList.size() - 1) * (y - r.top - 2) / (r.height() - 5), + 0, (int)_inventoryList.size() - 1); + } + return true; + + case KEVENT_KEY: + if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) + handleCheatKey(param1); + handleKeypress(param1); + return true; + + default: + break; + } + + return false; } -MadsView::~MadsView() { - delete _sceneAnimation; +bool MadsInterfaceView::handleCheatKey(int32 keycode) { + switch (keycode) { + case Common::KEYCODE_SPACE: + // TODO: Move player to current destination + return true; + + case Common::KEYCODE_t | (Common::KEYCODE_LALT): + case Common::KEYCODE_t | (Common::KEYCODE_RALT): + { + // Teleport to room + //Scene *sceneView = (Scene *)vm->_viewManager->getView(VIEWID_SCENE); + + + return true; + } + + default: + break; + } + + return false; } -void MadsView::refresh() { - // Draw any sprites - _spriteSlots.drawBackground(); +const char *CHEAT_SEQUENCE = "widepipe"; - // Process dirty areas - _textDisplay.setDirtyAreas(); +bool MadsInterfaceView::handleKeypress(int32 keycode) { + int flags = keycode >> 24; + int kc = keycode & 0xffff; - // Merge any identified dirty areas - _dirtyAreas.merge(1, DIRTY_AREAS_SIZE); - - // Copy dirty areas to the main display surface - _dirtyAreas.copy(_view, _bgSurface); + // Capitalise the letter if necessary + if (_cheatKeyCtr < CHEAT_SEQUENCE_MAX) { + if ((flags & Common::KBD_CTRL) && (kc == CHEAT_SEQUENCE[_cheatKeyCtr])) { + ++_cheatKeyCtr; + if (_cheatKeyCtr == CHEAT_SEQUENCE_MAX) + Dialog::display(_vm, 22, cheatingEnabledDesc); + return true; + } else { + _cheatKeyCtr = 0; + } + } - // Handle dirty areas for foreground objects - _spriteSlots.setDirtyAreas(); - _textDisplay.setDirtyAreas2(); - _dirtyAreas.merge(1, DIRTY_AREAS_SIZE); + // Handle the various keys + if ((keycode == Common::KEYCODE_ESCAPE) || (keycode == Common::KEYCODE_F1)) { + // Game menu + _madsVm->globals()->dialogType = DIALOG_GAME_MENU; + leaveScene(); + return false; + } else if (flags & Common::KBD_CTRL) { + // Handling of the different control key combinations + switch (kc) { + case Common::KEYCODE_i: + // Mouse to inventory + warning("TODO: Mouse to inventory"); + break; - // Draw foreground sprites - _spriteSlots.drawForeground(_view); + case Common::KEYCODE_k: + // Toggle hotspots + warning("TODO: Toggle hotspots"); + break; - // Draw text elements onto the view - _textDisplay.draw(_view); + case Common::KEYCODE_p: + // Player stats + warning("TODO: Player stats"); + break; - // Remove any sprite slots that are no longer needed - _spriteSlots.cleanUp(); + case Common::KEYCODE_q: + // Quit game + break; - // Deactivate any text display entries that are no longer needed - _textDisplay.cleanUp(); + case Common::KEYCODE_s: + // Activate sound + warning("TODO: Activate sound"); + break; + + case Common::KEYCODE_u: + // Rotate player + warning("TODO: Rotate player"); + break; + + case Common::KEYCODE_v: { + // Release version + Dialog *dlg = new Dialog(_vm, GameReleaseInfoStr, GameReleaseTitleStr); + _vm->_viewManager->addView(dlg); + _vm->_viewManager->moveToFront(dlg); + return false; + } + + default: + break; + } + } else if ((flags & Common::KBD_ALT) && (kc == Common::KEYCODE_q)) { + // Quit Game + + } else { + // Standard keypresses + switch (kc) { + case Common::KEYCODE_F2: + // Save game + _madsVm->globals()->dialogType = DIALOG_SAVE; + leaveScene(); + break; + case Common::KEYCODE_F3: + // Restore game + _madsVm->globals()->dialogType = DIALOG_RESTORE; + leaveScene(); + break; + } + } +//DIALOG_OPTIONS + return false; +} + +void MadsInterfaceView::leaveScene() { + // Close the scene + View *view = _madsVm->_viewManager->getView(VIEWID_SCENE); + _madsVm->_viewManager->deleteView(view); } } // End of namespace M4 diff --git a/engines/m4/mads_views.h b/engines/m4/mads_views.h index 98944e6468..926702a80b 100644 --- a/engines/m4/mads_views.h +++ b/engines/m4/mads_views.h @@ -34,24 +34,18 @@ namespace M4 { +#define MADS_SURFACE_HEIGHT 156 +#define MADS_SCREEN_HEIGHT 200 +#define MADS_Y_OFFSET ((MADS_SCREEN_HEIGHT - MADS_SURFACE_HEIGHT) / 2) + class MadsView; enum AbortTimerMode {ABORTMODE_0 = 0, ABORTMODE_1 = 1, ABORTMODE_2 = 2}; -class SpriteSlotSubset { -public: - int spriteListIndex; - int frameNumber; - int xp; - int yp; - int depth; - int scale; -}; - class MadsSpriteSlot { public: - int spriteType; - int seqIndex; + int spriteId; + int timerIndex; int spriteListIndex; int frameNumber; int xp; @@ -60,28 +54,20 @@ public: int scale; MadsSpriteSlot() { } - - bool operator==(const SpriteSlotSubset &other) const; - void copy(const SpriteSlotSubset &other); }; #define SPRITE_SLOTS_SIZE 50 -enum SpriteIdSpecial { - BACKGROUND_SPRITE = -4, FULL_SCREEN_REFRESH = -2, EXPIRED_SPRITE = -1, SPRITE_ZERO = 0, FOREGROUND_SPRITE = 1, - SPRITE_FOUR = 4 -}; +typedef Common::Array<Common::SharedPtr<SpriteAsset> > SpriteList; class MadsSpriteSlots { private: - MadsView &_owner; Common::Array<MadsSpriteSlot> _entries; - Common::Array<SpriteAsset *> _sprites; + SpriteList _sprites; public: int startIndex; - MadsSpriteSlots(MadsView &owner); - ~MadsSpriteSlots(); + MadsSpriteSlots(); MadsSpriteSlot &operator[](int idx) { assert(idx < SPRITE_SLOTS_SIZE); @@ -89,19 +75,18 @@ public: } SpriteAsset &getSprite(int idx) { assert(idx < (int)_sprites.size()); - return *_sprites[idx]; + return *_sprites[idx].get(); } int getIndex(); int addSprites(const char *resName); - void deleteSprites(int listIndex); - void clear(); - void deleteTimer(int seqIndex); + void clear() { + startIndex = 0; + _sprites.clear(); + } + void deleteTimer(int timerIndex); - void drawBackground(); - void drawForeground(View *view); - void setDirtyAreas(); - void fullRefresh(); + void draw(View *view); void cleanUp(); }; @@ -123,10 +108,9 @@ public: class MadsTextDisplay { private: - MadsView &_owner; Common::Array<MadsTextDisplayEntry> _entries; public: - MadsTextDisplay(MadsView &owner); + MadsTextDisplay(); MadsTextDisplayEntry &operator[](int idx) { assert(idx < TEXT_DISPLAY_SIZE); @@ -141,18 +125,15 @@ public: int add(int xp, int yp, uint fontColour, int charSpacing, const char *msg, Font *font); void clear(); void draw(View *view); - void setDirtyAreas(); - void setDirtyAreas2(); void cleanUp(); }; #define TIMED_TEXT_SIZE 10 -#define INDEFINITE_TIMEOUT 9999999 +#define TEXT_4A_SIZE 30 -enum KernelMessageFlags {KMSG_QUOTED = 1, KMSG_OWNER_TIMEOUT = 2, KMSG_SEQ_ENTRY = 4, KMSG_SCROLL = 8, KMSG_RIGHT_ALIGN = 0x10, - KMSG_CENTER_ALIGN = 0x20, KMSG_EXPIRE = 0x40, KMSG_ACTIVE = 0x80}; +enum KernelMessageFlags {KMSG_1 = 1, KMSG_2 = 2, KMSG_4 = 4, KMSG_8 = 8, KMSG_20 = 0x20, KMSG_40 = 0x40, KMSG_ACTIVE = 0x80}; -class MadsKernelMessageEntry { +class MadsKernelMessageListEntry { public: uint8 flags; int sequenceIndex; @@ -163,35 +144,31 @@ public: Common::Point position; int textDisplayIndex; int msgOffset; - int numTicks; + int field_E; uint32 frameTimer2; uint32 frameTimer; uint32 timeout; - int abortTimers; + bool field_1C; AbortTimerMode abortMode; uint16 actionNouns[3]; - char msg[100]; + const char *msg; }; class MadsKernelMessageList { private: MadsView &_owner; - Common::Array<MadsKernelMessageEntry> _entries; + Common::Array<MadsKernelMessageListEntry> _entries; Font *_talkFont; public: - int word_8469E; -public: MadsKernelMessageList(MadsView &owner); void clear(); - int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 abortTimers, uint32 timeout, const char *msg); - int addQuote(int quoteId, int abortTimers, uint32 timeout); - void scrollMessage(int msgIndex, int numTicks, bool quoted); + int add(const Common::Point &pt, uint fontColour, uint8 flags, uint8 v2, uint32 timeout, const char *msg); + int addQuote(int quoteId, int v2, uint32 timeout); + void unk1(int msgIndex, int v1, int v2); void setSeqIndex(int msgIndex, int seqIndex); void remove(int msgIndex); void reset(); - void update(); - void processText(int msgIndex); }; class ScreenObjectEntry { @@ -227,7 +204,7 @@ public: class DynamicHotspot { public: bool active; - int seqIndex; + int timerIndex; Common::Rect bounds; Common::Point pos; int facing; @@ -252,47 +229,13 @@ public: MadsDynamicHotspots(MadsView &owner); DynamicHotspot &operator[](uint idx) { return _entries[idx]; } - int add(int descId, int field14, int seqIndex, const Common::Rect &bounds); + int add(int descId, int field14, int timerIndex, const Common::Rect &bounds); int setPosition(int index, int xp, int yp, int facing); int set17(int index, int v); void remove(int index); void reset(); }; -class MadsDirtyArea { -public: - Common::Rect bounds; - Common::Rect bounds2; - bool textActive; - bool active; - - MadsDirtyArea() { active = false; } - void setArea(int width, int height, int maxWidth, int maxHeight); -}; - -#define DIRTY_AREAS_SIZE 90 -#define DIRTY_AREAS_TEXT_DISPLAY_IDX 50 - -class MadsDirtyAreas { -private: - MadsView &_owner; - Common::Array<MadsDirtyArea> _entries; -public: - MadsDirtyAreas(MadsView &owner); - - MadsDirtyArea &operator[](uint idx) { - assert(idx < _entries.size()); - return _entries[idx]; - } - - void setSpriteSlot(int dirtyIdx, const MadsSpriteSlot &spriteSlot); - void setTextDisplay(int dirtyIdx, const MadsTextDisplayEntry &textDisplay); - void merge(int startIndex, int count); - bool intersects(int idx1, int idx2); - void mergeAreas(int idx1, int idx2); - void copy(M4Surface *dest, M4Surface *src); -}; - enum SpriteAnimType {ANIMTYPE_CYCLED = 1, ANIMTYPE_REVERSIBLE = 2}; enum SequenceSubEntryMode {SM_0 = 0, SM_1 = 1, SM_FRAME_INDEX = 2}; @@ -323,10 +266,12 @@ struct MadsSequenceEntry { int scale; int dynamicHotspotIndex; - bool nonFixed; + int field_12; int field_13; - Common::Point msgPos; + int width; + int height; + int triggerCountdown; bool doneFlag; MadsSequenceSubEntries entries; @@ -351,42 +296,25 @@ public: void clear(); bool addSubEntry(int index, SequenceSubEntryMode mode, int frameIndex, int abortVal); int add(int spriteListIndex, int v0, int v1, int triggerCountdown, int delayTicks, int extraTicks, int numTicks, - int msgX, int msgY, bool nonFixed, char scale, uint8 depth, int frameInc, SpriteAnimType animType, + int height, int width, char field_12, char scale, uint8 depth, int frameInc, SpriteAnimType animType, int numSprites, int frameStart); - void remove(int seqIndex); - void setSpriteSlot(int seqIndex, MadsSpriteSlot &spriteSlot); - bool loadSprites(int seqIndex); + void remove(int timerIndex); + void setSpriteSlot(int timerIndex, MadsSpriteSlot &spriteSlot); + bool loadSprites(int timerIndex); void tick(); void delay(uint32 v1, uint32 v2); - void setAnimRange(int seqIndex, int startVal, int endVal); - void scan(); }; -class Animation { -protected: - MadsM4Engine *_vm; -public: - Animation(MadsM4Engine *vm); - virtual ~Animation(); - virtual void initialise(const Common::String &filename, uint16 flags, M4Surface *walkSurface, M4Surface *sceneSurface) = 0; - virtual void load(const Common::String &filename, int v0) = 0; - virtual void update() = 0; - virtual void setCurrentFrame(int frameNumber) = 0; -}; - - class MadsView { private: View *_view; public: - Animation *_sceneAnimation; MadsSpriteSlots _spriteSlots; MadsTextDisplay _textDisplay; MadsKernelMessageList _kernelMessages; ScreenObjects _screenObjects; MadsDynamicHotspots _dynamicHotspots; MadsSequenceList _sequenceList; - MadsDirtyAreas _dirtyAreas; int _textSpacing; int _ticksAmount; @@ -395,17 +323,62 @@ public: int8 _abortTimers2; AbortTimerMode _abortTimersMode; AbortTimerMode _abortTimersMode2; - Common::Point _posAdjust; - - M4Surface *_depthSurface; - M4Surface *_bgSurface; public: MadsView(View *view); - ~MadsView(); void refresh(); }; +#define CHEAT_SEQUENCE_MAX 8 + +class IntegerList : public Common::Array<int> { +public: + int indexOf(int v) { + for (uint i = 0; i < size(); ++i) + if (operator [](i) == v) + return i; + return -1; + } +}; + +enum InterfaceFontMode {ITEM_NORMAL, ITEM_HIGHLIGHTED, ITEM_SELECTED}; + +enum InterfaceObjects {ACTIONS_START = 0, SCROLL_UP = 10, SCROLL_SCROLLER = 11, SCROLL_DOWN = 12, + INVLIST_START = 13, VOCAB_START = 18}; + +class MadsInterfaceView : public GameInterfaceView { +private: + IntegerList _inventoryList; + RectList _screenObjects; + int _highlightedElement; + int _topIndex; + uint32 _nextScrollerTicks; + int _cheatKeyCtr; + + // Object display fields + int _selectedObject; + SpriteAsset *_objectSprites; + RGBList *_objectPalData; + int _objectFrameNumber; + + void setFontMode(InterfaceFontMode newMode); + bool handleCheatKey(int32 keycode); + bool handleKeypress(int32 keycode); + void leaveScene(); +public: + MadsInterfaceView(MadsM4Engine *vm); + ~MadsInterfaceView(); + + virtual void initialise(); + virtual void setSelectedObject(int objectNumber); + virtual void addObjectToInventory(int objectNumber); + int getSelectedObject() { return _selectedObject; } + int getInventoryObject(int objectIndex) { return _inventoryList[objectIndex]; } + + void onRefresh(RectList *rects, M4Surface *destSurface); + bool onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents); +}; + } #endif diff --git a/engines/m4/sprite.h b/engines/m4/sprite.h index 49a96a6c4a..022f3bbc4f 100644 --- a/engines/m4/sprite.h +++ b/engines/m4/sprite.h @@ -115,6 +115,18 @@ public: void loadDeltaRle(Common::SeekableReadStream* rleData, int destX, int destY); void loadMadsSprite(Common::SeekableReadStream* source); + void draw1(M4Surface *destSurface, int scale, int depth, int xp, int yp) { + // TODO: Properly implement drawing + copyTo(destSurface, xp, yp, 0); + } + void draw2(M4Surface *destSurface, int depth, int xp, int yp) { + // TODO: Properly implement drawing + copyTo(destSurface, xp, yp, 0); + } + void draw3(M4Surface *destSurface, int xp, int yp) { + // TODO: Properly implement drawing + copyTo(destSurface, xp, yp, 0); + } byte getTransparentColor() const; protected: }; diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index 149b6b6eba..389665db39 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -30,7 +30,7 @@ #include "mohawk/riven.h" #include "mohawk/livingbooks.h" #include "mohawk/sound.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" namespace Mohawk { diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index 7f2e0cb312..a7b1fe7fae 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -341,24 +341,6 @@ static const MohawkGameDescription gameDescriptions[] = { 0, }, - // Myst Masterpiece Edition - // French Windows - // From gamin (Included in "Myst: La Trilogie") - { - { - "myst", - "Masterpiece Edition", - AD_ENTRY1("MYST.DAT", "aea81633b2d2ae498f09072fb87263b6"), - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - Common::GUIO_NONE - }, - GType_MYST, - GF_ME, - 0, - }, - // Riven: The Sequel to Myst // Version 1.0 (5CD) // From clone2727 @@ -450,24 +432,6 @@ static const MohawkGameDescription gameDescriptions[] = { }, // Riven: The Sequel to Myst - // Version ? (DVD, From "Myst: La Trilogie") - // From gamin - { - { - "riven", - "", - AD_ENTRY1("a_Data.MHK", "aff2a384aaa9a0e0ec51010f708c5c04"), - Common::FR_FRA, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - Common::GUIO_NONE - }, - GType_RIVEN, - GF_DVD, - 0, - }, - - // Riven: The Sequel to Myst // Version ? (Demo, From "Prince of Persia Collector's Edition") // From Clone2727 { diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index 2ddfb47575..35691c36aa 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -78,8 +78,9 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : _vm(vm) { error("Myst requires greater than 256 colors to run"); if (_vm->getFeatures() & GF_ME) { - _jpegDecoder = new Graphics::JPEGDecoder(); - _pictDecoder = new Graphics::PictDecoder(_pixelFormat); + // We want to delete our own JPEG surfaces, so don't free after use. + _jpegDecoder = new JPEGDecoder(false); + _pictDecoder = new MystPICT(_jpegDecoder); } else { _jpegDecoder = NULL; _pictDecoder = NULL; @@ -151,10 +152,9 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { for (uint32 i = 0; i < _pictureFile.pictureCount; i++) if (_pictureFile.entries[i].id == image) { - if (_pictureFile.entries[i].type == 0) { - Graphics::Surface *jpegSurface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)); - surface->copyFrom(*jpegSurface); - } else if (_pictureFile.entries[i].type == 1) + if (_pictureFile.entries[i].type == 0) + surface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)); + else if (_pictureFile.entries[i].type == 1) surface = _pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size)); else error ("Unknown Picture File type %d", _pictureFile.entries[i].type); diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index dd1764e6d6..8d28e1ff4b 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -27,11 +27,11 @@ #define MOHAWK_GRAPHICS_H #include "mohawk/bitmap.h" +#include "mohawk/jpeg.h" #include "mohawk/livingbooks.h" +#include "mohawk/myst_pict.h" #include "common/file.h" -#include "graphics/pict.h" -#include "graphics/video/codecs/mjpeg.h" namespace Mohawk { @@ -96,16 +96,16 @@ public: void loadExternalPictureFile(uint16 stack); void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); void copyImageToScreen(uint16 image, Common::Rect dest); - void showCursor(); - void hideCursor(); + void showCursor(void); + void hideCursor(void); void changeCursor(uint16); void drawRect(Common::Rect rect, bool active); private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; - Graphics::PictDecoder *_pictDecoder; - Graphics::JPEGDecoder *_jpegDecoder; + MystPICT *_pictDecoder; + JPEGDecoder *_jpegDecoder; Graphics::PixelFormat _pixelFormat; struct PictureFile { diff --git a/engines/mohawk/jpeg.cpp b/engines/mohawk/jpeg.cpp new file mode 100644 index 0000000000..07ec54dfea --- /dev/null +++ b/engines/mohawk/jpeg.cpp @@ -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$ + * + */ + +#include "common/system.h" +#include "graphics/conversion.h" // For YUV2RGB + +#include "mohawk/jpeg.h" + +namespace Mohawk { + +JPEGDecoder::JPEGDecoder(bool freeSurfaceAfterUse) : Graphics::Codec(), _freeSurfaceAfterUse(freeSurfaceAfterUse) { + _jpeg = new Graphics::JPEG(); + _pixelFormat = g_system->getScreenFormat(); + _surface = NULL; +} + +JPEGDecoder::~JPEGDecoder() { + delete _jpeg; + + if (_surface) { + _surface->free(); + delete _surface; + } +} + +Graphics::Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream* stream) { + _jpeg->read(stream); + Graphics::Surface *ySurface = _jpeg->getComponent(1); + Graphics::Surface *uSurface = _jpeg->getComponent(2); + Graphics::Surface *vSurface = _jpeg->getComponent(3); + + Graphics::Surface *destSurface = NULL; + + // If we should free the surface after use, use the internal _surface storage + // (this should be used when using as a Codec, as the Codecs should free their + // surfaces when deleting the Codec object). Otherwise, create a new Surface + // as the destination. + if (_freeSurfaceAfterUse) { + if (!_surface) { + _surface = new Graphics::Surface(); + _surface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel); + } + destSurface = _surface; + } else { + destSurface = new Graphics::Surface(); + destSurface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel); + } + + assert(destSurface); + + for (uint16 i = 0; i < destSurface->h; i++) { + for (uint16 j = 0; j < destSurface->w; j++) { + byte r = 0, g = 0, b = 0; + Graphics::YUV2RGB(*((byte *)ySurface->getBasePtr(j, i)), *((byte *)uSurface->getBasePtr(j, i)), *((byte *)vSurface->getBasePtr(j, i)), r, g, b); + if (_pixelFormat.bytesPerPixel == 2) + *((uint16 *)destSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); + else + *((uint32 *)destSurface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); + } + } + + return destSurface; +} + +} // End of namespace Mohawk diff --git a/graphics/video/codecs/mjpeg.h b/engines/mohawk/jpeg.h index ab364fb5be..ec87b1e7af 100644 --- a/graphics/video/codecs/mjpeg.h +++ b/engines/mohawk/jpeg.h @@ -23,8 +23,8 @@ * */ -#ifndef GRAPHICS_MJPEG_H -#define GRAPHICS_MJPEG_H +#ifndef MOHAWK_JPEG_H +#define MOHAWK_JPEG_H #include "common/scummsys.h" #include "common/stream.h" @@ -33,26 +33,27 @@ #include "graphics/jpeg.h" #include "graphics/pixelformat.h" -namespace Graphics { +namespace Mohawk { -// Motion JPEG Decoder +// Mohawk's JPEG Decoder // Basically a wrapper around JPEG which converts to RGB and also functions // as a Codec. -class JPEGDecoder : public Codec { +class JPEGDecoder : public Graphics::Codec { public: - JPEGDecoder(); + JPEGDecoder(bool freeSurfaceAfterUse); ~JPEGDecoder(); - Surface *decodeImage(Common::SeekableReadStream *stream); - PixelFormat getPixelFormat() const { return _pixelFormat; } + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: - PixelFormat _pixelFormat; - JPEG *_jpeg; - Surface *_surface; + Graphics::PixelFormat _pixelFormat; + Graphics::JPEG *_jpeg; + Graphics::Surface *_surface; + bool _freeSurfaceAfterUse; }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk index bb79d4abac..b224a8b143 100644 --- a/engines/mohawk/module.mk +++ b/engines/mohawk/module.mk @@ -6,9 +6,11 @@ MODULE_OBJS = \ detection.o \ dialogs.o \ graphics.o \ + jpeg.o \ livingbooks.o \ mohawk.o \ myst.o \ + myst_pict.o \ myst_vars.o \ myst_saveload.o \ myst_scripts.o \ @@ -20,7 +22,13 @@ MODULE_OBJS = \ riven_scripts.o \ riven_vars.o \ sound.o \ - video.o + video/cinepak.o \ + video/qdm2.o \ + video/qtrle.o \ + video/qt_player.o \ + video/rpza.o \ + video/smc.o \ + video/video.o # This module can be built as a plugin diff --git a/engines/mohawk/mohawk.cpp b/engines/mohawk/mohawk.cpp index 6b4d8bb2d2..5bde6bbeec 100644 --- a/engines/mohawk/mohawk.cpp +++ b/engines/mohawk/mohawk.cpp @@ -35,7 +35,7 @@ #include "mohawk/mohawk.h" #include "mohawk/dialogs.h" #include "mohawk/sound.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" #include "sound/mixer.h" diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 9ff301c129..d1ef3b2137 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -33,7 +33,7 @@ #include "mohawk/dialogs.h" #include "mohawk/resource.h" #include "mohawk/resource_cache.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" namespace Mohawk { diff --git a/graphics/pict.cpp b/engines/mohawk/myst_pict.cpp index f0dd7bbc6f..794ec9b87f 100644 --- a/graphics/pict.cpp +++ b/engines/mohawk/myst_pict.cpp @@ -23,38 +23,26 @@ * */ -#include "common/stream.h" +#include "common/system.h" -#include "graphics/conversion.h" -#include "graphics/jpeg.h" -#include "graphics/pict.h" -#include "graphics/surface.h" +#include "mohawk/myst_pict.h" -namespace Graphics { +namespace Mohawk { // The PICT code is based off of the QuickDraw specs: -// http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-461.html -// http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-269.html +// http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-461.html -PictDecoder::PictDecoder(PixelFormat pixelFormat) { - _jpeg = new JPEG(); - _pixelFormat = pixelFormat; +MystPICT::MystPICT(JPEGDecoder *jpegDecoder) { + _jpegDecoder = jpegDecoder; + _pixelFormat = g_system->getScreenFormat(); } -PictDecoder::~PictDecoder() { - delete _jpeg; -} - -Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *palette) { - assert(stream); - - uint16 fileSize = stream->readUint16BE(); +Graphics::Surface *MystPICT::decodeImage(Common::SeekableReadStream *stream) { + // Skip initial 512 bytes (all 0's) + stream->seek(512, SEEK_CUR); - // If we have no file size here, we probably have a PICT from a file - // and not a resource. The other two bytes are the fileSize which we - // don't actually need (and already read if from a resource). - if (!fileSize) - stream->seek(512 + 2); + // Read in the first part of the header + /* uint16 fileSize = */ stream->readUint16BE(); _imageRect.top = stream->readUint16BE(); _imageRect.left = stream->readUint16BE(); @@ -64,12 +52,11 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale Graphics::Surface *image = new Graphics::Surface(); image->create(_imageRect.width(), _imageRect.height(), _pixelFormat.bytesPerPixel); - _isPaletted = false; // NOTE: This is only a subset of the full PICT format. // - Only V2 Images Supported - // - CompressedQuickTime (JPEG) compressed data is supported - // - DirectBitsRect/PackBitsRect compressed data is supported + // - JPEG Chunks are Supported + // - DirectBitsRect Chunks are Supported for (uint32 opNum = 0; !stream->eos() && !stream->err() && stream->pos() < stream->size(); opNum++) { uint16 opcode = stream->readUint16BE(); debug(2, "Found PICT opcode %04x", opcode); @@ -95,11 +82,8 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale error ("Unknown PICT version"); } else if (opcode == 0x001E) { // DefHilite // Ignore, Contains no Data - } else if (opcode == 0x0098) { // PackBitsRect - decodeDirectBitsRect(stream, image, true); - _isPaletted = true; } else if (opcode == 0x009A) { // DirectBitsRect - decodeDirectBitsRect(stream, image, false); + decodeDirectBitsRect(stream, image); } else if (opcode == 0x00A1) { // LongComment stream->readUint16BE(); uint16 dataSize = stream->readUint16BE(); @@ -122,71 +106,59 @@ Surface *PictDecoder::decodeImage(Common::SeekableReadStream *stream, byte *pale decodeCompressedQuickTime(stream, image); break; } else { - warning("Unknown PICT opcode %04x", opcode); + error ("Unknown PICT opcode %04x", opcode); } } - // If we got a palette throughout this nonsense, go and grab it - if (palette && _isPaletted) - memcpy(palette, _palette, 256 * 4); - return image; } -PictDecoder::PixMap PictDecoder::readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr) { - PixMap pixMap; - pixMap.baseAddr = hasBaseAddr ? stream->readUint32BE() : 0; - pixMap.rowBytes = stream->readUint16BE() & 0x3fff; - pixMap.bounds.top = stream->readUint16BE(); - pixMap.bounds.left = stream->readUint16BE(); - pixMap.bounds.bottom = stream->readUint16BE(); - pixMap.bounds.right = stream->readUint16BE(); - pixMap.pmVersion = stream->readUint16BE(); - pixMap.packType = stream->readUint16BE(); - pixMap.packSize = stream->readUint32BE(); - pixMap.hRes = stream->readUint32BE(); - pixMap.vRes = stream->readUint32BE(); - pixMap.pixelType = stream->readUint16BE(); - pixMap.pixelSize = stream->readUint16BE(); - pixMap.cmpCount = stream->readUint16BE(); - pixMap.cmpSize = stream->readUint16BE(); - pixMap.planeBytes = stream->readUint32BE(); - pixMap.pmTable = stream->readUint32BE(); - pixMap.pmReserved = stream->readUint32BE(); - return pixMap; -} - struct DirectBitsRectData { - PictDecoder::PixMap pixMap; + // PixMap + struct { + uint32 baseAddr; + uint16 rowBytes; + Common::Rect bounds; + uint16 pmVersion; + uint16 packType; + uint32 packSize; + uint32 hRes; + uint32 vRes; + uint16 pixelType; + uint16 pixelSize; + uint16 cmpCount; + uint16 cmpSize; + uint32 planeBytes; + uint32 pmTable; + uint32 pmReserved; + } pixMap; Common::Rect srcRect; Common::Rect dstRect; uint16 mode; }; -void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surface *image, bool hasPalette) { - static const PixelFormat directBitsFormat16 = PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - - // Clear the palette - memset(_palette, 0, sizeof(_palette)); +void MystPICT::decodeDirectBitsRect(Common::SeekableReadStream *stream, Graphics::Surface *image) { + static const Graphics::PixelFormat directBitsFormat16 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); DirectBitsRectData directBitsData; - directBitsData.pixMap = readPixMap(stream, !hasPalette); - - // Read in the palette if there is one present - if (hasPalette) { - // See http://developer.apple.com/legacy/mac/library/documentation/mac/QuickDraw/QuickDraw-267.html - stream->readUint32BE(); // seed - stream->readUint16BE(); // flags - uint16 colorCount = stream->readUint16BE() + 1; - - for (uint32 i = 0; i < colorCount; i++) { - stream->readUint16BE(); - _palette[i * 4] = stream->readUint16BE() >> 8; - _palette[i * 4 + 1] = stream->readUint16BE() >> 8; - _palette[i * 4 + 2] = stream->readUint16BE() >> 8; - } - } - + directBitsData.pixMap.baseAddr = stream->readUint32BE(); + directBitsData.pixMap.rowBytes = stream->readUint16BE() & 0x3fff; + directBitsData.pixMap.bounds.top = stream->readUint16BE(); + directBitsData.pixMap.bounds.left = stream->readUint16BE(); + directBitsData.pixMap.bounds.bottom = stream->readUint16BE(); + directBitsData.pixMap.bounds.right = stream->readUint16BE(); + directBitsData.pixMap.pmVersion = stream->readUint16BE(); + directBitsData.pixMap.packType = stream->readUint16BE(); + directBitsData.pixMap.packSize = stream->readUint32BE(); + directBitsData.pixMap.hRes = stream->readUint32BE(); + directBitsData.pixMap.vRes = stream->readUint32BE(); + directBitsData.pixMap.pixelType = stream->readUint16BE(); + directBitsData.pixMap.pixelSize = stream->readUint16BE(); + directBitsData.pixMap.cmpCount = stream->readUint16BE(); + directBitsData.pixMap.cmpSize = stream->readUint16BE(); + directBitsData.pixMap.planeBytes = stream->readUint32BE(); + directBitsData.pixMap.pmTable = stream->readUint32BE(); + directBitsData.pixMap.pmReserved = stream->readUint32BE(); directBitsData.srcRect.top = stream->readUint16BE(); directBitsData.srcRect.left = stream->readUint16BE(); directBitsData.srcRect.bottom = stream->readUint16BE(); @@ -197,44 +169,34 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa directBitsData.dstRect.right = stream->readUint16BE(); directBitsData.mode = stream->readUint16BE(); - if (directBitsData.pixMap.pixelSize != 8 && directBitsData.pixMap.pixelSize != 16 && directBitsData.pixMap.pixelSize != 32) - error("Unhandled DirectBitsRect bitsPerPixel %d", directBitsData.pixMap.pixelSize); + if (directBitsData.pixMap.pixelSize != 16 && directBitsData.pixMap.pixelSize != 32) + error("Unhandled directBitsRect bitsPerPixel %d", directBitsData.pixMap.pixelSize); - byte bytesPerPixel = (directBitsData.pixMap.pixelSize == 32) ? 3 : directBitsData.pixMap.pixelSize / 8; + byte bytesPerPixel = (directBitsData.pixMap.pixelSize == 16) ? 2 : 3; byte *buffer = new byte[image->w * image->h * bytesPerPixel]; // Read in amount of data per row for (uint16 i = 0; i < directBitsData.pixMap.bounds.height(); i++) { - // NOTE: Compression 0 is "default". The format in SCI games is packed when 0. - // In the future, we may need to have something to set the "default" packing - // format, but this is good for now. - - if (directBitsData.pixMap.packType == 1 || directBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte (on 24-bit) - // TODO: Finish this. Hasn't been needed (yet). - error("Unpacked DirectBitsRect data (padded)"); - } else if (directBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte (on 24-bit) - // TODO: Finish this. Hasn't been needed (yet). - error("Unpacked DirectBitsRect data (not padded)"); - } else if (directBitsData.pixMap.packType == 0 || directBitsData.pixMap.packType > 2) { // Packed + if (directBitsData.pixMap.packType == 1 || directBitsData.pixMap.rowBytes < 8) { // Unpacked, Pad-Byte + error("Pack Type = %d, Row Bytes = %d", directBitsData.pixMap.packType, directBitsData.pixMap.rowBytes); + // TODO + } else if (directBitsData.pixMap.packType == 2) { // Unpacked, No Pad-Byte + error("Pack Type = 2"); + // TODO + } else if (directBitsData.pixMap.packType > 2) { // Packed uint16 byteCount = (directBitsData.pixMap.rowBytes > 250) ? stream->readUint16BE() : stream->readByte(); decodeDirectBitsLine(buffer + i * image->w * bytesPerPixel, directBitsData.pixMap.rowBytes, stream->readStream(byteCount), bytesPerPixel); } } - - if (bytesPerPixel == 1) { - // Just copy to the image - memcpy(image->pixels, buffer, image->w * image->h); - } else if (bytesPerPixel == 2) { + + if (bytesPerPixel == 2) { // Convert from 16-bit to whatever surface we need for (uint16 y = 0; y < image->h; y++) { for (uint16 x = 0; x < image->w; x++) { byte r = 0, g = 0, b = 0; uint32 color = READ_BE_UINT16(buffer + (y * image->w + x) * bytesPerPixel); directBitsFormat16.colorToRGB(color, r, g, b); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); } } } else { @@ -244,10 +206,7 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa byte r = *(buffer + y * image->w * 3 + x); byte g = *(buffer + y * image->w * 3 + image->w + x); byte b = *(buffer + y * image->w * 3 + image->w * 2 + x); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); + *((uint16 *)image->getBasePtr(x, y)) = _pixelFormat.RGBToColor(r, g, b); } } } @@ -255,7 +214,7 @@ void PictDecoder::decodeDirectBitsRect(Common::SeekableReadStream *stream, Surfa delete[] buffer; } -void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel) { +void MystPICT::decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel) { uint32 dataDecoded = 0; byte bytesPerDecode = (bytesPerPixel == 2) ? 2 : 1; @@ -297,37 +256,17 @@ void PictDecoder::decodeDirectBitsLine(byte *out, uint32 length, Common::Seekabl // http://developer.apple.com/documentation/QuickTime/Rm/CompressDecompress/ImageComprMgr/F-Chapter/6WorkingwiththeImage.html // I'm just ignoring that because Myst ME uses none of that extra stuff. The offset is always the same. -void PictDecoder::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image) { +void MystPICT::decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image) { uint32 dataSize = stream->readUint32BE(); uint32 startPos = stream->pos(); - Common::SeekableReadStream *jpegStream = new Common::SeekableSubReadStream(stream, stream->pos() + 156, stream->pos() + dataSize); - - if (!_jpeg->read(jpegStream)) - error("PictDecoder::decodeCompressedQuickTime(): Could not decode JPEG data"); - - Surface *yComponent = _jpeg->getComponent(1); - Surface *uComponent = _jpeg->getComponent(2); - Surface *vComponent = _jpeg->getComponent(3); + Graphics::Surface *jpegImage = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(stream, stream->pos() + 156, stream->pos() + dataSize)); + stream->seek(startPos + dataSize); - Surface jpegImage; - jpegImage.create(yComponent->w, yComponent->h, _pixelFormat.bytesPerPixel); + image->copyFrom(*jpegImage); - for (uint16 i = 0; i < jpegImage.h; i++) { - for (uint16 j = 0; j < jpegImage.w; j++) { - byte r = 0, g = 0, b = 0; - YUV2RGB(*((byte *)yComponent->getBasePtr(j, i)), *((byte *)uComponent->getBasePtr(j, i)), *((byte *)vComponent->getBasePtr(j, i)), r, g, b); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)jpegImage.getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)jpegImage.getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); - } - } - - image->copyFrom(jpegImage); - stream->seek(startPos + dataSize); - jpegImage.free(); - delete jpegStream; + jpegImage->free(); + delete jpegImage; } -} // End of namespace Graphics +} // End of namespace Mohawk diff --git a/graphics/pict.h b/engines/mohawk/myst_pict.h index 12681f6128..0684e3352a 100644 --- a/graphics/pict.h +++ b/engines/mohawk/myst_pict.h @@ -23,61 +23,35 @@ * */ -#ifndef GRAPHICS_PICT_H -#define GRAPHICS_PICT_H +#ifndef MYST_PICT_H +#define MYST_PICT_H #include "common/rect.h" #include "common/scummsys.h" - +#include "common/stream.h" #include "graphics/pixelformat.h" +#include "graphics/surface.h" -namespace Common { - class SeekableReadStream; -} - -namespace Graphics { +#include "mohawk/jpeg.h" -class JPEG; -struct Surface; +namespace Mohawk { -class PictDecoder { +class MystPICT { public: - PictDecoder(Graphics::PixelFormat pixelFormat); - ~PictDecoder(); - Surface *decodeImage(Common::SeekableReadStream *stream, byte *palette = 0); - - struct PixMap { - uint32 baseAddr; - uint16 rowBytes; - Common::Rect bounds; - uint16 pmVersion; - uint16 packType; - uint32 packSize; - uint32 hRes; - uint32 vRes; - uint16 pixelType; - uint16 pixelSize; - uint16 cmpCount; - uint16 cmpSize; - uint32 planeBytes; - uint32 pmTable; - uint32 pmReserved; - }; - - static PixMap readPixMap(Common::SeekableReadStream *stream, bool hasBaseAddr = true); + MystPICT(JPEGDecoder *jpegDecoder); + ~MystPICT() {} + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); private: + JPEGDecoder *_jpegDecoder; Common::Rect _imageRect; - PixelFormat _pixelFormat; - JPEG *_jpeg; - byte _palette[256 * 4]; - bool _isPaletted; + Graphics::PixelFormat _pixelFormat; - void decodeDirectBitsRect(Common::SeekableReadStream *stream, Surface *image, bool hasPalette); + void decodeDirectBitsRect(Common::SeekableReadStream *stream, Graphics::Surface *image); void decodeDirectBitsLine(byte *out, uint32 length, Common::SeekableReadStream *data, byte bytesPerPixel); - void decodeCompressedQuickTime(Common::SeekableReadStream *stream, Surface *image); + void decodeCompressedQuickTime(Common::SeekableReadStream *stream, Graphics::Surface *image); }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp index 2f6d178da8..a453bb0985 100644 --- a/engines/mohawk/myst_scripts.cpp +++ b/engines/mohawk/myst_scripts.cpp @@ -27,7 +27,7 @@ #include "mohawk/graphics.h" #include "mohawk/myst_scripts.h" #include "mohawk/sound.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" #include "gui/message.h" diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index c646855bc7..0412144034 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -33,7 +33,7 @@ #include "mohawk/riven_external.h" #include "mohawk/riven_saveload.h" #include "mohawk/dialogs.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" namespace Mohawk { @@ -47,7 +47,6 @@ MohawkEngine_Riven::MohawkEngine_Riven(OSystem *syst, const MohawkGameDescriptio _cardData.hasData = false; _gameOver = false; _activatedSLST = false; - _ignoreNextMouseUp = false; _extrasFile = NULL; // Attempt to let game run from the CDs @@ -148,15 +147,10 @@ Common::Error MohawkEngine_Riven::run() { runHotspotScript(_curHotspot, kMouseDownScript); break; case Common::EVENT_LBUTTONUP: - // See RivenScript::switchCard() for more information on why we sometimes - // disable the next up event. - if (!_ignoreNextMouseUp) { - if (_curHotspot >= 0) - runHotspotScript(_curHotspot, kMouseUpScript); - else - checkInventoryClick(); - } - _ignoreNextMouseUp = false; + if (_curHotspot >= 0) + runHotspotScript(_curHotspot, kMouseUpScript); + else + checkInventoryClick(); break; case Common::EVENT_KEYDOWN: switch (event.kbd.keycode) { @@ -239,7 +233,6 @@ void MohawkEngine_Riven::changeToStack(uint16 n) { // Stop any videos playing _video->stopVideos(); - _video->clearMLST(); // Clear the old stack files out for (uint32 i = 0; i < _mhk.size(); i++) @@ -317,6 +310,7 @@ void MohawkEngine_Riven::refreshCard() { _gfx->clearWaterEffects(); _gfx->_activatedPLSTs.clear(); _video->stopVideos(); + _video->_mlstRecords.clear(); _gfx->drawPLST(1); _activatedSLST = false; diff --git a/engines/mohawk/riven.h b/engines/mohawk/riven.h index 11c3a4c0cb..f014b76fb8 100644 --- a/engines/mohawk/riven.h +++ b/engines/mohawk/riven.h @@ -113,6 +113,7 @@ public: Common::RandomSource *_rnd; Card _cardData; + bool _gameOver; GUI::Debugger *getDebugger(); @@ -146,10 +147,6 @@ private: uint32 *_vars; uint32 _varCount; - // Miscellaneous - bool _gameOver; - bool _ignoreNextMouseUp; - public: Common::SeekableReadStream *getExtrasResource(uint32 tag, uint16 id); bool _activatedSLST; @@ -183,9 +180,6 @@ public: uint32 *getLocalVar(uint32 index); uint32 *matchVarToString(Common::String varName); uint32 *matchVarToString(const char *varName); - - void setGameOver() { _gameOver = true; } - void ignoreNextMouseUp() { _ignoreNextMouseUp = true; } }; } // End of namespace Mohawk diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 4e6bba1c2a..99afacc5ce 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -27,7 +27,7 @@ #include "mohawk/riven.h" #include "mohawk/riven_external.h" #include "mohawk/sound.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" #include "gui/message.h" #include "common/events.h" @@ -210,28 +210,7 @@ void RivenExternal::runEndGame(uint16 video) { _vm->_video->playMovieBlocking(video); // TODO: Play until the last frame and then run the credits - _vm->setGameOver(); -} - -void RivenExternal::runDomeButtonMovie() { - // This command just plays the video of the button moving down and up. - _vm->_video->playMovieBlocking(2); -} - -void RivenExternal::runDomeCheck() { - // Check if we clicked while the golden frame was showing - - VideoHandle video = _vm->_video->findVideoHandle(1); - assert(video != NULL_VID_HANDLE); - - int32 curFrame = _vm->_video->getCurFrame(video); - int32 frameCount = _vm->_video->getFrameCount(video); - - // The final frame of the video is the 'golden' frame (double meaning: the - // frame that is the magic one is the one with the golden symbol) but we - // give a 3 frame leeway in either direction. - if (frameCount - curFrame < 3 || curFrame < 3) - *_vm->matchVarToString("domecheck") = 1; + _vm->_gameOver = true; } // ------------------------------------------------------------------------------------ @@ -239,13 +218,11 @@ void RivenExternal::runDomeCheck() { // ------------------------------------------------------------------------------------ void RivenExternal::xastartupbtnhide(uint16 argc, uint16 *argv) { - // The original game hides the start/setup buttons depending on an ini entry. - // It's safe to ignore this command. + // The original game hides the start/setup buttons depending on an ini entry. It's safe to ignore this command. } void RivenExternal::xasetupcomplete(uint16 argc, uint16 *argv) { - // The original game sets an ini entry to disable the setup button and use the - // start button only. It's safe to ignore this part of the command. + // The original game sets an ini entry to disable the setup button and use the start button only. It's safe to ignore this part of the command. _vm->_sound->stopSound(); _vm->changeToCard(1); } @@ -537,14 +514,13 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) { if (heat) { if (platform == 0) { _vm->_video->activateMLST(7, _vm->getCurCard()); - _vm->_video->playMovie(7); + // TODO: Play video (non-blocking) } else { _vm->_video->activateMLST(8, _vm->getCurCard()); - _vm->_video->playMovie(8); + // TODO: Play video (non-blocking) } } else { - _vm->_video->stopMovie(7); - _vm->_video->stopMovie(8); + // TODO: Stop MLST's 7 and 8 } _vm->refreshCard(); @@ -651,11 +627,11 @@ void RivenExternal::xbisland190_slidermw(uint16 argc, uint16 *argv) { } void RivenExternal::xbscpbtn(uint16 argc, uint16 *argv) { - runDomeButtonMovie(); + // TODO: Dome related } void RivenExternal::xbisland_domecheck(uint16 argc, uint16 *argv) { - runDomeCheck(); + // TODO: Dome related } void RivenExternal::xvalvecontrol(uint16 argc, uint16 *argv) { @@ -747,11 +723,11 @@ void RivenExternal::xgisland25_slidermw(uint16 argc, uint16 *argv) { } void RivenExternal::xgscpbtn(uint16 argc, uint16 *argv) { - runDomeButtonMovie(); + // TODO: Dome related } void RivenExternal::xgisland1490_domecheck(uint16 argc, uint16 *argv) { - runDomeCheck(); + // TODO: Dome related } void RivenExternal::xgplateau3160_dopools(uint16 argc, uint16 *argv) { @@ -1002,11 +978,11 @@ void RivenExternal::xjdome25_slidermw(uint16 argc, uint16 *argv) { } void RivenExternal::xjscpbtn(uint16 argc, uint16 *argv) { - runDomeButtonMovie(); + // TODO: Dome related } void RivenExternal::xjisland3500_domecheck(uint16 argc, uint16 *argv) { - runDomeCheck(); + // TODO: Dome related } int RivenExternal::jspitElevatorLoop() { @@ -1282,11 +1258,11 @@ void RivenExternal::xpisland990_elevcombo(uint16 argc, uint16 *argv) { } void RivenExternal::xpscpbtn(uint16 argc, uint16 *argv) { - runDomeButtonMovie(); + // TODO: Dome related } void RivenExternal::xpisland290_domecheck(uint16 argc, uint16 *argv) { - runDomeCheck(); + // TODO: Dome related } void RivenExternal::xpisland25_opencard(uint16 argc, uint16 *argv) { @@ -1481,11 +1457,11 @@ void RivenExternal::xtakeit(uint16 argc, uint16 *argv) { } void RivenExternal::xtscpbtn(uint16 argc, uint16 *argv) { - runDomeButtonMovie(); + // TODO: Dome related } void RivenExternal::xtisland4990_domecheck(uint16 argc, uint16 *argv) { - runDomeCheck(); + // TODO: Dome related } void RivenExternal::xtisland5056_opencard(uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/riven_external.h b/engines/mohawk/riven_external.h index 14bb51340c..8270a00854 100644 --- a/engines/mohawk/riven_external.h +++ b/engines/mohawk/riven_external.h @@ -57,8 +57,6 @@ private: int jspitElevatorLoop(); void runDemoBoundaryDialog(); void runEndGame(uint16 video); - void runDomeCheck(); - void runDomeButtonMovie(); // ----------------------------------------------------- // aspit (Main Menu, Books, Setup) external commands diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index d574a455c6..e809ad9642 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -28,7 +28,7 @@ #include "mohawk/riven_external.h" #include "mohawk/riven_scripts.h" #include "mohawk/sound.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" #include "common/stream.h" #include "graphics/cursorman.h" @@ -298,10 +298,13 @@ void RivenScript::processCommands(bool runCommands) { // Command 1: draw tBMP resource (tbmp_id, left, top, right, bottom, u0, u1, u2, u3) void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { - if (argc < 5) // Copy the image to the whole screen, ignoring the rest of the parameters + if (argc < 5) { + // Copy the image to the whole screen, ignoring the rest of the parameters _vm->_gfx->copyImageToScreen(argv[0], 0, 0, 608, 392); - else // Copy the image to a certain part of the screen + } else { + // Copy the image to a certain part of the screen _vm->_gfx->copyImageToScreen(argv[0], argv[1], argv[2], argv[3], argv[4]); + } // Now, update the screen _vm->_gfx->updateScreen(); @@ -310,12 +313,6 @@ void RivenScript::drawBitmap(uint16 op, uint16 argc, uint16 *argv) { // Command 2: go to card (card id) void RivenScript::switchCard(uint16 op, uint16 argc, uint16 *argv) { _vm->changeToCard(argv[0]); - - // WORKAROUND: If we changed card on a mouse down event, - // we want to ignore the next mouse up event so we don't - // change card when lifting the mouse on the next card. - if (_scriptType == kMouseDownScript) - _vm->ignoreNextMouseUp(); } // Command 3: play an SLST from the script @@ -550,8 +547,9 @@ void RivenScript::activateSLST(uint16 op, uint16 argc, uint16 *argv) { // Command 41: activate MLST record and play void RivenScript::activateMLSTAndPlay(uint16 op, uint16 argc, uint16 *argv) { + _vm->_video->enableMovie(argv[0] - 1); _vm->_video->activateMLST(argv[0], _vm->getCurCard()); - _vm->_video->playMovie(argv[0]); + // TODO: Play movie (blocking?) } // Command 43: activate BLST record (card hotspot enabling lists) diff --git a/graphics/video/codecs/cinepak.cpp b/engines/mohawk/video/cinepak.cpp index d3448bb8f7..2ffe6869ae 100644 --- a/graphics/video/codecs/cinepak.cpp +++ b/engines/mohawk/video/cinepak.cpp @@ -23,33 +23,32 @@ * */ -#include "graphics/video/codecs/cinepak.h" +#include "mohawk/video/cinepak.h" #include "common/system.h" +#include "graphics/conversion.h" // For YUV2RGB // Code here partially based off of ffmpeg ;) -namespace Graphics { - -// Convert a color from YUV to RGB colorspace, Cinepak style. -inline static void CPYUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) { - r = CLIP<int>(y + 2 * (v - 128), 0, 255); - g = CLIP<int>(y - (u - 128) / 2 - (v - 128), 0, 255); - b = CLIP<int>(y + 2 * (u - 128), 0, 255); -} +namespace Mohawk { #define PUT_PIXEL(offset, lum, u, v) \ - CPYUV2RGB(lum, u, v, r, g, b); \ + Graphics::CPYUV2RGB(lum, u, v, r, g, b); \ if (_pixelFormat.bytesPerPixel == 2) \ *((uint16 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \ else \ *((uint32 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b) -CinepakDecoder::CinepakDecoder() : Codec() { +CinepakDecoder::CinepakDecoder() : Graphics::Codec() { _curFrame.surface = NULL; _curFrame.strips = NULL; _y = 0; _pixelFormat = g_system->getScreenFormat(); + + // We're going to have to dither if we're running in 8bpp. + // We'll take RGBA8888 for best color performance in this case. + if (_pixelFormat.bytesPerPixel == 1) + _pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); } CinepakDecoder::~CinepakDecoder() { @@ -58,7 +57,7 @@ CinepakDecoder::~CinepakDecoder() { delete[] _curFrame.strips; } -Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { +Graphics::Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { _curFrame.flags = stream->readByte(); _curFrame.length = (stream->readByte() << 16) + stream->readUint16BE(); _curFrame.width = stream->readUint16BE(); @@ -80,7 +79,7 @@ Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { #endif if (!_curFrame.surface) { - _curFrame.surface = new Surface(); + _curFrame.surface = new Graphics::Surface(); _curFrame.surface->create(_curFrame.width, _curFrame.height, _pixelFormat.bytesPerPixel); } @@ -284,4 +283,4 @@ void CinepakDecoder::decodeVectors(Common::SeekableReadStream *stream, uint16 st } } -} // End of namespace Graphics +} // End of namespace Mohawk diff --git a/graphics/video/codecs/cinepak.h b/engines/mohawk/video/cinepak.h index 92351cdba8..3f4cbba17c 100644 --- a/graphics/video/codecs/cinepak.h +++ b/engines/mohawk/video/cinepak.h @@ -23,8 +23,8 @@ * */ -#ifndef GRAPHICS_CINEPAK_H -#define GRAPHICS_CINEPAK_H +#ifndef CINEPAK_H +#define CINEPAK_H #include "common/scummsys.h" #include "common/stream.h" @@ -34,7 +34,7 @@ #include "graphics/video/codecs/codec.h" -namespace Graphics { +namespace Mohawk { struct CinepakCodebook { byte y[4]; @@ -56,26 +56,26 @@ struct CinepakFrame { uint16 stripCount; CinepakStrip *strips; - Surface *surface; + Graphics::Surface *surface; }; -class CinepakDecoder : public Codec { +class CinepakDecoder : public Graphics::Codec { public: CinepakDecoder(); ~CinepakDecoder(); - Surface *decodeImage(Common::SeekableReadStream *stream); - PixelFormat getPixelFormat() const { return _pixelFormat; } + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: CinepakFrame _curFrame; int32 _y; - PixelFormat _pixelFormat; + Graphics::PixelFormat _pixelFormat; void loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize); void decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize); }; -} // End of namespace Graphics +} #endif diff --git a/graphics/video/codecs/qdm2.cpp b/engines/mohawk/video/qdm2.cpp index e12be21303..b91440f00d 100644 --- a/graphics/video/codecs/qdm2.cpp +++ b/engines/mohawk/video/qdm2.cpp @@ -25,270 +25,12 @@ // Based off ffmpeg's QDM2 decoder -#include "graphics/video/codecs/qdm2.h" +#include "mohawk/video/qdm2.h" +#include "mohawk/video/qdm2data.h" -#ifdef GRAPHICS_QDM2_H - -#include "sound/audiostream.h" -#include "graphics/video/codecs/qdm2data.h" - -#include "common/array.h" -#include "common/stream.h" #include "common/system.h" -namespace Graphics { - -enum { - SOFTCLIP_THRESHOLD = 27600, - HARDCLIP_THRESHOLD = 35716, - MPA_MAX_CHANNELS = 2, - MPA_FRAME_SIZE = 1152, - FF_INPUT_BUFFER_PADDING_SIZE = 8 -}; - -typedef int8 sb_int8_array[2][30][64]; - -/* bit input */ -/* buffer, buffer_end and size_in_bits must be present and used by every reader */ -struct GetBitContext { - const uint8 *buffer, *bufferEnd; - int index; - int sizeInBits; -}; - -struct QDM2SubPacket { - int type; - unsigned int size; - const uint8 *data; // pointer to subpacket data (points to input data buffer, it's not a private copy) -}; - -struct QDM2SubPNode { - QDM2SubPacket *packet; - struct QDM2SubPNode *next; // pointer to next packet in the list, NULL if leaf node -}; - -struct QDM2Complex { - float re; - float im; -}; - -struct FFTTone { - float level; - QDM2Complex *complex; - const float *table; - int phase; - int phase_shift; - int duration; - short time_index; - short cutoff; -}; - -struct FFTCoefficient { - int16 sub_packet; - uint8 channel; - int16 offset; - int16 exp; - uint8 phase; -}; - -struct VLC { - int32 bits; - int16 (*table)[2]; // code, bits - int32 table_size; - int32 table_allocated; -}; - -#include "common/pack-start.h" -struct QDM2FFT { - QDM2Complex complex[MPA_MAX_CHANNELS][256]; -} PACKED_STRUCT; -#include "common/pack-end.h" - -enum RDFTransformType { - RDFT, - IRDFT, - RIDFT, - IRIDFT -}; - -struct FFTComplex { - float re, im; -}; - -struct FFTContext { - int nbits; - int inverse; - uint16 *revtab; - FFTComplex *exptab; - FFTComplex *tmpBuf; - int mdctSize; // size of MDCT (i.e. number of input data * 2) - int mdctBits; // n = 2^nbits - // pre/post rotation tables - float *tcos; - float *tsin; - void (*fftPermute)(struct FFTContext *s, FFTComplex *z); - void (*fftCalc)(struct FFTContext *s, FFTComplex *z); - void (*imdctCalc)(struct FFTContext *s, float *output, const float *input); - void (*imdctHalf)(struct FFTContext *s, float *output, const float *input); - void (*mdctCalc)(struct FFTContext *s, float *output, const float *input); - int splitRadix; - int permutation; -}; - -enum { - FF_MDCT_PERM_NONE = 0, - FF_MDCT_PERM_INTERLEAVE = 1 -}; - -struct RDFTContext { - int nbits; - int inverse; - int signConvention; - - // pre/post rotation tables - float *tcos; - float *tsin; - FFTContext fft; -}; - -class QDM2Stream : public Audio::AudioStream { -public: - QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); - ~QDM2Stream(); - - bool isStereo() const { return _channels == 2; } - bool endOfData() const { return ((_stream->pos() == _stream->size()) && (_outputSamples.size() == 0)); } - int getRate() const { return _sampleRate; } - int readBuffer(int16 *buffer, const int numSamples); - -private: - Common::SeekableReadStream *_stream; - - // Parameters from codec header, do not change during playback - uint8 _channels; - uint16 _sampleRate; - uint16 _bitRate; - uint16 _blockSize; // Group - uint16 _frameSize; // FFT - uint16 _packetSize; // Checksum - - // Parameters built from header parameters, do not change during playback - int _groupOrder; // order of frame group - int _fftOrder; // order of FFT (actually fft order+1) - int _fftFrameSize; // size of fft frame, in components (1 comples = re + im) - int _sFrameSize; // size of data frame - int _frequencyRange; - int _subSampling; // subsampling: 0=25%, 1=50%, 2=100% */ - int _coeffPerSbSelect; // selector for "num. of coeffs. per subband" tables. Can be 0, 1, 2 - int _cmTableSelect; // selector for "coding method" tables. Can be 0, 1 (from init: 0-4) - - // Packets and packet lists - QDM2SubPacket _subPackets[16]; // the packets themselves - QDM2SubPNode _subPacketListA[16]; // list of all packets - QDM2SubPNode _subPacketListB[16]; // FFT packets B are on list - int _subPacketsB; // number of packets on 'B' list - QDM2SubPNode _subPacketListC[16]; // packets with errors? - QDM2SubPNode _subPacketListD[16]; // DCT packets - - // FFT and tones - FFTTone _fftTones[1000]; - int _fftToneStart; - int _fftToneEnd; - FFTCoefficient _fftCoefs[1000]; - int _fftCoefsIndex; - int _fftCoefsMinIndex[5]; - int _fftCoefsMaxIndex[5]; - int _fftLevelExp[6]; - //RDFTContext _rdftCtx; - QDM2FFT _fft; - - // I/O data - uint8 *_compressedData; - float _outputBuffer[1024]; - Common::Array<int16> _outputSamples; - - // Synthesis filter - int16 ff_mpa_synth_window[512]; - int16 _synthBuf[MPA_MAX_CHANNELS][512*2]; - int _synthBufOffset[MPA_MAX_CHANNELS]; - int32 _sbSamples[MPA_MAX_CHANNELS][128][32]; - - // Mixed temporary data used in decoding - float _toneLevel[MPA_MAX_CHANNELS][30][64]; - int8 _codingMethod[MPA_MAX_CHANNELS][30][64]; - int8 _quantizedCoeffs[MPA_MAX_CHANNELS][10][8]; - int8 _toneLevelIdxBase[MPA_MAX_CHANNELS][30][8]; - int8 _toneLevelIdxHi1[MPA_MAX_CHANNELS][3][8][8]; - int8 _toneLevelIdxMid[MPA_MAX_CHANNELS][26][8]; - int8 _toneLevelIdxHi2[MPA_MAX_CHANNELS][26]; - int8 _toneLevelIdx[MPA_MAX_CHANNELS][30][64]; - int8 _toneLevelIdxTemp[MPA_MAX_CHANNELS][30][64]; - - // Flags - bool _hasErrors; // packet has errors - int _superblocktype_2_3; // select fft tables and some algorithm based on superblock type - int _doSynthFilter; // used to perform or skip synthesis filter - - uint8 _subPacket; // 0 to 15 - int _noiseIdx; // index for dithering noise table - - byte _emptyBuffer[FF_INPUT_BUFFER_PADDING_SIZE]; - - VLC _vlcTabLevel; - VLC _vlcTabDiff; - VLC _vlcTabRun; - VLC _fftLevelExpAltVlc; - VLC _fftLevelExpVlc; - VLC _fftStereoExpVlc; - VLC _fftStereoPhaseVlc; - VLC _vlcTabToneLevelIdxHi1; - VLC _vlcTabToneLevelIdxMid; - VLC _vlcTabToneLevelIdxHi2; - VLC _vlcTabType30; - VLC _vlcTabType34; - VLC _vlcTabFftToneOffset[5]; - bool _vlcsInitialized; - void initVlc(void); - - uint16 _softclipTable[HARDCLIP_THRESHOLD - SOFTCLIP_THRESHOLD + 1]; - void softclipTableInit(void); - - float _noiseTable[4096]; - byte _randomDequantIndex[256][5]; - byte _randomDequantType24[128][3]; - void rndTableInit(void); - - float _noiseSamples[128]; - void initNoiseSamples(void); - - RDFTContext _rdftCtx; - - void average_quantized_coeffs(void); - void build_sb_samples_from_noise(int sb); - void fix_coding_method_array(int sb, int channels, sb_int8_array coding_method); - void fill_tone_level_array(int flag); - void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, - sb_int8_array coding_method, int nb_channels, - int c, int superblocktype_2_3, int cm_table_select); - void synthfilt_build_sb_samples(GetBitContext *gb, int length, int sb_min, int sb_max); - void init_quantized_coeffs_elem0(int8 *quantized_coeffs, GetBitContext *gb, int length); - void init_tone_level_dequantization(GetBitContext *gb, int length); - void process_subpacket_9(QDM2SubPNode *node); - void process_subpacket_10(QDM2SubPNode *node, int length); - void process_subpacket_11(QDM2SubPNode *node, int length); - void process_subpacket_12(QDM2SubPNode *node, int length); - void process_synthesis_subpackets(QDM2SubPNode *list); - void qdm2_decode_super_block(void); - void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, - int channel, int exp, int phase); - void qdm2_fft_decode_tones(int duration, GetBitContext *gb, int b); - void qdm2_decode_fft_packets(void); - void qdm2_fft_generate_tone(FFTTone *tone); - void qdm2_fft_tone_synthesizer(uint8 sub_packet); - void qdm2_calculate_fft(int channel); - void qdm2_synthesis_filter(uint8 index); - int qdm2_decodeFrame(Common::SeekableReadStream *in); -}; +namespace Mohawk { // Fix compilation for non C99-compliant compilers, like MSVC #ifndef int64_t @@ -3318,10 +3060,4 @@ int QDM2Stream::readBuffer(int16 *buffer, const int numSamples) { return decodedSamples; } -Audio::AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { - return new QDM2Stream(stream, extraData); -} - -} // End of namespace Graphics - -#endif +} // End of namespace Mohawk diff --git a/engines/mohawk/video/qdm2.h b/engines/mohawk/video/qdm2.h new file mode 100644 index 0000000000..ffa5f77030 --- /dev/null +++ b/engines/mohawk/video/qdm2.h @@ -0,0 +1,289 @@ +/* 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 MOHAWK_VIDEO_QDM2_H +#define MOHAWK_VIDEO_QDM2_H + +#include "sound/audiostream.h" +#include "common/array.h" +#include "common/stream.h" + +namespace Mohawk { + +enum { + SOFTCLIP_THRESHOLD = 27600, + HARDCLIP_THRESHOLD = 35716, + MPA_MAX_CHANNELS = 2, + MPA_FRAME_SIZE = 1152, + FF_INPUT_BUFFER_PADDING_SIZE = 8 +}; + +typedef int8 sb_int8_array[2][30][64]; + +/* bit input */ +/* buffer, buffer_end and size_in_bits must be present and used by every reader */ +struct GetBitContext { + const uint8 *buffer, *bufferEnd; + int index; + int sizeInBits; +}; + +struct QDM2SubPacket { + int type; + unsigned int size; + const uint8 *data; // pointer to subpacket data (points to input data buffer, it's not a private copy) +}; + +struct QDM2SubPNode { + QDM2SubPacket *packet; + struct QDM2SubPNode *next; // pointer to next packet in the list, NULL if leaf node +}; + +struct QDM2Complex { + float re; + float im; +}; + +struct FFTTone { + float level; + QDM2Complex *complex; + const float *table; + int phase; + int phase_shift; + int duration; + short time_index; + short cutoff; +}; + +struct FFTCoefficient { + int16 sub_packet; + uint8 channel; + int16 offset; + int16 exp; + uint8 phase; +}; + +struct VLC { + int32 bits; + int16 (*table)[2]; // code, bits + int32 table_size; + int32 table_allocated; +}; + +#include "common/pack-start.h" +struct QDM2FFT { + QDM2Complex complex[MPA_MAX_CHANNELS][256]; +} PACKED_STRUCT; +#include "common/pack-end.h" + +enum RDFTransformType { + RDFT, + IRDFT, + RIDFT, + IRIDFT +}; + +struct FFTComplex { + float re, im; +}; + +struct FFTContext { + int nbits; + int inverse; + uint16 *revtab; + FFTComplex *exptab; + FFTComplex *tmpBuf; + int mdctSize; // size of MDCT (i.e. number of input data * 2) + int mdctBits; // n = 2^nbits + // pre/post rotation tables + float *tcos; + float *tsin; + void (*fftPermute)(struct FFTContext *s, FFTComplex *z); + void (*fftCalc)(struct FFTContext *s, FFTComplex *z); + void (*imdctCalc)(struct FFTContext *s, float *output, const float *input); + void (*imdctHalf)(struct FFTContext *s, float *output, const float *input); + void (*mdctCalc)(struct FFTContext *s, float *output, const float *input); + int splitRadix; + int permutation; +}; + +enum { + FF_MDCT_PERM_NONE = 0, + FF_MDCT_PERM_INTERLEAVE = 1 +}; + +struct RDFTContext { + int nbits; + int inverse; + int signConvention; + + // pre/post rotation tables + float *tcos; + float *tsin; + FFTContext fft; +}; + +class QDM2Stream : public Audio::AudioStream { +public: + QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); + ~QDM2Stream(); + + bool isStereo() const { return _channels == 2; } + bool endOfData() const { return ((_stream->pos() == _stream->size()) && (_outputSamples.size() == 0)); } + int getRate() const { return _sampleRate; } + int readBuffer(int16 *buffer, const int numSamples); + +private: + Common::SeekableReadStream *_stream; + + // Parameters from codec header, do not change during playback + uint8 _channels; + uint16 _sampleRate; + uint16 _bitRate; + uint16 _blockSize; // Group + uint16 _frameSize; // FFT + uint16 _packetSize; // Checksum + + // Parameters built from header parameters, do not change during playback + int _groupOrder; // order of frame group + int _fftOrder; // order of FFT (actually fft order+1) + int _fftFrameSize; // size of fft frame, in components (1 comples = re + im) + int _sFrameSize; // size of data frame + int _frequencyRange; + int _subSampling; // subsampling: 0=25%, 1=50%, 2=100% */ + int _coeffPerSbSelect; // selector for "num. of coeffs. per subband" tables. Can be 0, 1, 2 + int _cmTableSelect; // selector for "coding method" tables. Can be 0, 1 (from init: 0-4) + + // Packets and packet lists + QDM2SubPacket _subPackets[16]; // the packets themselves + QDM2SubPNode _subPacketListA[16]; // list of all packets + QDM2SubPNode _subPacketListB[16]; // FFT packets B are on list + int _subPacketsB; // number of packets on 'B' list + QDM2SubPNode _subPacketListC[16]; // packets with errors? + QDM2SubPNode _subPacketListD[16]; // DCT packets + + // FFT and tones + FFTTone _fftTones[1000]; + int _fftToneStart; + int _fftToneEnd; + FFTCoefficient _fftCoefs[1000]; + int _fftCoefsIndex; + int _fftCoefsMinIndex[5]; + int _fftCoefsMaxIndex[5]; + int _fftLevelExp[6]; + //RDFTContext _rdftCtx; + QDM2FFT _fft; + + // I/O data + uint8 *_compressedData; + float _outputBuffer[1024]; + Common::Array<int16> _outputSamples; + + // Synthesis filter + int16 ff_mpa_synth_window[512]; + int16 _synthBuf[MPA_MAX_CHANNELS][512*2]; + int _synthBufOffset[MPA_MAX_CHANNELS]; + int32 _sbSamples[MPA_MAX_CHANNELS][128][32]; + + // Mixed temporary data used in decoding + float _toneLevel[MPA_MAX_CHANNELS][30][64]; + int8 _codingMethod[MPA_MAX_CHANNELS][30][64]; + int8 _quantizedCoeffs[MPA_MAX_CHANNELS][10][8]; + int8 _toneLevelIdxBase[MPA_MAX_CHANNELS][30][8]; + int8 _toneLevelIdxHi1[MPA_MAX_CHANNELS][3][8][8]; + int8 _toneLevelIdxMid[MPA_MAX_CHANNELS][26][8]; + int8 _toneLevelIdxHi2[MPA_MAX_CHANNELS][26]; + int8 _toneLevelIdx[MPA_MAX_CHANNELS][30][64]; + int8 _toneLevelIdxTemp[MPA_MAX_CHANNELS][30][64]; + + // Flags + bool _hasErrors; // packet has errors + int _superblocktype_2_3; // select fft tables and some algorithm based on superblock type + int _doSynthFilter; // used to perform or skip synthesis filter + + uint8 _subPacket; // 0 to 15 + int _noiseIdx; // index for dithering noise table + + byte _emptyBuffer[FF_INPUT_BUFFER_PADDING_SIZE]; + + VLC _vlcTabLevel; + VLC _vlcTabDiff; + VLC _vlcTabRun; + VLC _fftLevelExpAltVlc; + VLC _fftLevelExpVlc; + VLC _fftStereoExpVlc; + VLC _fftStereoPhaseVlc; + VLC _vlcTabToneLevelIdxHi1; + VLC _vlcTabToneLevelIdxMid; + VLC _vlcTabToneLevelIdxHi2; + VLC _vlcTabType30; + VLC _vlcTabType34; + VLC _vlcTabFftToneOffset[5]; + bool _vlcsInitialized; + void initVlc(void); + + uint16 _softclipTable[HARDCLIP_THRESHOLD - SOFTCLIP_THRESHOLD + 1]; + void softclipTableInit(void); + + float _noiseTable[4096]; + byte _randomDequantIndex[256][5]; + byte _randomDequantType24[128][3]; + void rndTableInit(void); + + float _noiseSamples[128]; + void initNoiseSamples(void); + + RDFTContext _rdftCtx; + + void average_quantized_coeffs(void); + void build_sb_samples_from_noise(int sb); + void fix_coding_method_array(int sb, int channels, sb_int8_array coding_method); + void fill_tone_level_array(int flag); + void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, + sb_int8_array coding_method, int nb_channels, + int c, int superblocktype_2_3, int cm_table_select); + void synthfilt_build_sb_samples(GetBitContext *gb, int length, int sb_min, int sb_max); + void init_quantized_coeffs_elem0(int8 *quantized_coeffs, GetBitContext *gb, int length); + void init_tone_level_dequantization(GetBitContext *gb, int length); + void process_subpacket_9(QDM2SubPNode *node); + void process_subpacket_10(QDM2SubPNode *node, int length); + void process_subpacket_11(QDM2SubPNode *node, int length); + void process_subpacket_12(QDM2SubPNode *node, int length); + void process_synthesis_subpackets(QDM2SubPNode *list); + void qdm2_decode_super_block(void); + void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, + int channel, int exp, int phase); + void qdm2_fft_decode_tones(int duration, GetBitContext *gb, int b); + void qdm2_decode_fft_packets(void); + void qdm2_fft_generate_tone(FFTTone *tone); + void qdm2_fft_tone_synthesizer(uint8 sub_packet); + void qdm2_calculate_fft(int channel); + void qdm2_synthesis_filter(uint8 index); + int qdm2_decodeFrame(Common::SeekableReadStream *in); +}; + +} // End of namespace Mohawk + +#endif diff --git a/graphics/video/codecs/qdm2data.h b/engines/mohawk/video/qdm2data.h index 25ed102c4e..f1c18db41c 100644 --- a/graphics/video/codecs/qdm2data.h +++ b/engines/mohawk/video/qdm2data.h @@ -23,12 +23,12 @@ * */ -#ifndef GRAPHICS_QDM2DATA_H -#define GRAPHICS_QDM2DATA_H +#ifndef MOHAWK_VIDEO_QDM2DATA_H +#define MOHAWK_VIDEO_QDM2DATA_H #include "common/scummsys.h" -namespace Graphics { +namespace Mohawk { /// VLC TABLES @@ -526,6 +526,6 @@ static const float type34_delta[10] = { // FIXME: covers 8 entries.. 0.138071194291115f,0.333333343267441f,0.60947573184967f,1.0f,0.0f, }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/graphics/video/qt_decoder.cpp b/engines/mohawk/video/qt_player.cpp index 49d2b0aed9..0ed05bb84d 100644 --- a/graphics/video/qt_decoder.cpp +++ b/engines/mohawk/video/qt_player.cpp @@ -31,33 +31,32 @@ // Seek function by Gael Chardon gael.dev@4now.net // -#include "graphics/video/qt_decoder.h" +#include "mohawk/video/qt_player.h" #include "common/debug.h" #include "common/endian.h" -#include "common/macresman.h" #include "common/util.h" #include "common/zlib.h" // Audio codecs #include "sound/decoders/adpcm.h" #include "sound/decoders/raw.h" -#include "graphics/video/codecs/qdm2.h" +#include "mohawk/video/qdm2.h" // Video codecs -#include "graphics/video/codecs/cinepak.h" -#include "graphics/video/codecs/mjpeg.h" -#include "graphics/video/codecs/qtrle.h" -#include "graphics/video/codecs/rpza.h" -#include "graphics/video/codecs/smc.h" +#include "mohawk/jpeg.h" +#include "mohawk/video/cinepak.h" +#include "mohawk/video/qtrle.h" +#include "mohawk/video/rpza.h" +#include "mohawk/video/smc.h" -namespace Graphics { +namespace Mohawk { //////////////////////////////////////////// -// QuickTimeDecoder +// QTPlayer //////////////////////////////////////////// -QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() { +QTPlayer::QTPlayer() : Graphics::VideoDecoder() { _audStream = NULL; _beginOffset = 0; _videoCodec = NULL; @@ -68,59 +67,55 @@ QuickTimeDecoder::QuickTimeDecoder() : VideoDecoder() { _fd = 0; _scaledSurface = 0; _dirtyPalette = false; - _resFork = new Common::MacResManager(); - - initParseTable(); } -QuickTimeDecoder::~QuickTimeDecoder() { +QTPlayer::~QTPlayer() { close(); - delete _resFork; } -uint16 QuickTimeDecoder::getWidth() const { +uint16 QTPlayer::getWidth() const { if (_videoStreamIndex < 0) return 0; return _streams[_videoStreamIndex]->width / getScaleMode(); } -uint16 QuickTimeDecoder::getHeight() const { +uint16 QTPlayer::getHeight() const { if (_videoStreamIndex < 0) return 0; return _streams[_videoStreamIndex]->height / getScaleMode(); } -uint32 QuickTimeDecoder::getFrameCount() const { +uint32 QTPlayer::getFrameCount() const { if (_videoStreamIndex < 0) return 0; return _streams[_videoStreamIndex]->nb_frames; } -byte QuickTimeDecoder::getBitsPerPixel() { +byte QTPlayer::getBitsPerPixel() { if (_videoStreamIndex < 0) return 0; return _streams[_videoStreamIndex]->bits_per_sample & 0x1F; } -uint32 QuickTimeDecoder::getCodecTag() { +uint32 QTPlayer::getCodecTag() { if (_videoStreamIndex < 0) return 0; return _streams[_videoStreamIndex]->codec_tag; } -ScaleMode QuickTimeDecoder::getScaleMode() const { +ScaleMode QTPlayer::getScaleMode() const { if (_videoStreamIndex < 0) return kScaleNormal; return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode); } -uint32 QuickTimeDecoder::getFrameDuration() { +uint32 QTPlayer::getFrameDuration() { if (_videoStreamIndex < 0) return 0; @@ -138,16 +133,17 @@ uint32 QuickTimeDecoder::getFrameDuration() { return 0; } -PixelFormat QuickTimeDecoder::getPixelFormat() const { +Graphics::PixelFormat QTPlayer::getPixelFormat() const { if (!_videoCodec) - return PixelFormat::createFormatCLUT8(); + return Graphics::PixelFormat::createFormatCLUT8(); return _videoCodec->getPixelFormat(); } -void QuickTimeDecoder::rewind() { - VideoDecoder::reset(); - _nextFrameStartTime = 0; +void QTPlayer::rewind() { + delete _videoCodec; _videoCodec = NULL; + _curFrame = -1; + _startTime = _nextFrameStartTime = 0; // Restart the audio too stopAudio(); @@ -158,7 +154,7 @@ void QuickTimeDecoder::rewind() { startAudio(); } -Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) { +Graphics::Codec *QTPlayer::createCodec(uint32 codecTag, byte bitsPerPixel) { if (codecTag == MKID_BE('cvid')) { // Cinepak: As used by most Myst and all Riven videos as well as some Myst ME videos. "The Chief" videos also use this. return new CinepakDecoder(); @@ -179,7 +175,7 @@ Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) { warning ("Sorenson Video 3 not yet supported"); } else if (codecTag == MKID_BE('jpeg')) { // Motion JPEG: Used by some Myst ME 10th Anniversary videos. - return new JPEGDecoder(); + return new JPEGDecoder(true); } else if (codecTag == MKID_BE('QkBk')) { // CDToons: Used by most of the Broderbund games. This is an unknown format so far. warning ("CDToons not yet supported"); @@ -190,24 +186,24 @@ Codec *QuickTimeDecoder::createCodec(uint32 codecTag, byte bitsPerPixel) { return NULL; } -void QuickTimeDecoder::startAudio() { +void QTPlayer::startAudio() { if (_audStream) // No audio/audio not supported g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream); } -void QuickTimeDecoder::stopAudio() { +void QTPlayer::stopAudio() { if (_audStream) { g_system->getMixer()->stopHandle(_audHandle); _audStream = NULL; // the mixer automatically frees the stream } } -void QuickTimeDecoder::pauseVideoIntern(bool pause) { +void QTPlayer::pauseVideoIntern(bool pause) { if (_audStream) g_system->getMixer()->pauseHandle(_audHandle, pause); } -Surface *QuickTimeDecoder::decodeNextFrame() { +Graphics::Surface *QTPlayer::decodeNextFrame() { if (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1) return NULL; @@ -220,7 +216,7 @@ Surface *QuickTimeDecoder::decodeNextFrame() { Common::SeekableReadStream *frameData = getNextFramePacket(); if (frameData) { - Surface *frame = _videoCodec->decodeImage(frameData); + Graphics::Surface *frame = _videoCodec->decodeImage(frameData); delete frameData; return scaleSurface(frame); } @@ -228,7 +224,7 @@ Surface *QuickTimeDecoder::decodeNextFrame() { return NULL; } -Surface *QuickTimeDecoder::scaleSurface(Surface *frame) { +Graphics::Surface *QTPlayer::scaleSurface(Graphics::Surface *frame) { if (getScaleMode() == kScaleNormal) return frame; @@ -241,22 +237,26 @@ Surface *QuickTimeDecoder::scaleSurface(Surface *frame) { return _scaledSurface; } -bool QuickTimeDecoder::endOfVideo() const { - return (!_audStream || _audStream->endOfData()) && (!_videoCodec || VideoDecoder::endOfVideo()); +bool QTPlayer::endOfVideo() const { + return (!_audStream || _audStream->endOfData()) && (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1); +} + +bool QTPlayer::needsUpdate() const { + return !endOfVideo() && getTimeToNextFrame() == 0; } -uint32 QuickTimeDecoder::getElapsedTime() const { +uint32 QTPlayer::getElapsedTime() const { if (_audStream) return g_system->getMixer()->getSoundElapsedTime(_audHandle); return g_system->getMillis() - _startTime; } -uint32 QuickTimeDecoder::getTimeToNextFrame() const { +uint32 QTPlayer::getTimeToNextFrame() const { if (endOfVideo() || _curFrame < 0) return 0; - // Convert from the QuickTime rate base to 1000 + // Convert from the Sega FILM base to 1000 uint32 nextFrameStartTime = _nextFrameStartTime * 1000 / _streams[_videoStreamIndex]->time_scale; uint32 elapsedTime = getElapsedTime(); @@ -266,47 +266,7 @@ uint32 QuickTimeDecoder::getTimeToNextFrame() const { return nextFrameStartTime - elapsedTime; } -bool QuickTimeDecoder::loadFile(const Common::String &filename) { - if (!_resFork->open(filename) || !_resFork->hasDataFork()) - return false; - - _foundMOOV = _foundMDAT = false; - _numStreams = 0; - _partial = 0; - _videoStreamIndex = _audioStreamIndex = -1; - _startTime = 0; - - MOVatom atom = { 0, 0, 0xffffffff }; - - if (_resFork->hasResFork()) { - // Search for a 'moov' resource - Common::MacResIDArray idArray = _resFork->getResIDArray(MKID_BE('moov')); - - if (!idArray.empty()) - _fd = _resFork->getResource(MKID_BE('moov'), idArray[0]); - - if (_fd) { - atom.size = _fd->size(); - if (readDefault(atom) < 0 || !_foundMOOV) - return false; - } - delete _fd; - - atom.type = 0; - atom.offset = 0; - atom.size = 0xffffffff; - } - - _fd = _resFork->getDataFork(); - - if (readDefault(atom) < 0 || !_foundMOOV || !_foundMDAT) - return false; - - init(); - return true; -} - -bool QuickTimeDecoder::load(Common::SeekableReadStream &stream) { +bool QTPlayer::load(Common::SeekableReadStream &stream) { _fd = &stream; _foundMOOV = _foundMDAT = false; _numStreams = 0; @@ -314,22 +274,21 @@ bool QuickTimeDecoder::load(Common::SeekableReadStream &stream) { _videoStreamIndex = _audioStreamIndex = -1; _startTime = 0; + initParseTable(); + MOVatom atom = { 0, 0, 0xffffffff }; - if (readDefault(atom) < 0 || !_foundMOOV || !_foundMDAT) { - _fd = 0; + if (readDefault(atom) < 0 || (!_foundMOOV && !_foundMDAT)) return false; - } - init(); - return true; -} + debug(0, "on_parse_exit_offset=%d", _fd->pos()); -void QuickTimeDecoder::init() { // some cleanup : make sure we are on the mdat atom if((uint32)_fd->pos() != _mdatOffset) _fd->seek(_mdatOffset, SEEK_SET); + _next_chunk_offset = _mdatOffset; // initialise reading + for (uint32 i = 0; i < _numStreams;) { if (_streams[i]->codec_type == CODEC_TYPE_MOV_OTHER) {// not audio, not video, delete delete _streams[i]; @@ -343,12 +302,14 @@ void QuickTimeDecoder::init() { for (uint32 i = 0; i < _numStreams; i++) { MOVStreamContext *sc = _streams[i]; - if (!sc->time_rate) + if(!sc->time_rate) sc->time_rate = 1; - if (!sc->time_scale) + if(!sc->time_scale) sc->time_scale = _timeScale; + //av_set_pts_info(s->streams[i], 64, sc->time_rate, sc->time_scale); + sc->duration /= sc->time_rate; sc->ffindex = i; @@ -376,64 +337,61 @@ void QuickTimeDecoder::init() { if (getScaleMode() != kScaleNormal) { // We have to initialize the scaled surface - _scaledSurface = new Surface(); + _scaledSurface = new Graphics::Surface(); _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel); } } + + return true; } -void QuickTimeDecoder::initParseTable() { +void QTPlayer::initParseTable() { static const ParseTable p[] = { - { &QuickTimeDecoder::readDefault, MKID_BE('dinf') }, - { &QuickTimeDecoder::readLeaf, MKID_BE('dref') }, - { &QuickTimeDecoder::readDefault, MKID_BE('edts') }, - { &QuickTimeDecoder::readELST, MKID_BE('elst') }, - { &QuickTimeDecoder::readHDLR, MKID_BE('hdlr') }, - { &QuickTimeDecoder::readMDAT, MKID_BE('mdat') }, - { &QuickTimeDecoder::readMDHD, MKID_BE('mdhd') }, - { &QuickTimeDecoder::readDefault, MKID_BE('mdia') }, - { &QuickTimeDecoder::readDefault, MKID_BE('minf') }, - { &QuickTimeDecoder::readMOOV, MKID_BE('moov') }, - { &QuickTimeDecoder::readMVHD, MKID_BE('mvhd') }, - { &QuickTimeDecoder::readLeaf, MKID_BE('smhd') }, - { &QuickTimeDecoder::readDefault, MKID_BE('stbl') }, - { &QuickTimeDecoder::readSTCO, MKID_BE('stco') }, - { &QuickTimeDecoder::readSTSC, MKID_BE('stsc') }, - { &QuickTimeDecoder::readSTSD, MKID_BE('stsd') }, - { &QuickTimeDecoder::readSTSS, MKID_BE('stss') }, - { &QuickTimeDecoder::readSTSZ, MKID_BE('stsz') }, - { &QuickTimeDecoder::readSTTS, MKID_BE('stts') }, - { &QuickTimeDecoder::readTKHD, MKID_BE('tkhd') }, - { &QuickTimeDecoder::readTRAK, MKID_BE('trak') }, - { &QuickTimeDecoder::readLeaf, MKID_BE('udta') }, - { &QuickTimeDecoder::readLeaf, MKID_BE('vmhd') }, - { &QuickTimeDecoder::readCMOV, MKID_BE('cmov') }, - { &QuickTimeDecoder::readWAVE, MKID_BE('wave') }, + { MKID_BE('dinf'), &QTPlayer::readDefault }, + { MKID_BE('dref'), &QTPlayer::readLeaf }, + { MKID_BE('edts'), &QTPlayer::readDefault }, + { MKID_BE('elst'), &QTPlayer::readELST }, + { MKID_BE('hdlr'), &QTPlayer::readHDLR }, + { MKID_BE('mdat'), &QTPlayer::readMDAT }, + { MKID_BE('mdhd'), &QTPlayer::readMDHD }, + { MKID_BE('mdia'), &QTPlayer::readDefault }, + { MKID_BE('minf'), &QTPlayer::readDefault }, + { MKID_BE('moov'), &QTPlayer::readMOOV }, + { MKID_BE('mvhd'), &QTPlayer::readMVHD }, + { MKID_BE('smhd'), &QTPlayer::readLeaf }, + { MKID_BE('stbl'), &QTPlayer::readDefault }, + { MKID_BE('stco'), &QTPlayer::readSTCO }, + { MKID_BE('stsc'), &QTPlayer::readSTSC }, + { MKID_BE('stsd'), &QTPlayer::readSTSD }, + { MKID_BE('stss'), &QTPlayer::readSTSS }, + { MKID_BE('stsz'), &QTPlayer::readSTSZ }, + { MKID_BE('stts'), &QTPlayer::readSTTS }, + { MKID_BE('tkhd'), &QTPlayer::readTKHD }, + { MKID_BE('trak'), &QTPlayer::readTRAK }, + { MKID_BE('udta'), &QTPlayer::readLeaf }, + { MKID_BE('vmhd'), &QTPlayer::readLeaf }, + { MKID_BE('cmov'), &QTPlayer::readCMOV }, + { MKID_BE('wave'), &QTPlayer::readWAVE }, { 0, 0 } }; _parseTable = p; } -int QuickTimeDecoder::readDefault(MOVatom atom) { +int QTPlayer::readDefault(MOVatom atom) { uint32 total_size = 0; MOVatom a; int err = 0; a.offset = atom.offset; - while(((total_size + 8) < atom.size) && !_fd->eos() && _fd->pos() < _fd->size() && !err) { + while(((total_size + 8) < atom.size) && !_fd->eos() && !err) { a.size = atom.size; a.type = 0; if (atom.size >= 8) { a.size = _fd->readUint32BE(); a.type = _fd->readUint32BE(); - - // Some QuickTime videos with resource forks have mdat chunks - // that are of size 0. Adjust it so it's the correct size. - if (a.type == MKID_BE('mdat') && a.size == 0) - a.size = _fd->size(); } total_size += 8; @@ -485,14 +443,14 @@ int QuickTimeDecoder::readDefault(MOVatom atom) { return err; } -int QuickTimeDecoder::readLeaf(MOVatom atom) { +int QTPlayer::readLeaf(MOVatom atom) { if (atom.size > 1) _fd->seek(atom.size, SEEK_SET); return 0; } -int QuickTimeDecoder::readMOOV(MOVatom atom) { +int QTPlayer::readMOOV(MOVatom atom) { if (readDefault(atom) < 0) return -1; @@ -506,7 +464,7 @@ int QuickTimeDecoder::readMOOV(MOVatom atom) { return 0; // now go for mdat } -int QuickTimeDecoder::readCMOV(MOVatom atom) { +int QTPlayer::readCMOV(MOVatom atom) { #ifdef USE_ZLIB // Read in the dcom atom _fd->readUint32BE(); @@ -557,7 +515,7 @@ int QuickTimeDecoder::readCMOV(MOVatom atom) { #endif } -int QuickTimeDecoder::readMVHD(MOVatom atom) { +int QTPlayer::readMVHD(MOVatom atom) { byte version = _fd->readByte(); // version _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags @@ -612,7 +570,7 @@ int QuickTimeDecoder::readMVHD(MOVatom atom) { return 0; } -int QuickTimeDecoder::readTRAK(MOVatom atom) { +int QTPlayer::readTRAK(MOVatom atom) { MOVStreamContext *sc = new MOVStreamContext(); if (!sc) @@ -627,7 +585,7 @@ int QuickTimeDecoder::readTRAK(MOVatom atom) { } // this atom contains actual media data -int QuickTimeDecoder::readMDAT(MOVatom atom) { +int QTPlayer::readMDAT(MOVatom atom) { if (atom.size == 0) // wrong one (MP4) return 0; @@ -644,7 +602,7 @@ int QuickTimeDecoder::readMDAT(MOVatom atom) { return 0; // now go for moov } -int QuickTimeDecoder::readTKHD(MOVatom atom) { +int QTPlayer::readTKHD(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; byte version = _fd->readByte(); @@ -704,7 +662,7 @@ int QuickTimeDecoder::readTKHD(MOVatom atom) { } // edit list atom -int QuickTimeDecoder::readELST(MOVatom atom) { +int QTPlayer::readELST(MOVatom atom) { _fd->readByte(); // version _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags uint32 editCount = _streams[_numStreams - 1]->edit_count = _fd->readUint32BE(); // entries @@ -723,7 +681,7 @@ int QuickTimeDecoder::readELST(MOVatom atom) { return 0; } -int QuickTimeDecoder::readHDLR(MOVatom atom) { +int QTPlayer::readHDLR(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; _fd->readByte(); // version @@ -764,7 +722,7 @@ int QuickTimeDecoder::readHDLR(MOVatom atom) { return 0; } -int QuickTimeDecoder::readMDHD(MOVatom atom) { +int QTPlayer::readMDHD(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; byte version = _fd->readByte(); @@ -791,7 +749,7 @@ int QuickTimeDecoder::readMDHD(MOVatom atom) { return 0; } -int QuickTimeDecoder::readSTSD(MOVatom atom) { +int QTPlayer::readSTSD(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; _fd->readByte(); // version @@ -976,7 +934,7 @@ int QuickTimeDecoder::readSTSD(MOVatom atom) { return 0; } -int QuickTimeDecoder::readSTSC(MOVatom atom) { +int QTPlayer::readSTSC(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; _fd->readByte(); // version @@ -1001,7 +959,7 @@ int QuickTimeDecoder::readSTSC(MOVatom atom) { return 0; } -int QuickTimeDecoder::readSTSS(MOVatom atom) { +int QTPlayer::readSTSS(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; _fd->readByte(); // version @@ -1024,7 +982,7 @@ int QuickTimeDecoder::readSTSS(MOVatom atom) { return 0; } -int QuickTimeDecoder::readSTSZ(MOVatom atom) { +int QTPlayer::readSTSZ(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; _fd->readByte(); // version @@ -1056,7 +1014,7 @@ static uint32 ff_gcd(uint32 a, uint32 b) { else return a; } -int QuickTimeDecoder::readSTTS(MOVatom atom) { +int QTPlayer::readSTTS(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; uint32 duration = 0; uint32 total_sample_count = 0; @@ -1096,7 +1054,7 @@ int QuickTimeDecoder::readSTTS(MOVatom atom) { return 0; } -int QuickTimeDecoder::readSTCO(MOVatom atom) { +int QTPlayer::readSTCO(MOVatom atom) { MOVStreamContext *st = _streams[_numStreams - 1]; _fd->readByte(); // version @@ -1130,7 +1088,7 @@ int QuickTimeDecoder::readSTCO(MOVatom atom) { return 0; } -int QuickTimeDecoder::readWAVE(MOVatom atom) { +int QTPlayer::readWAVE(MOVatom atom) { if (_numStreams < 1) return 0; @@ -1149,7 +1107,7 @@ int QuickTimeDecoder::readWAVE(MOVatom atom) { return 0; } -void QuickTimeDecoder::close() { +void QTPlayer::close() { stopAudio(); delete _videoCodec; _videoCodec = 0; @@ -1158,7 +1116,6 @@ void QuickTimeDecoder::close() { delete _streams[i]; delete _fd; - _fd = 0; if (_scaledSurface) { _scaledSurface->free(); @@ -1169,10 +1126,10 @@ void QuickTimeDecoder::close() { // The audio stream is deleted automatically _audStream = NULL; - VideoDecoder::reset(); + Graphics::VideoDecoder::reset(); } -Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket() { +Common::SeekableReadStream *QTPlayer::getNextFramePacket() { if (_videoStreamIndex < 0) return NULL; @@ -1225,22 +1182,17 @@ Common::SeekableReadStream *QuickTimeDecoder::getNextFramePacket() { return _fd->readStream(_streams[_videoStreamIndex]->sample_sizes[getCurFrame()]); } -bool QuickTimeDecoder::checkAudioCodecSupport(uint32 tag) { +bool QTPlayer::checkAudioCodecSupport(uint32 tag) { // Check if the codec is a supported codec - if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4')) + if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4') || tag == MKID_BE('QDM2')) return true; -#ifdef GRAPHICS_QDM2_H - if (tag == MKID_BE('QDM2')) - return true; -#endif - warning("Audio Codec Not Supported: \'%s\'", tag2str(tag)); return false; } -Audio::AudioStream *QuickTimeDecoder::createAudioStream(Common::SeekableReadStream *stream) { +Audio::AudioStream *QTPlayer::createAudioStream(Common::SeekableReadStream *stream) { if (!stream || _audioStreamIndex < 0) return NULL; @@ -1261,11 +1213,9 @@ Audio::AudioStream *QuickTimeDecoder::createAudioStream(Common::SeekableReadStre } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('ima4')) { // Riven uses this codec (as do some Myst ME videos) return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, _streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels, 34); -#ifdef GRAPHICS_QDM2_H } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('QDM2')) { // Several Myst ME videos use this codec - return makeQDM2Stream(stream, _streams[_audioStreamIndex]->extradata); -#endif + return new QDM2Stream(stream, _streams[_audioStreamIndex]->extradata); } error("Unsupported audio codec"); @@ -1273,12 +1223,12 @@ Audio::AudioStream *QuickTimeDecoder::createAudioStream(Common::SeekableReadStre return NULL; } -void QuickTimeDecoder::updateAudioBuffer() { +void QTPlayer::updateAudioBuffer() { if (!_audStream) return; - // Keep three streams in buffer so that if/when the first two end, it goes right into the next - for (; _audStream->numQueuedStreams() < 3 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) { + // Keep two streams in buffer so that when the first ends, it goes right into the next + for (; _audStream->numQueuedStreams() < 2 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) { Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); @@ -1319,4 +1269,4 @@ void QuickTimeDecoder::updateAudioBuffer() { } } -} // End of namespace Graphics +} // End of namespace Mohawk diff --git a/graphics/video/qt_decoder.h b/engines/mohawk/video/qt_player.h index db4ff8180b..6657d3edba 100644 --- a/graphics/video/qt_decoder.h +++ b/engines/mohawk/video/qt_player.h @@ -31,8 +31,8 @@ // Seek function by Gael Chardon gael.dev@4now.net // -#ifndef GRAPHICS_QT_DECODER_H -#define GRAPHICS_QT_DECODER_H +#ifndef MOHAWK_QT_PLAYER_H +#define MOHAWK_QT_PLAYER_H #include "common/scummsys.h" #include "common/queue.h" @@ -45,10 +45,9 @@ namespace Common { class File; - class MacResManager; } -namespace Graphics { +namespace Mohawk { enum ScaleMode { kScaleNormal = 1, @@ -56,10 +55,10 @@ enum ScaleMode { kScaleQuarter = 4 }; -class QuickTimeDecoder : public RewindableVideoDecoder { +class QTPlayer : public Graphics::RewindableVideoDecoder { public: - QuickTimeDecoder(); - virtual ~QuickTimeDecoder(); + QTPlayer(); + virtual ~QTPlayer(); /** * Returns the width of the video @@ -80,12 +79,6 @@ public: uint32 getFrameCount() const; /** - * Load a video file - * @param filename the filename to load - */ - bool loadFile(const Common::String &filename); - - /** * Load a QuickTime video file from a SeekableReadStream * @param stream the stream to load */ @@ -111,11 +104,12 @@ public: void setChunkBeginOffset(uint32 offset) { _beginOffset = offset; } bool isVideoLoaded() const { return _fd != 0; } - Surface *decodeNextFrame(); + Graphics::Surface *decodeNextFrame(); + bool needsUpdate() const; bool endOfVideo() const; uint32 getElapsedTime() const; uint32 getTimeToNextFrame() const; - PixelFormat getPixelFormat() const; + Graphics::PixelFormat getPixelFormat() const; // RewindableVideoDecoder API void rewind(); @@ -137,8 +131,8 @@ protected: }; struct ParseTable { - int (QuickTimeDecoder::*func)(MOVatom atom); uint32 type; + int (QTPlayer::*func)(MOVatom atom); }; struct MOVstts { @@ -227,6 +221,7 @@ protected: uint32 _duration; uint32 _mdatOffset; uint32 _mdatSize; + uint32 _next_chunk_offset; MOVStreamContext *_partial; uint32 _numStreams; int _ni; @@ -235,7 +230,6 @@ protected: byte _palette[256 * 3]; bool _dirtyPalette; uint32 _beginOffset; - Common::MacResManager *_resFork; void initParseTable(); Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream); @@ -244,7 +238,6 @@ protected: uint32 getFrameDuration(); uint32 getCodecTag(); byte getBitsPerPixel(); - void init(); Audio::QueuingAudioStream *_audStream; void startAudio(); @@ -253,13 +246,13 @@ protected: uint _curAudioChunk; Audio::SoundHandle _audHandle; - Codec *createCodec(uint32 codecTag, byte bitsPerPixel); - Codec *_videoCodec; + Graphics::Codec *createCodec(uint32 codecTag, byte bitsPerPixel); + Graphics::Codec *_videoCodec; uint32 _nextFrameStartTime; int8 _videoStreamIndex; - Surface *_scaledSurface; - Surface *scaleSurface(Surface *frame); + Graphics::Surface *_scaledSurface; + Graphics::Surface *scaleSurface(Graphics::Surface *frame); ScaleMode getScaleMode() const; void pauseVideoIntern(bool pause); @@ -284,6 +277,6 @@ protected: int readWAVE(MOVatom atom); }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/graphics/video/codecs/qtrle.cpp b/engines/mohawk/video/qtrle.cpp index 3e3fd4cfce..c06dbefcb3 100644 --- a/graphics/video/codecs/qtrle.cpp +++ b/engines/mohawk/video/qtrle.cpp @@ -26,7 +26,7 @@ // QuickTime RLE Decoder // Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson) -#include "graphics/video/codecs/qtrle.h" +#include "mohawk/video/qtrle.h" #include "common/scummsys.h" #include "common/stream.h" @@ -34,9 +34,9 @@ #include "graphics/colormasks.h" #include "graphics/surface.h" -namespace Graphics { +namespace Mohawk { -QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Codec() { +QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Graphics::Codec() { _bitsPerPixel = bitsPerPixel; _pixelFormat = g_system->getScreenFormat(); @@ -47,7 +47,7 @@ QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Cod debug(2, "QTRLE corrected width: %d", width); - _surface = new Surface(); + _surface = new Graphics::Surface(); _surface->create(width, height, _bitsPerPixel <= 8 ? 1 : _pixelFormat.bytesPerPixel); } @@ -239,7 +239,7 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, u while (rleCode--) { // Convert from RGB555 to the format specified by the Overlay byte r = 0, g = 0, b = 0; - colorToRGB<ColorMasks<555> >(rgb16, r, g, b); + Graphics::colorToRGB<Graphics::ColorMasks<555> >(rgb16, r, g, b); rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); } } else { @@ -252,7 +252,7 @@ void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, u // Convert from RGB555 to the format specified by the Overlay byte r = 0, g = 0, b = 0; - colorToRGB<ColorMasks<555> >(rgb16, r, g, b); + Graphics::colorToRGB<Graphics::ColorMasks<555> >(rgb16, r, g, b); rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); } } @@ -354,7 +354,7 @@ void QTRLEDecoder::decode32(Common::SeekableReadStream *stream, uint32 rowPtr, u } } -Surface *QTRLEDecoder::decodeImage(Common::SeekableReadStream *stream) { +Graphics::Surface *QTRLEDecoder::decodeImage(Common::SeekableReadStream *stream) { uint16 start_line = 0; uint16 height = _surface->h; @@ -417,4 +417,4 @@ QTRLEDecoder::~QTRLEDecoder() { _surface->free(); } -} // End of namespace Graphics +} // End of namespace Mohawk diff --git a/graphics/video/codecs/qtrle.h b/engines/mohawk/video/qtrle.h index efbef14411..2832bd6b24 100644 --- a/graphics/video/codecs/qtrle.h +++ b/engines/mohawk/video/qtrle.h @@ -23,27 +23,27 @@ * */ -#ifndef GRAPHICS_VIDEO_QTRLE_H -#define GRAPHICS_VIDEO_QTRLE_H +#ifndef MOHAWK_QTRLE_H +#define MOHAWK_QTRLE_H #include "graphics/pixelformat.h" #include "graphics/video/codecs/codec.h" -namespace Graphics { +namespace Mohawk { -class QTRLEDecoder : public Codec { +class QTRLEDecoder : public Graphics::Codec { public: QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel); ~QTRLEDecoder(); - Surface *decodeImage(Common::SeekableReadStream *stream); - PixelFormat getPixelFormat() const { return _pixelFormat; } + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: byte _bitsPerPixel; - Surface *_surface; - PixelFormat _pixelFormat; + Graphics::Surface *_surface; + Graphics::PixelFormat _pixelFormat; void decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); void decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp); @@ -53,6 +53,6 @@ private: void decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/graphics/video/codecs/rpza.cpp b/engines/mohawk/video/rpza.cpp index f0ed72e730..f48c055ae2 100644 --- a/graphics/video/codecs/rpza.cpp +++ b/engines/mohawk/video/rpza.cpp @@ -25,14 +25,14 @@ // Based off ffmpeg's RPZA decoder -#include "graphics/video/codecs/rpza.h" +#include "mohawk/video/rpza.h" #include "common/system.h" #include "graphics/colormasks.h" -namespace Graphics { +namespace Mohawk { -RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() { +RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Graphics::Codec() { _pixelFormat = g_system->getScreenFormat(); // We need to increase the surface size to a multiple of 4 @@ -42,7 +42,7 @@ RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() { debug(2, "RPZA corrected width: %d", width); - _surface = new Surface(); + _surface = new Graphics::Surface(); _surface->create(width, height, _pixelFormat.bytesPerPixel); } @@ -60,7 +60,7 @@ RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() { #define PUT_PIXEL(color) \ if ((int32)blockPtr < _surface->w * _surface->h) { \ byte r = 0, g = 0, b = 0; \ - colorToRGB<ColorMasks<555> >(color, r, g, b); \ + Graphics::colorToRGB<Graphics::ColorMasks<555> >(color, r, g, b); \ if (_pixelFormat.bytesPerPixel == 2) \ *((uint16 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \ else \ @@ -68,7 +68,7 @@ RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Codec() { } \ blockPtr++ -Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) { +Graphics::Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) { uint16 colorA = 0, colorB = 0; uint16 color4[4]; @@ -205,4 +205,4 @@ Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) { return _surface; } -} // End of namespace Graphics +} // End of namespace Mohawk diff --git a/graphics/video/codecs/rpza.h b/engines/mohawk/video/rpza.h index e6d32feb72..c6d0ada6f5 100644 --- a/graphics/video/codecs/rpza.h +++ b/engines/mohawk/video/rpza.h @@ -23,27 +23,27 @@ * */ -#ifndef GRAPHICS_VIDEO_RPZA_H -#define GRAPHICS_VIDEO_RPZA_H +#ifndef MOHAWK_RPZA_H +#define MOHAWK_RPZA_H #include "graphics/pixelformat.h" #include "graphics/video/codecs/codec.h" -namespace Graphics { +namespace Mohawk { -class RPZADecoder : public Codec { +class RPZADecoder : public Graphics::Codec { public: RPZADecoder(uint16 width, uint16 height); ~RPZADecoder() { delete _surface; } - Surface *decodeImage(Common::SeekableReadStream *stream); - PixelFormat getPixelFormat() const { return _pixelFormat; } + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } private: - Surface *_surface; - PixelFormat _pixelFormat; + Graphics::Surface *_surface; + Graphics::PixelFormat _pixelFormat; }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/graphics/video/codecs/smc.cpp b/engines/mohawk/video/smc.cpp index 4661e3dc84..4a0d16dfcc 100644 --- a/graphics/video/codecs/smc.cpp +++ b/engines/mohawk/video/smc.cpp @@ -25,9 +25,9 @@ // Based off ffmpeg's SMC decoder -#include "graphics/video/codecs/smc.h" +#include "mohawk/video/smc.h" -namespace Graphics { +namespace Mohawk { #define GET_BLOCK_COUNT() \ (opcode & 0x10) ? (1 + stream->readByte()) : 1 + (opcode & 0x0F); @@ -382,4 +382,4 @@ Graphics::Surface *SMCDecoder::decodeImage(Common::SeekableReadStream *stream) { return _surface; } -} // End of namespace Graphics +} // End of namespace Mohawk diff --git a/graphics/video/codecs/smc.h b/engines/mohawk/video/smc.h index 2d4355a83e..c52226100e 100644 --- a/graphics/video/codecs/smc.h +++ b/engines/mohawk/video/smc.h @@ -23,12 +23,12 @@ * */ -#ifndef GRAPHICS_VIDEO_SMC_H -#define GRAPHICS_VIDEO_SMC_H +#ifndef MOHAWK_VIDEO_SMC_H +#define MOHAWK_VIDEO_SMC_H #include "graphics/video/codecs/codec.h" -namespace Graphics { +namespace Mohawk { enum { CPAIR = 2, @@ -37,16 +37,16 @@ enum { COLORS_PER_TABLE = 256 }; -class SMCDecoder : public Codec { +class SMCDecoder : public Graphics::Codec { public: SMCDecoder(uint16 width, uint16 height); ~SMCDecoder() { delete _surface; } - Surface *decodeImage(Common::SeekableReadStream *stream); - PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); } + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } private: - Surface *_surface; + Graphics::Surface *_surface; // SMC color tables byte _colorPairs[COLORS_PER_TABLE * CPAIR]; @@ -54,6 +54,6 @@ private: byte _colorOctets[COLORS_PER_TABLE * COCTET]; }; -} // End of namespace Graphics +} // End of namespace Mohawk #endif diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video/video.cpp index a45a4294c8..86ecd4dedf 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video/video.cpp @@ -24,10 +24,10 @@ */ #include "mohawk/resource.h" -#include "mohawk/video.h" +#include "mohawk/video/video.h" +#include "mohawk/video/qt_player.h" #include "common/events.h" -#include "graphics/video/qt_decoder.h" namespace Mohawk { @@ -35,19 +35,18 @@ VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { } VideoManager::~VideoManager() { + _mlstRecords.clear(); stopVideos(); } void VideoManager::pauseVideos() { for (uint16 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].video) - _videoStreams[i]->pauseVideo(true); + _videoStreams[i]->pauseVideo(true); } void VideoManager::resumeVideos() { for (uint16 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].video) - _videoStreams[i]->pauseVideo(false); + _videoStreams[i]->pauseVideo(false); } void VideoManager::stopVideos() { @@ -90,7 +89,7 @@ void VideoManager::playMovieCentered(Common::String filename, bool clearScreen) void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { bool continuePlaying = true; - while (_videoStreams[videoHandle].video && !_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) { + while (!_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) { if (updateBackgroundMovies()) _vm->_system->updateScreen(); @@ -121,10 +120,8 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { _vm->_system->delayMillis(10); } - delete _videoStreams[videoHandle].video; - _videoStreams[videoHandle].video = 0; - _videoStreams[videoHandle].id = 0; - _videoStreams[videoHandle].filename.clear(); + _videoStreams[videoHandle]->close(); + _videoStreams.clear(); } void VideoManager::playBackgroundMovie(Common::String filename, int16 x, int16 y, bool loop) { @@ -155,9 +152,8 @@ bool VideoManager::updateBackgroundMovies() { _videoStreams[i]->rewind(); } else { delete _videoStreams[i].video; - _videoStreams[i].video = 0; - _videoStreams[i].id = 0; - _videoStreams[i].filename.clear(); + memset(&_videoStreams[i], 0, sizeof(VideoEntry)); + _videoStreams[i].video = NULL; continue; } } @@ -244,15 +240,7 @@ void VideoManager::activateMLST(uint16 mlstId, uint16 card) { if (mlstRecord.u1 != 1) warning("mlstRecord.u1 not 1"); - // We've found a match, add it if (mlstRecord.index == mlstId) { - // Make sure we don't have any duplicates - for (uint32 j = 0; j < _mlstRecords.size(); j++) - if (_mlstRecords[j].index == mlstRecord.index || _mlstRecords[j].code == mlstRecord.code) { - _mlstRecords.remove_at(j); - j--; - } - _mlstRecords.push_back(mlstRecord); break; } @@ -261,10 +249,6 @@ void VideoManager::activateMLST(uint16 mlstId, uint16 card) { delete mlstStream; } -void VideoManager::clearMLST() { - _mlstRecords.clear(); -} - void VideoManager::playMovie(uint16 id) { for (uint16 i = 0; i < _mlstRecords.size(); i++) if (_mlstRecords[i].code == id) { @@ -290,10 +274,8 @@ void VideoManager::stopMovie(uint16 id) { if (_mlstRecords[i].code == id) for (uint16 j = 0; j < _videoStreams.size(); j++) if (_mlstRecords[i].movieID == _videoStreams[j].id) { - delete _videoStreams[j].video; - _videoStreams[j].video = 0; - _videoStreams[j].id = 0; - _videoStreams[j].filename.clear(); + delete _videoStreams[i].video; + memset(&_videoStreams[i].video, 0, sizeof(VideoEntry)); return; } } @@ -334,7 +316,7 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool // Otherwise, create a new entry VideoEntry entry; - entry.video = new Graphics::QuickTimeDecoder(); + entry.video = new QTPlayer(); entry.x = x; entry.y = y; entry.filename = ""; @@ -364,7 +346,7 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u // Otherwise, create a new entry VideoEntry entry; - entry.video = new Graphics::QuickTimeDecoder(); + entry.video = new QTPlayer(); entry.x = x; entry.y = y; entry.filename = filename; @@ -392,24 +374,4 @@ VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, u return _videoStreams.size() - 1; } -VideoHandle VideoManager::findVideoHandle(uint16 id) { - for (uint16 i = 0; i < _mlstRecords.size(); i++) - if (_mlstRecords[i].code == id) - for (uint16 j = 0; j < _videoStreams.size(); j++) - if (_videoStreams[j].video && _mlstRecords[i].movieID == _videoStreams[j].id) - return j; - - return NULL_VID_HANDLE; -} - -int32 VideoManager::getCurFrame(const VideoHandle &handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getCurFrame(); -} - -uint32 VideoManager::getFrameCount(const VideoHandle &handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getFrameCount(); -} - } // End of namespace Mohawk diff --git a/engines/mohawk/video.h b/engines/mohawk/video/video.h index 6aa553e26b..a5d2bde65d 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video/video.h @@ -28,10 +28,6 @@ #include "graphics/pixelformat.h" -namespace Graphics { - class QuickTimeDecoder; -} - namespace Mohawk { class MohawkEngine; @@ -48,8 +44,10 @@ struct MLSTRecord { uint16 u1; }; +class QTPlayer; + struct VideoEntry { - Graphics::QuickTimeDecoder *video; + QTPlayer *video; uint16 x; uint16 y; bool loop; @@ -57,7 +55,7 @@ struct VideoEntry { uint16 id; // Riven only bool enabled; - Graphics::QuickTimeDecoder *operator->() const { assert(video); return video; } + QTPlayer *operator->() const { assert(video); return video; } }; typedef int32 VideoHandle; @@ -82,7 +80,6 @@ public: // Riven-related functions void activateMLST(uint16 mlstId, uint16 card); - void clearMLST(); void enableMovie(uint16 id); void disableMovie(uint16 id); void disableAllMovies(); @@ -90,23 +87,19 @@ public: void stopMovie(uint16 id); void playMovieBlocking(uint16 id); - // Handle functions - VideoHandle findVideoHandle(uint16 id); - int32 getCurFrame(const VideoHandle &handle); - uint32 getFrameCount(const VideoHandle &handle); + // Riven-related variables + Common::Array<MLSTRecord> _mlstRecords; private: MohawkEngine *_vm; - // Riven-related variables - Common::Array<MLSTRecord> _mlstRecords; + void waitUntilMovieEnds(VideoHandle videoHandle); // Keep tabs on any videos playing Common::Array<VideoEntry> _videoStreams; VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop); VideoHandle createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop); - void waitUntilMovieEnds(VideoHandle videoHandle); }; } // End of namespace Mohawk diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index b5eb82b456..8864c84e2f 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -42,7 +42,6 @@ Debugger::Debugger(Parallaction *vm) DCmd_Register("zones", WRAP_METHOD(Debugger, Cmd_Zones)); DCmd_Register("animations", WRAP_METHOD(Debugger, Cmd_Animations)); DCmd_Register("globalflags",WRAP_METHOD(Debugger, Cmd_GlobalFlags)); - DCmd_Register("toggleglobalflag",WRAP_METHOD(Debugger, Cmd_ToggleGlobalFlag)); DCmd_Register("localflags", WRAP_METHOD(Debugger, Cmd_LocalFlags)); DCmd_Register("locations", WRAP_METHOD(Debugger, Cmd_Locations)); DCmd_Register("gfxobjects", WRAP_METHOD(Debugger, Cmd_GfxObjects)); @@ -118,32 +117,6 @@ bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) { return true; } -bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) { - - int i; - - switch (argc) { - case 2: - i = _vm->_globalFlagsNames->lookup(argv[1]); - if (i == Table::notFound) { - DebugPrintf("invalid flag '%s'\n", argv[1]); - } else { - i--; - if ((_globalFlags & (1 << i)) == 0) - _globalFlags |= (1 << i); - else - _globalFlags &= ~(1 << i); - } - break; - - default: - DebugPrintf("toggleglobalflag <flag name>\n"); - - } - - return true; -} - bool Debugger::Cmd_LocalFlags(int argc, const char **argv) { uint32 flags = _vm->getLocationFlags(); diff --git a/engines/parallaction/debug.h b/engines/parallaction/debug.h index 5267206d04..54b578e95f 100644 --- a/engines/parallaction/debug.h +++ b/engines/parallaction/debug.h @@ -28,7 +28,6 @@ protected: bool Cmd_Animations(int argc, const char **argv); bool Cmd_LocalFlags(int argc, const char **argv); bool Cmd_GlobalFlags(int argc, const char **argv); - bool Cmd_ToggleGlobalFlag(int argc, const char **argv); bool Cmd_Locations(int argc, const char **argv); bool Cmd_GfxObjects(int argc, const char **argv); bool Cmd_Programs(int argc, const char** argv); diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index 7a28d18f17..6332600226 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -461,10 +461,6 @@ public: void Parallaction::enterDialogueMode(ZonePtr z) { - if (!z->u._speakDialogue) { - return; - } - debugC(1, kDebugDialogue, "Parallaction::enterDialogueMode(%s)", z->u._filename.c_str()); _dialogueMan = createDialogueManager(z); assert(_dialogueMan); diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index e145c0da94..d5d89616d2 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -337,7 +337,7 @@ DECLARE_COMMAND_OPCODE(speak) { return; } - if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak && ctxt._cmd->_zone->u._speakDialogue) { + if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak) { _vm->enterDialogueMode(ctxt._cmd->_zone); } else { _vm->_activeZone = ctxt._cmd->_zone; diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index bc1759ecd7..a7b1f6fe32 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -536,12 +536,12 @@ GfxObj *Gfx::renderFloatingLabel(Font *font, char *text) { setupLabelSurface(*cnv, w, h); - font->setColor((_gameType == GType_BRA) ? 0 : 7); + font->setColor((_vm->getGameType() == GType_BRA) ? 0 : 7); font->drawString((byte*)cnv->pixels + 1, cnv->w, text); font->drawString((byte*)cnv->pixels + 1 + cnv->w * 2, cnv->w, text); font->drawString((byte*)cnv->pixels + cnv->w, cnv->w, text); font->drawString((byte*)cnv->pixels + 2 + cnv->w, cnv->w, text); - font->setColor((_gameType == GType_BRA) ? 11 : 1); + font->setColor((_vm->getGameType() == GType_BRA) ? 11 : 1); font->drawString((byte*)cnv->pixels + 1 + cnv->w, cnv->w, text); } else { w = font->getStringWidth(text); @@ -833,7 +833,7 @@ void Gfx::setBackground(uint type, BackgroundInfo *info) { // The PC version of BRA needs the entries 20-31 of the palette to be constant, but // the background resource files are screwed up. The right colors come from an unused // bitmap (pointer.bmp). Nothing is known about the Amiga version so far. - if ((_gameType == GType_BRA) && (_vm->getPlatform() == Common::kPlatformPC)) { + if ((_vm->getGameType() == GType_BRA) && (_vm->getPlatform() == Common::kPlatformPC)) { int r, g, b; for (uint i = 16; i < 32; i++) { _backupPal.getEntry(i, r, g, b); diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index d6dd9feb19..60cc867226 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -204,13 +204,13 @@ int Input::updateGameInput() { return event; } - if (_gameType == GType_Nippon) { + if (_vm->getGameType() == GType_Nippon) { if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) { if (_keyPressed.keycode == Common::KEYCODE_l) event = kEvLoadGame; if (_keyPressed.keycode == Common::KEYCODE_s) event = kEvSaveGame; } } else - if (_gameType == GType_BRA) { + if (_vm->getGameType() == GType_BRA) { if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) { if (_keyPressed.keycode == Common::KEYCODE_F5) event = kEvIngameMenu; } @@ -326,13 +326,8 @@ bool Input::translateGameInput() { if ((_mouseButtons == kMouseLeftUp) && ((_activeItem._id != 0) || (ACTIONTYPE(z) == kZoneCommand))) { - bool noWalk = z->_flags & kFlagsNoWalk; // check the explicit no-walk flag - if (_gameType == GType_BRA) { - // action performed on object marked for self-use do not need walk in BRA - noWalk |= ((z->_flags & kFlagsYourself) != 0); - } - - if (noWalk) { + if (z->_flags & kFlagsNoWalk) { + // character doesn't need to walk to take specified action takeAction(z); } else { // action delayed: if Zone defined a moveto position the character is programmed to move there, @@ -357,7 +352,7 @@ bool Input::translateGameInput() { void Input::enterInventoryMode() { Common::Point mousePos; - getAbsoluteCursorPos(mousePos); + getCursorPos(mousePos); bool hitCharacter = _vm->hitZone(kZoneYou, mousePos.x, mousePos.y); if (hitCharacter) { diff --git a/engines/parallaction/objects.h b/engines/parallaction/objects.h index 36231cfcc5..50a789247f 100644 --- a/engines/parallaction/objects.h +++ b/engines/parallaction/objects.h @@ -88,9 +88,9 @@ enum ZoneFlags { kFlagsNoWalk = 0x800, // Zone: character doesn't need to walk towards object to interact // BRA specific - kFlagsYourself = 0x1000, // BRA: marks zones used by the character on him/herself + kFlagsYourself = 0x1000, kFlagsScaled = 0x2000, - kFlagsSelfuse = 0x4000, // BRA: marks zones to be preserved across location changes (see Parallaction::freeZones) + kFlagsSelfuse = 0x4000, kFlagsIsAnimation = 0x1000000, // BRA: used in walk code (trap check), to tell is a Zone is an Animation kFlagsAnimLinked = 0x2000000 }; diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index ce7525345a..cb208a17ff 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -102,8 +102,7 @@ Parallaction::~Parallaction() { Common::Error Parallaction::init() { - - _gameType = getGameType(); + _engineFlags = 0; _objectsNames = NULL; _globalFlagsNames = NULL; @@ -409,7 +408,7 @@ void Parallaction::drawAnimation(AnimationPtr anim) { uint16 layer = LAYER_FOREGROUND; uint16 scale = 100; - switch (_gameType) { + switch (getGameType()) { case GType_Nippon: if ((anim->_flags & kFlagsNoMasked) == 0) { // Layer in NS depends on where the animation is on the screen, for each animation. @@ -524,7 +523,7 @@ void Parallaction::enterCommentMode(ZonePtr z) { } // TODO: move this balloons stuff into DialogueManager and BalloonManager - if (_gameType == GType_Nippon) { + if (getGameType() == GType_Nippon) { if (!data->_filename.empty()) { if (data->_gfxobj == 0) { data->_gfxobj = _disk->loadStatic(data->_filename.c_str()); @@ -541,7 +540,7 @@ void Parallaction::enterCommentMode(ZonePtr z) { _gfx->setItem(_char._talk, 190, 80); } } else - if (_gameType == GType_BRA) { + if (getGameType() == GType_BRA) { _balloonMan->setSingleBalloon(data->_examineText.c_str(), 0, 0, 1, BalloonManager::kNormalColor); _gfx->setItem(_char._talk, 10, 80); } @@ -652,21 +651,13 @@ bool Parallaction::pickupItem(ZonePtr z) { return (slot != -1); } +// FIXME: input coordinates must be offseted to handle scrolling! bool Parallaction::checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y) { - // check if really a special zone - if (_gameType == GType_Nippon) { - // so-called special zones in NS have special x coordinates - if ((z->getX() != -2) && (z->getX() != -3)) { - return false; - } - } - if (_gameType == GType_BRA) { - // so far, special zones in BRA are only merge zones - if (ACTIONTYPE(z) != kZoneMerge) { - return false; - } + // not a special zone + if ((z->getX() != -2) && (z->getX() != -3)) { + return false; } - + // WORKAROUND: this huge condition is needed because we made TypeData a collection of structs // instead of an union. So, merge->_obj1 and get->_icon were just aliases in the original engine, // but we need to check it separately here. The same workaround is applied in freeZones. @@ -690,33 +681,7 @@ bool Parallaction::checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y) { return false; } -bool Parallaction::checkZoneType(ZonePtr z, uint32 type) { - if (_gameType == GType_Nippon) { - if ((type == 0) && (ITEMTYPE(z) == 0)) - return true; - } - - if (_gameType == GType_BRA) { - if (type == 0) { - if (ITEMTYPE(z) == 0) { - if (ACTIONTYPE(z) != kZonePath) { - return true; - } - } - if (ACTIONTYPE(z) == kZoneDoor) { - return true; - } - } - } - - if (z->_type == type) - return true; - if (ITEMTYPE(z) == type) - return true; - - return false; -} - +// FIXME: input coordinates must be offseted to handle scrolling! bool Parallaction::checkZoneBox(ZonePtr z, uint32 type, uint x, uint y) { if (z->_flags & kFlagsRemove) return false; @@ -724,30 +689,29 @@ bool Parallaction::checkZoneBox(ZonePtr z, uint32 type, uint x, uint y) { debugC(5, kDebugExec, "checkZoneBox for %s (type = %x, x = %i, y = %i)", z->_name, type, x, y); if (!z->hitRect(x, y)) { + // check for special zones (items defined in common.loc) if (checkSpecialZoneBox(z, type, x, y)) return true; - // check if self-use zone (nothing to do with kFlagsSelfuse) - if (_gameType == GType_Nippon) { - if (z->getX() != -1) { // no explicit self-use flag in NS - return false; - } - } - if (_gameType == GType_BRA) { - if (!(z->_flags & kFlagsYourself)) { - return false; - } - } - if (!_char._ani->hitFrameRect(x, y)) { + if (z->getX() != -1) + return false; + if (!_char._ani->hitFrameRect(x, y)) return false; - } - // we get here only if (x,y) hits the character and the zone is marked as self-use } - return checkZoneType(z, type); + // normal Zone + if ((type == 0) && (ITEMTYPE(z) == 0)) + return true; + if (z->_type == type) + return true; + if (ITEMTYPE(z) == type) + return true; + + return false; } +// FIXME: input coordinates must be offseted to handle scrolling! bool Parallaction::checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y) { if (z->_flags & kFlagsRemove) return false; @@ -763,14 +727,18 @@ bool Parallaction::checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y) { return false; } - return checkZoneType(z, type); -} + // NOTE: the implementation of the following lines is a different in the + // original... it is working so far, though + if ((type == 0) && (ITEMTYPE(z) == 0)) + return true; + if (z->_type == type) + return true; + if (ITEMTYPE(z) == type) + return true; -/* NOTE: hitZone needs to be passed absolute game coordinates to work. + return false; +} - When type is kZoneMerge, then x and y are the identifiers of the objects to merge, - and the above requirement does not apply. -*/ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { uint16 _di = y; uint16 _si = x; @@ -784,20 +752,14 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { } } + int16 _a, _b, _c, _d; bool _ef; for (AnimationList::iterator ait = _location._animations.begin(); ait != _location._animations.end(); ++ait) { AnimationPtr a = *ait; - _a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation - - if (!_a) { - if (_gameType == GType_BRA && ACTIONTYPE(a) != kZoneTrap) { - continue; - } - } - + _a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation _ef = a->hitFrameRect(_si, _di); _b = ((type != 0) || (a->_type == kZoneYou)) ? 0 : 1; // _b: (no type specified) AND (Animation is not the character) @@ -989,7 +951,7 @@ bool CharacterName::dummy() const { } void Parallaction::beep() { - if (_gameType == GType_Nippon) { + if (getGameType() == GType_Nippon) { _soundMan->execute(SC_SETSFXCHANNEL, 3); _soundMan->execute(SC_SETSFXVOLUME, 127); _soundMan->execute(SC_SETSFXLOOPING, (int32)0); diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 151bfd958d..a5769350bd 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -280,7 +280,6 @@ public: int32 _screenWidth; int32 _screenHeight; int32 _screenSize; - int _gameType; // subsystems Gfx *_gfx; @@ -361,7 +360,6 @@ public: uint32 getLocationFlags(); bool checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y); bool checkZoneBox(ZonePtr z, uint32 type, uint x, uint y); - bool checkZoneType(ZonePtr z, uint32 type); bool checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y); ZonePtr hitZone(uint32 type, uint16 x, uint16 y); void runZone(ZonePtr z); diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index c752c85d4f..9fd46cc9af 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -195,7 +195,7 @@ void Parallaction_br::runPendingZones() { if (_activeZone) { z = _activeZone; // speak Zone or sound _activeZone.reset(); - if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) { + if (ACTIONTYPE(z) == kZoneSpeak) { enterDialogueMode(z); } else { runZone(z); // FIXME: BRA doesn't handle sound yet @@ -205,7 +205,7 @@ void Parallaction_br::runPendingZones() { if (_activeZone2) { z = _activeZone2; // speak Zone or sound _activeZone2.reset(); - if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) { + if (ACTIONTYPE(z) == kZoneSpeak) { enterDialogueMode(z); } else { runZone(z); // FIXME: BRA doesn't handle sound yet diff --git a/engines/parallaction/parser.cpp b/engines/parallaction/parser.cpp index df1e91e8b4..928f3f5b74 100644 --- a/engines/parallaction/parser.cpp +++ b/engines/parallaction/parser.cpp @@ -44,10 +44,8 @@ Script::~Script() { /* * readLineIntern read a text line and prepares it for * parsing, by stripping the leading whitespace and - * changing tabs to spaces. It will stop on a CR, LF, or - * SUB (0x1A), which may all occur at the end of a script - * line. - * Returns an empty string (length = 0) when a line + * changing tabs to spaces. It will stop on a CR or LF, + * and return an empty string (length = 0) when a line * has no printable text in it. */ char *Script::readLineIntern(char *buf, size_t bufSize) { @@ -56,8 +54,7 @@ char *Script::readLineIntern(char *buf, size_t bufSize) { char c = _input->readSByte(); if (_input->eos()) break; - // break if EOL - if (c == '\n' || c == '\r' || c == (char)0x1A) + if (c == '\n' || c == '\r') break; if (c == '\t') c = ' '; diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index ff24a06ceb..be72cf73a1 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -286,7 +286,6 @@ void LocationParser_ns::parseAnimation(AnimationList &list, char *name) { debugC(5, kDebugParser, "parseAnimation(name: %s)", name); if (_vm->_location.findAnimation(name)) { - _zoneProg++; _script->skip("endanimation"); return; } @@ -1306,7 +1305,6 @@ void LocationParser_ns::parseZone(ZoneList &list, char *name) { debugC(5, kDebugParser, "parseZone(name: %s)", name); if (_vm->_location.findZone(name)) { - _zoneProg++; _script->skip("endzone"); return; } diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp index 407dd86ec3..1c724ddc1c 100644 --- a/engines/parallaction/sound_br.cpp +++ b/engines/parallaction/sound_br.cpp @@ -172,11 +172,11 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) { byte *pos = data; - if (memcmp("MSCt", pos, 4)) { + uint32 signature = read4high(pos); + if (memcmp("tCSM", &signature, 4)) { warning("Expected header not found in music file."); return false; } - pos += 4; _beats = read1(pos); _ppqn = read2low(pos); diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index ed8a9055ba..2911d9c451 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -330,6 +330,20 @@ Common::Error SagaEngine::run() { syncSoundSettings(); + +#if 0 + // FIXME: Disabled this code for now. We want to get rid of OSystem::kFeatureAutoComputeDirtyRects + // and this is the last place to make use of it. We need to find out whether doing + // so causes any regressions. If it does, we can reenable it, if not, we can remove + // this code in 0.13.0. + + // FIXME: This is the ugly way of reducing redraw overhead. It works + // well for 320x200 but it's unclear how well it will work for + // 640x480. + if (getGameId() == GID_ITE) + _system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true); +#endif + int msec = 0; _previousTicks = _system->getMillis(); diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index bad39d3065..16bc69115f 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -47,7 +47,6 @@ #include "sci/graphics/cursor.h" #include "sci/graphics/screen.h" #include "sci/graphics/paint.h" -#include "sci/graphics/paint16.h" #include "sci/graphics/palette.h" #include "sci/parser/vocabulary.h" @@ -105,7 +104,6 @@ Console::Console(SciEngine *engine) : GUI::Debugger() { DCmd_Register("resource_types", WRAP_METHOD(Console, cmdResourceTypes)); DCmd_Register("list", WRAP_METHOD(Console, cmdList)); DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep)); - DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts)); // Game DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame)); DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame)); @@ -278,8 +276,8 @@ void Console::postEnter() { #if 0 // Unused #define LOOKUP_SPECIES(species) (\ - (species >= 1000) ? species : *(s->_classTable[species].scriptposp) \ - + s->_classTable[species].class_offset) + (species >= 1000) ? species : *(s->_classtable[species].scriptposp) \ + + s->_classtable[species].class_offset) #endif bool Console::cmdHelp(int argc, const char **argv) { @@ -325,7 +323,6 @@ bool Console::cmdHelp(int argc, const char **argv) { DebugPrintf(" resource_types - Shows the valid resource types\n"); DebugPrintf(" list - Lists all the resources of a given type\n"); DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n"); - DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n"); DebugPrintf("\n"); DebugPrintf("Game:\n"); DebugPrintf(" save_game - Saves the current game state to the hard disk\n"); @@ -424,9 +421,10 @@ const char *selector_name(EngineState *s, int selector) { bool Console::cmdGetVersion(int argc, const char **argv) { const char *viewTypeDesc[] = { "Unknown", "EGA", "VGA", "VGA SCI1.1", "Amiga" }; + EngineState *s = _engine->_gamestate; bool hasVocab997 = g_sci->getResMan()->testResource(ResourceId(kResourceTypeVocab, VOCAB_RESOURCE_SELECTORS)) ? true : false; - DebugPrintf("Game ID: %s\n", _engine->getGameID()); + DebugPrintf("Game ID: %s\n", s->_gameId.c_str()); DebugPrintf("Emulated interpreter version: %s\n", getSciVersionDesc(getSciVersion())); DebugPrintf("\n"); DebugPrintf("Detected features:\n"); @@ -813,40 +811,6 @@ bool Console::cmdHexgrep(int argc, const char **argv) { return true; } -bool Console::cmdVerifyScripts(int argc, const char **argv) { - if (getSciVersion() < SCI_VERSION_1_1) { - DebugPrintf("This script check is only meant for SCI1.1-SCI2.1 games\n"); - return true; - } - - Common::List<ResourceId> *resources = _engine->getResMan()->listResources(kResourceTypeScript); - sort(resources->begin(), resources->end(), ResourceIdLess()); - Common::List<ResourceId>::iterator itr = resources->begin(); - - DebugPrintf("%d SCI1.1-SCI2.1 scripts found, performing sanity checks...\n", resources->size()); - - Resource *script, *heap; - while (itr != resources->end()) { - script = _engine->getResMan()->findResource(*itr, false); - if (!script) - DebugPrintf("Error: script %d couldn't be loaded\n", itr->number); - - heap = _engine->getResMan()->findResource(*itr, false); - if (!heap) - DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->number); - - if (script && heap && (script->size + heap->size > 65535)) - DebugPrintf("Error: script and heap %d together are larger than 64KB (%d bytes)\n", - itr->number, script->size + heap->size); - - ++itr; - } - - DebugPrintf("SCI1.1-SCI2.1 script check finished\n"); - - return true; -} - bool Console::cmdList(int argc, const char **argv) { if (argc < 2) { DebugPrintf("Lists all the resources of a given type\n"); @@ -957,6 +921,22 @@ bool Console::cmdRestoreGame(int argc, const char **argv) { } bool Console::cmdRestartGame(int argc, const char **argv) { + if (argc != 2) { + DebugPrintf("Restarts the game. There are two ways to restart a SCI game:\n"); + DebugPrintf("%s play - calls the game object's play() method\n", argv[0]); + DebugPrintf("%s replay - calls the replay() methody\n", argv[0]); + return true; + } + + if (!scumm_stricmp(argv[1], "play")) { + _engine->_gamestate->restarting_flags |= SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; + } else if (!scumm_stricmp(argv[1], "replay")) { + _engine->_gamestate->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; + } else { + DebugPrintf("Invalid usage of %s\n", argv[0]); + return true; + } + _engine->_gamestate->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; script_abort_flag = 1; @@ -965,11 +945,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 < _engine->_gamestate->_segMan->classTableSize(); i++) { - if (_engine->_gamestate->_segMan->_classTable[i].reg.segment) { + for (uint i = 0; i < _engine->_gamestate->_segMan->_classtable.size(); i++) { + if (_engine->_gamestate->_segMan->_classtable[i].reg.segment) { DebugPrintf(" Class 0x%x at %04x:%04x (script 0x%x)\n", i, - PRINT_REG(_engine->_gamestate->_segMan->_classTable[i].reg), - _engine->_gamestate->_segMan->_classTable[i].script); + PRINT_REG(_engine->_gamestate->_segMan->_classtable[i].reg), + _engine->_gamestate->_segMan->_classtable[i].script); } } @@ -1149,11 +1129,7 @@ bool Console::cmdUndither(int argc, const char **argv) { bool flag = atoi(argv[1]) ? true : false; _engine->_gfxScreen->debugUnditherSetState(flag); - if (flag) - DebugPrintf("undithering ENABLED\n"); - else - DebugPrintf("undithering DISABLED\n"); - return true; + return false; } bool Console::cmdPicVisualize(int argc, const char **argv) { @@ -1165,16 +1141,7 @@ bool Console::cmdPicVisualize(int argc, const char **argv) { bool state = atoi(argv[1]) ? true : false; - if (_engine->_resMan->getViewType() == kViewEga) { - _engine->_gfxPaint16->debugSetEGAdrawingVisualize(state); - if (state) - DebugPrintf("picture visualization ENABLED\n"); - else - DebugPrintf("picture visualization DISABLED\n"); - } else { - DebugPrintf("picture visualization only available for EGA games\n"); - } - return true; + return _engine->_gui->debugEGAdrawingVisualize(state); } bool Console::cmdPlayVideo(int argc, const char **argv) { @@ -1281,13 +1248,13 @@ bool Console::segmentInfo(int nr) { case SEG_TYPE_SCRIPT: { Script *scr = (Script *)mobj; - DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->getBufSize(), (uint)scr->getBufSize()); - if (scr->getExportTable()) - DebugPrintf(" Exports: %4d at %d\n", scr->getExportsNr(), (int)(((const byte *)scr->getExportTable()) - ((const byte *)scr->_buf))); + DebugPrintf("script.%03d locked by %d, bufsize=%d (%x)\n", scr->_nr, scr->getLockers(), (uint)scr->_bufSize, (uint)scr->_bufSize); + if (scr->_exportTable) + DebugPrintf(" Exports: %4d at %d\n", scr->_numExports, (int)(((byte *)scr->_exportTable) - ((byte *)scr->_buf))); else DebugPrintf(" Exports: none\n"); - DebugPrintf(" Synonyms: %4d\n", scr->getSynonymsNr()); + DebugPrintf(" Synonyms: %4d\n", scr->_numSynonyms); if (scr->_localsBlock) DebugPrintf(" Locals : %4d in segment 0x%x\n", scr->_localsBlock->_locals.size(), scr->_localsSegment); @@ -1301,7 +1268,7 @@ bool Console::segmentInfo(int nr) { for (it = scr->_objects.begin(); it != end; ++it) { DebugPrintf(" "); // Object header - const Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos()); + Object *obj = _engine->_gamestate->_segMan->getObject(it->_value.getPos()); if (obj) DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(it->_value.getPos()), _engine->_gamestate->_segMan->getObjectName(it->_value.getPos()), @@ -1348,7 +1315,7 @@ bool Console::segmentInfo(int nr) { objpos.segment = nr; DebugPrintf(" [%04x] %s; copy of ", i, _engine->_gamestate->_segMan->getObjectName(objpos)); // Object header - const Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos()); + Object *obj = _engine->_gamestate->_segMan->getObject(ct->_table[i].getPos()); if (obj) DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(ct->_table[i].getPos()), _engine->_gamestate->_segMan->getObjectName(ct->_table[i].getPos()), @@ -1556,12 +1523,12 @@ bool Console::cmdToggleSound(int argc, const char **argv) { int handle = id.segment << 16 | id.offset; // frobnicate handle if (id.segment) { - SegManager *segMan = _engine->_gamestate->_segMan; // for writeSelectorValue + SegManager *segMan = _engine->_gamestate->_segMan; // for PUT_SEL32V _engine->_gamestate->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED); _engine->_gamestate->_sound.sfx_remove_song(handle); - writeSelectorValue(segMan, id, SELECTOR(signal), SIGNAL_OFFSET); - writeSelectorValue(segMan, id, SELECTOR(nodePtr), 0); - writeSelectorValue(segMan, id, SELECTOR(handle), 0); + PUT_SEL32V(segMan, id, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(segMan, id, SELECTOR(nodePtr), 0); + PUT_SEL32V(segMan, id, SELECTOR(handle), 0); } #else @@ -1795,7 +1762,7 @@ bool Console::cmdVMVars(int argc, const char **argv) { const char *varabbrev = "gltp"; const char *vartype_pre = strchr(varabbrev, *argv[1]); int vartype; - int idx; + int idx = atoi(argv[2]); if (!vartype_pre) { DebugPrintf("Invalid variable type '%c'\n", *argv[1]); @@ -1804,26 +1771,6 @@ bool Console::cmdVMVars(int argc, const char **argv) { vartype = vartype_pre - varabbrev; - char *endPtr = 0; - int idxLen = strlen(argv[2]); - const char *lastChar = argv[2] + idxLen - (idxLen == 0 ? 0 : 1); - - if ((strncmp(argv[2], "0x", 2) == 0) || (*lastChar == 'h')) { - // hexadecimal number - idx = strtol(argv[2], &endPtr, 16); - if ((*endPtr != 0) && (*endPtr != 'h')) { - DebugPrintf("Invalid hexadecimal index '%s'\n", argv[2]); - return true; - } - } else { - // decimal number - idx = strtol(argv[2], &endPtr, 10); - if (*endPtr != 0) { - DebugPrintf("Invalid decimal index '%s'\n", argv[2]); - return true; - } - } - if (idx < 0) { DebugPrintf("Invalid: negative index\n"); return true; @@ -2262,8 +2209,8 @@ bool Console::cmdDisassemble(int argc, const char **argv) { return true; } - const Object *obj = _engine->_gamestate->_segMan->getObject(objAddr); - int selectorId = _engine->getKernel()->findSelector(argv[2]); + Object *obj = _engine->_gamestate->_segMan->getObject(objAddr); + int selector_id = _engine->getKernel()->findSelector(argv[2]); reg_t addr; if (!obj) { @@ -2271,12 +2218,12 @@ bool Console::cmdDisassemble(int argc, const char **argv) { return true; } - if (selectorId < 0) { + if (selector_id < 0) { DebugPrintf("Not a valid selector name."); return true; } - if (lookupSelector(_engine->_gamestate->_segMan, objAddr, selectorId, NULL, &addr) != kSelectorMethod) { + if (lookup_selector(_engine->_gamestate->_segMan, objAddr, selector_id, NULL, &addr) != kSelectorMethod) { DebugPrintf("Not a method."); return true; } @@ -2356,20 +2303,20 @@ bool Console::cmdSend(int argc, const char **argv) { } const char *selector_name = argv[2]; - int selectorId = _engine->getKernel()->findSelector(selector_name); + int selector_id = _engine->getKernel()->findSelector(selector_name); - if (selectorId < 0) { + if (selector_id < 0) { DebugPrintf("Unknown selector: \"%s\"\n", selector_name); return true; } - const Object *o = _engine->_gamestate->_segMan->getObject(object); + Object *o = _engine->_gamestate->_segMan->getObject(object); if (o == NULL) { DebugPrintf("Address \"%04x:%04x\" is not an object\n", PRINT_REG(object)); return true; } - SelectorType selector_type = lookupSelector(_engine->_gamestate->_segMan, object, selectorId, 0, 0); + SelectorType selector_type = lookup_selector(_engine->_gamestate->_segMan, object, selector_id, 0, 0); if (selector_type == kSelectorNone) { DebugPrintf("Object does not support selector: \"%s\"\n", selector_name); @@ -2382,7 +2329,7 @@ bool Console::cmdSend(int argc, const char **argv) { // Create the data block for send_selecor() at the top of the stack: // [selector_number][argument_counter][arguments...] StackPtr stackframe = _engine->_gamestate->_executionStack.back().sp; - stackframe[0] = make_reg(0, selectorId); + stackframe[0] = make_reg(0, selector_id); stackframe[1] = make_reg(0, send_argc); for (int i = 0; i < send_argc; i++) { if (parse_reg_t(_engine->_gamestate, argv[3+i], &stackframe[2+i], false)) { @@ -3054,8 +3001,8 @@ int Console::printNode(reg_t addr) { int Console::printObject(reg_t pos) { EngineState *s = _engine->_gamestate; // for the several defines in this function - const Object *obj = s->_segMan->getObject(pos); - const Object *var_container = obj; + Object *obj = s->_segMan->getObject(pos); + Object *var_container = obj; uint i; if (!obj) { @@ -3067,7 +3014,7 @@ int Console::printObject(reg_t pos) { DebugPrintf("[%04x:%04x] %s : %3d vars, %3d methods\n", PRINT_REG(pos), s->_segMan->getObjectName(pos), obj->getVarCount(), obj->getMethodCount()); - if (!obj->isClass()) + if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) var_container = s->_segMan->getObject(obj->getSuperClassSelector()); DebugPrintf(" -- member variables:\n"); for (i = 0; (uint)i < obj->getVarCount(); i++) { @@ -3084,7 +3031,7 @@ int Console::printObject(reg_t pos) { if (!val.segment) DebugPrintf(" (%d)", val.offset); - const Object *ref = s->_segMan->getObject(val); + Object *ref = s->_segMan->getObject(val); if (ref) DebugPrintf(" (%s)", s->_segMan->getObjectName(val)); @@ -3096,7 +3043,7 @@ int Console::printObject(reg_t pos) { DebugPrintf(" [%03x] %s = %04x:%04x\n", obj->getFuncSelector(i), selector_name(s, obj->getFuncSelector(i)), PRINT_REG(fptr)); } if (s->_segMan->_heap[pos.segment]->getType() == SEG_TYPE_SCRIPT) - DebugPrintf("\nOwner script: %d\n", s->_segMan->getScript(pos.segment)->_nr); + DebugPrintf("\nOwner script:\t%d\n", s->_segMan->getScript(pos.segment)->_nr); return 0; } diff --git a/engines/sci/console.h b/engines/sci/console.h index 2b13e03ef6..c88795ae26 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -73,7 +73,6 @@ private: bool cmdResourceTypes(int argc, const char **argv); bool cmdList(int argc, const char **argv); bool cmdHexgrep(int argc, const char **argv); - bool cmdVerifyScripts(int argc, const char **argv); // Game bool cmdSaveGame(int argc, const char **argv); bool cmdRestoreGame(int argc, const char **argv); diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index aba2b0b74e..2a83c8e32a 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -120,154 +120,6 @@ static const PlainGameDescriptor SciGameTitles[] = { {0, 0} }; -struct OldNewIdTableEntry { - const char *oldId; - const char *newId; - SciVersion version; -}; - -static const OldNewIdTableEntry s_oldNewTable[] = { - { "arthur", "camelot", SCI_VERSION_NONE }, - { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga - { "brain", "castlebrain", SCI_VERSION_1_LATE }, - { "demo", "christmas1988", SCI_VERSION_NONE }, - { "card", "christmas1990", SCI_VERSION_1_EARLY, }, - { "card", "christmas1992", SCI_VERSION_1_1 }, - { "RH Budget", "cnick-longbow", SCI_VERSION_NONE }, - // iceman is the same - { "icedemo", "iceman", SCI_VERSION_NONE }, - // longbow is the same - { "eco", "ecoquest", SCI_VERSION_NONE }, - { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo - { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full - { "tales", "fairytales", SCI_VERSION_NONE }, - { "fp", "freddypharkas", SCI_VERSION_NONE }, - { "emc", "funseeker", SCI_VERSION_NONE }, - { "gk", "gk1", SCI_VERSION_NONE }, - // gk2 is the same - { "hoyledemo", "hoyle1", SCI_VERSION_NONE }, - { "cardgames", "hoyle1", SCI_VERSION_NONE }, - { "solitare", "hoyle2", SCI_VERSION_NONE }, - // hoyle3 is the same - // hoyle4 is the same - { "brain", "islandbrain", SCI_VERSION_1_1 }, - { "demo000", "kq1sci", SCI_VERSION_NONE }, - { "kq1", "kq1sci", SCI_VERSION_NONE }, - { "kq4", "kq4sci", SCI_VERSION_NONE }, - // kq5 is the same - // kq6 is the same - // kq7 is the same - { "mm1", "laurabow", SCI_VERSION_NONE }, - { "cb1", "laurabow", SCI_VERSION_NONE }, - { "lb2", "laurabow2", SCI_VERSION_NONE }, - { "rh", "longbow", SCI_VERSION_NONE }, - { "ll1", "lsl1sci", SCI_VERSION_NONE }, - { "lsl1", "lsl1sci", SCI_VERSION_NONE }, - // lsl2 is the same - { "lsl3", "lsl3", SCI_VERSION_NONE }, - { "ll5", "lsl5", SCI_VERSION_NONE }, - // lsl5 is the same - // lsl6 is the same - { "mg", "mothergoose", SCI_VERSION_NONE }, - { "twisty", "pepper", SCI_VERSION_NONE }, - { "scary", "phantasmagoria", SCI_VERSION_NONE }, - // TODO: distinguish the full version of Phantasmagoria from the demo - { "pq1", "pq1sci", SCI_VERSION_NONE }, - { "pq", "pq2", SCI_VERSION_NONE }, - // pq3 is the same - // pq4 is the same - { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA - { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA - { "trial", "qfg2", SCI_VERSION_NONE }, - { "hq2demo", "qfg2", SCI_VERSION_NONE }, - // rama is the same - // TODO: distinguish the full version of rama from the demo - { "thegame", "slater", SCI_VERSION_NONE }, - { "sq1demo", "sq1sci", SCI_VERSION_NONE }, - { "sq1", "sq1sci", SCI_VERSION_NONE }, - // sq3 is the same - // sq4 is the same - // sq5 is the same - // sq6 is the same - // TODO: distinguish the full version of SQ6 from the demo - // torin is the same - - - // TODO: SCI3 IDs - - { "", "", SCI_VERSION_NONE } -}; - -Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, ResourceManager *resMan) { - // Convert the id to lower case, so that we match all upper/lower case variants. - sierraId.toLowercase(); - - // If the game has less than the expected scripts, it's a demo - uint32 demoThreshold = 100; - // ...but there are some exceptions - if (sierraId == "brain" || sierraId == "lsl1" || - sierraId == "mg" || sierraId == "pq" || - sierraId == "jones" || - sierraId == "cardgames" || sierraId == "solitare" || - sierraId == "hoyle3" || sierraId == "hoyle4") - demoThreshold = 40; - if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4") - demoThreshold = 150; - - Common::List<ResourceId> *resources = resMan->listResources(kResourceTypeScript, -1); - if (resources->size() < demoThreshold) { - *gameFlags |= ADGF_DEMO; - - // Crazy Nick's Picks - if (sierraId == "lsl1" && resources->size() == 34) - return "cnick-lsl"; - if (sierraId == "sq4" && resources->size() == 34) - return "cnick-sq"; - - // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read) - - // Handle Astrochicken 1 (SQ3) and 2 (SQ4) - if (sierraId == "sq3" && resources->size() == 20) - return "astrochicken"; - if (sierraId == "sq4") - return "msastrochicken"; - } - - if (sierraId == "torin" && resources->size() == 226) // Torin's Passage demo - *gameFlags |= ADGF_DEMO; - - for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) { - if (sierraId == cur->oldId) { - // Distinguish same IDs from the SCI version - if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion()) - continue; - - return cur->newId; - } - } - - if (sierraId == "glory") { - // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1), - // or qfg4 full (SCI2) - // qfg1 VGA doesn't have view 1 - if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) - return "qfg1"; - - // qfg4 full is SCI2 - if (getSciVersion() == SCI_VERSION_2) - return "qfg4"; - - // qfg4 demo has less than 50 scripts - if (resources->size() < 50) - return "qfg4"; - - // Otherwise it's qfg3 - return "qfg3"; - } - - return sierraId; -} - #include "sci/detection_tables.h" /** @@ -353,6 +205,50 @@ Common::Language charToScummVMLanguage(const char c) { } } +#define READ_UINT16(buf) (!resMan->isSci11Mac() ? READ_LE_UINT16(buf) : READ_BE_UINT16(buf)) + +// Finds the internal ID of the current game from script 0 +Common::String getSierraGameId(ResourceManager *resMan) { + Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, 0), false); + Script *script000 = new Script(); + script000->init(0, resMan); + script000->mcpyInOut(0, script->data, script->size); + uint16 curOffset = (getSciVersion() == SCI_VERSION_0_EARLY) ? 2 : 0; + uint16 objLength = 0; + int objType = 0; + int16 exportsOffset = 0; + Common::String sierraId; + + // Now find the export table of the script + do { + objType = READ_UINT16(script000->_buf + curOffset); + if (!objType) + break; + + objLength = READ_UINT16(script000->_buf + curOffset + 2); + curOffset += 4; // skip header + + if (objType == SCI_OBJ_EXPORTS) { + exportsOffset = READ_UINT16(script000->_buf + curOffset + 2); + break; + } + curOffset += objLength - 4; + } while (objType != 0 && curOffset < script->size - 2); + + // The game object is the first export. Script 0 is always at segment 1 + reg_t gameObj = make_reg(1, exportsOffset); + + // TODO: stop using the segment manager and read the object name here + SegManager *segMan = new SegManager(resMan); + script_instantiate(resMan, segMan, 0); + sierraId = segMan->getObjectName(gameObj); + delete segMan; + + delete script000; + + return sierraId; +} + const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fslist) const { bool foundResMap = false; bool foundRes000 = false; @@ -464,16 +360,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const Common::FSList &fsl s_fallbackDesc.platform = Common::kPlatformAmiga; // Determine the game id - Common::String sierraGameId = resMan->findSierraGameId(); - - // If we don't have a game id, the game is not SCI - if (sierraGameId.empty()) { - SearchMan.remove("SCI_detection"); - delete resMan; - return 0; - } - - Common::String gameId = convertSierraGameId(sierraGameId, &s_fallbackDesc.flags, resMan); + Common::String gameId = convertSierraGameId(getSierraGameId(resMan).c_str(), &s_fallbackDesc.flags, resMan); strncpy(s_fallbackGameIdBuf, gameId.c_str(), sizeof(s_fallbackGameIdBuf) - 1); s_fallbackGameIdBuf[sizeof(s_fallbackGameIdBuf) - 1] = 0; // Make sure string is NULL terminated s_fallbackDesc.gameid = s_fallbackGameIdBuf; @@ -548,8 +435,8 @@ bool SciMetaEngine::hasFeature(MetaEngineFeature f) const { bool SciEngine::hasFeature(EngineFeature f) const { return //(f == kSupportsRTL) || - (f == kSupportsLoadingDuringRuntime) || - (f == kSupportsSavingDuringRuntime); + (f == kSupportsLoadingDuringRuntime); + //(f == kSupportsSavingDuringRuntime); } SaveStateList SciMetaEngine::listSaves(const char *target) const { diff --git a/engines/sci/engine/features.cpp b/engines/sci/engine/features.cpp index 48f7c2d64f..e51867332a 100644 --- a/engines/sci/engine/features.cpp +++ b/engines/sci/engine/features.cpp @@ -56,7 +56,7 @@ reg_t GameFeatures::getDetectionAddr(const Common::String &objName, Selector slc } if (methodNum == -1) { - if (lookupSelector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { + if (lookup_selector(_segMan, objAddr, slc, NULL, &addr) != kSelectorMethod) { warning("getDetectionAddr: target selector is not a method of object %s", objName.c_str()); return NULL_REG; } @@ -87,7 +87,7 @@ bool GameFeatures::autoDetectSoundType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->getBufSize()) + if (opcode == op_ret || offset >= script->_bufSize) break; // The play method of the Sound object pushes the DoSound command @@ -189,7 +189,7 @@ SciVersion GameFeatures::detectSetCursorType() { } // Now we check what the number variable holds in the handCursor object. - uint16 number = readSelectorValue(_segMan, objAddr, SELECTOR(number)); + uint16 number = GET_SEL32V(_segMan, objAddr, SELECTOR(number)); // If the number is 0, it uses views and therefore the SCI1.1 kSetCursor semantics, // otherwise it uses the SCI0 early kSetCursor semantics. @@ -223,7 +223,7 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->getBufSize()) + if (opcode == op_ret || offset >= script->_bufSize) break; if (opcode == op_lofsa || opcode == op_lofss) { @@ -231,13 +231,13 @@ bool GameFeatures::autoDetectLofsType(int methodNum) { uint16 lofs = opparams[0]; // Check for going out of bounds when interpreting as abs/rel - if (lofs >= script->getBufSize()) + if (lofs >= script->_bufSize) _lofsType = SCI_VERSION_0_EARLY; if ((signed)offset + (int16)lofs < 0) _lofsType = SCI_VERSION_1_MIDDLE; - if ((signed)offset + (int16)lofs >= (signed)script->getBufSize()) + if ((signed)offset + (int16)lofs >= (signed)script->_bufSize) _lofsType = SCI_VERSION_1_MIDDLE; if (_lofsType != SCI_VERSION_NONE) @@ -266,7 +266,7 @@ SciVersion GameFeatures::detectLofsType() { // Find a function of the game object which invokes lofsa/lofss reg_t gameClass = _segMan->findObjectByName("Game"); - const Object *obj = _segMan->getObject(gameClass); + Object *obj = _segMan->getObject(gameClass); bool found = false; for (uint m = 0; m < obj->getMethodCount(); m++) { @@ -309,7 +309,7 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->getBufSize()) + if (opcode == op_ret || offset >= script->_bufSize) break; if (opcode == op_callk) { @@ -332,47 +332,18 @@ bool GameFeatures::autoDetectGfxFunctionsType(int methodNum) { SciVersion GameFeatures::detectGfxFunctionsType() { if (_gfxFunctionsType == SCI_VERSION_NONE) { - if (getSciVersion() == SCI_VERSION_0_EARLY) { - // Old SCI0 games always used old graphics functions - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } else if (getSciVersion() >= SCI_VERSION_01) { - // SCI01 and newer games always used new graphics functions + // This detection only works (and is only needed) for SCI0 games + if (getSciVersion() >= SCI_VERSION_01) { _gfxFunctionsType = SCI_VERSION_0_LATE; - } else { // SCI0 late + } else if (getSciVersion() > SCI_VERSION_0_EARLY) { // Check if the game is using an overlay - bool searchRoomObj = false; - reg_t rmObjAddr = _segMan->findObjectByName("Rm"); - - if (_kernel->_selectorCache.overlay != -1) { - // The game has an overlay selector, check how it calls kDrawPicto determine - // the graphics functions type used - if (lookupSelector(_segMan, rmObjAddr, _kernel->_selectorCache.overlay, NULL, NULL) == kSelectorMethod) { - if (!autoDetectGfxFunctionsType()) { - warning("Graphics functions detection failed, taking an educated guess"); - - // Try detecting the graphics function types from the existence of the motionCue - // selector (which is a bit of a hack) - if (_kernel->findSelector("motionCue") != -1) - _gfxFunctionsType = SCI_VERSION_0_LATE; - else - _gfxFunctionsType = SCI_VERSION_0_EARLY; - } - } else { - // The game has an overlay selector, but it's not a method of the Rm object - // (like in Hoyle 1 and 2), so search for other methods - searchRoomObj = true; - } - } else { - // The game doesn't have an overlay selector, so search for it manually - searchRoomObj = true; - } + bool found = false; - if (searchRoomObj) { - // If requested, check if any method of the Rm object is calling kDrawPic, - // as the overlay selector might be missing in demos - bool found = false; + if (_kernel->_selectorCache.overlay == -1) { + // No overlay selector found, check if any method of the Rm object + // is calling kDrawPic, as the overlay selector might be missing in demos - const Object *obj = _segMan->getObject(rmObjAddr); + Object *obj = _segMan->getObject(_segMan->findObjectByName("Rm")); for (uint m = 0; m < obj->getMethodCount(); m++) { found = autoDetectGfxFunctionsType(m); if (found) @@ -380,11 +351,30 @@ SciVersion GameFeatures::detectGfxFunctionsType() { } if (!found) { - // No method of the Rm object is calling kDrawPic, thus the game - // doesn't have overlays and is using older graphics functions + // No overlay selector found, therefore the game is definitely + // using old graphics functions _gfxFunctionsType = SCI_VERSION_0_EARLY; } + } else { // _kernel->_selectorCache.overlay != -1 + // An in-between case: The game does not have a shiftParser + // selector, but it does have an overlay selector, so it uses an + // overlay. Therefore, check it to see how it calls kDrawPic to + // determine the graphics functions type used + + if (!autoDetectGfxFunctionsType()) { + warning("Graphics functions detection failed, taking an educated guess"); + + // Try detecting the graphics function types from the existence of the motionCue + // selector (which is a bit of a hack) + if (_kernel->findSelector("motionCue") != -1) + _gfxFunctionsType = SCI_VERSION_0_LATE; + else + _gfxFunctionsType = SCI_VERSION_0_EARLY; + } } + } else { // (getSciVersion() == SCI_VERSION_0_EARLY) + // Old SCI0 games always used old graphics functions + _gfxFunctionsType = SCI_VERSION_0_EARLY; } debugC(1, kDebugLevelVM, "Detected graphics functions type: %s", getSciVersionDesc(_gfxFunctionsType)); @@ -412,7 +402,7 @@ bool GameFeatures::autoDetectSci21KernelType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->getBufSize()) + if (opcode == op_ret || offset >= script->_bufSize) break; if (opcode == op_callk) { @@ -465,7 +455,7 @@ bool GameFeatures::autoDetectMoveCountType() { opcode = extOpcode >> 1; // Check for end of script - if (opcode == op_ret || offset >= script->getBufSize()) + if (opcode == op_ret || offset >= script->_bufSize) break; if (opcode == op_callk) { diff --git a/engines/sci/engine/game.cpp b/engines/sci/engine/game.cpp index bc10099e52..4ac2a22531 100644 --- a/engines/sci/engine/game.cpp +++ b/engines/sci/engine/game.cpp @@ -41,6 +41,141 @@ namespace Sci { +struct OldNewIdTableEntry { + const char *oldId; + const char *newId; + SciVersion version; +}; + +static const OldNewIdTableEntry s_oldNewTable[] = { + { "arthur", "camelot", SCI_VERSION_NONE }, + { "brain", "castlebrain", SCI_VERSION_1_MIDDLE }, // Amiga + { "brain", "castlebrain", SCI_VERSION_1_LATE }, + { "demo", "christmas1988", SCI_VERSION_NONE }, + { "card", "christmas1990", SCI_VERSION_1_EARLY, }, + { "card", "christmas1992", SCI_VERSION_1_1 }, + { "RH Budget", "cnick-longbow", SCI_VERSION_NONE }, + // iceman is the same + { "icedemo", "iceman", SCI_VERSION_NONE }, + // longbow is the same + { "eco", "ecoquest", SCI_VERSION_NONE }, + { "eco2", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 demo + { "rain", "ecoquest2", SCI_VERSION_NONE }, // EcoQuest 2 full + { "fp", "freddypharkas", SCI_VERSION_NONE }, + { "emc", "funseeker", SCI_VERSION_NONE }, + { "gk", "gk1", SCI_VERSION_NONE }, + { "hoyledemo", "hoyle1", SCI_VERSION_NONE }, + { "cardgames", "hoyle1", SCI_VERSION_NONE }, + { "solitare", "hoyle2", SCI_VERSION_NONE }, + // hoyle3 is the same + // hoyle4 is the same + { "brain", "islandbrain", SCI_VERSION_1_1 }, + { "demo000", "kq1sci", SCI_VERSION_NONE }, + { "kq1", "kq1sci", SCI_VERSION_NONE }, + { "kq4", "kq4sci", SCI_VERSION_NONE }, + { "mm1", "laurabow", SCI_VERSION_NONE }, + { "cb1", "laurabow", SCI_VERSION_NONE }, + { "lb2", "laurabow2", SCI_VERSION_NONE }, + { "rh", "longbow", SCI_VERSION_NONE }, + { "ll1", "lsl1sci", SCI_VERSION_NONE }, + { "lsl1", "lsl1sci", SCI_VERSION_NONE }, + // lsl2 is the same + { "lsl3", "lsl3", SCI_VERSION_NONE }, + { "ll5", "lsl5", SCI_VERSION_NONE }, + // lsl5 is the same + // lsl6 is the same + { "mg", "mothergoose", SCI_VERSION_NONE }, + { "twisty", "pepper", SCI_VERSION_NONE }, + { "pq1", "pq1sci", SCI_VERSION_NONE }, + { "pq", "pq2", SCI_VERSION_NONE }, + // pq3 is the same + // pq4 is the same + { "tales", "fairytales", SCI_VERSION_NONE }, + { "hq", "qfg1", SCI_VERSION_NONE }, // QFG1 SCI0/EGA + { "glory", "qfg1", SCI_VERSION_0_LATE }, // QFG1 SCI0/EGA + { "trial", "qfg2", SCI_VERSION_NONE }, + { "hq2demo", "qfg2", SCI_VERSION_NONE }, + { "thegame", "slater", SCI_VERSION_NONE }, + { "sq1demo", "sq1sci", SCI_VERSION_NONE }, + { "sq1", "sq1sci", SCI_VERSION_NONE }, + // sq3 is the same + // sq4 is the same + // sq5 is the same + // torin is the same + + // TODO: SCI2.1, SCI3 IDs + + { "", "", SCI_VERSION_NONE } +}; + +Common::String convertSierraGameId(const char *gameId, uint32 *gameFlags, ResourceManager *resMan) { + // Convert the id to lower case, so that we match all upper/lower case variants. + Common::String sierraId = gameId; + sierraId.toLowercase(); + + // If the game has less than the expected scripts, it's a demo + uint32 demoThreshold = 100; + // ...but there are some exceptions + if (sierraId == "brain" || sierraId == "lsl1" || + sierraId == "mg" || sierraId == "pq" || + sierraId == "jones" || + sierraId == "cardgames" || sierraId == "solitare" || + sierraId == "hoyle3" || sierraId == "hoyle4") + demoThreshold = 40; + if (sierraId == "fp" || sierraId == "gk" || sierraId == "pq4") + demoThreshold = 150; + + Common::List<ResourceId> *resources = resMan->listResources(kResourceTypeScript, -1); + if (resources->size() < demoThreshold) { + *gameFlags |= ADGF_DEMO; + + // Crazy Nick's Picks + if (sierraId == "lsl1" && resources->size() == 34) + return "cnick-lsl"; + if (sierraId == "sq4" && resources->size() == 34) + return "cnick-sq"; + + // TODO: cnick-kq, cnick-laurabow and cnick-longbow (their resources can't be read) + + // Handle Astrochicken 1 (SQ3) and 2 (SQ4) + if (sierraId == "sq3" && resources->size() == 20) + return "astrochicken"; + if (sierraId == "sq4") + return "msastrochicken"; + } + + for (const OldNewIdTableEntry *cur = s_oldNewTable; cur->oldId[0]; ++cur) { + if (sierraId == cur->oldId) { + // Distinguish same IDs from the SCI version + if (cur->version != SCI_VERSION_NONE && cur->version != getSciVersion()) + continue; + + return cur->newId; + } + } + + if (sierraId == "glory") { + // This could either be qfg1 VGA, qfg3 or qfg4 demo (all SCI1.1), + // or qfg4 full (SCI2) + // qfg1 VGA doesn't have view 1 + if (!resMan->testResource(ResourceId(kResourceTypeView, 1))) + return "qfg1"; + + // qfg4 full is SCI2 + if (getSciVersion() == SCI_VERSION_2) + return "qfg4"; + + // qfg4 demo has less than 50 scripts + if (resources->size() < 50) + return "qfg4"; + + // Otherwise it's qfg3 + return "qfg3"; + } + + return sierraId; +} + #ifdef USE_OLD_MUSIC_FUNCTIONS int game_init_sound(EngineState *s, int sound_flags, SciVersion soundVersion) { if (getSciVersion() > SCI_VERSION_0_LATE) @@ -67,7 +202,13 @@ int script_init_engine(EngineState *s) { s->script_000 = s->_segMan->getScript(script_000_segment); - s->_segMan->initSysStrings(); + s->sys_strings = s->_segMan->allocateSysStrings(&s->sys_strings_segment); + + // Allocate static buffer for savegame and CWD directories + SystemString *str = &s->sys_strings->_strings[SYS_STRING_SAVEDIR]; + str->_name = "savedir"; + str->_maxSize = MAX_SAVE_DIR_SIZE; + str->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); s->r_acc = s->r_prev = NULL_REG; s->restAdjust = 0; @@ -99,26 +240,35 @@ int game_init(EngineState *s) { return 1; } - // Reset parser - Vocabulary *voc = g_sci->getVocabulary(); - if (voc) { - voc->parserIsValid = false; // Invalidate parser - voc->parser_event = NULL_REG; // Invalidate parser event - voc->parser_base = make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_PARSER_BASE); + if (s->_voc) { + s->_voc->parserIsValid = false; // Invalidate parser + s->_voc->parser_event = NULL_REG; // Invalidate parser event + s->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); } // Initialize menu TODO: Actually this should be another init() if (g_sci->_gfxMenu) g_sci->_gfxMenu->reset(); - s->restoring = false; + s->successor = NULL; // No successor + + SystemString *str = &s->sys_strings->_strings[SYS_STRING_PARSER_BASE]; + str->_name = "parser-base"; + str->_maxSize = MAX_PARSER_BASE; + str->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); s->game_start_time = g_system->getMillis(); s->last_wait_time = s->game_start_time; srand(g_system->getMillis()); // Initialize random number generator - s->_gameObj = g_sci->getResMan()->findGameObject(); +// script_dissect(0, s->_selectorNames); + // The first entry in the export table of script 0 points to the game object + s->_gameObj = s->_segMan->lookupScriptExport(0, 0); + uint32 gameFlags = 0; // unused + s->_gameId = convertSierraGameId(s->_segMan->getObjectName(s->_gameObj), &gameFlags, g_sci->getResMan()); + + debug(2, " \"%s\" at %04x:%04x", s->_gameId.c_str(), PRINT_REG(s->_gameObj)); #ifdef USE_OLD_MUSIC_FUNCTIONS if (s->sfx_init_flags & SFX_STATE_FLAG_NOSOUND) @@ -134,8 +284,9 @@ int game_init(EngineState *s) { } int game_exit(EngineState *s) { - if (!s->restoring) { - s->_executionStack.clear(); + s->_executionStack.clear(); + + if (!s->successor) { #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); // Reinit because some other code depends on having a valid state diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 0b7198d1bc..49266f3a18 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -332,12 +332,11 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("DoSync", kDoSync, ".*"), DEFUN("MemorySegment", kMemorySegment, "iri*"), DEFUN("Intersections", kIntersections, "iiiiriiiri"), - DEFUN("MergePoly", kMergePoly, "rli"), DEFUN("ResCheck", kResCheck, "iii*"), DEFUN("SetQuitStr", kSetQuitStr, "r"), DEFUN("ShowMovie", kShowMovie, ".*"), DEFUN("SetVideoMode", kSetVideoMode, "i"), - DEFUN("Platform", kPlatform, ".*"), + DEFUN("Platform", kPlatform, "i.*"), DEFUN("TextColors", kTextColors, ".*"), DEFUN("TextFonts", kTextFonts, ".*"), DEFUN("Portrait", kPortrait, ".*"), @@ -383,6 +382,7 @@ SciKernelFunction kfunct_mappers[] = { DEFUN("ShiftScreen", kShiftScreen, ".*"), DEFUN("ListOps", kListOps, ".*"), DEFUN("ATan", kATan, ".*"), + DEFUN("MergePoly", kMergePoly, ".*"), DEFUN("Record", kRecord, ".*"), DEFUN("PlayBack", kPlayBack, ".*"), DEFUN("DbugStr", kDbugStr, ".*"), @@ -628,7 +628,7 @@ int Kernel::findRegType(reg_t reg) { switch (mobj->getType()) { case SEG_TYPE_SCRIPT: - if (reg.offset <= (*(Script *)mobj).getBufSize() && + if (reg.offset <= (*(Script *)mobj)._bufSize && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT((*(Script *)mobj)._buf + reg.offset)) { return ((Script *)mobj)->getObject(reg.offset) ? KSIG_OBJECT : KSIG_REF; diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 8f8f34f74e..7717743e19 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -414,7 +414,6 @@ reg_t kDoAudio(EngineState *s, int argc, reg_t *argv); reg_t kDoSync(EngineState *s, int argc, reg_t *argv); reg_t kMemorySegment(EngineState *s, int argc, reg_t *argv); reg_t kIntersections(EngineState *s, int argc, reg_t *argv); -reg_t kMergePoly(EngineState *s, int argc, reg_t *argv); reg_t kResCheck(EngineState *s, int argc, reg_t *argv); reg_t kSetQuitStr(EngineState *s, int argc, reg_t *argv); reg_t kShowMovie(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel32.cpp b/engines/sci/engine/kernel32.cpp index b132543206..465e0e92df 100644 --- a/engines/sci/engine/kernel32.cpp +++ b/engines/sci/engine/kernel32.cpp @@ -501,7 +501,7 @@ reg_t kArray(EngineState *s, int argc, reg_t *argv) { if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; - return readSelector(s->_segMan, argv[1], SELECTOR(data)); + return GET_SEL32(s->_segMan, argv[1], SELECTOR(data)); default: error("Unknown kArray subop %d", argv[0].toUint16()); } @@ -575,16 +575,16 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { uint32 count = argv[5].toSint16() == -1 ? string2.size() - index2 + 1 : argv[5].toUint16(); // We have a special case here for argv[1] being a system string - if (argv[1].segment == s->_segMan->getSysStringsSegment()) { + if (argv[1].segment == s->sys_strings_segment) { // Resize if necessary - const uint16 sysStringId = argv[1].toUint16(); - if ((uint32)s->_segMan->sysStrings->_strings[sysStringId]._maxSize < index1 + count) { - free(s->_segMan->sysStrings->_strings[sysStringId]._value); - s->_segMan->sysStrings->_strings[sysStringId]._maxSize = index1 + count; - s->_segMan->sysStrings->_strings[sysStringId]._value = (char *)calloc(index1 + count, sizeof(char)); + if ((uint32)s->sys_strings->_strings[argv[1].toUint16()]._maxSize < index1 + count) { + delete[] s->sys_strings->_strings[argv[1].toUint16()]._value; + s->sys_strings->_strings[argv[1].toUint16()]._maxSize = index1 + count; + s->sys_strings->_strings[argv[1].toUint16()]._value = new char[index1 + count]; + memset(s->sys_strings->_strings[argv[1].toUint16()]._value, 0, index1 + count); } - strncpy(s->_segMan->sysStrings->_strings[sysStringId]._value + index1, string2.c_str() + index2, count); + strncpy(s->sys_strings->_strings[argv[1].toUint16()]._value + index1, string2.c_str() + index2, count); } else { SciString *string1 = s->_segMan->lookupString(argv[1]); @@ -624,7 +624,7 @@ reg_t kString(EngineState *s, int argc, reg_t *argv) { if (!s->_segMan->isHeapObject(argv[1])) return argv[1]; - return readSelector(s->_segMan, argv[1], SELECTOR(data)); + return GET_SEL32(s->_segMan, argv[1], SELECTOR(data)); case 10: // Stringlen return make_reg(0, s->_segMan->strlen(argv[1])); case 11: { // Printf @@ -689,12 +689,12 @@ reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { /* reg_t viewObj = argv[0]; - uint16 viewId = readSelectorValue(s->_segMan, viewObj, SELECTOR(view)); - int16 loopNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(loop)); - int16 celNo = readSelectorValue(s->_segMan, viewObj, SELECTOR(cel)); + uint16 viewId = GET_SEL32V(s->_segMan, viewObj, SELECTOR(view)); + int16 loopNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(loop)); + int16 celNo = GET_SEL32V(s->_segMan, viewObj, SELECTOR(cel)); //int16 leftPos = 0; //int16 topPos = 0; - int16 priority = readSelectorValue(s->_segMan, viewObj, SELECTOR(priority)); + int16 priority = GET_SEL32V(s->_segMan, viewObj, SELECTOR(priority)); //int16 control = 0; */ @@ -761,10 +761,10 @@ reg_t kOnMe(EngineState *s, int argc, reg_t *argv) { Common::Rect nsRect; // Get the bounding rectangle of the object - nsRect.left = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsLeft)); - nsRect.top = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsTop)); - nsRect.right = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsRight)); - nsRect.bottom = readSelectorValue(s->_segMan, targetObject, SELECTOR(nsBottom)); + nsRect.left = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsLeft)); + nsRect.top = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsTop)); + nsRect.right = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsRight)); + nsRect.bottom = GET_SEL32V(s->_segMan, targetObject, SELECTOR(nsBottom)); //warning("kOnMe: (%d, %d) on object %04x:%04x, parameter %d", argv[0].toUint16(), argv[1].toUint16(), PRINT_REG(argv[2]), argv[3].toUint16()); @@ -779,7 +779,7 @@ reg_t kInPolygon(EngineState *s, int argc, reg_t *argv) { reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { // TODO: argument 0 is usually 0, and arguments 1 and 2 are usually 1 reg_t object = argv[3]; - Common::String text = s->_segMan->getString(readSelector(s->_segMan, object, SELECTOR(text))); + Common::String text = s->_segMan->getString(GET_SEL32(s->_segMan, object, SELECTOR(text))); debug("kCreateTextBitmap: %s", text.c_str()); return NULL_REG; diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index 3e096074e6..156035b30d 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -49,18 +49,16 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; Common::Point mousePos; - // Limit the mouse cursor position, if necessary - g_sci->_gfxCursor->refreshPosition(); mousePos = g_sci->_gfxCursor->getPosition(); // If there's a simkey pending, and the game wants a keyboard event, use the // simkey instead of a normal event if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { - writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event - writeSelectorValue(segMan, obj, SELECTOR(message), g_debug_simulated_key); - writeSelectorValue(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on - writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); - writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); + PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event + PUT_SEL32V(segMan, obj, SELECTOR(message), g_debug_simulated_key); + PUT_SEL32V(segMan, obj, SELECTOR(modifiers), SCI_KEYMOD_NUMLOCK); // Numlock on + PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x); + PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y); g_debug_simulated_key = 0; return make_reg(0, 1); } @@ -69,11 +67,11 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { oldy = mousePos.y; curEvent = s->_event->get(mask); - if (g_sci->getVocabulary()) - g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event + if (s->_voc) + s->_voc->parser_event = NULL_REG; // Invalidate parser event - writeSelectorValue(segMan, obj, SELECTOR(x), mousePos.x); - writeSelectorValue(segMan, obj, SELECTOR(y), mousePos.y); + PUT_SEL32V(segMan, obj, SELECTOR(x), mousePos.x); + PUT_SEL32V(segMan, obj, SELECTOR(y), mousePos.y); //s->_gui->moveCursor(s->gfx_state->pointer_pos.x, s->gfx_state->pointer_pos.y); @@ -83,12 +81,12 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { break; case SCI_EVENT_KEYBOARD: - writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event + PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_KEYBOARD); // Keyboard event s->r_acc = make_reg(0, 1); - writeSelectorValue(segMan, obj, SELECTOR(message), curEvent.character); + PUT_SEL32V(segMan, obj, SELECTOR(message), curEvent.character); // We only care about the translated character - writeSelectorValue(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); + PUT_SEL32V(segMan, obj, SELECTOR(modifiers), curEvent.modifiers & modifier_mask); break; case SCI_EVENT_MOUSE_RELEASE: @@ -113,9 +111,9 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { break; } - writeSelectorValue(segMan, obj, SELECTOR(type), curEvent.type); - writeSelectorValue(segMan, obj, SELECTOR(message), 0); - writeSelectorValue(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask); + PUT_SEL32V(segMan, obj, SELECTOR(type), curEvent.type); + PUT_SEL32V(segMan, obj, SELECTOR(message), 0); + PUT_SEL32V(segMan, obj, SELECTOR(modifiers), (curEvent.modifiers | extra_bits) & modifier_mask); s->r_acc = make_reg(0, 1); } break; @@ -167,9 +165,9 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; SegManager *segMan = s->_segMan; - if (readSelectorValue(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard + if (GET_SEL32V(segMan, obj, SELECTOR(type)) == SCI_EVENT_KEYBOARD) { // Keyboard int mover = -1; - switch (readSelectorValue(segMan, obj, SELECTOR(message))) { + switch (GET_SEL32V(segMan, obj, SELECTOR(message))) { case SCI_KEY_HOME: mover = 8; break; @@ -203,8 +201,8 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { } if (mover >= 0) { - writeSelectorValue(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK); - writeSelectorValue(segMan, obj, SELECTOR(message), mover); + PUT_SEL32V(segMan, obj, SELECTOR(type), SCI_EVENT_JOYSTICK); + PUT_SEL32V(segMan, obj, SELECTOR(message), mover); return make_reg(0, 1); } else return NULL_REG; @@ -219,13 +217,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; if (obj.segment) { - int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); - int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); + int16 x = GET_SEL32V(segMan, obj, SELECTOR(x)); + int16 y = GET_SEL32V(segMan, obj, SELECTOR(y)); g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject); - writeSelectorValue(segMan, obj, SELECTOR(x), x); - writeSelectorValue(segMan, obj, SELECTOR(y), y); + PUT_SEL32V(segMan, obj, SELECTOR(x), x); + PUT_SEL32V(segMan, obj, SELECTOR(y), y); } return s->r_acc; @@ -238,13 +236,13 @@ reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; if (obj.segment) { - int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); - int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); + int16 x = GET_SEL32V(segMan, obj, SELECTOR(x)); + int16 y = GET_SEL32V(segMan, obj, SELECTOR(y)); g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject); - writeSelectorValue(segMan, obj, SELECTOR(x), x); - writeSelectorValue(segMan, obj, SELECTOR(y), y); + PUT_SEL32V(segMan, obj, SELECTOR(x), x); + PUT_SEL32V(segMan, obj, SELECTOR(y), y); } return s->r_acc; diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index c48fb4035f..e6b9a5388c 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -44,8 +44,6 @@ struct SavegameDesc { int id; int date; int time; - int version; - char name[SCI_MAX_SAVENAME_LENGTH]; }; /* @@ -247,10 +245,13 @@ static void fgets_wrapper(EngineState *s, char *dest, int maxsize, int handle) { debugC(2, kDebugLevelFile, "FGets'ed \"%s\"", dest); } -static bool _savegame_index_struct_compare(const SavegameDesc &l, const SavegameDesc &r) { - if (l.date != r.date) - return (l.date > r.date); - return (l.time > r.time); +static int _savegame_index_struct_compare(const void *a, const void *b) { + const SavegameDesc *A = (const SavegameDesc *)a; + const SavegameDesc *B = (const SavegameDesc *)b; + + if (B->date != A->date) + return B->date - A->date; + return B->time - A->time; } void listSavegames(Common::Array<SavegameDesc> &saves) { @@ -264,7 +265,7 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { Common::SeekableReadStream *in; if ((in = saveFileMan->openForLoading(filename))) { SavegameMetadata meta; - if (!get_savegame_metadata(in, &meta) || meta.savegame_name.empty()) { + if (!get_savegame_metadata(in, &meta)) { // invalid delete in; continue; @@ -277,13 +278,6 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { // We need to fix date in here, because we save DDMMYYYY instead of YYYYMMDD, so sorting wouldnt work desc.date = ((desc.date & 0xFFFF) << 16) | ((desc.date & 0xFF0000) >> 8) | ((desc.date & 0xFF000000) >> 24); desc.time = meta.savegame_time; - desc.version = meta.savegame_version; - - if (meta.savegame_name.lastChar() == '\n') - meta.savegame_name.deleteLastChar(); - - Common::strlcpy(desc.name, meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH); - debug(3, "Savegame in file %s ok, id %d", filename.c_str(), desc.id); saves.push_back(desc); @@ -291,18 +285,35 @@ void listSavegames(Common::Array<SavegameDesc> &saves) { } // Sort the list by creation date of the saves - Common::sort(saves.begin(), saves.end(), _savegame_index_struct_compare); + qsort(saves.begin(), saves.size(), sizeof(SavegameDesc), _savegame_index_struct_compare); } bool Console::cmdListSaves(int argc, const char **argv) { Common::Array<SavegameDesc> saves; listSavegames(saves); + Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + for (uint i = 0; i < saves.size(); i++) { Common::String filename = g_sci->getSavegameName(saves[i].id); - DebugPrintf("%s: '%s'\n", filename.c_str(), saves[i].name); - } + Common::SeekableReadStream *in; + if ((in = saveFileMan->openForLoading(filename))) { + SavegameMetadata meta; + if (!get_savegame_metadata(in, &meta)) { + // invalid + delete in; + continue; + } + + if (!meta.savegame_name.empty()) { + if (meta.savegame_name.lastChar() == '\n') + meta.savegame_name.deleteLastChar(); + DebugPrintf("%s: '%s'\n", filename.c_str(), meta.savegame_name.c_str()); + } + delete in; + } + } return true; } @@ -417,7 +428,7 @@ reg_t kGetSaveDir(EngineState *s, int argc, reg_t *argv) { warning("kGetSaveDir called with %d parameter(s): %04x:%04x", argc, PRINT_REG(argv[0])); #endif - return make_reg(s->_segMan->getSysStringsSegment(), SYS_STRING_SAVEDIR); + return make_reg(s->sys_strings_segment, SYS_STRING_SAVEDIR); } reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { @@ -438,30 +449,44 @@ reg_t kCheckFreeSpace(EngineState *s, int argc, reg_t *argv) { reg_t kCheckSaveGame(EngineState *s, int argc, reg_t *argv) { Common::String game_id = s->_segMan->getString(argv[0]); - uint16 savedir_nr = argv[1].toUint16(); + int savedir_nr = argv[1].toUint16(); debug(3, "kCheckSaveGame(%s, %d)", game_id.c_str(), savedir_nr); Common::Array<SavegameDesc> saves; listSavegames(saves); - // Check for savegame slot being out of range - if (savedir_nr >= saves.size()) - return NULL_REG; + savedir_nr = saves[savedir_nr].id; - // Check for compatible savegame version - int ver = saves[savedir_nr].version; - if (ver < MINIMUM_SAVEGAME_VERSION || ver > CURRENT_SAVEGAME_VERSION) + if (savedir_nr > MAX_SAVEGAME_NR - 1) { return NULL_REG; + } - // Otherwise we assume the savegame is OK - return make_reg(0, 1); + Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + Common::String filename = g_sci->getSavegameName(savedir_nr); + Common::SeekableReadStream *in; + if ((in = saveFileMan->openForLoading(filename))) { + // found a savegame file + + SavegameMetadata meta; + if (!get_savegame_metadata(in, &meta)) { + // invalid + s->r_acc = make_reg(0, 0); + } else { + s->r_acc = make_reg(0, 1); + } + delete in; + } else { + s->r_acc = make_reg(0, 1); + } + + return s->r_acc; } reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) { Common::String game_id = s->_segMan->getString(argv[0]); reg_t nametarget = argv[1]; - reg_t *slot = s->_segMan->derefRegPtr(argv[2], 0); + reg_t *nameoffsets = s->_segMan->derefRegPtr(argv[2], 0); debug(3, "kGetSaveFiles(%s)", game_id.c_str()); @@ -469,17 +494,42 @@ reg_t kGetSaveFiles(EngineState *s, int argc, reg_t *argv) { listSavegames(saves); s->r_acc = NULL_REG; + Common::SaveFileManager *saveFileMan = g_engine->getSaveFileManager(); + + for (uint i = 0; i < saves.size(); i++) { + Common::String filename = g_sci->getSavegameName(saves[i].id); + Common::SeekableReadStream *in; + if ((in = saveFileMan->openForLoading(filename))) { + // found a savegame file + + SavegameMetadata meta; + if (!get_savegame_metadata(in, &meta)) { + // invalid + delete in; + continue; + } + + if (!meta.savegame_name.empty()) { + if (meta.savegame_name.lastChar() == '\n') + meta.savegame_name.deleteLastChar(); - for (uint i = 0; i < MIN<uint>(saves.size(), MAX_SAVEGAME_NR); i++) { - *slot++ = s->r_acc; // Store savegame ID - ++s->r_acc.offset; // Increase number of files found + *nameoffsets = s->r_acc; // Store savegame ID + ++s->r_acc.offset; // Increase number of files found - s->_segMan->strcpy(nametarget, saves[i].name); + nameoffsets++; // Make sure the next ID string address is written to the next pointer + Common::String name = meta.savegame_name; + if (name.size() > SCI_MAX_SAVENAME_LENGTH-1) + name = Common::String(meta.savegame_name.c_str(), SCI_MAX_SAVENAME_LENGTH-1); + s->_segMan->strcpy(nametarget, name.c_str()); - // Increase name offset pointer accordingly - nametarget.offset += SCI_MAX_SAVENAME_LENGTH; + // Increase name offset pointer accordingly + nametarget.offset += SCI_MAX_SAVENAME_LENGTH; + } + delete in; + } } + //free(gfname); s->_segMan->strcpy(nametarget, ""); // Terminate list return s->r_acc; diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 17d2cd630e..d587790b6c 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -23,10 +23,8 @@ * */ -#include "engines/util.h" #include "graphics/cursorman.h" #include "graphics/video/avi_decoder.h" -#include "graphics/video/qt_decoder.h" #include "graphics/surface.h" #include "sci/sci.h" @@ -53,8 +51,8 @@ namespace Sci { void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *argv) { - GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); - uint16 signal = readSelectorValue(s->_segMan, object, SELECTOR(signal)); + GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); + uint16 signal = GET_SEL32V(s->_segMan, object, SELECTOR(signal)); int16 loopNo; int16 maxLoops; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); @@ -93,7 +91,7 @@ void _k_dirloop(reg_t object, uint16 angle, EngineState *s, int argc, reg_t *arg if ((loopNo > 1) && (maxLoops < 4)) return; - writeSelectorValue(s->_segMan, object, SELECTOR(loop), loopNo); + PUT_SEL32V(s->_segMan, object, SELECTOR(loop), loopNo); } static reg_t kSetCursorSci0(EngineState *s, int argc, reg_t *argv) { @@ -439,7 +437,7 @@ reg_t kCelWide(EngineState *s, int argc, reg_t *argv) { reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; - GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); + GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); int16 loopCount; loopCount = g_sci->_gfxCache->kernelViewGetLoopCount(viewId); @@ -451,8 +449,8 @@ reg_t kNumLoops(EngineState *s, int argc, reg_t *argv) { reg_t kNumCels(EngineState *s, int argc, reg_t *argv) { reg_t object = argv[0]; - GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); - int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop)); + GuiResourceId viewId = GET_SEL32V(s->_segMan, object, SELECTOR(view)); + int16 loopNo = GET_SEL32V(s->_segMan, object, SELECTOR(loop)); int16 celCount; celCount = g_sci->_gfxCache->kernelViewGetCelCount(viewId, loopNo); @@ -527,9 +525,9 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { // WORKAROUND for a problem in LSL1VGA. This allows the casino door to be opened, // till the actual problem is found - if (!strcmp(g_sci->getGameID(), "lsl1sci") && s->currentRoomNumber() == 300) { - int top = readSelectorValue(s->_segMan, object, SELECTOR(brTop)); - writeSelectorValue(s->_segMan, object, SELECTOR(brTop), top + 2); + if (s->_gameId == "lsl1sci" && s->currentRoomNumber() == 300) { + int top = GET_SEL32V(s->_segMan, object, SELECTOR(brTop)); + PUT_SEL32V(s->_segMan, object, SELECTOR(brTop), top + 2); } return s->r_acc; @@ -743,12 +741,12 @@ Common::Rect kControlCreateRect(int16 x, int16 y, int16 x1, int16 y1) { } void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { - int16 type = readSelectorValue(s->_segMan, controlObject, SELECTOR(type)); - int16 style = readSelectorValue(s->_segMan, controlObject, SELECTOR(state)); - int16 x = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsLeft)); - int16 y = readSelectorValue(s->_segMan, controlObject, SELECTOR(nsTop)); - GuiResourceId fontId = readSelectorValue(s->_segMan, controlObject, SELECTOR(font)); - reg_t textReference = readSelector(s->_segMan, controlObject, SELECTOR(text)); + int16 type = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type)); + int16 style = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state)); + int16 x = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsLeft)); + int16 y = GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsTop)); + GuiResourceId fontId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(font)); + reg_t textReference = GET_SEL32(s->_segMan, controlObject, SELECTOR(text)); Common::String text; Common::Rect rect; TextAlignment alignment; @@ -764,8 +762,8 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { bool isAlias = false; rect = kControlCreateRect(x, y, - readSelectorValue(s->_segMan, controlObject, SELECTOR(nsRight)), - readSelectorValue(s->_segMan, controlObject, SELECTOR(nsBottom))); + GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsRight)), + GET_SEL32V(s->_segMan, controlObject, SELECTOR(nsBottom))); if (!textReference.isNull()) text = s->_segMan->getString(textReference); @@ -777,32 +775,32 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { return; case SCI_CONTROLS_TYPE_TEXT: - alignment = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); + alignment = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode)); debugC(2, kDebugLevelGraphics, "drawing text %04x:%04x ('%s') to %d,%d, mode=%d", PRINT_REG(controlObject), text.c_str(), x, y, alignment); g_sci->_gfxControls->kernelDrawText(rect, controlObject, g_sci->strSplit(text.c_str()).c_str(), fontId, alignment, style, hilite); return; case SCI_CONTROLS_TYPE_TEXTEDIT: - mode = readSelectorValue(s->_segMan, controlObject, SELECTOR(mode)); - maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(max)); - cursorPos = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor)); + mode = GET_SEL32V(s->_segMan, controlObject, SELECTOR(mode)); + maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(max)); + cursorPos = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor)); debugC(2, kDebugLevelGraphics, "drawing edit control %04x:%04x (text %04x:%04x, '%s') to %d,%d", PRINT_REG(controlObject), PRINT_REG(textReference), text.c_str(), x, y); g_sci->_gfxControls->kernelDrawTextEdit(rect, controlObject, g_sci->strSplit(text.c_str(), NULL).c_str(), fontId, mode, style, cursorPos, maxChars, hilite); return; case SCI_CONTROLS_TYPE_ICON: - viewId = readSelectorValue(s->_segMan, controlObject, SELECTOR(view)); + viewId = GET_SEL32V(s->_segMan, controlObject, SELECTOR(view)); { - int l = readSelectorValue(s->_segMan, controlObject, SELECTOR(loop)); + int l = GET_SEL32V(s->_segMan, controlObject, SELECTOR(loop)); loopNo = (l & 0x80) ? l - 256 : l; - int c = readSelectorValue(s->_segMan, controlObject, SELECTOR(cel)); + int c = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cel)); celNo = (c & 0x80) ? c - 256 : c; // Game-specific: *ONLY* the jones EGA/VGA sierra interpreter contain code using priority selector // ALL other games use a hardcoded -1 (madness!) // We are detecting jones/talkie as "jones" as well, but the sierra interpreter of talkie doesnt have this // "hack". Hopefully it wont cause regressions (the code causes regressions if used against kq5/floppy) - if (!strcmp(g_sci->getGameID(), "jones")) - priority = readSelectorValue(s->_segMan, controlObject, SELECTOR(priority)); + if (s->_gameId == "jones") + priority = GET_SEL32V(s->_segMan, controlObject, SELECTOR(priority)); else priority = -1; } @@ -815,17 +813,17 @@ void _k_GenericDrawControl(EngineState *s, reg_t controlObject, bool hilite) { if (type == SCI_CONTROLS_TYPE_LIST_ALIAS) isAlias = true; - maxChars = readSelectorValue(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry - cursorOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(cursor)); + maxChars = GET_SEL32V(s->_segMan, controlObject, SELECTOR(x)); // max chars per entry + cursorOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(cursor)); if (g_sci->getKernel()->_selectorCache.topString != -1) { // Games from early SCI1 onwards use topString - upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(topString)); + upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(topString)); } else { // Earlier games use lsTop or brTop - if (lookupSelector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable) - upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(brTop)); + if (lookup_selector(s->_segMan, controlObject, g_sci->getKernel()->_selectorCache.brTop, NULL, NULL) == kSelectorVariable) + upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(brTop)); else - upperOffset = readSelectorValue(s->_segMan, controlObject, SELECTOR(lsTop)); + upperOffset = GET_SEL32V(s->_segMan, controlObject, SELECTOR(lsTop)); } // Count string entries in NULL terminated string list @@ -876,8 +874,8 @@ reg_t kDrawControl(EngineState *s, int argc, reg_t *argv) { // Disable the "Change Directory" button, as we don't allow the game engine to // change the directory where saved games are placed if (objName == "changeDirI") { - int state = readSelectorValue(s->_segMan, controlObject, SELECTOR(state)); - writeSelectorValue(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED); + int state = GET_SEL32V(s->_segMan, controlObject, SELECTOR(state)); + PUT_SEL32V(s->_segMan, controlObject, SELECTOR(state), (state | SCI_CONTROLS_STYLE_DISABLED) & ~SCI_CONTROLS_STYLE_ENABLED); } _k_GenericDrawControl(s, controlObject, false); @@ -896,7 +894,7 @@ reg_t kEditControl(EngineState *s, int argc, reg_t *argv) { reg_t eventObject = argv[1]; if (!controlObject.isNull()) { - int16 controlType = readSelectorValue(s->_segMan, controlObject, SELECTOR(type)); + int16 controlType = GET_SEL32V(s->_segMan, controlObject, SELECTOR(type)); switch (controlType) { case SCI_CONTROLS_TYPE_TEXTEDIT: @@ -985,7 +983,7 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) { bool hiresMode = (argc > 7) ? true : false; reg_t upscaledHiresHandle = (argc > 7) ? argv[7] : NULL_REG; - if (!strcmp(g_sci->getGameID(), "freddypharkas") || !strcmp(g_sci->getGameID(), "freddypharkas-demo")) { + if ((s->_gameId == "freddypharkas") || (s->_gameId == "freddypharkas-demo")) { // WORKAROUND // Script 24 contains code that draws the game menu on screen. It uses a temp variable for setting priority that // is not set. in Sierra sci this happens to be 8250h. In our sci temporary variables are initialized thus we would @@ -996,7 +994,7 @@ reg_t kDrawCel(EngineState *s, int argc, reg_t *argv) { priority = 15; } - if (!strcmp(g_sci->getGameID(), "laurabow2")) { + if (s->_gameId == "laurabow2") { // WORKAROUND // see the one above if ((viewId == 995) && (priority == 0)) @@ -1082,12 +1080,11 @@ reg_t kDisplay(EngineState *s, int argc, reg_t *argv) { reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { // Hide the cursor if it's showing and then show it again if it was // previously visible. - bool reshowCursor = g_sci->_gfxCursor->isVisible(); + bool reshowCursor; + + reshowCursor = g_sci->_gfxCursor->isVisible(); if (reshowCursor) g_sci->_gfxCursor->kernelHide(); - - uint16 screenWidth = g_system->getWidth(); - uint16 screenHeight = g_system->getHeight(); Graphics::VideoDecoder *videoDecoder = 0; @@ -1097,18 +1094,8 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { if (g_sci->getPlatform() == Common::kPlatformMacintosh) { // Mac QuickTime // The only argument is the string for the video - - // HACK: Switch to 16bpp graphics for Cinepak. - initGraphics(screenWidth, screenHeight, screenWidth > 320, NULL); - - if (g_system->getScreenFormat().bytesPerPixel == 1) { - warning("This video requires >8bpp color to be displayed, but could not switch to RGB color mode."); - return NULL_REG; - } - - videoDecoder = new Graphics::QuickTimeDecoder(); - if (!videoDecoder->loadFile(filename)) - error("Could not open '%s'", filename.c_str()); + warning("TODO: Play QuickTime movie '%s'", filename.c_str()); + return s->r_acc; } else { // DOS SEQ // SEQ's are called with no subops, just the string and delay @@ -1123,7 +1110,7 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } } } else { - // Windows AVI + // Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh) // TODO: This appears to be some sort of subop. case 0 contains the string // for the video, so we'll just play it from there for now. @@ -1155,10 +1142,10 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { } if (videoDecoder) { - uint16 x = (screenWidth - videoDecoder->getWidth()) / 2; - uint16 y = (screenHeight - videoDecoder->getHeight()) / 2; + uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2; + uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2; - while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { + while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) { if (videoDecoder->needsUpdate()) { Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { @@ -1177,15 +1164,9 @@ reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) { g_system->delayMillis(10); } - - // HACK: Switch back to 8bpp if we played a QuickTime video. - // We also won't be copying the screen to the SCI screen... - if (g_system->getScreenFormat().bytesPerPixel != 1) - initGraphics(screenWidth, screenHeight, screenWidth > 320); - else - g_sci->_gfxScreen->kernelSyncWithFramebuffer(); delete videoDecoder; + g_sci->_gfxScreen->kernelSyncWithFramebuffer(); } if (reshowCursor) diff --git a/engines/sci/engine/klists.cpp b/engines/sci/engine/klists.cpp index f06f3eec77..c04454ca3d 100644 --- a/engines/sci/engine/klists.cpp +++ b/engines/sci/engine/klists.cpp @@ -155,10 +155,28 @@ reg_t kDisposeList(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +static reg_t _k_new_node(EngineState *s, reg_t value, reg_t key) { + reg_t nodebase; + Node *n = s->_segMan->allocateNode(&nodebase); + + if (!n) { + error("[Kernel] Out of memory while creating a node"); + return NULL_REG; + } + + n->pred = n->succ = NULL_REG; + n->key = key; + n->value = value; + + return nodebase; +} + reg_t kNewNode(EngineState *s, int argc, reg_t *argv) { - reg_t nodeValue = argv[0]; - reg_t nodeKey = (argc == 2) ? argv[1] : NULL_REG; - s->r_acc = s->_segMan->newNode(nodeValue, nodeKey); + + if (argc == 1) + s->r_acc = _k_new_node(s, argv[0], argv[0]); + else + s->r_acc = _k_new_node(s, argv[0], argv[1]); debugC(2, kDebugLevelNodes, "New nodebase at %04x:%04x", PRINT_REG(s->r_acc)); @@ -397,11 +415,11 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { reg_t dest = argv[1]; reg_t order_func = argv[2]; - int input_size = (int16)readSelectorValue(segMan, source, SELECTOR(size)); + int input_size = (int16)GET_SEL32V(segMan, source, SELECTOR(size)); int i; - reg_t input_data = readSelector(segMan, source, SELECTOR(elements)); - reg_t output_data = readSelector(segMan, dest, SELECTOR(elements)); + reg_t input_data = GET_SEL32(segMan, source, SELECTOR(elements)); + reg_t output_data = GET_SEL32(segMan, dest, SELECTOR(elements)); List *list; Node *node; @@ -412,10 +430,10 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { if (output_data.isNull()) { list = s->_segMan->allocateList(&output_data); list->first = list->last = NULL_REG; - writeSelector(segMan, dest, SELECTOR(elements), output_data); + PUT_SEL32(segMan, dest, SELECTOR(elements), output_data); } - writeSelectorValue(segMan, dest, SELECTOR(size), input_size); + PUT_SEL32V(segMan, dest, SELECTOR(size), input_size); list = s->_segMan->lookupList(input_data); node = s->_segMan->lookupNode(list->first); @@ -424,7 +442,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { i = 0; while (node) { - invokeSelector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value); + invoke_selector(INV_SEL(s, order_func, doit, kStopOnInvalidSelector), 1, node->value); temp_array[i].key = node->key; temp_array[i].value = node->value; temp_array[i].order = s->r_acc; @@ -435,7 +453,7 @@ reg_t kSort(EngineState *s, int argc, reg_t *argv) { qsort(temp_array, input_size, sizeof(sort_temp_t), sort_temp_cmp); for (i = 0;i < input_size;i++) { - reg_t lNode = s->_segMan->newNode(temp_array[i].value, temp_array[i].key); + reg_t lNode = _k_new_node(s, temp_array[i].key, temp_array[i].value); _k_add_to_end(s, output_data, lNode); } @@ -515,15 +533,15 @@ reg_t kListEachElementDo(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // This can only happen with 3 params (list, target selector, variable) if (argc != 3) { warning("kListEachElementDo: Attempted to modify a variable selector with %d params", argc); } else { - writeSelector(s->_segMan, curObject, slc, argv[2]); + write_selector(s->_segMan, curObject, slc, argv[2]); } } else { - invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); } curNode = s->_segMan->lookupNode(nextNode); @@ -548,11 +566,11 @@ reg_t kListFirstTrue(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? warning("kListFirstTrue: Attempted to access a variable selector"); } else { - invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); // Check if the result is true if (!s->r_acc.isNull()) @@ -582,11 +600,11 @@ reg_t kListAllTrue(EngineState *s, int argc, reg_t *argv) { curObject = curNode->value; // First, check if the target selector is a variable - if (lookupSelector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { + if (lookup_selector(s->_segMan, curObject, slc, &address, NULL) == kSelectorVariable) { // Can this happen with variable selectors? warning("kListAllTrue: Attempted to access a variable selector"); } else { - invokeSelectorArgv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); + invoke_selector_argv(s, curObject, slc, kContinueOnInvalidSelector, argc, argv, argc - 2, argv + 2); // Check if the result isn't true if (s->r_acc.isNull()) diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index 68dc2fbba4..450dca3770 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -32,14 +32,14 @@ #include "sci/engine/kernel.h" #include "sci/engine/gc.h" #include "sci/graphics/gui.h" -#include "sci/graphics/maciconbar.h" namespace Sci { reg_t kRestartGame(EngineState *s, int argc, reg_t *argv) { s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; + s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; // This appears to help - s->shrinkStackToBase(); + shrink_execution_stack(s, s->execution_stack_base + 1); script_abort_flag = 1; // Force vm to abort ASAP return NULL_REG; @@ -62,9 +62,9 @@ reg_t kGameIsRestarting(EngineState *s, int argc, reg_t *argv) { // LSL3 calculates a machinespeed variable during game startup (right after the filthy questions) // This one would go through w/o throttling resulting in having to do 1000 pushups or something // Another way of handling this would be delaying incrementing of "machineSpeed" selector - if (!strcmp(g_sci->getGameID(), "lsl3") && s->currentRoomNumber() == 290) + if (s->_gameId == "lsl3" && s->currentRoomNumber() == 290) s->_throttleTrigger = true; - if (!strcmp(g_sci->getGameID(), "iceman") && s->currentRoomNumber() == 27) { + if (s->_gameId == "iceman" && s->currentRoomNumber() == 27) { s->_throttleTrigger = true; neededSleep = 60; } @@ -252,15 +252,10 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { break; } case K_MEMORY_PEEK : { - if (!argv[1].segment) { - // This occurs in KQ5CD when interacting with certain objects - warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); - return s->r_acc; - } - SegmentRef ref = s->_segMan->dereference(argv[1]); if (!ref.isValid() || ref.maxSize < 2) { + // This occurs in KQ5CD when interacting with certain objects warning("Attempt to peek invalid memory at %04x:%04x", PRINT_REG(argv[1])); return s->r_acc; } @@ -303,12 +298,9 @@ reg_t kMemory(EngineState *s, int argc, reg_t *argv) { reg_t kIconBar(EngineState *s, int argc, reg_t *argv) { // TODO... - if (argv[0].toUint16() == 4 && argv[1].toUint16() == 0) { + if (argv[0].toUint16() == 4 && argv[1].toUint16() == 0) for (int i = 0; i < argv[2].toUint16(); i++) - g_sci->_gfxMacIconBar->addIcon(argv[i + 3]); - - g_sci->_gfxMacIconBar->drawIcons(); - } + warning("kIconBar: Icon Object %d = %04x:%04x", i, PRINT_REG(argv[i + 3])); // Other calls seem to handle selecting/deselecting them diff --git a/engines/sci/engine/kmovement.cpp b/engines/sci/engine/kmovement.cpp index 499aeabcc6..fcaf0d7ea0 100644 --- a/engines/sci/engine/kmovement.cpp +++ b/engines/sci/engine/kmovement.cpp @@ -158,8 +158,8 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "SetJump for object at %04x:%04x", PRINT_REG(object)); debugC(2, kDebugLevelBresen, "xStep: %d, yStep: %d", vx, vy); - writeSelectorValue(segMan, object, SELECTOR(xStep), vx); - writeSelectorValue(segMan, object, SELECTOR(yStep), vy); + PUT_SEL32V(segMan, object, SELECTOR(xStep), vx); + PUT_SEL32V(segMan, object, SELECTOR(yStep), vy); return s->r_acc; } @@ -168,9 +168,9 @@ reg_t kSetJump(EngineState *s, int argc, reg_t *argv) { #define _K_BRESEN_AXIS_Y 1 static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t mover, int step_factor, int deltax, int deltay) { - reg_t client = readSelector(segMan, mover, SELECTOR(client)); - int stepx = (int16)readSelectorValue(segMan, client, SELECTOR(xStep)) * step_factor; - int stepy = (int16)readSelectorValue(segMan, client, SELECTOR(yStep)) * step_factor; + reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); + int stepx = (int16)GET_SEL32V(segMan, client, SELECTOR(xStep)) * step_factor; + int stepy = (int16)GET_SEL32V(segMan, client, SELECTOR(yStep)) * step_factor; int numsteps_x = stepx ? (abs(deltax) + stepx - 1) / stepx : 0; int numsteps_y = stepy ? (abs(deltay) + stepy - 1) / stepy : 0; int bdi, i1; @@ -191,15 +191,15 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m /* if (abs(deltax) > abs(deltay)) {*/ // Bresenham on y if (numsteps_y < numsteps_x) { - writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y); - writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1); + PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_Y); + PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltay < 0) ? -1 : 1); //i1 = 2 * (abs(deltay) - abs(deltay_step * numsteps)) * abs(deltax_step); //bdi = -abs(deltax); i1 = 2 * (abs(deltay) - abs(deltay_step * (numsteps - 1))) * abs(deltax_step); bdi = -abs(deltax); } else { // Bresenham on x - writeSelectorValue(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X); - writeSelectorValue(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1); + PUT_SEL32V(segMan, mover, SELECTOR(b_xAxis), _K_BRESEN_AXIS_X); + PUT_SEL32V(segMan, mover, SELECTOR(b_incr), (deltax < 0) ? -1 : 1); //i1= 2 * (abs(deltax) - abs(deltax_step * numsteps)) * abs(deltay_step); //bdi = -abs(deltay); i1 = 2 * (abs(deltax) - abs(deltax_step * (numsteps - 1))) * abs(deltay_step); @@ -207,26 +207,26 @@ static void initialize_bresen(SegManager *segMan, int argc, reg_t *argv, reg_t m } - writeSelectorValue(segMan, mover, SELECTOR(dx), deltax_step); - writeSelectorValue(segMan, mover, SELECTOR(dy), deltay_step); + PUT_SEL32V(segMan, mover, SELECTOR(dx), deltax_step); + PUT_SEL32V(segMan, mover, SELECTOR(dy), deltay_step); debugC(2, kDebugLevelBresen, "Init bresen for mover %04x:%04x: d=(%d,%d)", PRINT_REG(mover), deltax, deltay); debugC(2, kDebugLevelBresen, " steps=%d, mv=(%d, %d), i1= %d, i2=%d", numsteps, deltax_step, deltay_step, i1, bdi*2); - //writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre? - writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi); - writeSelectorValue(segMan, mover, SELECTOR(b_i1), i1); - writeSelectorValue(segMan, mover, SELECTOR(b_i2), bdi * 2); + //PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), numsteps); // Needed for HQ1/Ogre? + PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi); + PUT_SEL32V(segMan, mover, SELECTOR(b_i1), i1); + PUT_SEL32V(segMan, mover, SELECTOR(b_i2), bdi * 2); } reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; - reg_t client = readSelector(segMan, mover, SELECTOR(client)); + reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); - int deltax = (int16)readSelectorValue(segMan, mover, SELECTOR(x)) - (int16)readSelectorValue(segMan, client, SELECTOR(x)); - int deltay = (int16)readSelectorValue(segMan, mover, SELECTOR(y)) - (int16)readSelectorValue(segMan, client, SELECTOR(y)); + int deltax = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)) - (int16)GET_SEL32V(segMan, client, SELECTOR(x)); + int deltay = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)) - (int16)GET_SEL32V(segMan, client, SELECTOR(y)); int step_factor = (argc < 1) ? argv[1].toUint16() : 1; initialize_bresen(s->_segMan, argc, argv, mover, step_factor, deltax, deltay); @@ -240,42 +240,42 @@ reg_t kInitBresen(EngineState *s, int argc, reg_t *argv) { reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { SegManager *segMan = s->_segMan; reg_t mover = argv[0]; - reg_t client = readSelector(segMan, mover, SELECTOR(client)); + reg_t client = GET_SEL32(segMan, mover, SELECTOR(client)); - int x = (int16)readSelectorValue(segMan, client, SELECTOR(x)); - int y = (int16)readSelectorValue(segMan, client, SELECTOR(y)); + int x = (int16)GET_SEL32V(segMan, client, SELECTOR(x)); + int y = (int16)GET_SEL32V(segMan, client, SELECTOR(y)); int oldx, oldy, destx, desty, dx, dy, bdi, bi1, bi2, movcnt, bdelta, axis; - uint16 signal = readSelectorValue(segMan, client, SELECTOR(signal)); + uint16 signal = GET_SEL32V(segMan, client, SELECTOR(signal)); int completed = 0; - int max_movcnt = readSelectorValue(segMan, client, SELECTOR(moveSpeed)); + int max_movcnt = GET_SEL32V(segMan, client, SELECTOR(moveSpeed)); if (getSciVersion() > SCI_VERSION_01) signal &= ~kSignalHitObstacle; - writeSelector(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0 + PUT_SEL32(segMan, client, SELECTOR(signal), make_reg(0, signal)); // This is a NOP for SCI0 oldx = x; oldy = y; - destx = (int16)readSelectorValue(segMan, mover, SELECTOR(x)); - desty = (int16)readSelectorValue(segMan, mover, SELECTOR(y)); - dx = (int16)readSelectorValue(segMan, mover, SELECTOR(dx)); - dy = (int16)readSelectorValue(segMan, mover, SELECTOR(dy)); - bdi = (int16)readSelectorValue(segMan, mover, SELECTOR(b_di)); - bi1 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i1)); - bi2 = (int16)readSelectorValue(segMan, mover, SELECTOR(b_i2)); - movcnt = readSelectorValue(segMan, mover, SELECTOR(b_movCnt)); - bdelta = (int16)readSelectorValue(segMan, mover, SELECTOR(b_incr)); - axis = (int16)readSelectorValue(segMan, mover, SELECTOR(b_xAxis)); + destx = (int16)GET_SEL32V(segMan, mover, SELECTOR(x)); + desty = (int16)GET_SEL32V(segMan, mover, SELECTOR(y)); + dx = (int16)GET_SEL32V(segMan, mover, SELECTOR(dx)); + dy = (int16)GET_SEL32V(segMan, mover, SELECTOR(dy)); + bdi = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_di)); + bi1 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i1)); + bi2 = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_i2)); + movcnt = GET_SEL32V(segMan, mover, SELECTOR(b_movCnt)); + bdelta = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_incr)); + axis = (int16)GET_SEL32V(segMan, mover, SELECTOR(b_xAxis)); //printf("movecnt %d, move speed %d\n", movcnt, max_movcnt); if (g_sci->_features->handleMoveCount()) { if (max_movcnt > movcnt) { ++movcnt; - writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? + PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? return NULL_REG; } else { movcnt = 0; - writeSelectorValue(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? + PUT_SEL32V(segMan, mover, SELECTOR(b_movCnt), movcnt); // Needed for HQ1/Ogre? } } @@ -288,7 +288,7 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { dy += bdelta; } - writeSelectorValue(segMan, mover, SELECTOR(b_di), bdi); + PUT_SEL32V(segMan, mover, SELECTOR(b_di), bdi); x += dx; y += dy; @@ -310,32 +310,33 @@ reg_t kDoBresen(EngineState *s, int argc, reg_t *argv) { debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x", PRINT_REG(mover)); } - writeSelectorValue(segMan, client, SELECTOR(x), x); - writeSelectorValue(segMan, client, SELECTOR(y), y); + PUT_SEL32V(segMan, client, SELECTOR(x), x); + PUT_SEL32V(segMan, client, SELECTOR(y), y); debugC(2, kDebugLevelBresen, "New data: (x,y)=(%d,%d), di=%d", x, y, bdi); if (g_sci->getKernel()->_selectorCache.cantBeHere != -1) { - invokeSelector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0); + invoke_selector(INV_SEL(s, client, cantBeHere, kStopOnInvalidSelector), 0); s->r_acc = make_reg(0, !s->r_acc.offset); } else { - invokeSelector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0); + invoke_selector(INV_SEL(s, client, canBeHere, kStopOnInvalidSelector), 0); } if (!s->r_acc.offset) { // Contains the return value - signal = readSelectorValue(segMan, client, SELECTOR(signal)); + signal = GET_SEL32V(segMan, client, SELECTOR(signal)); - writeSelectorValue(segMan, client, SELECTOR(x), oldx); - writeSelectorValue(segMan, client, SELECTOR(y), oldy); - writeSelectorValue(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); + PUT_SEL32V(segMan, client, SELECTOR(x), oldx); + PUT_SEL32V(segMan, client, SELECTOR(y), oldy); + PUT_SEL32V(segMan, client, SELECTOR(signal), (signal | kSignalHitObstacle)); debugC(2, kDebugLevelBresen, "Finished mover %04x:%04x by collision", PRINT_REG(mover)); completed = 1; } - if ((getSciVersion() >= SCI_VERSION_1_EGA)) + // FIXME: find out why iceman needs this and we ask for version > SCI01 + if ((getSciVersion() > SCI_VERSION_01) || (s->_gameId == "iceman")) if (completed) - invokeSelector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0); + invoke_selector(INV_SEL(s, mover, moveDone, kStopOnInvalidSelector), 0); return make_reg(0, completed); } @@ -377,15 +378,15 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } - client = readSelector(segMan, avoider, SELECTOR(client)); + client = GET_SEL32(segMan, avoider, SELECTOR(client)); if (!s->_segMan->isHeapObject(client)) { warning("DoAvoider() where client %04x:%04x is not an object", PRINT_REG(client)); return NULL_REG; } - looper = readSelector(segMan, client, SELECTOR(looper)); - mover = readSelector(segMan, client, SELECTOR(mover)); + looper = GET_SEL32(segMan, client, SELECTOR(looper)); + mover = GET_SEL32(segMan, client, SELECTOR(mover)); if (!s->_segMan->isHeapObject(mover)) { if (mover.segment) { @@ -394,38 +395,38 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - destx = readSelectorValue(segMan, mover, SELECTOR(x)); - desty = readSelectorValue(segMan, mover, SELECTOR(y)); + destx = GET_SEL32V(segMan, mover, SELECTOR(x)); + desty = GET_SEL32V(segMan, mover, SELECTOR(y)); debugC(2, kDebugLevelBresen, "Doing avoider %04x:%04x (dest=%d,%d)", PRINT_REG(avoider), destx, desty); - if (invokeSelector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) { + if (invoke_selector(INV_SEL(s, mover, doit, kContinueOnInvalidSelector) , 0)) { error("Mover %04x:%04x of avoider %04x:%04x doesn't have a doit() funcselector", PRINT_REG(mover), PRINT_REG(avoider)); return NULL_REG; } - mover = readSelector(segMan, client, SELECTOR(mover)); + mover = GET_SEL32(segMan, client, SELECTOR(mover)); if (!mover.segment) // Mover has been disposed? return s->r_acc; // Return gracefully. - if (invokeSelector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) { + if (invoke_selector(INV_SEL(s, client, isBlocked, kContinueOnInvalidSelector) , 0)) { error("Client %04x:%04x of avoider %04x:%04x doesn't" " have an isBlocked() funcselector", PRINT_REG(client), PRINT_REG(avoider)); return NULL_REG; } - dx = destx - readSelectorValue(segMan, client, SELECTOR(x)); - dy = desty - readSelectorValue(segMan, client, SELECTOR(y)); + dx = destx - GET_SEL32V(segMan, client, SELECTOR(x)); + dy = desty - GET_SEL32V(segMan, client, SELECTOR(y)); angle = getAngle(dx, dy); debugC(2, kDebugLevelBresen, "Movement (%d,%d), angle %d is %sblocked", dx, dy, angle, (s->r_acc.offset) ? " " : "not "); if (s->r_acc.offset) { // isBlocked() returned non-zero int rotation = (rand() & 1) ? 45 : (360 - 45); // Clockwise/counterclockwise - int oldx = readSelectorValue(segMan, client, SELECTOR(x)); - int oldy = readSelectorValue(segMan, client, SELECTOR(y)); - int xstep = readSelectorValue(segMan, client, SELECTOR(xStep)); - int ystep = readSelectorValue(segMan, client, SELECTOR(yStep)); + int oldx = GET_SEL32V(segMan, client, SELECTOR(x)); + int oldy = GET_SEL32V(segMan, client, SELECTOR(y)); + int xstep = GET_SEL32V(segMan, client, SELECTOR(xStep)); + int ystep = GET_SEL32V(segMan, client, SELECTOR(yStep)); int moves; debugC(2, kDebugLevelBresen, " avoider %04x:%04x", PRINT_REG(avoider)); @@ -434,23 +435,23 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { int move_x = (int)(sin(angle * PI / 180.0) * (xstep)); int move_y = (int)(-cos(angle * PI / 180.0) * (ystep)); - writeSelectorValue(segMan, client, SELECTOR(x), oldx + move_x); - writeSelectorValue(segMan, client, SELECTOR(y), oldy + move_y); + PUT_SEL32V(segMan, client, SELECTOR(x), oldx + move_x); + PUT_SEL32V(segMan, client, SELECTOR(y), oldy + move_y); debugC(2, kDebugLevelBresen, "Pos (%d,%d): Trying angle %d; delta=(%d,%d)", oldx, oldy, angle, move_x, move_y); - if (invokeSelector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) { + if (invoke_selector(INV_SEL(s, client, canBeHere, kContinueOnInvalidSelector) , 0)) { error("Client %04x:%04x of avoider %04x:%04x doesn't" " have a canBeHere() funcselector", PRINT_REG(client), PRINT_REG(avoider)); return NULL_REG; } - writeSelectorValue(segMan, client, SELECTOR(x), oldx); - writeSelectorValue(segMan, client, SELECTOR(y), oldy); + PUT_SEL32V(segMan, client, SELECTOR(x), oldx); + PUT_SEL32V(segMan, client, SELECTOR(y), oldy); if (s->r_acc.offset) { // We can be here debugC(2, kDebugLevelBresen, "Success"); - writeSelectorValue(segMan, client, SELECTOR(heading), angle); + PUT_SEL32V(segMan, client, SELECTOR(heading), angle); return make_reg(0, angle); } @@ -463,17 +464,17 @@ reg_t kDoAvoider(EngineState *s, int argc, reg_t *argv) { warning("DoAvoider failed for avoider %04x:%04x", PRINT_REG(avoider)); } else { - int heading = readSelectorValue(segMan, client, SELECTOR(heading)); + int heading = GET_SEL32V(segMan, client, SELECTOR(heading)); if (heading == -1) return s->r_acc; // No change - writeSelectorValue(segMan, client, SELECTOR(heading), angle); + PUT_SEL32V(segMan, client, SELECTOR(heading), angle); s->r_acc = make_reg(0, angle); if (looper.segment) { - if (invokeSelector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) { + if (invoke_selector(INV_SEL(s, looper, doit, kContinueOnInvalidSelector), 2, angle, client)) { error("Looper %04x:%04x of avoider %04x:%04x doesn't" " have a doit() funcselector", PRINT_REG(looper), PRINT_REG(avoider)); } else diff --git a/engines/sci/engine/kparse.cpp b/engines/sci/engine/kparse.cpp index 785ff39d22..0254d21642 100644 --- a/engines/sci/engine/kparse.cpp +++ b/engines/sci/engine/kparse.cpp @@ -42,7 +42,6 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { reg_t heap_said_block = argv[0]; byte *said_block; int new_lastmatch; - Vocabulary *voc = g_sci->getVocabulary(); #ifdef DEBUG_PARSER const int debug_parser = 1; #else @@ -64,7 +63,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { s->_voc->decipherSaidBlock(said_block); #endif - if (voc->parser_event.isNull() || (readSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed)))) { + if (s->_voc->parser_event.isNull() || (GET_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed)))) { return NULL_REG; } @@ -78,7 +77,7 @@ reg_t kSaid(EngineState *s, int argc, reg_t *argv) { s->r_acc = make_reg(0, 1); if (new_lastmatch != SAID_PARTIAL_MATCH) - writeSelectorValue(s->_segMan, voc->parser_event, SELECTOR(claimed), 1); + PUT_SEL32V(s->_segMan, s->_voc->parser_event, SELECTOR(claimed), 1); } else { return NULL_REG; @@ -93,15 +92,15 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { char *error; ResultWordList words; reg_t event = argv[1]; - Vocabulary *voc = g_sci->getVocabulary(); + Vocabulary *voc = s->_voc; - voc->parser_event = event; + s->_voc->parser_event = event; bool res = voc->tokenizeString(words, string.c_str(), &error); - voc->parserIsValid = false; /* not valid */ + s->_voc->parserIsValid = false; /* not valid */ if (res && !words.empty()) { - voc->synonymizeTokens(words); + s->_voc->synonymizeTokens(words); s->r_acc = make_reg(0, 1); @@ -116,32 +115,32 @@ reg_t kParse(EngineState *s, int argc, reg_t *argv) { if (syntax_fail) { s->r_acc = make_reg(0, 1); - writeSelectorValue(segMan, event, SELECTOR(claimed), 1); + PUT_SEL32V(segMan, event, SELECTOR(claimed), 1); - invokeSelector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos); + invoke_selector(INV_SEL(s, s->_gameObj, syntaxFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos); /* Issue warning */ debugC(2, kDebugLevelParser, "Tree building failed"); } else { - voc->parserIsValid = true; - writeSelectorValue(segMan, event, SELECTOR(claimed), 0); + s->_voc->parserIsValid = true; + PUT_SEL32V(segMan, event, SELECTOR(claimed), 0); #ifdef DEBUG_PARSER - voc->dumpParseTree(); + s->_voc->dumpParseTree(); #endif } } else { s->r_acc = make_reg(0, 0); - writeSelectorValue(segMan, event, SELECTOR(claimed), 1); + PUT_SEL32V(segMan, event, SELECTOR(claimed), 1); if (error) { - s->_segMan->strcpy(voc->parser_base, error); + s->_segMan->strcpy(s->_voc->parser_base, error); debugC(2, kDebugLevelParser, "Word unknown: %s", error); /* Issue warning: */ - invokeSelector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, voc->parser_base, stringpos); + invoke_selector(INV_SEL(s, s->_gameObj, wordFail, kStopOnInvalidSelector), 2, s->_voc->parser_base, stringpos); free(error); return make_reg(0, 1); /* Tell them that it didn't work */ } @@ -157,29 +156,28 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { Node *node; int script; int numSynonyms = 0; - Vocabulary *voc = g_sci->getVocabulary(); // Only SCI0-SCI1 EGA games had a parser. In newer versions, this is a stub if (getSciVersion() > SCI_VERSION_1_EGA) return s->r_acc; - voc->clearSynonyms(); + s->_voc->clearSynonyms(); - list = s->_segMan->lookupList(readSelector(segMan, object, SELECTOR(elements))); + list = s->_segMan->lookupList(GET_SEL32(segMan, object, SELECTOR(elements))); node = s->_segMan->lookupNode(list->first); while (node) { reg_t objpos = node->value; int seg; - script = readSelectorValue(segMan, objpos, SELECTOR(number)); + script = GET_SEL32V(segMan, objpos, SELECTOR(number)); seg = s->_segMan->getScriptSegment(script); if (seg > 0) numSynonyms = s->_segMan->getScript(seg)->getSynonymsNr(); if (numSynonyms) { - const byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); + byte *synonyms = s->_segMan->getScript(seg)->getSynonyms(); if (synonyms) { debugC(2, kDebugLevelParser, "Setting %d synonyms for script.%d", @@ -195,7 +193,7 @@ reg_t kSetSynonyms(EngineState *s, int argc, reg_t *argv) { synonym_t tmp; tmp.replaceant = (int16)READ_LE_UINT16(synonyms + i * 4); tmp.replacement = (int16)READ_LE_UINT16(synonyms + i * 4 + 2); - voc->addSynonym(tmp); + s->_voc->addSynonym(tmp); } } else warning("Synonyms of script.%03d were requested, but script is not available", script); diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 857ccc2a08..25d967c247 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -337,15 +337,15 @@ static void draw_point(EngineState *s, Common::Point p, int start, int width, in static void draw_polygon(EngineState *s, reg_t polygon, int width, int height) { SegManager *segMan = s->_segMan; - reg_t points = readSelector(segMan, polygon, SELECTOR(points)); + reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); #ifdef ENABLE_SCI32 if (segMan->isHeapObject(points)) - points = readSelector(segMan, points, SELECTOR(data)); + points = GET_SEL32(segMan, points, SELECTOR(data)); #endif - int size = readSelectorValue(segMan, polygon, SELECTOR(size)); - int type = readSelectorValue(segMan, polygon, SELECTOR(type)); + int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); + int type = GET_SEL32V(segMan, polygon, SELECTOR(type)); Common::Point first, prev; int i; @@ -386,15 +386,15 @@ static void draw_input(EngineState *s, reg_t poly_list, Common::Point start, Com } static void print_polygon(SegManager *segMan, reg_t polygon) { - reg_t points = readSelector(segMan, polygon, SELECTOR(points)); + reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); #ifdef ENABLE_SCI32 if (segMan->isHeapObject(points)) - points = readSelector(segMan, points, SELECTOR(data)); + points = GET_SEL32(segMan, points, SELECTOR(data)); #endif - int size = readSelectorValue(segMan, polygon, SELECTOR(size)); - int type = readSelectorValue(segMan, polygon, SELECTOR(type)); + int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); + int type = GET_SEL32V(segMan, polygon, SELECTOR(type)); int i; Common::Point point; @@ -1036,13 +1036,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { // Returns : (Polygon *) The converted polygon, or NULL on error SegManager *segMan = s->_segMan; int i; - reg_t points = readSelector(segMan, polygon, SELECTOR(points)); - int size = readSelectorValue(segMan, polygon, SELECTOR(size)); + reg_t points = GET_SEL32(segMan, polygon, SELECTOR(points)); + int size = GET_SEL32V(segMan, polygon, SELECTOR(size)); #ifdef ENABLE_SCI32 // SCI32 stores the actual points in the data property of points (in a new array) if (segMan->isHeapObject(points)) - points = readSelector(segMan, points, SELECTOR(data)); + points = GET_SEL32(segMan, points, SELECTOR(data)); #endif if (size == 0) { @@ -1050,13 +1050,13 @@ static Polygon *convert_polygon(EngineState *s, reg_t polygon) { return NULL; } - Polygon *poly = new Polygon(readSelectorValue(segMan, polygon, SELECTOR(type))); + Polygon *poly = new Polygon(GET_SEL32V(segMan, polygon, SELECTOR(type))); int skip = 0; // WORKAROUND: broken polygon in lsl1sci, room 350, after opening elevator // Polygon has 17 points but size is set to 19 - if ((size == 19) && !strcmp(g_sci->getGameID(), "lsl1sci")) { + if ((size == 19) && (s->_gameId == "lsl1sci")) { if ((s->currentRoomNumber() == 350) && (read_point(segMan, points, 18) == Common::Point(108, 137))) { debug(1, "Applying fix for broken polygon in lsl1sci, room 350"); @@ -1121,7 +1121,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co if (polygon) { pf_s->polygons.push_back(polygon); - count += readSelectorValue(segMan, node->value, SELECTOR(size)); + count += GET_SEL32V(segMan, node->value, SELECTOR(size)); } node = s->_segMan->lookupNode(node->succ); @@ -1174,7 +1174,7 @@ static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Co // WORKAROUND LSL5 room 660. Priority glitch due to us choosing a different path // than SSCI. Happens when Patti walks to the control room. - if (!strcmp(g_sci->getGameID(), "lsl5") && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) { + if ((s->_gameId == "lsl5") && (s->currentRoomNumber() == 660) && (Common::Point(67, 131) == *new_start) && (Common::Point(229, 101) == *new_end)) { debug(1, "[avoidpath] Applying fix for priority problem in LSL5, room 660"); pf_s->_prependPoint = new_start; new_start = new Common::Point(77, 107); @@ -1394,7 +1394,7 @@ reg_t kAvoidPath(EngineState *s, int argc, reg_t *argv) { if (argc < 7) error("[avoidpath] Not enough arguments"); - poly_list = (!argv[4].isNull() ? readSelector(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG); + poly_list = (!argv[4].isNull() ? GET_SEL32(s->_segMan, argv[4], SELECTOR(elements)) : NULL_REG); width = argv[5].toUint16(); height = argv[6].toUint16(); if (argc > 7) @@ -1694,38 +1694,4 @@ reg_t kIntersections(EngineState *s, int argc, reg_t *argv) { } } -// This is a quite rare kernel function. An example of when it's called -// is in QFG1VGA, after killing any monster. -reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { - // 3 parameters: raw polygon data, polygon list, list size - reg_t polygonData = argv[0]; - - // TODO: actually merge the polygon - // In QFG1VGA, there are no immediately visible side-effects - // of this being a stub. - -#if 0 - List *list = s->_segMan->lookupList(argv[1]); - Node *node = s->_segMan->lookupNode(list->first); - // List size is not needed - - Polygon *polygon; - int count = 0; - - while (node) { - polygon = convert_polygon(s, node->value); - - if (polygon) { - count += readSelectorValue(s->_segMan, node->value, SELECTOR(size)); - } - - node = s->_segMan->lookupNode(node->succ); - } -#endif - - warning("Stub: kMergePoly"); - - return polygonData; -} - } // End of namespace Sci diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 722d0175d1..ba29f64966 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -111,7 +111,7 @@ reg_t kResCheck(EngineState *s, int argc, reg_t *argv) { reg_t kClone(EngineState *s, int argc, reg_t *argv) { reg_t parent_addr = argv[0]; - const Object *parent_obj = s->_segMan->getObject(parent_addr); + Object *parent_obj = s->_segMan->getObject(parent_addr); reg_t clone_addr; Clone *clone_obj; // same as Object* @@ -132,7 +132,7 @@ reg_t kClone(EngineState *s, int argc, reg_t *argv) { *clone_obj = *parent_obj; // Mark as clone - clone_obj->markAsClone(); + clone_obj->setInfoSelector(make_reg(0, SCRIPT_INFO_CLONE)); clone_obj->setSpeciesSelector(clone_obj->getPos()); if (parent_obj->isClass()) clone_obj->setSuperClassSelector(parent_obj->getPos()); @@ -154,14 +154,14 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } - if (!victim_obj->isClone()) { + if (victim_obj->getInfoSelector().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 return s->r_acc; } // QFG3 clears clones with underbits set - //if (readSelectorValue(victim_addr, underBits)) + //if (GET_SEL32V(victim_addr, underBits)) // warning("Clone %04x:%04x was cleared with underBits set", PRINT_REG(victim_addr)); #if 0 @@ -181,7 +181,7 @@ reg_t kDisposeClone(EngineState *s, int argc, reg_t *argv) { // Returns script dispatch address index in the supplied script reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { int script = argv[0].toUint16(); - uint16 index = (argc > 1) ? argv[1].toUint16() : 0; + int index = (argc > 1) ? argv[1].toUint16() : 0; if (argv[0].segment) return argv[0]; @@ -193,30 +193,18 @@ reg_t kScriptID(EngineState *s, int argc, reg_t *argv) { Script *scr = s->_segMan->getScript(scriptSeg); - if (!scr->getExportsNr()) { - // This is normal. Some scripts don't have a dispatch (exports) table, - // and this call is probably used to load them in memory, ignoring - // the return value. If only one argument is passed, this call is done - // only to load the script in memory. Thus, don't show any warning, - // as no return value is expected - if (argc == 2) - warning("Script 0x%x does not have a dispatch table and export %d " - "was requested from it", script, index); + if (!scr->_numExports) { + // FIXME: Is this fatal? This occurs in SQ4CD + warning("Script 0x%x does not have a dispatch table", script); return NULL_REG; } - if (index > scr->getExportsNr()) { - error("Dispatch index too big: %d > %d", index, scr->getExportsNr()); + if (index > scr->_numExports) { + error("Dispatch index too big: %d > %d", index, scr->_numExports); return NULL_REG; } - uint16 address = scr->validateExportFunc(index); - - // Point to the heap for SCI1.1+ games - if (getSciVersion() >= SCI_VERSION_1_1) - address += scr->getScriptSize(); - - return make_reg(scriptSeg, address); + return make_reg(scriptSeg, scr->validateExportFunc(index)); } reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) { @@ -256,7 +244,7 @@ reg_t kRespondsTo(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; int selector = argv[1].toUint16(); - return make_reg(0, s->_segMan->isHeapObject(obj) && lookupSelector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone); + return make_reg(0, s->_segMan->isHeapObject(obj) && lookup_selector(s->_segMan, obj, selector, NULL, NULL) != kSelectorNone); } } // End of namespace Sci diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 2681b612e9..426c682e11 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -138,36 +138,10 @@ reg_t kReadNumber(EngineState *s, int argc, reg_t *argv) { while (isspace((unsigned char)*source)) source++; /* Skip whitespace */ - int16 result = 0; - - if (*source == '$') { - // hexadecimal input - result = (int16)strtol(source + 1, NULL, 16); - } else { - // decimal input, we can not use strtol/atoi in here, because sierra used atoi BUT it was a non standard compliant - // atoi, that didnt do clipping. In SQ4 we get the door code in here and that's even larger than uint32! - if (*source == '-') { - result = -1; - source++; - } - while (*source) { - if ((*source < '0') || (*source > '9')) { - // Sierras atoi stopped processing at anything different than number - // Sometimes the input has a trailing space, that's fine (example: lsl3) - if (*source != ' ') { - // TODO: this happens in lsl5 right in the intro -> we get '1' '3' 0xCD 0xCD 0xCD 0xCD 0xCD - // find out why this happens and fix it - warning("Invalid character in kReadNumber input"); - } - break; - } - result *= 10; - result += *source - 0x30; - source++; - } - } - - return make_reg(0, result); + if (*source == '$') /* SCI uses this for hex numbers */ + return make_reg(0, (int16)strtol(source + 1, NULL, 16)); /* Hex */ + else + return make_reg(0, (int16)strtol(source, NULL, 10)); /* Force decimal */ } @@ -267,7 +241,7 @@ reg_t kFormat(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 // If the string is a string object, get to the actual string in the data selector if (s->_segMan->isObject(reg)) - reg = readSelector(s->_segMan, reg, SELECTOR(data)); + reg = GET_SEL32(s->_segMan, reg, SELECTOR(data)); #endif Common::String tempsource = (reg == NULL_REG) ? "" : g_sci->getKernel()->lookupText(reg, diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 798f889460..fef2b9a19e 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -383,7 +383,7 @@ void EngineState::saveLoadWithSerializer(Common::Serializer &s) { sync_SegManagerPtr(s, _segMan); - syncArray<Class>(s, _segMan->_classTable); + syncArray<Class>(s, _segMan->_classtable); #ifdef USE_OLD_MUSIC_FUNCTIONS sync_songlib(s, _sound._songlib); @@ -541,8 +541,8 @@ void Script::saveLoadWithSerializer(Common::Serializer &s) { } } - s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numExports - s.skip(4, VER(9), VER(19)); // OBSOLETE: Used to be _numSynonyms + s.syncAsSint32LE(_numExports); + s.syncAsSint32LE(_numSynonyms); s.syncAsSint32LE(_lockers); // Sync _objects. This is a hashmap, and we use the following on disk format: @@ -731,6 +731,15 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename return 1; } +/* + if (s->sound_server) { + if ((s->sound_server->save)(s, dirname)) { + warning("Saving failed for the sound subsystem"); + //chdir(".."); + return 1; + } + } +*/ Common::Serializer ser(0, fh); sync_SavegameMetadata(ser, meta); Graphics::saveThumbnail(*fh); @@ -739,6 +748,26 @@ int gamestate_save(EngineState *s, Common::WriteStream *fh, const char* savename return 0; } +static byte *find_unique_script_block(EngineState *s, byte *buf, int type) { + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); + + if (oldScriptHeader) + buf += 2; + + do { + int seeker_type = READ_LE_UINT16(buf); + + if (seeker_type == 0) break; + if (seeker_type == type) return buf; + + int seeker_size = READ_LE_UINT16(buf + 2); + assert(seeker_size > 0); + buf += seeker_size; + } while (1); + + return NULL; +} + // TODO: This should probably be turned into an EngineState or DataStack method. static void reconstruct_stack(EngineState *retval) { SegmentId stack_seg = retval->_segMan->findSegmentByType(SEG_TYPE_STACK); @@ -748,37 +777,99 @@ static void reconstruct_stack(EngineState *retval) { retval->stack_top = stack->_entries + stack->_capacity; } +static void load_script(EngineState *s, Script *scr) { + scr->_buf = (byte *)malloc(scr->_bufSize); + assert(scr->_buf); + + Resource *script = g_sci->getResMan()->findResource(ResourceId(kResourceTypeScript, scr->_nr), 0); + assert(script != 0); + + assert(scr->_bufSize >= script->size); + memcpy(scr->_buf, script->data, script->size); + + if (getSciVersion() >= SCI_VERSION_1_1) { + Resource *heap = g_sci->getResMan()->findResource(ResourceId(kResourceTypeHeap, scr->_nr), 0); + assert(heap != 0); + + scr->_heapStart = scr->_buf + scr->_scriptSize; + + assert(scr->_bufSize - scr->_scriptSize <= heap->size); + memcpy(scr->_heapStart, heap->data, heap->size); + } +} + // TODO: Move thie function to a more appropriate place, such as vm.cpp or script.cpp void SegManager::reconstructScripts(EngineState *s) { uint i; + SegmentObj *mobj; for (i = 0; i < _heap.size(); i++) { - if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT) + mobj = _heap[i]; + if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT) continue; - Script *scr = (Script *)_heap[i]; - scr->load(g_sci->getResMan()); + Script *scr = (Script *)mobj; + + // FIXME: Unify this code with script_instantiate_* ? + load_script(s, scr); scr->_localsBlock = (scr->_localsSegment == 0) ? NULL : (LocalVariables *)(_heap[scr->_localsSegment]); + if (getSciVersion() >= SCI_VERSION_1_1) { + scr->_exportTable = 0; + scr->_synonyms = 0; + if (READ_LE_UINT16(scr->_buf + 6) > 0) { + scr->setExportTableOffset(6); + s->_segMan->scriptRelocateExportsSci11(i); + } + } else { + scr->_exportTable = (uint16 *) find_unique_script_block(s, scr->_buf, SCI_OBJ_EXPORTS); + scr->_synonyms = find_unique_script_block(s, scr->_buf, SCI_OBJ_SYNONYMS); + scr->_exportTable += 3; + } + scr->_codeBlocks.clear(); - for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) - it->_value._baseObj = scr->_buf + it->_value.getPos().offset; + ObjMap::iterator it; + const ObjMap::iterator end = scr->_objects.end(); + for (it = scr->_objects.begin(); it != end; ++it) { + byte *data = scr->_buf + it->_value.getPos().offset; + it->_value._baseObj = data; + } } for (i = 0; i < _heap.size(); i++) { - if (!_heap[i] || _heap[i]->getType() != SEG_TYPE_SCRIPT) + mobj = _heap[i]; + if (!mobj || mobj->getType() != SEG_TYPE_SCRIPT) continue; - Script *scr = (Script *)_heap[i]; - - for (ObjMap::iterator it = scr->_objects.begin(); it != scr->_objects.end(); ++it) { - reg_t addr = it->_value.getPos(); - Object *obj = scr->scriptObjInit(addr, false); + Script *scr = (Script *)mobj; - if (getSciVersion() < SCI_VERSION_1_1) { - if (!obj->initBaseObject(this, addr, false)) { - warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); - scr->scriptObjRemove(addr); + // FIXME: Unify this code with Script::scriptObjInit ? + ObjMap::iterator it; + const ObjMap::iterator end = scr->_objects.end(); + for (it = scr->_objects.begin(); it != end; ++it) { + byte *data = scr->_buf + it->_value.getPos().offset; + + if (getSciVersion() >= 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 )); + + it->_value._baseMethod = funct_area; + it->_value._baseVars = prop_area; + } else { + int funct_area = READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET); + Object *_baseObj; + + _baseObj = s->_segMan->getObject(it->_value.getSpeciesSelector()); + + if (!_baseObj) { + warning("Object without a base class: Script %d, index %d (reg address %04x:%04x", + scr->_nr, i, PRINT_REG(it->_value.getSpeciesSelector())); + continue; } + it->_value.setVarCount(_baseObj->getVarCount()); + it->_value._baseObj = _baseObj->_baseObj; + + it->_value._baseMethod = (uint16 *)(data + funct_area); + it->_value._baseVars = (uint16 *)(data + it->_value.getVarCount() * 2 + SCRIPT_SELECTOR_OFFSET); } } } @@ -821,6 +912,7 @@ static void reconstruct_sounds(EngineState *s) { #endif void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { + EngineState *retval; #ifdef USE_OLD_MUSIC_FUNCTIONS SongLibrary temp; #endif @@ -855,68 +947,86 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { thumbnail = 0; } - s->reset(true); - s->saveLoadWithSerializer(ser); // FIXME: Error handling? + // Create a new EngineState object + retval = new EngineState(s->_voc, s->_segMan); + retval->_event = s->_event; + + // Copy some old data + retval->_soundCmd = s->_soundCmd; + + // Copy memory segment + retval->_memorySegmentSize = s->_memorySegmentSize; + memcpy(retval->_memorySegment, s->_memorySegment, s->_memorySegmentSize); + + retval->saveLoadWithSerializer(ser); // FIXME: Error handling? #ifdef USE_OLD_MUSIC_FUNCTIONS s->_sound.sfx_exit(); #endif // Set exec stack base to zero - s->execution_stack_base = 0; + retval->execution_stack_base = 0; // Now copy all current state information #ifdef USE_OLD_MUSIC_FUNCTIONS - temp = s->_sound._songlib; - s->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType()); - s->sfx_init_flags = s->sfx_init_flags; - s->_sound._songlib.freeSounds(); - s->_sound._songlib = temp; - s->_soundCmd->updateSfxState(&retval->_sound); + temp = retval->_sound._songlib; + retval->_sound.sfx_init(g_sci->getResMan(), s->sfx_init_flags, g_sci->_features->detectDoSoundType()); + retval->sfx_init_flags = s->sfx_init_flags; + retval->_sound._songlib.freeSounds(); + retval->_sound._songlib = temp; + retval->_soundCmd->updateSfxState(&retval->_sound); #endif - reconstruct_stack(s); - s->_segMan->reconstructScripts(s); - s->_segMan->reconstructClones(); - s->_gameObj = s->_gameObj; - s->script_000 = s->_segMan->getScript(s->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD)); - s->gc_countdown = GC_INTERVAL - 1; + reconstruct_stack(retval); + retval->_segMan->reconstructScripts(retval); + retval->_segMan->reconstructClones(); + retval->_gameObj = s->_gameObj; + retval->script_000 = retval->_segMan->getScript(retval->_segMan->getScriptSegment(0, SCRIPT_GET_DONT_LOAD)); + retval->gc_countdown = GC_INTERVAL - 1; + retval->sys_strings_segment = retval->_segMan->findSegmentByType(SEG_TYPE_SYS_STRINGS); + retval->sys_strings = (SystemStrings *)(retval->_segMan->_heap[retval->sys_strings_segment]); // Time state: - s->last_wait_time = g_system->getMillis(); - s->game_start_time = g_system->getMillis(); + retval->last_wait_time = g_system->getMillis(); + retval->game_start_time = g_system->getMillis(); + + // static parser information: + + if (retval->_voc) + retval->_voc->parser_base = make_reg(s->sys_strings_segment, SYS_STRING_PARSER_BASE); - s->restoring = false; + retval->successor = NULL; + retval->_gameId = s->_gameId; #ifdef USE_OLD_MUSIC_FUNCTIONS - s->_sound._it = NULL; - s->_sound._flags = s->_sound._flags; - s->_sound._song = NULL; - s->_sound._suspended = s->_sound._suspended; - reconstruct_sounds(s); + retval->_sound._it = NULL; + retval->_sound._flags = s->_sound._flags; + retval->_sound._song = NULL; + retval->_sound._suspended = s->_sound._suspended; + reconstruct_sounds(retval); #else - s->_soundCmd->reconstructPlayList(meta.savegame_version); + retval->_soundCmd->reconstructPlayList(meta.savegame_version); #endif // Message state: - s->_msgState = new MessageState(s->_segMan); + retval->_msgState = new MessageState(retval->_segMan); #ifdef ENABLE_SCI32 if (g_sci->_gui32) { g_sci->_gui32->init(); } else { #endif - g_sci->_gui->resetEngineState(s); + g_sci->_gui->resetEngineState(retval); g_sci->_gui->init(g_sci->_features->usesOldGfxFunctions()); #ifdef ENABLE_SCI32 } #endif - s->restoring = true; + s->successor = retval; // Set successor script_abort_flag = 2; // Abort current game with replay - s->shrinkStackToBase(); + shrink_execution_stack(s, s->execution_stack_base + 1); } bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) { diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index 7be05381da..bad79fca27 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -36,7 +36,7 @@ namespace Sci { struct EngineState; enum { - CURRENT_SAVEGAME_VERSION = 20, + CURRENT_SAVEGAME_VERSION = 19, MINIMUM_SAVEGAME_VERSION = 9 }; diff --git a/engines/sci/engine/script.cpp b/engines/sci/engine/script.cpp index 1f32e50b67..85a07f0efc 100644 --- a/engines/sci/engine/script.cpp +++ b/engines/sci/engine/script.cpp @@ -123,13 +123,13 @@ void SegManager::createClassTable() { error("SegManager: failed to open vocab 996"); int totalClasses = vocab996->size >> 2; - _classTable.resize(totalClasses); + _classtable.resize(totalClasses); for (uint16 classNr = 0; classNr < totalClasses; classNr++) { uint16 scriptNr = READ_SCI11ENDIAN_UINT16(vocab996->data + classNr * 4 + 2); - _classTable[classNr].reg = NULL_REG; - _classTable[classNr].script = scriptNr; + _classtable[classNr].reg = NULL_REG; + _classtable[classNr].script = scriptNr; } _resMan->unlockResource(vocab996); @@ -139,11 +139,11 @@ reg_t SegManager::getClassAddress(int classnr, ScriptLoadType lock, reg_t caller if (classnr == 0xffff) return NULL_REG; - 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()); + 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]; + Class *the_class = &_classtable[classnr]; if (!the_class->reg.segment) { getScriptSegment(the_class->script, lock); @@ -175,7 +175,7 @@ void SegManager::scriptInitialiseLocals(reg_t location) { Script *scr = getScript(location.segment); unsigned int count; - VERIFY(location.offset + 1 < (uint16)scr->getBufSize(), "Locals beyond end of script\n"); + VERIFY(location.offset + 1 < (uint16)scr->_bufSize, "Locals beyond end of script\n"); if (getSciVersion() >= SCI_VERSION_1_1) count = READ_SCI11ENDIAN_UINT16(scr->_buf + location.offset - 2); @@ -185,61 +185,81 @@ void SegManager::scriptInitialiseLocals(reg_t location) { scr->_localsOffset = location.offset; - if (!(location.offset + count * 2 + 1 < scr->getBufSize())) { - warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->getBufSize()); - count = (scr->getBufSize() - location.offset) >> 1; + if (!(location.offset + count * 2 + 1 < scr->_bufSize)) { + warning("Locals extend beyond end of script: offset %04x, count %x vs size %x", location.offset, count, (uint)scr->_bufSize); + count = (scr->_bufSize - location.offset) >> 1; } LocalVariables *locals = allocLocalsSegment(scr, count); if (locals) { uint i; - const byte *base = (const byte *)(scr->_buf + location.offset); + byte *base = (byte *)(scr->_buf + location.offset); for (i = 0; i < count; i++) locals->_locals[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(base + i * 2)); } } +void SegManager::scriptRelocateExportsSci11(SegmentId seg) { + Script *scr = getScript(seg); + for (int i = 0; i < scr->_numExports; i++) { + /* We are forced to use an ugly heuristic here to distinguish function + exports from object/class exports. The former kind points into the + script resource, the latter into the heap resource. */ + uint16 location = READ_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i)); + + if ((location < scr->_heapSize - 1) && (READ_SCI11ENDIAN_UINT16(scr->_heapStart + location) == SCRIPT_OBJECT_MAGIC_NUMBER)) { + WRITE_SCI11ENDIAN_UINT16((byte *)(scr->_exportTable + i), location + scr->_heapStart - scr->_buf); + } else { + // Otherwise it's probably a function export, + // and we don't need to do anything. + } + } +} + void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { Script *scr = getScript(seg); - const byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; + byte *seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - if (READ_SCI11ENDIAN_UINT16(seeker + 14) & kInfoFlagClass) { // -info- selector + if (READ_SCI11ENDIAN_UINT16(seeker + 14) & SCRIPT_INFO_CLASS) { int classpos = seeker - scr->_buf; int species = READ_SCI11ENDIAN_UINT16(seeker + 10); - if (species < 0 || species >= (int)_classTable.size()) { + if (species < 0 || species >= (int)_classtable.size()) { error("Invalid species %d(0x%x) not in interval [0,%d) while instantiating script %d", - species, species, _classTable.size(), scr->_nr); + species, species, _classtable.size(), scr->_nr); return; } - _classTable[species].reg.segment = seg; - _classTable[species].reg.offset = classpos; + _classtable[species].reg.segment = seg; + _classtable[species].reg.offset = classpos; } seeker += READ_SCI11ENDIAN_UINT16(seeker + 2) * 2; } seeker = scr->_heapStart + 4 + READ_SCI11ENDIAN_UINT16(scr->_heapStart + 2) * 2; while (READ_SCI11ENDIAN_UINT16(seeker) == SCRIPT_OBJECT_MAGIC_NUMBER) { - reg_t reg = make_reg(seg, seeker - scr->_buf); - Object *obj = scr->scriptObjInit(reg); + reg_t reg; + Object *obj; + + reg.segment = seg; + reg.offset = seeker - scr->_buf; + obj = scr->scriptObjInit(reg); + +#if 0 + if (obj->_variables[5].offset != 0xffff) { + obj->_variables[5] = INST_LOOKUP_CLASS(obj->_variables[5].offset); + baseObj = getObject(obj->_variables[5]); + obj->variable_names_nr = baseObj->variables_nr; + obj->_baseObj = baseObj->_baseObj; + } +#endif // Copy base from species class, as we need its selector IDs obj->setSuperClassSelector( getClassAddress(obj->getSuperClassSelector().offset, SCRIPT_GET_LOCK, NULL_REG)); - // If object is instance, get -propDict- from class and set it for this object - // This is needed for ::isMemberOf() to work. - // Example testcase - room 381 of sq4cd - if isMemberOf() doesn't work, talk-clicks on the robot will act like - // clicking on ego - if (!obj->isClass()) { - reg_t classObject = obj->getSuperClassSelector(); - Object *classObj = getObject(classObject); - obj->setPropDictSelector(classObj->getPropDictSelector()); - } - // Set the -classScript- selector to the script number. // FIXME: As this selector is filled in at run-time, it is likely // that it is supposed to hold a pointer. The Obj::isKindOf method @@ -252,24 +272,90 @@ void SegManager::scriptInitialiseObjectsSci11(SegmentId seg) { } } -void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { + + +int script_instantiate_common(ResourceManager *resMan, SegManager *segMan, int script_nr, Resource **script, Resource **heap, int *was_new) { + *was_new = 1; + + *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); + if (getSciVersion() >= SCI_VERSION_1_1) + *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); + + if (!*script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { + warning("Script 0x%x requested but not found", script_nr); + if (getSciVersion() >= SCI_VERSION_1_1) { + if (*heap) + warning("Inconsistency: heap resource WAS found"); + else if (*script) + warning("Inconsistency: script resource WAS found"); + } + return 0; + } + + SegmentId seg_id = segMan->getScriptSegment(script_nr); + Script *scr = segMan->getScriptIfLoaded(seg_id); + if (scr) { + if (!scr->isMarkedAsDeleted()) { + scr->incrementLockers(); + return seg_id; + } else { + scr->freeScript(); + } + } else { + scr = segMan->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; + } + } + + scr->init(script_nr, resMan); + + reg_t reg; + reg.segment = seg_id; + reg.offset = 0; + + // Set heap position (beyond the size word) + scr->setLockers(1); + scr->setExportTableOffset(0); + scr->setSynonymsOffset(0); + scr->setSynonymsNr(0); + + *was_new = 0; + + return seg_id; +} + +#define INST_LOOKUP_CLASS(id) ((id == 0xffff)? NULL_REG : segMan->getClassAddress(id, SCRIPT_GET_LOCK, addr)) + +int script_instantiate_sci0(ResourceManager *resMan, SegManager *segMan, int script_nr) { int objType; uint32 objLength = 0; + int relocation = -1; + Resource *script; + int was_new; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); + const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, NULL, &was_new); uint16 curOffset = oldScriptHeader ? 2 : 0; + if (was_new) + return seg_id; + + Script *scr = segMan->getScript(seg_id); + scr->mcpyInOut(0, script->data, script->size); + if (oldScriptHeader) { // Old script block // There won't be a localvar block in this case // Instead, the script starts with a 16 bit int specifying the // number of locals we need; these are then allocated and zeroed. - int localsCount = READ_LE_UINT16(scr->_buf); - if (localsCount) - segMan->scriptInitialiseLocalsZero(segmentId, localsCount); + int locals_nr = READ_LE_UINT16(script->data); + if (locals_nr) + segMan->scriptInitialiseLocalsZero(seg_id, locals_nr); } // Now do a first pass through the script objects to find the - // local variable blocks + // export table and local variable block do { objType = scr->getHeap(curOffset); @@ -277,30 +363,48 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { break; objLength = scr->getHeap(curOffset + 2); + + // This happens in some demos (e.g. the EcoQuest 1 demo). Not sure what is the + // actual cause of it, but the scripts of these demos can't be loaded properly + // and we're stuck forever in this loop, as objLength never changes + if (!objLength) { + warning("script_instantiate_sci0: objLength is 0, unable to parse script"); + return 0; + } + curOffset += 4; // skip header switch (objType) { + case SCI_OBJ_EXPORTS: + scr->setExportTableOffset(curOffset); + break; + case SCI_OBJ_SYNONYMS: + scr->setSynonymsOffset(curOffset); + scr->setSynonymsNr((objLength) / 4); + break; case SCI_OBJ_LOCALVARS: - segMan->scriptInitialiseLocals(make_reg(segmentId, curOffset)); + segMan->scriptInitialiseLocals(make_reg(seg_id, curOffset)); break; + case SCI_OBJ_CLASS: { int classpos = curOffset - SCRIPT_OBJECT_MAGIC_OFFSET; int species = scr->getHeap(curOffset - SCRIPT_OBJECT_MAGIC_OFFSET + SCRIPT_SPECIES_OFFSET); - if (species < 0 || species >= (int)segMan->classTableSize()) { - if (species == (int)segMan->classTableSize()) { + if (species < 0 || species >= (int)segMan->_classtable.size()) { + if (species == (int)segMan->_classtable.size()) { // Happens in the LSL2 demo warning("Applying workaround for an off-by-one invalid species access"); - segMan->resizeClassTable(segMan->classTableSize() + 1); + segMan->_classtable.resize(segMan->_classtable.size() + 1); } else { - error("Invalid species %d(0x%x) not in interval " - "[0,%d) while instantiating script at segment %d\n", - species, species, segMan->classTableSize(), - segmentId); - return; + warning("Invalid species %d(0x%x) not in interval " + "[0,%d) while instantiating script %d\n", + species, species, segMan->_classtable.size(), + script_nr); + return 0; } } - segMan->setClassOffset(species, make_reg(segmentId, classpos)); + segMan->_classtable[species].reg.segment = seg_id; + segMan->_classtable[species].reg.offset = classpos; // Set technical class position-- into the block allocated for it } break; @@ -310,7 +414,7 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { } curOffset += objLength - 4; - } while (objType != 0 && curOffset < scr->getScriptSize() - 2); + } while (objType != 0 && curOffset < script->size - 2); // And now a second pass to adjust objects and class pointers, and the general pointers objLength = 0; @@ -324,7 +428,7 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { objLength = scr->getHeap(curOffset + 2); curOffset += 4; // skip header - reg_t addr = make_reg(segmentId, curOffset); + reg_t addr = make_reg(seg_id, curOffset); switch (objType) { case SCI_OBJ_CODE: @@ -333,61 +437,93 @@ void script_instantiate_sci0(Script *scr, int segmentId, SegManager *segMan) { case SCI_OBJ_OBJECT: case SCI_OBJ_CLASS: { // object or class? Object *obj = scr->scriptObjInit(addr); - obj->initSpecies(segMan, addr); - if (!obj->initBaseObject(segMan, addr)) { + // Instantiate the superclass, if neccessary + obj->setSpeciesSelector(INST_LOOKUP_CLASS(obj->getSpeciesSelector().offset)); + + Object *baseObj = segMan->getObject(obj->getSpeciesSelector()); + + if (baseObj) { + obj->setVarCount(baseObj->getVarCount()); + // Copy base from species class, as we need its selector IDs + obj->_baseObj = baseObj->_baseObj; + + obj->setSuperClassSelector(INST_LOOKUP_CLASS(obj->getSuperClassSelector().offset)); + } else { warning("Failed to locate base object for object at %04X:%04X; skipping", PRINT_REG(addr)); + scr->scriptObjRemove(addr); } } // if object or class break; + case SCI_OBJ_POINTERS: // A relocation table + relocation = addr.offset; + break; + default: break; } curOffset += objLength - 4; - } while (objType != 0 && curOffset < scr->getScriptSize() - 2); + } while (objType != 0 && curOffset < script->size - 2); + + if (relocation >= 0) + scr->scriptRelocate(make_reg(seg_id, relocation)); + + return seg_id; // instantiation successful } -int script_instantiate(ResourceManager *resMan, SegManager *segMan, int scriptNum) { - SegmentId segmentId = segMan->getScriptSegment(scriptNum); - Script *scr = segMan->getScriptIfLoaded(segmentId); - if (scr) { - if (!scr->isMarkedAsDeleted()) { - scr->incrementLockers(); - return segmentId; - } else { - scr->freeScript(); - } - } else { - scr = segMan->allocateScript(scriptNum, &segmentId); - } +int script_instantiate_sci11(ResourceManager *resMan, SegManager *segMan, int script_nr) { + Resource *script, *heap; + int _heapStart; + reg_t reg; + int was_new; - scr->init(scriptNum, resMan); - scr->load(resMan); + const int seg_id = script_instantiate_common(resMan, segMan, script_nr, &script, &heap, &was_new); - if (getSciVersion() >= SCI_VERSION_1_1) { - int heapStart = scr->getScriptSize(); - segMan->scriptInitialiseLocals(make_reg(segmentId, heapStart + 4)); - segMan->scriptInitialiseObjectsSci11(segmentId); - scr->relocate(make_reg(segmentId, READ_SCI11ENDIAN_UINT16(scr->_heapStart))); - } else { - script_instantiate_sci0(scr, segmentId, segMan); - byte *relocationBlock = scr->findBlock(SCI_OBJ_POINTERS); - if (relocationBlock) - scr->relocate(make_reg(segmentId, relocationBlock - scr->_buf + 4)); - } + if (was_new) + return seg_id; + + Script *scr = segMan->getScript(seg_id); + + _heapStart = script->size; + if (script->size & 2) + _heapStart++; + + scr->mcpyInOut(0, script->data, script->size); + scr->mcpyInOut(_heapStart, heap->data, heap->size); - return segmentId; + if (READ_SCI11ENDIAN_UINT16(script->data + 6) > 0) + scr->setExportTableOffset(6); + + reg.segment = seg_id; + reg.offset = _heapStart + 4; + segMan->scriptInitialiseLocals(reg); + + segMan->scriptRelocateExportsSci11(seg_id); + segMan->scriptInitialiseObjectsSci11(seg_id); + + reg.offset = READ_SCI11ENDIAN_UINT16(heap->data); + scr->heapRelocate(reg); + + return seg_id; +} + +int script_instantiate(ResourceManager *resMan, SegManager *segMan, int script_nr) { + if (getSciVersion() >= SCI_VERSION_1_1) + return script_instantiate_sci11(resMan, segMan, script_nr); + else + return script_instantiate_sci0(resMan, segMan, script_nr); } void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) { bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); reg_t reg = make_reg(seg, oldScriptHeader ? 2 : 0); - int objType, objLength = 0; + int objType, objLength; Script *scr = segMan->getScript(seg); // Make a pass over the object in order uninstantiate all superclasses + objLength = 0; do { reg.offset += objLength; // Step over the last checked object @@ -407,7 +543,7 @@ void script_uninstantiate_sci0(SegManager *segMan, int script_nr, SegmentId seg) superclass = scr->getHeap(reg.offset + SCRIPT_SUPERCLASS_OFFSET); // Get superclass... if (superclass >= 0) { - int superclass_script = segMan->getClass(superclass).script; + int superclass_script = segMan->_classtable[superclass].script; if (superclass_script == script_nr) { if (scr->getLockers()) @@ -441,9 +577,9 @@ void script_uninstantiate(SegManager *segMan, int script_nr) { return; // Free all classtable references to this script - for (uint i = 0; i < segMan->classTableSize(); i++) - if (segMan->getClass(i).reg.segment == segment) - segMan->setClassOffset(i, NULL_REG); + for (uint i = 0; i < segMan->_classtable.size(); i++) + if (segMan->_classtable[i].reg.segment == segment) + segMan->_classtable[i].reg = NULL_REG; if (getSciVersion() < SCI_VERSION_1_1) script_uninstantiate_sci0(segMan, script_nr, segment); diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index 103a016972..4b60626b2e 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -67,6 +67,37 @@ extern const char *selector_name(EngineState *s, int selector); DebugState g_debugState; +int propertyOffsetToId(SegManager *segMan, int prop_ofs, reg_t objp) { + Object *obj = segMan->getObject(objp); + byte *selectoroffset; + int selectors; + + if (!obj) { + warning("Applied propertyOffsetToId on non-object at %04x:%04x", PRINT_REG(objp)); + return -1; + } + + selectors = obj->getVarCount(); + + if (getSciVersion() < SCI_VERSION_1_1) + selectoroffset = ((byte *)(obj->_baseObj)) + SCRIPT_SELECTOR_OFFSET + selectors * 2; + else { + if (!(obj->getInfoSelector().offset & SCRIPT_INFO_CLASS)) { + obj = segMan->getObject(obj->getSuperClassSelector()); + selectoroffset = (byte *)obj->_baseVars; + } else + selectoroffset = (byte *)obj->_baseVars; + } + + if (prop_ofs < 0 || (prop_ofs >> 1) >= selectors) { + warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d]) on object at %04x:%04x", + prop_ofs, prop_ofs >> 1, selectors - 1, PRINT_REG(objp)); + return -1; + } + + return READ_SCI11ENDIAN_UINT16(selectoroffset + prop_ofs); +} + // Disassembles one command from the heap, returns address of next command or 0 if a ret was encountered. reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecode) { SegmentObj *mobj = s->_segMan->getSegment(pos.segment, SEG_TYPE_SCRIPT); @@ -85,7 +116,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod script_entity = (Script *)mobj; scr = script_entity->_buf; - scr_size = script_entity->getBufSize(); + scr_size = script_entity->_bufSize; if (pos.offset >= scr_size) { warning("Trying to disassemble beyond end of script"); @@ -193,11 +224,10 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod if (pos == scriptState.xs->addr.pc) { // Extra information if debugging the current opcode if ((opcode == op_pTos) || (opcode == op_sTop) || (opcode == op_pToa) || (opcode == op_aTop) || (opcode == op_dpToa) || (opcode == op_ipToa) || (opcode == op_dpTos) || (opcode == op_ipTos)) { - const Object *obj = s->_segMan->getObject(scriptState.xs->objp); - if (!obj) - warning("Attempted to reference on non-object at %04x:%04x", PRINT_REG(scriptState.xs->objp)); - else - printf(" (%s)", selector_name(s, obj->propertyOffsetToId(s->_segMan, scr[pos.offset + 1]))); + int prop_ofs = scr[pos.offset + 1]; + int prop_id = propertyOffsetToId(s->_segMan, prop_ofs, scriptState.xs->objp); + + printf(" (%s)", selector_name(s, prop_id)); } } @@ -246,7 +276,7 @@ reg_t disassemble(EngineState *s, reg_t pos, int print_bw_tag, int print_bytecod printf(" %s::%s[", name, (selector > kernel->getSelectorNamesSize()) ? "<invalid>" : selector_name(s, selector)); - switch (lookupSelector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) { + switch (lookup_selector(s->_segMan, called_obj_addr, selector, 0, &fun_ref)) { case kSelectorMethod: printf("FUNCT"); argc += restmod; @@ -303,7 +333,7 @@ void script_debug(EngineState *s) { if (mobj) { Script *scr = (Script *)mobj; byte *code_buf = scr->_buf; - int code_buf_size = scr->getBufSize(); + int code_buf_size = scr->_bufSize; int opcode = scriptState.xs->addr.pc.offset >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset]; int op = opcode >> 1; int paramb1 = scriptState.xs->addr.pc.offset + 1 >= code_buf_size ? 0 : code_buf[scriptState.xs->addr.pc.offset + 1]; diff --git a/engines/sci/engine/seg_manager.cpp b/engines/sci/engine/seg_manager.cpp index 4d3e6f754e..b18d76e1a7 100644 --- a/engines/sci/engine/seg_manager.cpp +++ b/engines/sci/engine/seg_manager.cpp @@ -54,6 +54,7 @@ SegManager::SegManager(ResourceManager *resMan) { createClassTable(); } +// Destroy the object, free the memorys if allocated before SegManager::~SegManager() { resetSegMan(); } @@ -76,29 +77,10 @@ void SegManager::resetSegMan() { Hunks_seg_id = 0; // Reinitialize class table - _classTable.clear(); + _classtable.clear(); createClassTable(); } -void SegManager::initSysStrings() { - sysStrings = (SystemStrings *)allocSegment(new SystemStrings(), &sysStringsSegment); - - // Allocate static buffer for savegame and CWD directories - SystemString *strSaveDir = &sysStrings->_strings[SYS_STRING_SAVEDIR]; - strSaveDir->_name = "savedir"; - strSaveDir->_maxSize = MAX_SAVE_DIR_SIZE; - strSaveDir->_value = (char *)calloc(MAX_SAVE_DIR_SIZE, sizeof(char)); - // Set the savegame dir (actually, we set it to a fake value, - // since we cannot let the game control where saves are stored) - ::strcpy(strSaveDir->_value, ""); - - // Allocate static buffer for the parser base - SystemString *strParserBase = &sysStrings->_strings[SYS_STRING_PARSER_BASE]; - strParserBase->_name = "parser-base"; - strParserBase->_maxSize = MAX_PARSER_BASE; - strParserBase->_value = (char *)calloc(MAX_PARSER_BASE, sizeof(char)); -} - SegmentId SegManager::findFreeSegment() const { // FIXME: This is a very crude approach: We find a free segment id by scanning // from the start. This can be slow if the number of segments becomes large. @@ -174,7 +156,7 @@ int SegManager::deallocate(SegmentId seg, bool recursive) { } bool SegManager::isHeapObject(reg_t pos) { - const Object *obj = getObject(pos); + Object *obj = getObject(pos); if (obj == NULL || (obj && obj->isFreed())) return false; Script *scr = getScriptIfLoaded(pos.segment); @@ -241,7 +223,7 @@ Object *SegManager::getObject(reg_t pos) { warning("getObject(): Trying to get an invalid object"); } else if (mobj->getType() == SEG_TYPE_SCRIPT) { Script *scr = (Script *)mobj; - if (pos.offset <= scr->getBufSize() && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET + if (pos.offset <= scr->_bufSize && pos.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(scr->_buf + pos.offset)) { obj = scr->getObject(pos.offset); } @@ -252,7 +234,7 @@ Object *SegManager::getObject(reg_t pos) { } const char *SegManager::getObjectName(reg_t pos) { - const Object *obj = getObject(pos); + Object *obj = getObject(pos); if (!obj) return "<no such object>"; @@ -293,7 +275,7 @@ reg_t SegManager::findObjectByName(const Common::String &name, int index) { // It's a script or a clone table, scan all objects in it for (; idx < max_index; ++idx) { - const Object *obj = NULL; + Object *obj = NULL; reg_t objpos; objpos.offset = 0; objpos.segment = i; @@ -411,6 +393,10 @@ DataStack *SegManager::allocateStack(int size, SegmentId *segid) { return retval; } +SystemStrings *SegManager::allocateSysStrings(SegmentId *segid) { + return (SystemStrings *)allocSegment(new SystemStrings(), segid); +} + void SegManager::freeHunkEntry(reg_t addr) { if (addr.isNull()) { warning("Attempt to free a Hunk from a null address"); @@ -499,7 +485,7 @@ void SegManager::reconstructClones() { continue; CloneTable::Entry &seeker = ct->_table[j]; - const Object *baseObj = getObject(seeker.getSpeciesSelector()); + Object *baseObj = getObject(seeker.getSpeciesSelector()); seeker.cloneFromObject(baseObj); if (!baseObj) warning("Clone entry without a base class: %d", j); @@ -537,16 +523,6 @@ Node *SegManager::allocateNode(reg_t *addr) { return &(table->_table[offset]); } -reg_t SegManager::newNode(reg_t value, reg_t key) { - reg_t nodebase; - Node *n = allocateNode(&nodebase); - n->pred = n->succ = NULL_REG; - n->key = key; - n->value = value; - - return nodebase; -} - List *SegManager::lookupList(reg_t addr) { if (getSegmentType(addr.segment) != SEG_TYPE_LISTS) { warning("Attempt to use non-list %04x:%04x as list", PRINT_REG(addr)); diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index 9312f51f9d..e8bbdbdb3f 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -112,6 +112,12 @@ public: SegmentId getScriptSegment(int script_nr, ScriptLoadType load); // TODO: document this + reg_t lookupScriptExport(int script_nr, int export_index) { + SegmentId seg = getScriptSegment(script_nr, SCRIPT_GET_DONT_LOAD); + return make_reg(seg, getScript(seg)->validateExportFunc(export_index)); + } + + // TODO: document this reg_t getClassAddress(int classnr, ScriptLoadType lock, reg_t caller); /** @@ -182,10 +188,15 @@ public: // 5. System Strings /** - * Initializes the system string table. + * Allocates a system string table + * See also sys_string_acquire(); + * @param[in] segid Segment ID of the stack + * @returns The physical stack */ - void initSysStrings(); + SystemStrings *allocateSysStrings(SegmentId *segid); + + // 5. System Strings // 6, 7. Lists and Nodes @@ -204,14 +215,6 @@ public: Node *allocateNode(reg_t *addr); /** - * Allocate and initialize a new list node. - * @param[in] value The value to set the node to - * @param[in] key The key to set - * @return Pointer to the newly initialized list node - */ - reg_t newNode(reg_t value, reg_t key); - - /** * Resolves a list pointer to a list. * @param addr The address to resolve * @return The list referenced, or NULL on error @@ -429,22 +432,12 @@ public: */ reg_t findObjectByName(const Common::String &name, int index = -1); + void scriptRelocateExportsSci11(SegmentId seg); void scriptInitialiseObjectsSci11(SegmentId seg); - uint32 classTableSize() { return _classTable.size(); } - Class getClass(int index) { return _classTable[index]; } - void setClassOffset(int index, reg_t offset) { _classTable[index].reg = offset; } - void resizeClassTable(uint32 size) { _classTable.resize(size); } - - /** - * Obtains the system strings segment ID - */ - SegmentId getSysStringsSegment() { return sysStringsSegment; } - public: // TODO: make private Common::Array<SegmentObj *> _heap; - // Only accessible from saveLoadWithSerializer() - Common::Array<Class> _classTable; /**< Table of all classes */ + Common::Array<Class> _classtable; /**< Table of all classes */ #ifdef ENABLE_SCI32 SciArray<reg_t> *allocateArray(reg_t *addr); @@ -467,13 +460,6 @@ private: SegmentId Nodes_seg_id; ///< ID of the (a) node segment SegmentId Hunks_seg_id; ///< ID of the (a) hunk segment - /* System strings */ - SegmentId sysStringsSegment; -public: // TODO: make private. Only kString() needs direct access - SystemStrings *sysStrings; - -private: - #ifdef ENABLE_SCI32 SegmentId Arrays_seg_id; SegmentId String_seg_id; diff --git a/engines/sci/engine/segment.cpp b/engines/sci/engine/segment.cpp index 0e0a759d4b..ab1a68d165 100644 --- a/engines/sci/engine/segment.cpp +++ b/engines/sci/engine/segment.cpp @@ -27,7 +27,6 @@ #include "sci/sci.h" #include "sci/engine/features.h" -#include "sci/engine/script.h" // for SCI_OBJ_EXPORTS and SCI_OBJ_SYNONYMS #include "sci/engine/segment.h" #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" @@ -101,7 +100,8 @@ Script::Script() : SegmentObj(SEG_TYPE_SCRIPT) { _localsSegment = 0; _localsBlock = NULL; - _markedAsDeleted = false; + _relocated = false; + _markedAsDeleted = 0; } Script::~Script() { @@ -117,39 +117,54 @@ void Script::freeScript() { _codeBlocks.clear(); } -void Script::init(int script_nr, ResourceManager *resMan) { - Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); +bool Script::init(int script_nr, ResourceManager *resMan) { + setScriptSize(script_nr, resMan); + + _buf = (byte *)malloc(_bufSize); + + if (!_buf) { + freeScript(); + warning("Not enough memory space for script size"); + _bufSize = 0; + return false; + } _localsOffset = 0; _localsBlock = NULL; _codeBlocks.clear(); + _relocated = false; _markedAsDeleted = false; _nr = script_nr; - _buf = 0; - _heapStart = 0; + + if (getSciVersion() >= SCI_VERSION_1_1) + _heapStart = _buf + _scriptSize; + else + _heapStart = _buf; + + return true; +} + +void Script::setScriptSize(int script_nr, ResourceManager *resMan) { + Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, script_nr), 0); + Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); + bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); _scriptSize = script->size; - _bufSize = script->size; - _heapSize = 0; + _heapSize = 0; // Set later - _lockers = 1; - - if (getSciVersion() == SCI_VERSION_0_EARLY) { - _bufSize += READ_LE_UINT16(script->data) * 2; - } else if (getSciVersion() >= SCI_VERSION_1_1) { - /** - * In SCI11, the heap was in a separate space from the script. - * We append it to the end of the script, and adjust addressing accordingly. - * However, since we address the heap with a 16-bit pointer, the combined - * size of the stack and the heap must be 64KB. So far this has worked - * for SCI11, SCI2 and SCI21 games. SCI3 games use a different script format, - * and theoretically they can exceed the 64KB boundary using relocation. - */ - Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, script_nr), 0); - _bufSize += heap->size; + if (!script || (getSciVersion() >= SCI_VERSION_1_1 && !heap)) { + error("SegManager::setScriptSize: failed to load %s", !script ? "script" : "heap"); + } + if (oldScriptHeader) { + _bufSize = script->size + READ_LE_UINT16(script->data) * 2; + //locals_size = READ_LE_UINT16(script->data) * 2; + } else if (getSciVersion() < SCI_VERSION_1_1) { + _bufSize = script->size; + } else { + _bufSize = script->size + heap->size; _heapSize = heap->size; // Ensure that the start of the heap resource can be word-aligned. @@ -158,56 +173,12 @@ void Script::init(int script_nr, ResourceManager *resMan) { _scriptSize++; } - // As mentioned above, the script and the heap together should not exceed 64KB - if (_bufSize > 65535) - error("Script and heap sizes combined exceed 64K. This means a fundamental " - "design bug was made regarding SCI1.1 and newer games.\nPlease " - "report this error to the ScummVM team"); - } -} - -void Script::load(ResourceManager *resMan) { - Resource *script = resMan->findResource(ResourceId(kResourceTypeScript, _nr), 0); - assert(script != 0); - - _buf = (byte *)malloc(_bufSize); - assert(_buf); - - assert(_bufSize >= script->size); - memcpy(_buf, script->data, script->size); - - if (getSciVersion() >= SCI_VERSION_1_1) { - Resource *heap = resMan->findResource(ResourceId(kResourceTypeHeap, _nr), 0); - assert(heap != 0); - - _heapStart = _buf + _scriptSize; - - assert(_bufSize - _scriptSize <= heap->size); - memcpy(_heapStart, heap->data, heap->size); - } - - _codeBlocks.clear(); - - _exportTable = 0; - _numExports = 0; - _synonyms = 0; - _numSynonyms = 0; - - if (getSciVersion() >= SCI_VERSION_1_1) { - if (READ_LE_UINT16(_buf + 1 + 5) > 0) { - _exportTable = (const uint16 *)(_buf + 1 + 5 + 2); - _numExports = READ_SCI11ENDIAN_UINT16(_exportTable - 1); - } - } else { - _exportTable = (const uint16 *)findBlock(SCI_OBJ_EXPORTS); - if (_exportTable) { - _numExports = READ_SCI11ENDIAN_UINT16(_exportTable + 1); - _exportTable += 3; // skip header plus 2 bytes (_exportTable is a uint16 pointer) - } - _synonyms = findBlock(SCI_OBJ_SYNONYMS); - if (_synonyms) { - _numSynonyms = READ_SCI11ENDIAN_UINT16(_synonyms + 2) / 4; - _synonyms += 4; // skip header + if (_bufSize > 65535) { + error("Script and heap sizes combined exceed 64K." + "This means a fundamental design bug was made in SCI\n" + "regarding SCI1.1 games.\nPlease report this so it can be" + "fixed in the next major version"); + return; } } } @@ -223,26 +194,19 @@ Object *Script::getObject(uint16 offset) { return 0; } -const Object *Script::getObject(uint16 offset) const { - if (_objects.contains(offset)) - return &_objects[offset]; - else - return 0; -} - -Object *Script::scriptObjInit(reg_t obj_pos, bool fullObjectInit) { +Object *Script::scriptObjInit(reg_t obj_pos) { Object *obj; - if (getSciVersion() < SCI_VERSION_1_1 && fullObjectInit) + if (getSciVersion() < SCI_VERSION_1_1) obj_pos.offset += 8; // magic offset (SCRIPT_OBJECT_MAGIC_OFFSET) VERIFY(obj_pos.offset < _bufSize, "Attempt to initialize object beyond end of script\n"); obj = allocateObject(obj_pos.offset); - VERIFY(obj_pos.offset + kOffsetFunctionArea < (int)_bufSize, "Function area pointer stored beyond end of script\n"); + VERIFY(obj_pos.offset + SCRIPT_FUNCTAREAPTR_OFFSET < (int)_bufSize, "Function area pointer stored beyond end of script\n"); - obj->init(_buf, obj_pos, fullObjectInit); + obj->init(_buf, obj_pos); return obj; } @@ -254,34 +218,37 @@ void Script::scriptObjRemove(reg_t obj_pos) { _objects.erase(obj_pos.toUint16()); } -// This helper function is used by Script::relocateLocal and Object::relocate -static bool relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location, size_t scriptSize) { +int Script::relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location) { int rel = location - block_location; if (rel < 0) - return false; + return 0; uint idx = rel >> 1; if (idx >= block.size()) - return false; + return 0; if (rel & 1) { warning("Attempt to relocate odd variable #%d.5e (relative to %04x)\n", idx, block_location); - return false; + return 0; } block[idx].segment = segment; // Perform relocation if (getSciVersion() >= SCI_VERSION_1_1) - block[idx].offset += scriptSize; + block[idx].offset += _scriptSize; - return true; + return 1; } -bool Script::relocateLocal(SegmentId segment, int location) { +int Script::relocateLocal(SegmentId segment, int location) { if (_localsBlock) - return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location, _scriptSize); + return relocateBlock(_localsBlock->_locals, _localsOffset, segment, location); else - return false; + return 0; // No hands, no cookies +} + +int Script::relocateObject(Object &obj, SegmentId segment, int location) { + return relocateBlock(obj._variables, obj.getPos().offset, segment, location); } void Script::scriptAddCodeBlock(reg_t location) { @@ -291,35 +258,61 @@ void Script::scriptAddCodeBlock(reg_t location) { _codeBlocks.push_back(cb); } -void Script::relocate(reg_t block) { - byte *heap = _buf; - uint16 heapSize = (uint16)_bufSize; - uint16 heapOffset = 0; +void Script::scriptRelocate(reg_t block) { + VERIFY(block.offset < (uint16)_bufSize && READ_SCI11ENDIAN_UINT16(_buf + block.offset) * 2 + block.offset < (uint16)_bufSize, + "Relocation block outside of script\n"); + + int count = READ_SCI11ENDIAN_UINT16(_buf + block.offset); + + for (int i = 0; i <= count; i++) { + int pos = READ_SCI11ENDIAN_UINT16(_buf + block.offset + 2 + (i * 2)); + if (!pos) + continue; // FIXME: A hack pending investigation + + if (!relocateLocal(block.segment, pos)) { + bool done = false; + uint k; + + ObjMap::iterator it; + const ObjMap::iterator end = _objects.end(); + for (it = _objects.begin(); !done && it != end; ++it) { + if (relocateObject(it->_value, block.segment, pos)) + done = true; + } + + for (k = 0; !done && k < _codeBlocks.size(); k++) { + if (pos >= _codeBlocks[k].pos.offset && + pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size) + done = true; + } - if (getSciVersion() >= SCI_VERSION_1_1) { - heap = _heapStart; - heapSize = (uint16)_heapSize; - heapOffset = _scriptSize; + if (!done) { + printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); + printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count); + if (_localsBlock) + printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); + else + printf("- No locals\n"); + for (it = _objects.begin(), k = 0; it != end; ++it, ++k) + printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); + // SQ3 script 71 has broken relocation entries. + printf("Trying to continue anyway...\n"); + } + } } +} - VERIFY(block.offset < (uint16)heapSize && READ_SCI11ENDIAN_UINT16(heap + block.offset) * 2 + block.offset < (uint16)heapSize, +void Script::heapRelocate(reg_t block) { + VERIFY(block.offset < (uint16)_heapSize && READ_SCI11ENDIAN_UINT16(_heapStart + block.offset) * 2 + block.offset < (uint16)_bufSize, "Relocation block outside of script\n"); - int count = READ_SCI11ENDIAN_UINT16(heap + block.offset); - int exportIndex = 0; + if (_relocated) + return; + _relocated = true; + int count = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset); for (int i = 0; i < count; i++) { - int pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset; - // This occurs in SCI01/SCI1 games where every usually one export - // value is zero. It seems that in this situation, we should skip - // the export and move to the next one, though the total count - // of valid exports remains the same - if (!pos) { - exportIndex++; - pos = READ_SCI11ENDIAN_UINT16(heap + block.offset + 2 + (exportIndex * 2)) + heapOffset; - if (!pos) - error("Script::relocate(): Consecutive zero exports found"); - } + int pos = READ_SCI11ENDIAN_UINT16(_heapStart + block.offset + 2 + (i * 2)) + _scriptSize; if (!relocateLocal(block.segment, pos)) { bool done = false; @@ -328,33 +321,22 @@ void Script::relocate(reg_t block) { ObjMap::iterator it; const ObjMap::iterator end = _objects.end(); for (it = _objects.begin(); !done && it != end; ++it) { - if (it->_value.relocate(block.segment, pos, _scriptSize)) + if (relocateObject(it->_value, block.segment, pos)) done = true; } - // Sanity check for SCI0-SCI1 - if (getSciVersion() < SCI_VERSION_1_1) { - for (k = 0; !done && k < _codeBlocks.size(); k++) { - if (pos >= _codeBlocks[k].pos.offset && - pos < _codeBlocks[k].pos.offset + _codeBlocks[k].size) - done = true; - } - } - if (!done) { - debug("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); - debug("Relocation failed for index %04x (%d/%d)\n", pos, exportIndex + 1, count); + printf("While processing relocation block %04x:%04x:\n", PRINT_REG(block)); + printf("Relocation failed for index %04x (%d/%d)\n", pos, i + 1, count); if (_localsBlock) - debug("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); + printf("- locals: %d at %04x\n", _localsBlock->_locals.size(), _localsOffset); else - debug("- No locals\n"); + printf("- No locals\n"); for (it = _objects.begin(), k = 0; it != end; ++it, ++k) - debug("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); - debug("Trying to continue anyway...\n"); + printf("- obj#%d at %04x w/ %d vars\n", k, it->_value.getPos().offset, it->_value.getVarCount()); + error("Breakpoint in %s, line %d", __FILE__, __LINE__); } } - - exportIndex++; } } @@ -375,6 +357,16 @@ void Script::setLockers(int lockers) { _lockers = lockers; } +void Script::setExportTableOffset(int offset) { + if (offset) { + _exportTable = (uint16 *)(_buf + offset + 2); + _numExports = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable - 1)); + } else { + _exportTable = NULL; + _numExports = 0; + } +} + uint16 Script::validateExportFunc(int pubfunct) { bool exportsAreWide = (g_sci->_features->detectLofsType() == SCI_VERSION_1_MIDDLE); @@ -385,35 +377,27 @@ uint16 Script::validateExportFunc(int pubfunct) { if (exportsAreWide) pubfunct *= 2; - uint16 offset = READ_SCI11ENDIAN_UINT16(_exportTable + pubfunct); + uint16 offset = READ_SCI11ENDIAN_UINT16((byte *)(_exportTable + pubfunct)); VERIFY(offset < _bufSize, "invalid export function pointer"); return offset; } -byte *Script::findBlock(int type) { - byte *buf = _buf; - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); - - if (oldScriptHeader) - buf += 2; - - do { - int seekerType = READ_LE_UINT16(buf); - - if (seekerType == 0) - break; - if (seekerType == type) - return buf; +void Script::setSynonymsOffset(int offset) { + _synonyms = _buf + offset; +} - int seekerSize = READ_LE_UINT16(buf + 2); - assert(seekerSize > 0); - buf += seekerSize; - } while (1); +byte *Script::getSynonyms() const { + return _synonyms; +} - return NULL; +void Script::setSynonymsNr(int n) { + _numSynonyms = n; } +int Script::getSynonymsNr() const { + return _numSynonyms; +} // memory operations @@ -527,7 +511,7 @@ SegmentRef SystemStrings::dereference(reg_t pointer) { //-------------------- script -------------------- -reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) const { +reg_t Script::findCanonicAddress(SegManager *segMan, reg_t addr) { addr.offset = 0; return addr; } @@ -543,13 +527,13 @@ void Script::freeAtAddress(SegManager *segMan, reg_t addr) { segMan->deallocateScript(_nr); } -void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { +void Script::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { (*note)(param, make_reg(segId, 0)); } -void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { if (addr.offset <= _bufSize && addr.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(_buf + addr.offset)) { - const Object *obj = getObject(addr.offset); + Object *obj = getObject(addr.offset); if (obj) { // Note all local variables, if we have a local variable environment if (_localsSegment) @@ -569,14 +553,16 @@ void Script::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback not //-------------------- clones -------------------- -void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void CloneTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { + Clone *clone; + // assert(addr.segment == _segId); if (!isValidEntry(addr.offset)) { error("Unexpected request for outgoing references from clone at %04x:%04x", PRINT_REG(addr)); } - const Clone *clone = &(_table[addr.offset]); + clone = &(_table[addr.offset]); // Emit all member variables (including references to the 'super' delegate) for (uint i = 0; i < clone->getVarCount(); i++) @@ -611,7 +597,7 @@ void CloneTable::freeAtAddress(SegManager *segMan, reg_t addr) { //-------------------- locals -------------------- -reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const { +reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) { // Reference the owning script SegmentId owner_seg = segMan->getScriptSegment(script_id); @@ -620,7 +606,7 @@ reg_t LocalVariables::findCanonicAddress(SegManager *segMan, reg_t addr) const { return make_reg(owner_seg, 0); } -void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { // assert(addr.segment == _segId); for (uint i = 0; i < _locals.size(); i++) @@ -629,12 +615,12 @@ void LocalVariables::listAllOutgoingReferences(reg_t addr, void *param, NoteCall //-------------------- stack -------------------- -reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) const { +reg_t DataStack::findCanonicAddress(SegManager *segMan, reg_t addr) { addr.offset = 0; return addr; } -void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void DataStack::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { fprintf(stderr, "Emitting %d stack entries\n", _capacity); for (int i = 0; i < _capacity; i++) (*note)(param, _entries[i]); @@ -647,13 +633,13 @@ void ListTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void ListTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { if (!isValidEntry(addr.offset)) { warning("Invalid list referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - const List *list = &(_table[addr.offset]); + List *list = &(_table[addr.offset]); note(param, list->first); note(param, list->last); @@ -667,12 +653,12 @@ void NodeTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { if (!isValidEntry(addr.offset)) { warning("Invalid node referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - const Node *node = &(_table[addr.offset]); + Node *node = &(_table[addr.offset]); // We need all four here. Can't just stick with 'pred' OR 'succ' because node operations allow us // to walk around from any given node @@ -687,43 +673,20 @@ void NodeTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback //-------------------- object ---------------------------- -void Object::init(byte *buf, reg_t obj_pos, bool initVariables) { - byte *data = buf + obj_pos.offset; - _baseObj = data; - _pos = obj_pos; - - if (getSciVersion() < SCI_VERSION_1_1) { - _variables.resize(READ_LE_UINT16(data + kOffsetSelectorCounter)); - _baseVars = (const uint16 *)(_baseObj + _variables.size() * 2); - _baseMethod = (const uint16 *)(data + READ_LE_UINT16(data + kOffsetFunctionArea)); - _methodCount = READ_LE_UINT16(_baseMethod - 1); - } else { - _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2)); - _baseVars = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4)); - _baseMethod = (const uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6)); - _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod); - } - - if (initVariables) { - for (uint i = 0; i < _variables.size(); i++) - _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2))); - } -} - -const Object *Object::getClass(SegManager *segMan) const { +Object *Object::getClass(SegManager *segMan) { return isClass() ? this : segMan->getObject(getSuperClassSelector()); } -int Object::locateVarSelector(SegManager *segMan, Selector slc) const { - const byte *buf; +int Object::locateVarSelector(SegManager *segMan, Selector slc) { + byte *buf; uint varnum; if (getSciVersion() < SCI_VERSION_1_1) { varnum = getVarCount(); - int selector_name_offset = varnum * 2 + kOffsetSelectorSegment; + int selector_name_offset = varnum * 2 + SCRIPT_SELECTOR_OFFSET; buf = _baseObj + selector_name_offset; } else { - const Object *obj = getClass(segMan); + Object *obj = getClass(segMan); varnum = obj->getVariable(1).toUint16(); buf = (byte *)obj->_baseVars; } @@ -735,72 +698,14 @@ int Object::locateVarSelector(SegManager *segMan, Selector slc) const { return -1; // Failed } -bool Object::relocate(SegmentId segment, int location, size_t scriptSize) { - return relocateBlock(_variables, getPos().offset, segment, location, scriptSize); -} - -int Object::propertyOffsetToId(SegManager *segMan, int propertyOffset) const { - int selectors = getVarCount(); - - if (propertyOffset < 0 || (propertyOffset >> 1) >= selectors) { - warning("Applied propertyOffsetToId to invalid property offset %x (property #%d not in [0..%d])", - propertyOffset, propertyOffset >> 1, selectors - 1); - return -1; - } - - if (getSciVersion() < SCI_VERSION_1_1) { - const byte *selectoroffset = ((const byte *)(_baseObj)) + kOffsetSelectorSegment + selectors * 2; - return READ_SCI11ENDIAN_UINT16(selectoroffset + propertyOffset); - } else { - const Object *obj = this; - if (!isClass()) - obj = segMan->getObject(getSuperClassSelector()); - - return READ_SCI11ENDIAN_UINT16((const byte *)obj->_baseVars + propertyOffset); - } -} - -void Object::initSpecies(SegManager *segMan, reg_t addr) { - uint16 speciesOffset = getSpeciesSelector().offset; - - if (speciesOffset == 0xffff) // -1 - setSpeciesSelector(NULL_REG); // no species - else - setSpeciesSelector(segMan->getClassAddress(speciesOffset, SCRIPT_GET_LOCK, addr)); -} - -void Object::initSuperClass(SegManager *segMan, reg_t addr) { - uint16 superClassOffset = getSuperClassSelector().offset; - - if (superClassOffset == 0xffff) // -1 - setSuperClassSelector(NULL_REG); // no superclass - else - setSuperClassSelector(segMan->getClassAddress(superClassOffset, SCRIPT_GET_LOCK, addr)); -} - -bool Object::initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass) { - const Object *baseObj = segMan->getObject(getSpeciesSelector()); - - if (baseObj) { - _variables.resize(baseObj->getVarCount()); - // Copy base from species class, as we need its selector IDs - _baseObj = baseObj->_baseObj; - if (doInitSuperClass) - initSuperClass(segMan, addr); - return true; - } - - return false; -} - //-------------------- dynamic memory -------------------- -reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) const { +reg_t DynMem::findCanonicAddress(SegManager *segMan, reg_t addr) { addr.offset = 0; return addr; } -void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { +void DynMem::listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { (*note)(param, make_reg(segId, 0)); } @@ -819,13 +724,13 @@ void ArrayTable::freeAtAddress(SegManager *segMan, reg_t sub_addr) { freeEntry(sub_addr.offset); } -void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) const { +void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback note) { if (!isValidEntry(addr.offset)) { warning("Invalid array referenced for outgoing references: %04x:%04x", PRINT_REG(addr)); return; } - const SciArray<reg_t> *array = &(_table[addr.offset]); + SciArray<reg_t> *array = &(_table[addr.offset]); for (uint32 i = 0; i < array->getSize(); i++) { reg_t value = array->getValue(i); @@ -834,7 +739,7 @@ void ArrayTable::listAllOutgoingReferences(reg_t addr, void *param, NoteCallback } } -Common::String SciString::toString() const { +Common::String SciString::toString() { if (_type != 3) error("SciString::toString(): Array is not a string"); @@ -845,7 +750,7 @@ Common::String SciString::toString() const { return string; } -void SciString::fromString(const Common::String &string) { +void SciString::fromString(Common::String string) { if (_type != 3) error("SciString::fromString(): Array is not a string"); diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index f1b6dccaa2..1089ada475 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -112,7 +112,7 @@ public: * * @param sub_addr base address whose canonic address is to be found */ - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const { return sub_addr; } + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) { return sub_addr; } /** * Deallocates all memory associated with the specified address. @@ -125,7 +125,7 @@ public: * @param note Invoked for each address on which free_at_address() makes sense * @param param parameter passed to 'note' */ - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const {} + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) {} /** * Iterates over all references reachable from the specified object. @@ -134,7 +134,7 @@ public: * @param note Invoked for each outgoing reference within the object * Note: This function may also choose to report numbers (segment 0) as adresses */ - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const {} + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) {} }; @@ -194,8 +194,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -205,22 +205,6 @@ enum { OBJECT_FLAG_FREED = (1 << 0) }; -enum infoSelectorFlags { - kInfoFlagClone = 0x0001, - kInfoFlagClass = 0x8000 -}; - -enum ObjectOffsets { - kOffsetLocalVariables = -6, - kOffsetFunctionArea = -4, - kOffsetSelectorCounter = -2, - kOffsetSelectorSegment = 0, - kOffsetInfoSelectorSci0 = 4, - kOffsetNamePointerSci0 = 6, - kOffsetInfoSelectorSci11 = 14, - kOffsetNamePointerSci11 = 16 -}; - class Object { public: Object() { @@ -230,34 +214,31 @@ public: ~Object() { } - reg_t getSpeciesSelector() const { return _variables[_offset]; } + reg_t getSpeciesSelector() { return _variables[_offset]; } void setSpeciesSelector(reg_t value) { _variables[_offset] = value; } - reg_t getSuperClassSelector() const { return _variables[_offset + 1]; } + reg_t getSuperClassSelector() { return _variables[_offset + 1]; } void setSuperClassSelector(reg_t value) { _variables[_offset + 1] = value; } - reg_t getInfoSelector() const { return _variables[_offset + 2]; } - void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; } + reg_t getInfoSelector() { return _variables[_offset + 2]; } + void setInfoSelector(reg_t value) { _variables[_offset + 2] = value; } - reg_t getNameSelector() const { return _variables[_offset + 3]; } - void setNameSelector(reg_t value) { _variables[_offset + 3] = value; } + reg_t getNameSelector() { return _variables[_offset + 3]; } + void setNameSelector(reg_t value) { _variables[_offset + 3] = value; } - reg_t getPropDictSelector() const { return _variables[2]; } - void setPropDictSelector(reg_t value) { _variables[2] = value; } - - reg_t getClassScriptSelector() const { return _variables[4]; } + reg_t getClassScriptSelector() { return _variables[4]; } void setClassScriptSelector(reg_t value) { _variables[4] = value; } - Selector getVarSelector(uint16 i) const { return READ_SCI11ENDIAN_UINT16(_baseVars + i); } + Selector getVarSelector(uint16 i) { return READ_SCI11ENDIAN_UINT16(_baseVars + i); } - reg_t getFunction(uint16 i) const { + reg_t getFunction(uint16 i) { uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? _methodCount + 1 + i : i * 2 + 2; - return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16(_baseMethod + offset)); + return make_reg(_pos.segment, READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset))); } - Selector getFuncSelector(uint16 i) const { + Selector getFuncSelector(uint16 i) { uint16 offset = (getSciVersion() < SCI_VERSION_1_1) ? i : i * 2 + 1; - return READ_SCI11ENDIAN_UINT16(_baseMethod + offset); + return READ_SCI11ENDIAN_UINT16((byte *) (_baseMethod + offset)); } /** @@ -266,7 +247,7 @@ public: * superclasses, i.e. failure may be returned even if one of the * superclasses defines the funcselector */ - int funcSelectorPosition(Selector sel) const { + int funcSelectorPosition(Selector sel) { for (uint i = 0; i < _methodCount; i++) if (getFuncSelector(i) == sel) return i; @@ -275,56 +256,61 @@ public: } /** - * Determines if the object explicitly defines slc as a varselector. - * Returns -1 if not found. + * Determines if the object explicitly defines slc as a varselector + * Returns -1 if not found */ - int locateVarSelector(SegManager *segMan, Selector slc) const; - - bool isClass() const { return (getInfoSelector().offset & kInfoFlagClass); } - const Object *getClass(SegManager *segMan) const; + int locateVarSelector(SegManager *segMan, Selector slc); - void markAsClone() { setInfoSelector(make_reg(0, kInfoFlagClone)); } - bool isClone() const { return (getInfoSelector().offset & kInfoFlagClone); } + bool isClass() { return (getInfoSelector().offset & SCRIPT_INFO_CLASS); } + Object *getClass(SegManager *segMan); void markAsFreed() { _flags |= OBJECT_FLAG_FREED; } - bool isFreed() const { return _flags & OBJECT_FLAG_FREED; } + bool isFreed() { return _flags & OBJECT_FLAG_FREED; } + + void setVarCount(uint size) { _variables.resize(size); } + uint getVarCount() { return _variables.size(); } - uint getVarCount() const { return _variables.size(); } + void init(byte *buf, reg_t obj_pos) { + byte *data = (byte *)(buf + obj_pos.offset); + _baseObj = data; + _pos = obj_pos; - void init(byte *buf, reg_t obj_pos, bool initVariables = true); + if (getSciVersion() < SCI_VERSION_1_1) { + _variables.resize(READ_LE_UINT16(data + SCRIPT_SELECTORCTR_OFFSET)); + _baseVars = (uint16 *)(_baseObj + _variables.size() * 2); + _baseMethod = (uint16 *)(data + READ_LE_UINT16(data + SCRIPT_FUNCTAREAPTR_OFFSET)); + _methodCount = READ_LE_UINT16(_baseMethod - 1); + } else { + _variables.resize(READ_SCI11ENDIAN_UINT16(data + 2)); + _baseVars = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 4)); + _baseMethod = (uint16 *)(buf + READ_SCI11ENDIAN_UINT16(data + 6)); + _methodCount = READ_SCI11ENDIAN_UINT16(_baseMethod); + } + + for (uint i = 0; i < _variables.size(); i++) + _variables[i] = make_reg(0, READ_SCI11ENDIAN_UINT16(data + (i * 2))); + } - reg_t getVariable(uint var) const { return _variables[var]; } - reg_t &getVariableRef(uint var) { return _variables[var]; } + reg_t getVariable(uint var) { return _variables[var]; } - uint16 getMethodCount() const { return _methodCount; } - reg_t getPos() const { return _pos; } + uint16 getMethodCount() { return _methodCount; } + reg_t getPos() { return _pos; } void saveLoadWithSerializer(Common::Serializer &ser); - void cloneFromObject(const Object *obj) { + void cloneFromObject(Object *obj) { _baseObj = obj ? obj->_baseObj : NULL; _baseMethod = obj ? obj->_baseMethod : NULL; _baseVars = obj ? obj->_baseVars : NULL; } - bool relocate(SegmentId segment, int location, size_t scriptSize); - - int propertyOffsetToId(SegManager *segMan, int propertyOffset) const; - - void initSpecies(SegManager *segMan, reg_t addr); - void initSuperClass(SegManager *segMan, reg_t addr); - bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true); - // TODO: make private - // Only SegManager::reconstructScripts() is left needing direct access to these -public: - const byte *_baseObj; /**< base + object offset within base */ + Common::Array<reg_t> _variables; + byte *_baseObj; /**< base + object offset within base */ + uint16 *_baseVars; /**< Pointer to the varselector area for this object */ + uint16 *_baseMethod; /**< Pointer to the method selector area for this object */ private: - const uint16 *_baseVars; /**< Pointer to the varselector area for this object */ - const uint16 *_baseMethod; /**< Pointer to the method selector area for this object */ - - Common::Array<reg_t> _variables; uint16 _methodCount; int _flags; uint16 _offset; @@ -342,27 +328,20 @@ class Script : public SegmentObj { public: int _nr; /**< Script number */ byte *_buf; /**< Static data buffer, or NULL if not used */ - byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */ - - uint32 getScriptSize() { return _scriptSize; } - uint32 getHeapSize() { return _heapSize; } - uint32 getBufSize() { return _bufSize; } - -protected: - int _lockers; /**< Number of classes and objects that require this script */ - -private: + size_t _bufSize; size_t _scriptSize; size_t _heapSize; - size_t _bufSize; - const uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */ - uint16 _numExports; /**< Number of entries in the exports table */ + byte *_heapStart; /**< Start of heap if SCI1.1, NULL otherwise */ - const byte *_synonyms; /**< Synonyms block or 0 if not present*/ - uint16 _numSynonyms; /**< Number of entries in the synonyms block */ + uint16 *_exportTable; /**< Abs. offset of the export table or 0 if not present */ + int _numExports; /**< Number of entries in the exports table */ - Common::Array<CodeBlock> _codeBlocks; + byte *_synonyms; /**< Synonyms block or 0 if not present*/ + int _numSynonyms; /**< Number of entries in the synonyms block */ + +protected: + int _lockers; /**< Number of classes and objects that require this script */ public: /** @@ -375,6 +354,8 @@ public: SegmentId _localsSegment; /**< The local variable segment */ LocalVariables *_localsBlock; + Common::Array<CodeBlock> _codeBlocks; + bool _relocated; bool _markedAsDeleted; public: @@ -382,21 +363,19 @@ public: ~Script(); void freeScript(); - void init(int script_nr, ResourceManager *resMan); - void load(ResourceManager *resMan); + bool init(int script_nr, ResourceManager *resMan); virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const; - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); Object *allocateObject(uint16 offset); Object *getObject(uint16 offset); - const Object *getObject(uint16 offset) const; /** * Informs the segment manager that a code block must be relocated @@ -413,7 +392,7 @@ public: * @returns A newly created Object describing the object, * stored within the relevant script */ - Object *scriptObjInit(reg_t obj_pos, bool fullObjectInit = true); + Object *scriptObjInit(reg_t obj_pos); /** * Removes a script object @@ -428,10 +407,14 @@ public: * @param obj_pos Location (segment, offset) of the block * @return Location of the relocation block */ - void relocate(reg_t block); + void scriptRelocate(reg_t block); + + void heapRelocate(reg_t block); private: - bool relocateLocal(SegmentId segment, int location); + int relocateLocal(SegmentId segment, int location); + int relocateBlock(Common::Array<reg_t> &block, int block_location, SegmentId segment, int location); + int relocateObject(Object &obj, SegmentId segment, int location); public: // script lock operations @@ -452,28 +435,22 @@ public: void setLockers(int lockers); /** - * Retrieves a pointer to the exports of this script - * @return pointer to the exports. - */ - const uint16 *getExportTable() const { return _exportTable; } - - /** - * Retrieves the number of exports of script. - * @return the number of exports of this script - */ - uint16 getExportsNr() const { return _numExports; } - - /** * Retrieves a pointer to the synonyms associated with this script * @return pointer to the synonyms, in non-parsed format. */ - const byte *getSynonyms() const { return _synonyms; } + byte *getSynonyms() const; /** * Retrieves the number of synonyms associated with this script. * @return the number of synonyms associated with this script */ - uint16 getSynonymsNr() const { return _numSynonyms; } + int getSynonymsNr() const; + + /** + * Sets the script-relative offset of the exports table. + * @param offset script-relative exports table offset + */ + void setExportTableOffset(int offset); /** * Validate whether the specified public function is exported by @@ -485,6 +462,19 @@ public: uint16 validateExportFunc(int pubfunct); /** + * Sets the script-relative offset of the synonyms associated with this script. + * @param offset script-relative offset of the synonyms block + */ + void setSynonymsOffset(int offset); + + /** + * Sets the number of synonyms associated with this script, + * @param nr number of synonyms, as to be stored within the script + */ + void setSynonymsNr(int nr); + + + /** * Marks the script as deleted. * This will not actually delete the script. If references remain present on the * heap or the stack, the script will stay in memory in a quasi-deleted state until @@ -518,10 +508,8 @@ public: */ int16 getHeap(uint16 offset) const; - /** - * Finds the pointer where a block of a specific type starts from - */ - byte *findBlock(int type); +private: + void setScriptSize(int script_nr, ResourceManager *resMan); }; /** Data stack */ @@ -541,8 +529,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -630,7 +618,7 @@ public: entries_used--; } - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const { + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) { for (uint i = 0; i < _table.size(); i++) if (isValidEntry(i)) (*note)(param, make_reg(segId, i)); @@ -643,7 +631,7 @@ struct CloneTable : public Table<Clone> { CloneTable() : Table<Clone>(SEG_TYPE_CLONES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -654,7 +642,7 @@ struct NodeTable : public Table<Node> { NodeTable() : Table<Node>(SEG_TYPE_NODES) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -665,7 +653,7 @@ struct ListTable : public Table<List> { ListTable() : Table<List>(SEG_TYPE_LISTS) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -700,8 +688,8 @@ public: virtual bool isValidOffset(uint16 offset) const; virtual SegmentRef dereference(reg_t pointer); - virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr) const; - virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note) const; + virtual reg_t findCanonicAddress(SegManager *segMan, reg_t sub_addr); + virtual void listAllDeallocatable(SegmentId segId, void *param, NoteCallback note); virtual void saveLoadWithSerializer(Common::Serializer &ser); }; @@ -794,7 +782,7 @@ public: _size = _actualSize = size; } - T getValue(uint16 index) const { + T getValue(uint16 index) { if (index >= _size) error("SciArray::getValue(): %d is out of bounds (%d)", index, _size); @@ -808,8 +796,8 @@ public: _data[index] = value; } - byte getType() const { return _type; } - uint32 getSize() const { return _size; } + byte getType() { return _type; } + uint32 getSize() { return _size; } T *getRawData() { return _data; } protected: @@ -826,15 +814,15 @@ public: // We overload destroy to ensure the string type is 3 after destroying void destroy() { SciArray<char>::destroy(); _type = 3; } - Common::String toString() const; - void fromString(const Common::String &string); + Common::String toString(); + void fromString(Common::String string); }; struct ArrayTable : public Table<SciArray<reg_t> > { ArrayTable() : Table<SciArray<reg_t> >(SEG_TYPE_ARRAY) {} virtual void freeAtAddress(SegManager *segMan, reg_t sub_addr); - virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note) const; + virtual void listAllOutgoingReferences(reg_t object, void *param, NoteCallback note); void saveLoadWithSerializer(Common::Serializer &ser); SegmentRef dereference(reg_t pointer); diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 04a1b8fbba..e226c4b574 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -153,11 +153,11 @@ void Kernel::mapSelectors() { FIND_SELECTOR(subtitleLang); FIND_SELECTOR(parseLang); FIND_SELECTOR(overlay); + FIND_SELECTOR(setCursor); FIND_SELECTOR(topString); FIND_SELECTOR(scaleSignal); FIND_SELECTOR(scaleX); FIND_SELECTOR(scaleY); - FIND_SELECTOR(iconIndex); #ifdef ENABLE_SCI32 FIND_SELECTOR(data); @@ -175,32 +175,32 @@ void Kernel::mapSelectors() { #endif } -reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) { +reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id) { ObjVarRef address; - if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) + if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable) return NULL_REG; else return *address.getPointer(segMan); } -void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) { +void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value) { ObjVarRef address; - if ((selectorId < 0) || (selectorId > (int)g_sci->getKernel()->getSelectorNamesSize())) { + if ((selector_id < 0) || (selector_id > (int)g_sci->getKernel()->getSelectorNamesSize())) { warning("Attempt to write to invalid selector %d of" - " object at %04x:%04x.", selectorId, PRINT_REG(object)); + " object at %04x:%04x.", selector_id, PRINT_REG(object)); return; } - if (lookupSelector(segMan, object, selectorId, &address, NULL) != kSelectorVariable) + if (lookup_selector(segMan, object, selector_id, &address, NULL) != kSelectorVariable) warning("Selector '%s' of object at %04x:%04x could not be" - " written to", g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); + " written to", g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); else *address.getPointer(segMan) = value; } -int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, +int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, int k_argc, StackPtr k_argp, int argc, const reg_t *argv) { int i; int framesize = 2 + 1 * argc; @@ -208,21 +208,21 @@ int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInv int slc_type; StackPtr stackframe = k_argp + k_argc; - stackframe[0] = make_reg(0, selectorId); // The selector we want to call + stackframe[0] = make_reg(0, selector_id); // The selector we want to call stackframe[1] = make_reg(0, argc); // Argument count - slc_type = lookupSelector(s->_segMan, object, selectorId, NULL, &address); + slc_type = lookup_selector(s->_segMan, object, selector_id, NULL, &address); if (slc_type == kSelectorNone) { warning("Selector '%s' of object at %04x:%04x could not be invoked", - g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); + g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); if (noinvalid == kStopOnInvalidSelector) error("[Kernel] Not recoverable: VM was halted"); return 1; } if (slc_type == kSelectorVariable) { warning("Attempting to invoke variable selector %s of object %04x:%04x", - g_sci->getKernel()->getSelectorName(selectorId).c_str(), PRINT_REG(object)); + g_sci->getKernel()->getSelectorName(selector_id).c_str(), PRINT_REG(object)); return 0; } @@ -242,7 +242,7 @@ int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInv return 0; } -int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, +int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, int k_argc, StackPtr k_argp, int argc, ...) { va_list argp; reg_t *args = new reg_t[argc]; @@ -252,28 +252,28 @@ int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocat args[i] = va_arg(argp, reg_t); va_end(argp); - int retval = invokeSelectorArgv(s, object, selectorId, noinvalid, k_argc, k_argp, argc, args); + int retval = invoke_selector_argv(s, object, selector_id, noinvalid, k_argc, k_argp, argc, args); delete[] args; return retval; } -SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector selectorId, ObjVarRef *varp, reg_t *fptr) { - const Object *obj = segMan->getObject(obj_location); +SelectorType lookup_selector(SegManager *segMan, reg_t obj_location, Selector selector_id, ObjVarRef *varp, reg_t *fptr) { + Object *obj = segMan->getObject(obj_location); int index; bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); // Early SCI versions used the LSB in the selector ID as a read/write // toggle, meaning that we must remove it for selector lookup. if (oldScriptHeader) - selectorId &= ~1; + selector_id &= ~1; if (!obj) { - error("lookupSelector(): Attempt to send to non-object or invalid script. Address was %04x:%04x", + error("lookup_selector(): Attempt to send to non-object or invalid script. Address was %04x:%04x", PRINT_REG(obj_location)); } - index = obj->locateVarSelector(segMan, selectorId); + index = obj->locateVarSelector(segMan, selector_id); if (index >= 0) { // Found it as a variable @@ -285,7 +285,7 @@ SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector sel } else { // Check if it's a method, with recursive lookup in superclasses while (obj) { - index = obj->funcSelectorPosition(selectorId); + index = obj->funcSelectorPosition(selector_id); if (index >= 0) { if (fptr) *fptr = obj->getFunction(index); @@ -300,7 +300,7 @@ SelectorType lookupSelector(SegManager *segMan, reg_t obj_location, Selector sel } -// return _lookupSelector_function(segMan, obj, selectorId, fptr); +// return _lookup_selector_function(segMan, obj, selector_id, fptr); } } // End of namespace Sci diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index f50b9ab1b3..70eeb34d93 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -34,15 +34,20 @@ namespace Sci { + +/******************** Selector functionality ********************/ + enum SelectorInvocation { kStopOnInvalidSelector = 0, kContinueOnInvalidSelector = 1 }; + /** * Map a selector name to a selector id. Shortcut for accessing the selector cache. */ #define SELECTOR(_slc_) (g_sci->getKernel()->_selectorCache._slc_) +//#define SELECTOR(_slc_) _slc_ /** * Retrieves a selector from an object. @@ -53,8 +58,8 @@ enum SelectorInvocation { * This macro halts on error. 'selector' must be a selector name registered in vm.h's * SelectorCache and mapped in script.cpp. */ -reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId); -#define readSelectorValue(segMan, _obj_, _slc_) (readSelector(segMan, _obj_, _slc_).offset) +#define GET_SEL32(segMan, _obj_, _slc_) read_selector(segMan, _obj_, _slc_) +#define GET_SEL32V(segMan, _obj_, _slc_) (GET_SEL32(segMan, _obj_, _slc_).offset) /** * Writes a selector value to an object. @@ -65,25 +70,27 @@ reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId); * This macro halts on error. 'selector' must be a selector name registered in vm.h's * SelectorCache and mapped in script.cpp. */ -void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value); -#define writeSelectorValue(segMan, _obj_, _slc_, _val_) writeSelector(segMan, _obj_, _slc_, make_reg(0, _val_)) +#define PUT_SEL32(segMan, _obj_, _slc_, _val_) write_selector(segMan, _obj_, _slc_, _val_) +#define PUT_SEL32V(segMan, _obj_, _slc_, _val_) PUT_SEL32(segMan, _obj_, _slc_, make_reg(0, _val_)) -/** - * Invokes a selector from an object. - */ -int invokeSelector(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, - int k_argc, StackPtr k_argp, int argc, ...); -int invokeSelectorArgv(EngineState *s, reg_t object, int selectorId, SelectorInvocation noinvalid, - int k_argc, StackPtr k_argp, int argc, const reg_t *argv); /** - * Kludge for use with invokeSelector(). Used for compatibility with compilers + * Kludge for use with invoke_selector(). Used for compatibility with compilers * that cannot handle vararg macros. */ #define INV_SEL(s, _object_, _selector_, _noinvalid_) \ s, _object_, g_sci->getKernel()->_selectorCache._selector_, _noinvalid_, argc, argv +reg_t read_selector(SegManager *segMan, reg_t object, Selector selector_id); +void write_selector(SegManager *segMan, reg_t object, Selector selector_id, reg_t value); +int invoke_selector(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, + int k_argc, StackPtr k_argp, int argc, ...); +int invoke_selector_argv(EngineState *s, reg_t object, int selector_id, SelectorInvocation noinvalid, + int k_argc, StackPtr k_argp, int argc, const reg_t *argv); + + + } // End of namespace Sci #endif // SCI_ENGINE_KERNEL_H diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index b642cd8dc9..bd78639c77 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -69,52 +69,51 @@ static const uint16 s_halfWidthSJISMap[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -EngineState::EngineState(SegManager *segMan) -: _segMan(segMan), _dirseeker() { +EngineState::EngineState(Vocabulary *voc, SegManager *segMan) +: _voc(voc), _segMan(segMan), _dirseeker() { - reset(false); -} - -EngineState::~EngineState() { - delete _msgState; -} - -void EngineState::reset(bool isRestoring) { #ifdef USE_OLD_MUSIC_FUNCTIONS sfx_init_flags = 0; #endif - if (!isRestoring) { - script_000 = 0; - _gameObj = NULL_REG; + restarting_flags = 0; - _memorySegmentSize = 0; - _soundCmd = 0; + last_wait_time = 0; - restarting_flags = 0; + _fileHandles.resize(5); - execution_stack_base = 0; - _executionStackPosChanged = false; + execution_stack_base = 0; + _executionStackPosChanged = false; - _fileHandles.resize(5); + r_acc = NULL_REG; + restAdjust = 0; + r_prev = NULL_REG; - r_acc = NULL_REG; - restAdjust = 0; - r_prev = NULL_REG; + stack_base = 0; + stack_top = 0; - stack_base = 0; - stack_top = 0; - } + script_000 = 0; - last_wait_time = 0; + sys_strings_segment = 0; + sys_strings = 0; + + _gameObj = NULL_REG; gc_countdown = 0; + successor = 0; + _throttleCounter = 0; _throttleLastTime = 0; _throttleTrigger = false; - restoring = false; + _memorySegmentSize = 0; + + _soundCmd = 0; +} + +EngineState::~EngineState() { + delete _msgState; } void EngineState::wait(int16 ticks) { @@ -136,15 +135,6 @@ void EngineState::setRoomNumber(uint16 roomNumber) { script_000->_localsBlock->_locals[13] = make_reg(0, roomNumber); } -void EngineState::shrinkStackToBase() { - uint size = execution_stack_base + 1; - assert(_executionStack.size() >= size); - Common::List<ExecStack>::iterator iter = _executionStack.begin(); - for (uint i = 0; i < size; ++i) - ++iter; - _executionStack.erase(iter, _executionStack.end()); -} - static kLanguage charToLanguage(const char c) { switch (c) { case 'F': @@ -228,7 +218,7 @@ kLanguage SciEngine::getSciLanguage() { lang = K_LANG_ENGLISH; if (_kernel->_selectorCache.printLang != -1) { - lang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang)); + lang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang)); if ((getSciVersion() >= SCI_VERSION_1_1) || (lang == K_LANG_NONE)) { // If language is set to none, we use the language from the game detector. @@ -263,7 +253,7 @@ kLanguage SciEngine::getSciLanguage() { } // Store language in printLang selector - writeSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang); + PUT_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(printLang), lang); } } @@ -275,7 +265,7 @@ Common::String SciEngine::strSplit(const char *str, const char *sep) { kLanguage subLang = K_LANG_NONE; if (_kernel->_selectorCache.subtitleLang != -1) { - subLang = (kLanguage)readSelectorValue(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang)); + subLang = (kLanguage)GET_SEL32V(_gamestate->_segMan, _gamestate->_gameObj, SELECTOR(subtitleLang)); } kLanguage secondLang; diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 431aac884e..c4b995806f 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -76,7 +76,8 @@ enum { enum { SCI_GAME_IS_NOT_RESTARTING = 0, SCI_GAME_WAS_RESTARTED = 1, - SCI_GAME_IS_RESTARTING_NOW = 2 + SCI_GAME_IS_RESTARTING_NOW = 2, + SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE = 4 }; class FileHandle { @@ -95,13 +96,16 @@ public: struct EngineState : public Common::Serializable { public: - EngineState(SegManager *segMan); + EngineState(Vocabulary *voc, SegManager *segMan); virtual ~EngineState(); virtual void saveLoadWithSerializer(Common::Serializer &ser); public: SegManager *_segMan; /**< The segment manager */ + Vocabulary *_voc; + + Common::String _gameId; /**< Designation of the primary object (which inherits from Game) */ /* Non-VM information */ @@ -153,11 +157,9 @@ public: uint16 currentRoomNumber() const; void setRoomNumber(uint16 roomNumber); - /** - * Shrink execution stack to size. - * Contains an assert it is not already smaller. - */ - void shrinkStackToBase(); + /* System strings */ + SegmentId sys_strings_segment; + SystemStrings *sys_strings; reg_t _gameObj; /**< Pointer to the game object */ @@ -174,12 +176,7 @@ public: uint _memorySegmentSize; byte _memorySegment[kMemorySegmentMax]; - /** - * Resets the engine state. - */ - void reset(bool isRestoring); - - bool restoring; /**< A flag to indicate if a game is being restored */ + EngineState *successor; /**< Successor of this state: Used for restoring */ }; } // End of namespace Sci diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 36f5ec6893..e2ee2e1971 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -120,7 +120,7 @@ static reg_t &validate_property(Object *obj, int index) { return dummyReg; } - return obj->getVariableRef(index); + return obj->_variables[index]; } static StackPtr validate_stack_addr(EngineState *s, StackPtr sp) { @@ -212,7 +212,7 @@ static void validate_write_var(reg_t *r, reg_t *stack_base, int type, int max, i if (!stopGroopPos.isNull()) { // does the game have a stopGroop object? // Find the "client" member variable of the stopGroop object, and update it ObjVarRef varp; - if (lookupSelector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) { + if (lookup_selector(segMan, stopGroopPos, kernel->_selectorCache.client, &varp, NULL) == kSelectorVariable) { reg_t *clientVar = varp.getPointer(segMan); *clientVar = value; } @@ -341,7 +341,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt int selector; int argc; int origin = s->_executionStack.size()-1; // Origin: Used for debugging - bool printSendActions = false; + int print_send_action = 0; // We return a pointer to the new active ExecStack // The selector calls we catch are stored below: @@ -370,7 +370,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt if (bp->type == BREAK_SELECTOR && !strncmp(bp->name.c_str(), method_name, cmplen)) { Console *con = g_sci->getSciDebugger(); con->DebugPrintf("Break on %s (in [%04x:%04x])\n", method_name, PRINT_REG(send_obj)); - printSendActions = true; + print_send_action = 1; g_debugState.debugging = true; g_debugState.breakpointWasHit = true; break; @@ -383,7 +383,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #endif // VM_DEBUG_SEND ObjVarRef varp; - switch (lookupSelector(s->_segMan, send_obj, selector, &varp, &funcp)) { + switch (lookup_selector(s->_segMan, send_obj, selector, &varp, &funcp)) { case kSelectorNone: error("Send to invalid selector 0x%x of object at %04x:%04x", 0xffff & selector, PRINT_REG(send_obj)); break; @@ -398,34 +398,22 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt #endif // VM_DEBUG_SEND // argc == 0: read selector - // argc != 0: write selector - if (printSendActions && !argc) { // read selector - debug("[read selector]\n"); - printSendActions = false; + // argc == 1: write selector + // argc > 1: write selector? + if (print_send_action && argc == 0) { // read selector + printf("[read selector]\n"); + print_send_action = 0; } - if (printSendActions && argc) { + if (print_send_action && argc > 0) { reg_t oldReg = *varp.getPointer(s->_segMan); reg_t newReg = argp[1]; - debug("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg)); - printSendActions = false; + printf("[write to selector: change %04x:%04x to %04x:%04x]\n", PRINT_REG(oldReg), PRINT_REG(newReg)); + print_send_action = 0; } - if (argc > 1) { - // argc can indeed be bigger than 1 in some cases, and it seems correct - // (i.e. we should skip that many bytes later on)... question is, why - // does this occur? Could such calls be used to point to data after X - // bytes in the heap? What are the skipped bytes in this case? - // In SQ4CD, this occurs with the returnVal selector of object - // Sq4GlobalNarrator when the game starts, and right after the narrator - // is heard (e.g. after he talks when examining something) - reg_t oldReg = *varp.getPointer(s->_segMan); - reg_t newReg = argp[1]; - warning("send_selector(): argc = %d while modifying variable selector " - "%x (%s) of object %04x:%04x (%s) from %04x:%04x to %04x:%04x", - argc, selector, g_sci->getKernel()->getSelectorName(selector).c_str(), PRINT_REG(send_obj), - s->_segMan->getObjectName(send_obj), PRINT_REG(oldReg), PRINT_REG(newReg)); - } + if (argc > 1) + warning("send_selector(): more than 1 parameter (%d) while modifying a variable selector", argc); { CallsStruct call; @@ -450,9 +438,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } printf(") at %04x:%04x\n", PRINT_REG(funcp)); #endif // VM_DEBUG_SEND - if (printSendActions) { - debug("[invoke selector]\n"); - printSendActions = false; + if (print_send_action) { + printf("[invoke selector]\n"); + print_send_action = 0; } { @@ -468,7 +456,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt } break; - } // switch (lookupSelector()) + } // switch (lookup_selector()) framesize -= (2 + argc); argp += argc + 1; @@ -489,7 +477,9 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt _exec_varselectors(s); - return s->_executionStack.empty() ? NULL : &(s->_executionStack.back()); + if (s->_executionStack.empty()) + return NULL; + return &(s->_executionStack.back()); } static ExecStack *add_exec_stack_varselector(Common::List<ExecStack> &execStack, reg_t objp, int argc, StackPtr argp, Selector selector, const ObjVarRef& address, int origin) { @@ -593,26 +583,19 @@ static void callKernelFunc(EngineState *s, int kernelFuncNum, int argc) { //warning("callk %s", kernelFunc.orig_name.c_str()); - // TODO: SCI2.1 equivalent - if (g_loadFromLauncher >= 0 && ( - (kernelFuncNum == 0x8 && getSciVersion() <= SCI_VERSION_1_1) || // DrawPic - (kernelFuncNum == 0x3d && getSciVersion() == SCI_VERSION_2) // GetSaveDir - //(kernelFuncNum == 0x28 && getSciVersion() == SCI_VERSION_2_1) // AddPlane - )) { - - // A game is being loaded from the launcher, and the game is about to draw something on - // screen, hence all initialization has taken place (i.e. menus have been constructed etc). - // Therefore, inject a kRestoreGame call here, instead of the requested function. - // The restore call is injected here mainly for games which have a menu, as the menu is - // constructed when the game starts and is not reconstructed when a saved game is loaded. - int saveSlot = g_loadFromLauncher; - g_loadFromLauncher = -1; // invalidate slot, so that we don't load again - - if (saveSlot < 0) - error("Requested to load invalid save slot"); // should never happen, really - - reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL) - kRestoreGame(s, 2, restoreArgv); + // TODO: SCI2/SCI2.1+ equivalent, once saving/loading works in SCI2/SCI2.1+ + if (g_loadFromLauncher >= 0 && kernelFuncNum == 0x8) { + // A game is being loaded from the launcher, and kDisplay is called, all initialization has taken + // place (i.e. menus have been constructed etc). Therefore, inject a kRestoreGame call + // here, instead of the requested function. + int saveSlot = g_loadFromLauncher; + g_loadFromLauncher = -1; // invalidate slot, so that we don't load again + + if (saveSlot < 0) + error("Requested to load invalid save slot"); // should never happen, really + + reg_t restoreArgv[2] = { NULL_REG, make_reg(0, saveSlot) }; // special call (argv[0] is NULL) + kRestoreGame(s, 2, restoreArgv); } else { // Call kernel function s->r_acc = kernelFunc.fun(s, argc, argv); @@ -741,7 +724,7 @@ void run_vm(EngineState *s, bool restoring) { } if (!restoring) - s->execution_stack_base = s->_executionStack.size() - 1; + s->execution_stack_base = s->_executionStack.size()-1; #ifndef DISABLE_VALIDATIONS // Initialize maximum variable count @@ -791,7 +774,7 @@ void run_vm(EngineState *s, bool restoring) { obj = s->_segMan->getObject(scriptState.xs->objp); code_buf = scr->_buf; #ifndef DISABLE_VALIDATIONS - code_buf_size = scr->getBufSize(); + code_buf_size = scr->_bufSize; #endif local_script = s->_segMan->getScriptIfLoaded(scriptState.xs->local_segment); if (!local_script) { @@ -839,14 +822,12 @@ void run_vm(EngineState *s, bool restoring) { #ifndef DISABLE_VALIDATIONS if (scriptState.xs->sp < scriptState.xs->fp) - error("run_vm(): stack underflow, sp: %04x:%04x, fp: %04x:%04x", - PRINT_REG(*scriptState.xs->sp), PRINT_REG(*scriptState.xs->fp)); + error("run_vm(): stack underflow"); scriptState.variables_max[VAR_TEMP] = scriptState.xs->sp - scriptState.xs->fp; if (scriptState.xs->addr.pc.offset >= code_buf_size) - error("run_vm(): program counter gone astray, addr: %d, code buffer size: %d", - scriptState.xs->addr.pc.offset, code_buf_size); + error("run_vm(): program counter gone astray"); #endif // Get opcode @@ -1202,7 +1183,7 @@ void run_vm(EngineState *s, bool restoring) { StackPtr old_fp = scriptState.xs->fp; ExecStack *old_xs = &(s->_executionStack.back()); - if ((int)s->_executionStack.size() - 1 == s->execution_stack_base) { // Have we reached the base? + if ((int)s->_executionStack.size()-1 == s->execution_stack_base) { // Have we reached the base? s->execution_stack_base = old_execution_stack_base; // Restore stack base s->_executionStack.pop_back(); @@ -1405,7 +1386,7 @@ void run_vm(EngineState *s, bool restoring) { switch (g_sci->_features->detectLofsType()) { case SCI_VERSION_1_1: - s->r_acc.offset = opparams[0] + local_script->getScriptSize(); + s->r_acc.offset = opparams[0] + local_script->_scriptSize; break; case SCI_VERSION_1_MIDDLE: s->r_acc.offset = opparams[0]; @@ -1427,7 +1408,7 @@ void run_vm(EngineState *s, bool restoring) { switch (g_sci->_features->detectLofsType()) { case SCI_VERSION_1_1: - r_temp.offset = opparams[0] + local_script->getScriptSize(); + r_temp.offset = opparams[0] + local_script->_scriptSize; break; case SCI_VERSION_1_MIDDLE: r_temp.offset = opparams[0]; @@ -1695,16 +1676,17 @@ static void _init_stack_base_with_selector(EngineState *s, Selector selector) { } static EngineState *_game_run(EngineState *&s) { - bool restoring = false; + EngineState *successor = NULL; + int game_is_finished = 0; if (DebugMan.isDebugChannelEnabled(kDebugLevelOnStartup)) g_sci->getSciDebugger()->attach(); do { s->_executionStackPosChanged = false; - run_vm(s, restoring); + run_vm(s, successor ? true : false); if (s->restarting_flags & SCI_GAME_IS_RESTARTING_NOW) { // Restart was requested? - restoring = false; + successor = NULL; s->_executionStack.clear(); s->_executionStackPosChanged = false; @@ -1719,13 +1701,15 @@ static EngineState *_game_run(EngineState *&s) { send_selector(s, s->_gameObj, s->_gameObj, s->stack_base, 2, s->stack_base); script_abort_flag = 0; - s->restarting_flags = SCI_GAME_WAS_RESTARTED; + s->restarting_flags = SCI_GAME_WAS_RESTARTED | SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; } else { - restoring = s->restoring; - if (restoring) { + successor = s->successor; + if (successor) { game_exit(s); - s->restoring = false; + delete s; + s = successor; + if (script_abort_flag == 2) { debugC(2, kDebugLevelVM, "Restarting with replay()"); s->_executionStack.clear(); // Restart with replay @@ -1738,9 +1722,9 @@ static EngineState *_game_run(EngineState *&s) { script_abort_flag = 0; } else - break; // exit loop + game_is_finished = 1; } - } while (true); + } while (!game_is_finished); return s; } @@ -1748,7 +1732,7 @@ static EngineState *_game_run(EngineState *&s) { int game_run(EngineState **_s) { EngineState *s = *_s; - debugC(2, kDebugLevelVM, "Calling %s::play()", g_sci->getGameID()); + debugC(2, kDebugLevelVM, "Calling %s::play()", s->_gameId.c_str()); _init_stack_base_with_selector(s, g_sci->getKernel()->_selectorCache.play); // Call the play selector // Now: Register the first element on the execution stack- @@ -1772,12 +1756,22 @@ void quit_vm() { g_debugState.runningStep = 0; } -reg_t *ObjVarRef::getPointer(SegManager *segMan) const { +void shrink_execution_stack(EngineState *s, uint size) { + assert(s->_executionStack.size() >= size); + Common::List<ExecStack>::iterator iter; + iter = s->_executionStack.begin(); + for (uint i = 0; i < size; ++i) + ++iter; + s->_executionStack.erase(iter, s->_executionStack.end()); +} + +reg_t* ObjVarRef::getPointer(SegManager *segMan) const { Object *o = segMan->getObject(obj); - return o ? &o->getVariableRef(varindex) : 0; + if (!o) return 0; + return &(o->_variables[varindex]); } -reg_t *ExecStack::getVarPointer(SegManager *segMan) const { +reg_t* ExecStack::getVarPointer(SegManager *segMan) const { assert(type == EXEC_STACK_TYPE_VARSELECTOR); return addr.varp.getPointer(segMan); } diff --git a/engines/sci/engine/vm.h b/engines/sci/engine/vm.h index 42ee55cd06..8e40fed818 100644 --- a/engines/sci/engine/vm.h +++ b/engines/sci/engine/vm.h @@ -43,9 +43,44 @@ class ResourceManager; /** Number of bytes to be allocated for the stack */ #define VM_STACK_SIZE 0x1000 +/** Maximum number of calls residing on the stack */ +#define SCRIPT_MAX_EXEC_STACK 256 +/** Maximum number of entries in the class table */ +#define SCRIPT_MAX_CLASSTABLE_SIZE 256 +/** Maximum number of cloned objects on the heap */ +#define SCRIPT_MAX_CLONES 256 + + +/** Object-relative offset of the selector area inside a script */ +#define SCRIPT_SELECTOR_OFFSET 8 -8 + +/** Object-relative offset of the pointer to the underlying script's local variables */ +#define SCRIPT_LOCALVARPTR_OFFSET 2 -8 + +/** Object-relative offset of the selector counter */ +#define SCRIPT_SELECTORCTR_OFFSET 6 -8 + +/** Object-relative offset of the offset of the function area */ +#define SCRIPT_FUNCTAREAPTR_OFFSET 4 -8 + +/** Offset that has to be added to the function area pointer */ +#define SCRIPT_FUNCTAREAPTR_MAGIC 8 -8 + +/** Offset of the name pointer */ +#define SCRIPT_NAME_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 14 -8 : 16) + +/** Object-relative offset of the -info- selector */ +#define SCRIPT_INFO_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 12 -8 : 14) + +/** Flag fo the -info- selector */ +#define SCRIPT_INFO_CLONE 0x0001 + +/** Flag for the -info- selector */ +#define SCRIPT_INFO_CLASS 0x8000 + + /** Magical object identifier */ #define SCRIPT_OBJECT_MAGIC_NUMBER 0x1234 - /** Offset of this identifier */ #define SCRIPT_OBJECT_MAGIC_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? -8 : 0) @@ -54,10 +89,13 @@ class ResourceManager; #define SCRIPT_SUPERCLASS_OFFSET (getSciVersion() < SCI_VERSION_1_1 ? 10 -8 : 12) +/** Magic adjustment value for lofsa and lofss */ +#define SCRIPT_LOFS_MAGIC 3 + /** Stack pointer value: Use predecessor's value */ #define CALL_SP_CARRY NULL -/** Types of selectors as returned by lookupSelector() below. */ +/** Types of selectors as returned by lookup_selector() below. */ enum SelectorType { kSelectorNone = 0, kSelectorVariable, @@ -157,9 +195,7 @@ struct SelectorCache { // Used for auto detection purposes Selector overlay; ///< Used to determine if a game is using old gfx functions or not - - // SCI1.1 Mac icon bar selectors - Selector iconIndex; ///< Used to index icon bar objects + Selector setCursor; ///< For cursor semantics autodetection #ifdef ENABLE_SCI32 Selector data; // Used by Array()/String() @@ -340,7 +376,7 @@ int script_init_engine(EngineState *); * kSelectorMethod if the selector represents a * method */ -SelectorType lookupSelector(SegManager *segMan, reg_t obj, Selector selectorid, +SelectorType lookup_selector(SegManager *segMan, reg_t obj, Selector selectorid, ObjVarRef *varp, reg_t *fptr); /** @@ -433,6 +469,12 @@ int game_exit(EngineState *s); void quit_vm(); /** + * Shrink execution stack to size. + * Contains an assert it is not already smaller. + */ +void shrink_execution_stack(EngineState *s, uint size); + +/** * Read a PMachine instruction from a memory buffer and return its length. * * @param[in] src address from which to start parsing diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index c3be22b143..53f4675f56 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -319,6 +319,8 @@ sciEvent SciEvent::get(unsigned int mask) { //sci_event_t error_event = { SCI_EVT_ERROR, 0, 0, 0 }; sciEvent event = { 0, 0, 0, 0 }; + // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position + // Update the screen here, since it's called very often g_system->updateScreen(); @@ -387,6 +389,7 @@ void SciEvent::sleep(uint32 msecs) { while (true) { // let backend process events and update the screen get(SCI_EVENT_PEEK); + // TODO: we need to call Cursor::refreshPosition() before each screen update to limit the mouse cursor position time = g_system->getMillis(); if (time + 10 < wakeup_time) { g_system->delayMillis(10); diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index c201b2cfb7..71c7b7dd7f 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -92,10 +92,10 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { } } - signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); + signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal)); if (!(signal & kSignalFrozen)) { // Call .doit method of that object - invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0); + invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.doit, kContinueOnInvalidSelector, argc, argv, 0); // Lookup node again, since the nodetable it was in may have been reallocated curNode = _s->_segMan->lookupNode(curAddress); } @@ -109,15 +109,7 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { } bool sortHelper(const AnimateEntry* entry1, const AnimateEntry* entry2) { - if (entry1->y == entry2->y) { - // if both y and z are the same, use the order we were given originally - // this is needed for special cases like iceman room 35 - if (entry1->z == entry2->z) - return entry1->givenOrderNo < entry2->givenOrderNo; - else - return entry1->z < entry2->z; - } - return entry1->y < entry2->y; + return (entry1->y == entry2->y) ? (entry1->z < entry2->z) : (entry1->y < entry2->y); } void GfxAnimate::makeSortedList(List *list) { @@ -164,22 +156,21 @@ void GfxAnimate::makeSortedList(List *list) { listEntry->object = curObject; // Get data from current object - listEntry->givenOrderNo = listNr; - listEntry->viewId = readSelectorValue(_s->_segMan, curObject, SELECTOR(view)); - listEntry->loopNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(loop)); - listEntry->celNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(cel)); - listEntry->paletteNo = readSelectorValue(_s->_segMan, curObject, SELECTOR(palette)); - listEntry->x = readSelectorValue(_s->_segMan, curObject, SELECTOR(x)); - listEntry->y = readSelectorValue(_s->_segMan, curObject, SELECTOR(y)); - listEntry->z = readSelectorValue(_s->_segMan, curObject, SELECTOR(z)); - listEntry->priority = readSelectorValue(_s->_segMan, curObject, SELECTOR(priority)); - listEntry->signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); + listEntry->viewId = GET_SEL32V(_s->_segMan, curObject, SELECTOR(view)); + listEntry->loopNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(loop)); + listEntry->celNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(cel)); + listEntry->paletteNo = GET_SEL32V(_s->_segMan, curObject, SELECTOR(palette)); + listEntry->x = GET_SEL32V(_s->_segMan, curObject, SELECTOR(x)); + listEntry->y = GET_SEL32V(_s->_segMan, curObject, SELECTOR(y)); + listEntry->z = GET_SEL32V(_s->_segMan, curObject, SELECTOR(z)); + listEntry->priority = GET_SEL32V(_s->_segMan, curObject, SELECTOR(priority)); + listEntry->signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal)); if (getSciVersion() >= SCI_VERSION_1_1) { // Cel scaling - listEntry->scaleSignal = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleSignal)); + listEntry->scaleSignal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleSignal)); if (listEntry->scaleSignal & kScaleSignalDoScaling) { - listEntry->scaleX = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleX)); - listEntry->scaleY = readSelectorValue(_s->_segMan, curObject, SELECTOR(scaleY)); + listEntry->scaleX = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleX)); + listEntry->scaleY = GET_SEL32V(_s->_segMan, curObject, SELECTOR(scaleY)); } else { listEntry->scaleX = 128; listEntry->scaleY = 128; @@ -228,11 +219,11 @@ void GfxAnimate::fill(byte &old_picNotValid) { // adjust loop and cel, if any of those is invalid if (listEntry->loopNo >= view->getLoopCount()) { listEntry->loopNo = 0; - writeSelectorValue(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(loop), listEntry->loopNo); } if (listEntry->celNo >= view->getCelCount(listEntry->loopNo)) { listEntry->celNo = 0; - writeSelectorValue(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(cel), listEntry->celNo); } // Create rect according to coordinates and given cel @@ -241,17 +232,17 @@ void GfxAnimate::fill(byte &old_picNotValid) { } else { view->getCelRect(listEntry->loopNo, listEntry->celNo, listEntry->x, listEntry->y, listEntry->z, &listEntry->celRect); } - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsLeft), listEntry->celRect.left); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsTop), listEntry->celRect.top); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsRight), listEntry->celRect.right); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(nsBottom), listEntry->celRect.bottom); signal = listEntry->signal; // Calculate current priority according to y-coordinate if (!(signal & kSignalFixedPriority)) { listEntry->priority = _ports->kernelCoordinateToPriority(listEntry->y); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(priority), listEntry->priority); } if (signal & kSignalNoUpdate) { @@ -291,14 +282,14 @@ void GfxAnimate::update() { if (signal & kSignalNoUpdate) { if (!(signal & kSignalRemoveView)) { - bitsHandle = readSelector(_s->_segMan, curObject, SELECTOR(underBits)); + bitsHandle = GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits)); if (_screen->_picNotValid != 1) { _paint16->bitsRestore(bitsHandle); listEntry->showBitsFlag = true; } else { _paint16->bitsFree(bitsHandle); } - writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0); } signal &= 0xFFFF ^ kSignalForceUpdate; signal &= signal & kSignalViewUpdated ? 0xFFFF ^ (kSignalViewUpdated | kSignalNoUpdate) : 0xFFFF; @@ -348,7 +339,7 @@ void GfxAnimate::update() { bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_VISUAL|GFX_SCREEN_MASK_PRIORITY); else bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL); - writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); + PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); } listEntry->signal = signal; } @@ -396,7 +387,7 @@ void GfxAnimate::drawCels() { if (!(signal & (kSignalNoUpdate | kSignalHidden | kSignalAlwaysUpdate))) { // Save background bitsHandle = _paint16->bitsSave(listEntry->celRect, GFX_SCREEN_MASK_ALL); - writeSelector(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); + PUT_SEL32(_s->_segMan, curObject, SELECTOR(underBits), bitsHandle); // draw corresponding cel _paint16->drawCel(listEntry->viewId, listEntry->loopNo, listEntry->celNo, listEntry->celRect, listEntry->priority, listEntry->paletteNo, listEntry->scaleX, listEntry->scaleY); @@ -432,10 +423,10 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) { if (listEntry->showBitsFlag || !(signal & (kSignalRemoveView | kSignalNoUpdate) || (!(signal & kSignalRemoveView) && (signal & kSignalNoUpdate) && oldPicNotValid))) { - lsRect.left = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft)); - lsRect.top = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop)); - lsRect.right = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight)); - lsRect.bottom = readSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom)); + lsRect.left = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft)); + lsRect.top = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop)); + lsRect.right = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight)); + lsRect.bottom = GET_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom)); workerRect = lsRect; workerRect.clip(listEntry->celRect); @@ -447,10 +438,10 @@ void GfxAnimate::updateScreen(byte oldPicNotValid) { _paint16->bitsShow(lsRect); workerRect = listEntry->celRect; } - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsLeft), workerRect.left); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsTop), workerRect.top); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsRight), workerRect.right); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(lsBottom), workerRect.bottom); _paint16->bitsShow(workerRect); if (signal & kSignalHidden) { @@ -481,7 +472,7 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) { signal = listEntry->signal; // Finally update signal - writeSelectorValue(_s->_segMan, curObject, SELECTOR(signal), signal); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(signal), signal); listIterator++; } @@ -490,16 +481,16 @@ void GfxAnimate::restoreAndDelete(int argc, reg_t *argv) { listEntry = *listIterator; curObject = listEntry->object; // We read out signal here again, this is not by accident but to ensure that we got an up-to-date signal - signal = readSelectorValue(_s->_segMan, curObject, SELECTOR(signal)); + signal = GET_SEL32V(_s->_segMan, curObject, SELECTOR(signal)); if ((signal & (kSignalNoUpdate | kSignalRemoveView)) == 0) { - _paint16->bitsRestore(readSelector(_s->_segMan, curObject, SELECTOR(underBits))); - writeSelectorValue(_s->_segMan, curObject, SELECTOR(underBits), 0); + _paint16->bitsRestore(GET_SEL32(_s->_segMan, curObject, SELECTOR(underBits))); + PUT_SEL32V(_s->_segMan, curObject, SELECTOR(underBits), 0); } if (signal & kSignalDisposeMe) { // Call .delete_ method of that object - invokeSelector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0); + invoke_selector(_s, curObject, g_sci->getKernel()->_selectorCache.delete_, kContinueOnInvalidSelector, argc, argv, 0); } listIterator--; } diff --git a/engines/sci/graphics/animate.h b/engines/sci/graphics/animate.h index 706b7182cf..2cc59b1767 100644 --- a/engines/sci/graphics/animate.h +++ b/engines/sci/graphics/animate.h @@ -40,7 +40,7 @@ enum ViewSignals { kSignalAlwaysUpdate = 0x0020, kSignalForceUpdate = 0x0040, kSignalRemoveView = 0x0080, - kSignalFrozen = 0x0100, // I got frozen today!! + kSignalFrozen = 0x0100, kSignalExtraActor = 0x0200, // unused by us, defines all actors that may be included into the background if speed is too slow kSignalHitObstacle = 0x0400, // used in the actor movement code by kDoBresen() kSignalDoesntTurn = 0x0800, // used by _k_dirloop() to determine if an actor can turn or not @@ -57,7 +57,6 @@ enum ViewScaleSignals { }; struct AnimateEntry { - int16 givenOrderNo; reg_t object; GuiResourceId viewId; int16 loopNo; diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 3102edc2fa..36dd2d4aed 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -79,12 +79,12 @@ bool GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &c while (curNode) { curObject = curNode->value; if (curObject != checkObject) { - signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); + signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal)); if ((signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate)) == 0) { - curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); - curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); - curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); - curRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); + curRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft)); + curRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop)); + curRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight)); + curRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom)); // Check if curRect is within checkRect // TODO: This check is slightly odd, because it means that a rect is not contained // in itself. It may very well be that the original SCI engine did it just @@ -115,29 +115,29 @@ uint16 GfxCompare::kernelOnControl(byte screenMask, const Common::Rect &rect) { void GfxCompare::kernelSetNowSeen(reg_t objectReference) { GfxView *view = NULL; Common::Rect celRect(0, 0); - GuiResourceId viewId = (GuiResourceId)readSelectorValue(_segMan, objectReference, SELECTOR(view)); + GuiResourceId viewId = (GuiResourceId)GET_SEL32V(_segMan, objectReference, SELECTOR(view)); // HACK: Ignore invalid views for now (perhaps unimplemented text views?) if (viewId == 0xFFFF) // invalid view return; - int16 loopNo = readSelectorValue(_segMan, objectReference, SELECTOR(loop)); - int16 celNo = readSelectorValue(_segMan, objectReference, SELECTOR(cel)); - int16 x = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(x)); - int16 y = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(y)); + int16 loopNo = GET_SEL32V(_segMan, objectReference, SELECTOR(loop)); + int16 celNo = GET_SEL32V(_segMan, objectReference, SELECTOR(cel)); + int16 x = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(x)); + int16 y = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(y)); int16 z = 0; if (_kernel->_selectorCache.z > -1) - z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z)); + z = (int16)GET_SEL32V(_segMan, objectReference, SELECTOR(z)); // now get cel rectangle view = _cache->getView(viewId); view->getCelRect(loopNo, celNo, x, y, z, &celRect); - if (lookupSelector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) { - writeSelectorValue(_segMan, objectReference, SELECTOR(nsLeft), celRect.left); - writeSelectorValue(_segMan, objectReference, SELECTOR(nsRight), celRect.right); - writeSelectorValue(_segMan, objectReference, SELECTOR(nsTop), celRect.top); - writeSelectorValue(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom); + if (lookup_selector(_segMan, objectReference, _kernel->_selectorCache.nsTop, NULL, NULL) == kSelectorVariable) { + PUT_SEL32V(_segMan, objectReference, SELECTOR(nsLeft), celRect.left); + PUT_SEL32V(_segMan, objectReference, SELECTOR(nsRight), celRect.right); + PUT_SEL32V(_segMan, objectReference, SELECTOR(nsTop), celRect.top); + PUT_SEL32V(_segMan, objectReference, SELECTOR(nsBottom), celRect.bottom); } } @@ -147,10 +147,10 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { uint16 signal, controlMask; bool result; - checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); - checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); - checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); - checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); + checkRect.left = GET_SEL32V(_segMan, curObject, SELECTOR(brLeft)); + checkRect.top = GET_SEL32V(_segMan, curObject, SELECTOR(brTop)); + checkRect.right = GET_SEL32V(_segMan, curObject, SELECTOR(brRight)); + checkRect.bottom = GET_SEL32V(_segMan, curObject, SELECTOR(brBottom)); if (!checkRect.isValidRect()) { // can occur in Iceman - HACK? TODO: is this really occuring in sierra sci? check this warning("kCan(t)BeHere - invalid rect %d, %d -> %d, %d", checkRect.left, checkRect.top, checkRect.right, checkRect.bottom); @@ -159,8 +159,8 @@ bool GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { adjustedRect = _coordAdjuster->onControl(checkRect); - signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); - controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); + signal = GET_SEL32V(_segMan, curObject, SELECTOR(signal)); + controlMask = GET_SEL32V(_segMan, curObject, SELECTOR(illegalBits)); result = (isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask) ? false : true; if ((result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) { List *list = _segMan->lookupList(listReference); @@ -183,14 +183,14 @@ bool GfxCompare::kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, } void GfxCompare::kernelBaseSetter(reg_t object) { - if (lookupSelector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) { - int16 x = readSelectorValue(_segMan, object, SELECTOR(x)); - int16 y = readSelectorValue(_segMan, object, SELECTOR(y)); - int16 z = (_kernel->_selectorCache.z > -1) ? readSelectorValue(_segMan, object, SELECTOR(z)) : 0; - int16 yStep = readSelectorValue(_segMan, object, SELECTOR(yStep)); - GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view)); - int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop)); - int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel)); + if (lookup_selector(_segMan, object, _kernel->_selectorCache.brLeft, NULL, NULL) == kSelectorVariable) { + int16 x = GET_SEL32V(_segMan, object, SELECTOR(x)); + int16 y = GET_SEL32V(_segMan, object, SELECTOR(y)); + int16 z = (_kernel->_selectorCache.z > -1) ? GET_SEL32V(_segMan, object, SELECTOR(z)) : 0; + int16 yStep = GET_SEL32V(_segMan, object, SELECTOR(yStep)); + GuiResourceId viewId = GET_SEL32V(_segMan, object, SELECTOR(view)); + int16 loopNo = GET_SEL32V(_segMan, object, SELECTOR(loop)); + int16 celNo = GET_SEL32V(_segMan, object, SELECTOR(cel)); // HACK: Ignore invalid views for now (perhaps unimplemented text views?) if (viewId == 0xFFFF) // invalid view @@ -203,10 +203,10 @@ void GfxCompare::kernelBaseSetter(reg_t object) { celRect.bottom = y + 1; celRect.top = celRect.bottom - yStep; - writeSelectorValue(_segMan, object, SELECTOR(brLeft), celRect.left); - writeSelectorValue(_segMan, object, SELECTOR(brRight), celRect.right); - writeSelectorValue(_segMan, object, SELECTOR(brTop), celRect.top); - writeSelectorValue(_segMan, object, SELECTOR(brBottom), celRect.bottom); + PUT_SEL32V(_segMan, object, SELECTOR(brLeft), celRect.left); + PUT_SEL32V(_segMan, object, SELECTOR(brRight), celRect.right); + PUT_SEL32V(_segMan, object, SELECTOR(brTop), celRect.top); + PUT_SEL32V(_segMan, object, SELECTOR(brBottom), celRect.bottom); } } diff --git a/engines/sci/graphics/controls.cpp b/engines/sci/graphics/controls.cpp index 26af9741c2..f744d6e7f1 100644 --- a/engines/sci/graphics/controls.cpp +++ b/engines/sci/graphics/controls.cpp @@ -143,9 +143,9 @@ void GfxControls::texteditSetBlinkTime() { } void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { - uint16 cursorPos = readSelectorValue(_segMan, controlObject, SELECTOR(cursor)); - uint16 maxChars = readSelectorValue(_segMan, controlObject, SELECTOR(max)); - reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); + uint16 cursorPos = GET_SEL32V(_segMan, controlObject, SELECTOR(cursor)); + uint16 maxChars = GET_SEL32V(_segMan, controlObject, SELECTOR(max)); + reg_t textReference = GET_SEL32(_segMan, controlObject, SELECTOR(text)); Common::String text; uint16 textSize, eventType, eventKey = 0; bool textChanged = false; @@ -158,14 +158,14 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { if (!eventObject.isNull()) { textSize = text.size(); - eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); + eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type)); switch (eventType) { case SCI_EVENT_MOUSE_PRESS: // TODO: Implement mouse support for cursor change break; case SCI_EVENT_KEYBOARD: - eventKey = readSelectorValue(_segMan, eventObject, SELECTOR(message)); + eventKey = GET_SEL32V(_segMan, eventObject, SELECTOR(message)); switch (eventKey) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { @@ -207,9 +207,9 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { if (textChanged) { GuiResourceId oldFontId = _text16->GetFontId(); - GuiResourceId fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); - rect = Common::Rect(readSelectorValue(_segMan, controlObject, SELECTOR(nsLeft)), readSelectorValue(_segMan, controlObject, SELECTOR(nsTop)), - readSelectorValue(_segMan, controlObject, SELECTOR(nsRight)), readSelectorValue(_segMan, controlObject, SELECTOR(nsBottom))); + GuiResourceId fontId = GET_SEL32V(_segMan, controlObject, SELECTOR(font)); + rect = Common::Rect(GET_SEL32V(_segMan, controlObject, SELECTOR(nsLeft)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsTop)), + GET_SEL32V(_segMan, controlObject, SELECTOR(nsRight)), GET_SEL32V(_segMan, controlObject, SELECTOR(nsBottom))); _text16->SetFont(fontId); if (textAddChar) { // We check, if we are really able to add the new char @@ -241,7 +241,7 @@ void GfxControls::kernelTexteditChange(reg_t controlObject, reg_t eventObject) { } } - writeSelectorValue(_segMan, controlObject, SELECTOR(cursor), cursorPos); + PUT_SEL32V(_segMan, controlObject, SELECTOR(cursor), cursorPos); } int GfxControls::getPicNotValid() { diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp index 422df52f27..40ef655be7 100644 --- a/engines/sci/graphics/coordadjuster.cpp +++ b/engines/sci/graphics/coordadjuster.cpp @@ -93,18 +93,18 @@ GfxCoordAdjuster32::~GfxCoordAdjuster32() { } void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) { - //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY)); - //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX)); + //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY)); + //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX)); //*x = ( *x * _screen->getWidth()) / resX; //*y = ( *y * _screen->getHeight()) / resY; - x -= readSelectorValue(_segMan, planeObject, SELECTOR(left)); - y -= readSelectorValue(_segMan, planeObject, SELECTOR(top)); + x -= GET_SEL32V(_segMan, planeObject, SELECTOR(left)); + y -= GET_SEL32V(_segMan, planeObject, SELECTOR(top)); } void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) { - //int16 resY = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resY)); - //int16 resX = readSelectorValue(_s->_segMan, planeObj, SELECTOR(resX)); - x += readSelectorValue(_segMan, planeObject, SELECTOR(left)); - y += readSelectorValue(_segMan, planeObject, SELECTOR(top)); + //int16 resY = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resY)); + //int16 resX = GET_SEL32V(_s->_segMan, planeObj, SELECTOR(resX)); + x += GET_SEL32V(_segMan, planeObject, SELECTOR(left)); + y += GET_SEL32V(_segMan, planeObject, SELECTOR(top)); //*x = ( *x * resX) / _screen->getWidth(); //*y = ( *y * resY) / _screen->getHeight(); } diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 3cc5ca5447..78253bd913 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -54,7 +54,7 @@ GfxFrameout::~GfxFrameout() { void GfxFrameout::kernelAddPlane(reg_t object) { _planes.push_back(object); - int16 planePri = readSelectorValue(_segMan, object, SELECTOR(priority)) & 0xFFFF; + int16 planePri = GET_SEL32V(_segMan, object, SELECTOR(priority)) & 0xFFFF; if (planePri > _highPlanePri) _highPlanePri = planePri; } @@ -74,7 +74,7 @@ void GfxFrameout::kernelDeletePlane(reg_t object) { _highPlanePri = 0; for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) { - int16 planePri = readSelectorValue(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF; + int16 planePri = GET_SEL32V(_segMan, _planes[planeNr], SELECTOR(priority)) & 0xFFFF; if (planePri > _highPlanePri) _highPlanePri = planePri; } @@ -126,29 +126,29 @@ void GfxFrameout::kernelFrameout() { for (uint32 planeNr = 0; planeNr < _planes.size(); planeNr++) { planeObject = _planes[planeNr]; - planePriority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); + planePriority = GET_SEL32V(_segMan, planeObject, SELECTOR(priority)); if (planePriority == -1) // Plane currently not meant to be shown continue; - planeRect.top = readSelectorValue(_segMan, planeObject, SELECTOR(top)); - planeRect.left = readSelectorValue(_segMan, planeObject, SELECTOR(left)); - planeRect.bottom = readSelectorValue(_segMan, planeObject, SELECTOR(bottom)); - planeRect.right = readSelectorValue(_segMan, planeObject, SELECTOR(right)); - planeResY = readSelectorValue(_segMan, planeObject, SELECTOR(resY)); - planeResX = readSelectorValue(_segMan, planeObject, SELECTOR(resX)); + planeRect.top = GET_SEL32V(_segMan, planeObject, SELECTOR(top)); + planeRect.left = GET_SEL32V(_segMan, planeObject, SELECTOR(left)); + planeRect.bottom = GET_SEL32V(_segMan, planeObject, SELECTOR(bottom)); + planeRect.right = GET_SEL32V(_segMan, planeObject, SELECTOR(right)); + planeResY = GET_SEL32V(_segMan, planeObject, SELECTOR(resY)); + planeResX = GET_SEL32V(_segMan, planeObject, SELECTOR(resX)); planeRect.top = (planeRect.top * _screen->getHeight()) / planeResY; planeRect.left = (planeRect.left * _screen->getWidth()) / planeResX; planeRect.bottom = (planeRect.bottom * _screen->getHeight()) / planeResY; planeRect.right = (planeRect.right * _screen->getWidth()) / planeResX; - planeBack = readSelectorValue(_segMan, planeObject, SELECTOR(back)); + planeBack = GET_SEL32V(_segMan, planeObject, SELECTOR(back)); if (planeBack) { _paint32->fillRect(planeRect, planeBack); } - planePictureNr = readSelectorValue(_segMan, planeObject, SELECTOR(picture)); + planePictureNr = GET_SEL32V(_segMan, planeObject, SELECTOR(picture)); if ((planePictureNr != 0xFFFF) && (planePictureNr != 0xFFFE)) { planePicture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, planePictureNr, false); planePictureCels = planePicture->getSci32celCount(); @@ -161,19 +161,19 @@ void GfxFrameout::kernelFrameout() { itemEntry = itemData; for (uint32 itemNr = 0; itemNr < _screenItems.size(); itemNr++) { itemObject = _screenItems[itemNr]; - itemPlane = readSelector(_segMan, itemObject, SELECTOR(plane)); + itemPlane = GET_SEL32(_segMan, itemObject, SELECTOR(plane)); if (planeObject == itemPlane) { // Found an item on current plane - itemEntry->viewId = readSelectorValue(_segMan, itemObject, SELECTOR(view)); - itemEntry->loopNo = readSelectorValue(_segMan, itemObject, SELECTOR(loop)); - itemEntry->celNo = readSelectorValue(_segMan, itemObject, SELECTOR(cel)); - itemEntry->x = readSelectorValue(_segMan, itemObject, SELECTOR(x)); - itemEntry->y = readSelectorValue(_segMan, itemObject, SELECTOR(y)); - itemEntry->z = readSelectorValue(_segMan, itemObject, SELECTOR(z)); - itemEntry->priority = readSelectorValue(_segMan, itemObject, SELECTOR(priority)); - itemEntry->signal = readSelectorValue(_segMan, itemObject, SELECTOR(signal)); - itemEntry->scaleX = readSelectorValue(_segMan, itemObject, SELECTOR(scaleX)); - itemEntry->scaleY = readSelectorValue(_segMan, itemObject, SELECTOR(scaleY)); + itemEntry->viewId = GET_SEL32V(_segMan, itemObject, SELECTOR(view)); + itemEntry->loopNo = GET_SEL32V(_segMan, itemObject, SELECTOR(loop)); + itemEntry->celNo = GET_SEL32V(_segMan, itemObject, SELECTOR(cel)); + itemEntry->x = GET_SEL32V(_segMan, itemObject, SELECTOR(x)); + itemEntry->y = GET_SEL32V(_segMan, itemObject, SELECTOR(y)); + itemEntry->z = GET_SEL32V(_segMan, itemObject, SELECTOR(z)); + itemEntry->priority = GET_SEL32V(_segMan, itemObject, SELECTOR(priority)); + itemEntry->signal = GET_SEL32V(_segMan, itemObject, SELECTOR(signal)); + itemEntry->scaleX = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleX)); + itemEntry->scaleY = GET_SEL32V(_segMan, itemObject, SELECTOR(scaleY)); itemEntry->object = itemObject; itemEntry->y = ((itemEntry->y * _screen->getHeight()) / planeResY); @@ -240,12 +240,12 @@ void GfxFrameout::kernelFrameout() { // This doesn't work for SCI2.1 games... if (getSciVersion() == SCI_VERSION_2) { Kernel *kernel = g_sci->getKernel(); - if (lookupSelector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) { - Common::String text = _segMan->getString(readSelector(_segMan, itemEntry->object, SELECTOR(text))); - int16 fontRes = readSelectorValue(_segMan, itemEntry->object, SELECTOR(font)); + if (lookup_selector(_segMan, itemEntry->object, kernel->_selectorCache.text, NULL, NULL) == kSelectorVariable) { + Common::String text = _segMan->getString(GET_SEL32(_segMan, itemEntry->object, SELECTOR(text))); + int16 fontRes = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(font)); GfxFont *font = new GfxFontFromResource(_resMan, _screen, fontRes); - bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed)); - uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore)); + bool dimmed = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(dimmed)); + uint16 foreColor = GET_SEL32V(_segMan, itemEntry->object, SELECTOR(fore)); uint16 curX = itemEntry->x; uint16 curY = itemEntry->y; for (uint32 i = 0; i < text.size(); i++) { diff --git a/engines/sci/graphics/gui.cpp b/engines/sci/graphics/gui.cpp index e427edd732..46f7fcd689 100644 --- a/engines/sci/graphics/gui.cpp +++ b/engines/sci/graphics/gui.cpp @@ -92,7 +92,7 @@ void SciGui::resetEngineState(EngineState *s) { } void SciGui::init(bool usesOldGfxFunctions) { - _ports->init(usesOldGfxFunctions, this, _paint16, _text16); + _ports->init(usesOldGfxFunctions, this, _paint16, _text16, _s->_gameId); _paint16->init(_animate, _text16); } @@ -136,4 +136,9 @@ void SciGui::portraitShow(Common::String resourceName, Common::Point position, u void SciGui::portraitUnload(uint16 portraitId) { } +bool SciGui::debugEGAdrawingVisualize(bool state) { + _paint16->setEGAdrawingVisualize(state); + return false; +} + } // End of namespace Sci diff --git a/engines/sci/graphics/gui.h b/engines/sci/graphics/gui.h index 7663036117..732e195026 100644 --- a/engines/sci/graphics/gui.h +++ b/engines/sci/graphics/gui.h @@ -61,6 +61,8 @@ public: virtual void portraitShow(Common::String resourceName, Common::Point position, uint16 resourceNum, uint16 noun, uint16 verb, uint16 cond, uint16 seq); virtual void portraitUnload(uint16 portraitId); + virtual bool debugEGAdrawingVisualize(bool state); + // FIXME: Don't store EngineState virtual void resetEngineState(EngineState *s); diff --git a/engines/sci/graphics/maciconbar.cpp b/engines/sci/graphics/maciconbar.cpp deleted file mode 100644 index a06e98ccbf..0000000000 --- a/engines/sci/graphics/maciconbar.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* 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 "sci/sci.h" -#include "sci/engine/selector.h" -#include "sci/engine/state.h" -#include "sci/graphics/maciconbar.h" -#include "sci/graphics/palette.h" - -#include "common/stream.h" -#include "common/system.h" -#include "graphics/pict.h" -#include "graphics/surface.h" - -namespace Sci { - -void GfxMacIconBar::addIcon(reg_t obj) { - _iconBarObjects.push_back(obj); -} - -void GfxMacIconBar::drawIcons() { - // Draw the icons to the bottom of the screen - - byte *pal = new byte[256 * 4]; - Graphics::PictDecoder *pict = new Graphics::PictDecoder(Graphics::PixelFormat::createFormatCLUT8()); - uint32 lastX = 0; - - for (uint32 i = 0; i < _iconBarObjects.size(); i++) { - uint32 iconIndex = readSelectorValue(g_sci->getEngineState()->_segMan, _iconBarObjects[i], SELECTOR(iconIndex)); - Resource *res = g_sci->getResMan()->findResource(ResourceId(kResourceTypeMacIconBarPictN, iconIndex + 1), false); - if (!res) - continue; - - Common::MemoryReadStream *stream = new Common::MemoryReadStream(res->data, res->size); - Graphics::Surface *surf = pict->decodeImage(stream, pal); - remapColors(surf, pal); - - g_system->copyRectToScreen((byte *)surf->pixels, surf->pitch, lastX, 200, MIN<uint32>(surf->w, 320 - lastX), surf->h); - - lastX += surf->w; - surf->free(); - delete surf; - delete stream; - } - - delete pict; - delete[] pal; -} - -void GfxMacIconBar::remapColors(Graphics::Surface *surf, byte *palette) { - byte *pixels = (byte *)surf->pixels; - - // Remap to the screen palette - for (uint16 i = 0; i < surf->w * surf->h; i++) { - byte color = *pixels; - - byte r = palette[color * 4]; - byte g = palette[color * 4 + 1]; - byte b = palette[color * 4 + 2]; - - // For black, make sure the index is 0 - if (r == 0 && g == 0 && b == 0) - *pixels++ = 0; - else - *pixels++ = g_sci->_gfxPalette->kernelFindColor(r, g, b); - } -} - -} // End of namespace Sci diff --git a/engines/sci/graphics/menu.cpp b/engines/sci/graphics/menu.cpp index 880e1aba12..5e3b419fe3 100644 --- a/engines/sci/graphics/menu.cpp +++ b/engines/sci/graphics/menu.cpp @@ -378,7 +378,7 @@ void GfxMenu::calculateMenuAndItemWidth() { } reg_t GfxMenu::kernelSelect(reg_t eventObject) { - int16 eventType = readSelectorValue(_segMan, eventObject, SELECTOR(type)); + int16 eventType = GET_SEL32V(_segMan, eventObject, SELECTOR(type)); int16 keyPress, keyModifier; Common::Point mousePosition; GuiMenuItemList::iterator itemIterator = _itemList.begin(); @@ -390,8 +390,8 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) { switch (eventType) { case SCI_EVENT_KEYBOARD: - keyPress = readSelectorValue(_segMan, eventObject, SELECTOR(message)); - keyModifier = readSelectorValue(_segMan, eventObject, SELECTOR(modifiers)); + keyPress = GET_SEL32V(_segMan, eventObject, SELECTOR(message)); + keyModifier = GET_SEL32V(_segMan, eventObject, SELECTOR(modifiers)); // If tab got pressed, handle it here as if it was Ctrl-I - at least sci0 also did it that way if (keyPress == SCI_KEY_TAB) { keyModifier = SCI_KEYMOD_CTRL; @@ -465,7 +465,7 @@ reg_t GfxMenu::kernelSelect(reg_t eventObject) { _ports->setPort(_oldPort); if ((itemEntry) || (forceClaimed)) - writeSelector(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1)); + PUT_SEL32(_segMan, eventObject, SELECTOR(claimed), make_reg(0, 1)); if (itemEntry) return make_reg(0, (itemEntry->menuId << 8) | (itemEntry->id)); return NULL_REG; diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index ff4f3bec52..d0975f3d3d 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -61,7 +61,7 @@ void GfxPaint16::init(GfxAnimate *animate, GfxText16 *text16) { _EGAdrawingVisualize = false; } -void GfxPaint16::debugSetEGAdrawingVisualize(bool state) { +void GfxPaint16::setEGAdrawingVisualize(bool state) { _EGAdrawingVisualize = state; } @@ -354,8 +354,7 @@ void GfxPaint16::bitsRestore(reg_t memoryHandle) { } void GfxPaint16::bitsFree(reg_t memoryHandle) { - if (!memoryHandle.isNull()) // happens in KQ5CD - _segMan->freeHunkEntry(memoryHandle); + _segMan->freeHunkEntry(memoryHandle); } void GfxPaint16::kernelDrawPicture(GuiResourceId pictureId, int16 animationNr, bool animationBlackoutFlag, bool mirroredFlag, bool addToFlag, int16 EGApaletteNo) { diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h index 65f9dd0d9c..b18c879387 100644 --- a/engines/sci/graphics/paint16.h +++ b/engines/sci/graphics/paint16.h @@ -50,7 +50,7 @@ public: void init(GfxAnimate *animate, GfxText16 *text16); - void debugSetEGAdrawingVisualize(bool state); + void setEGAdrawingVisualize(bool state); void drawPicture(GuiResourceId pictureId, int16 animationNr, bool mirroredFlag, bool addToFlag, GuiResourceId paletteId); void drawCelAndShow(GuiResourceId viewId, int16 loopNo, int16 celNo, uint16 leftPos, uint16 topPos, byte priority, uint16 paletteNo, uint16 scaleX = 128, uint16 scaleY = 128); diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 3c4cf7e964..6fe472f5fa 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -30,9 +30,8 @@ #include "sci/sci.h" #include "sci/engine/state.h" -#include "sci/graphics/maciconbar.h" -#include "sci/graphics/palette.h" #include "sci/graphics/screen.h" +#include "sci/graphics/palette.h" namespace Sci { @@ -312,10 +311,6 @@ void GfxPalette::setOnScreen() { if (_resMan->isAmiga32color()) return; _screen->setPalette(&_sysPalette); - - // Redraw the Mac SCI1.1 Icon bar every palette change - if (g_sci->_gfxMacIconBar) - g_sci->_gfxMacIconBar->drawIcons(); } bool GfxPalette::kernelSetFromResource(GuiResourceId resourceId, bool force) { diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index a59153f116..74f651a88a 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -69,20 +69,15 @@ void GfxPicture::draw(int16 animationNr, bool mirroredFlag, bool addToFlag, int1 headerSize = READ_LE_UINT16(_resource->data); switch (headerSize) { case 0x26: // SCI 1.1 VGA picture - _resourceType = SCI_PICTURE_TYPE_SCI11; - if (_addToFlag) - _priority = 15; drawSci11Vga(); break; #ifdef ENABLE_SCI32 case 0x0e: // SCI32 VGA picture - _resourceType = SCI_PICTURE_TYPE_SCI32; drawSci32Vga(); break; #endif default: // VGA, EGA or Amiga vector data - _resourceType = SCI_PICTURE_TYPE_REGULAR; drawVectorData(_resource->data, _resource->size); } } @@ -113,8 +108,9 @@ void GfxPicture::drawSci11Vga() { _palette->set(&palette, true); // display Cel-data - if (has_cel) - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0); + if (has_cel) { + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, 0, 0, false); + } // process vector data drawVectorData(inbuffer + vector_dataPos, vector_size); @@ -143,7 +139,6 @@ void GfxPicture::drawSci32Vga(int16 celNo) { // HACK _mirroredFlag = false; _addToFlag = false; - _resourceType = SCI_PICTURE_TYPE_SCI32; if ((celNo == -1) || (celNo == 0)) { // Create palette and set it @@ -160,14 +155,14 @@ void GfxPicture::drawSci32Vga(int16 celNo) { cel_LiteralPos = READ_LE_UINT32(inbuffer + cel_headerPos + 28); cel_relXpos = READ_LE_UINT16(inbuffer + cel_headerPos + 38); cel_relYpos = READ_LE_UINT16(inbuffer + cel_headerPos + 40); - drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos); + drawCelData(inbuffer, size, cel_headerPos, cel_RlePos, cel_LiteralPos, cel_relXpos, cel_relYpos, true); cel_headerPos += 42; celCount--; } } #endif -void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY) { +void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header) { byte *celBitmap = NULL; byte *ptr = NULL; byte *headerPtr = inbuffer + headerPos; @@ -184,16 +179,11 @@ void GfxPicture::drawCelData(byte *inbuffer, int size, int headerPos, int rlePos int pixelNr, pixelCount; #ifdef ENABLE_SCI32 - if (_resourceType != SCI_PICTURE_TYPE_SCI32) { + if (!hasSci32Header) { #endif displaceX = (signed char)headerPtr[4]; displaceY = (unsigned char)headerPtr[5]; - if (_resourceType == SCI_PICTURE_TYPE_SCI11) { - // SCI1.1 uses hardcoded clearcolor for pictures, even if cel header specifies otherwise - clearColor = _screen->getColorWhite(); - } else { - clearColor = headerPtr[6]; - } + clearColor = headerPtr[6]; #ifdef ENABLE_SCI32 } else { displaceX = READ_LE_UINT16(headerPtr + 4); // probably signed?!? @@ -528,29 +518,10 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; - // Pattern opcodes are handled in sierra sci1.1+ as actual NOPs and normally they definitely should not occur - // inside picture data for such games case PIC_OP_SET_PATTERN: - if (_resourceType >= SCI_PICTURE_TYPE_SCI11) { - if (strcmp(g_sci->getGameID(), "sq4") == 0) { - // WORKAROUND: For SQ4 / for some pictures handle this like a terminator - // This picture includes garbage data, first a set pattern w/o parameter and then short pattern - // I guess that garbage is a left over from the sq4-floppy (sci1) to sq4-cd (sci1.1) conversion - switch (_resourceId) { - case 381: - case 376: - return; - default: - break; - } - } - error("pic-operation set pattern inside sci1.1+ vector data"); - } pattern_Code = data[curPos++]; break; case PIC_OP_SHORT_PATTERNS: - if (_resourceType >= SCI_PICTURE_TYPE_SCI11) - error("pic-operation short pattern inside sci1.1+ vector data"); vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture); vectorGetAbsCoords(data, curPos, x, y); vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture); @@ -561,8 +532,6 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; case PIC_OP_MEDIUM_PATTERNS: - if (_resourceType >= SCI_PICTURE_TYPE_SCI11) - error("pic-operation medium pattern inside sci1.1+ vector data"); vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture); vectorGetAbsCoords(data, curPos, x, y); vectorPattern(x, y, pic_color, pic_priority, pic_control, pattern_Code, pattern_Texture); @@ -573,8 +542,6 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { } break; case PIC_OP_ABSOLUTE_PATTERN: - if (_resourceType >= SCI_PICTURE_TYPE_SCI11) - error("pic-operation absolute pattern inside sci1.1+ vector data"); while (vectorIsNonOpcode(data[curPos])) { vectorGetPatternTexture(data, curPos, pattern_Code, pattern_Texture); vectorGetAbsCoords(data, curPos, x, y); @@ -618,7 +585,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { vectorGetAbsCoordsNoMirror(data, curPos, x, y); size = READ_LE_UINT16(data + curPos); curPos += 2; _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false); curPos += size; break; case PIC_OPX_EGA_SET_PRIORITY_TABLE: @@ -660,7 +627,7 @@ void GfxPicture::drawVectorData(byte *data, int dataSize) { _priority = pic_priority; // set global priority so the cel gets drawn using current priority as well if (pic_priority == 255) _priority = 0; // if priority not set, use priority 0 - drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y); + drawCelData(data, _resource->size, curPos, curPos + 8, 0, x, y, false); curPos += size; break; case PIC_OPX_VGA_PRIORITY_TABLE_EQDIST: diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h index 5a86539b37..3374c33b52 100644 --- a/engines/sci/graphics/picture.h +++ b/engines/sci/graphics/picture.h @@ -32,12 +32,6 @@ namespace Sci { #define SCI_PATTERN_CODE_USE_TEXTURE 0x20 #define SCI_PATTERN_CODE_PENSIZE 0x07 -enum { - SCI_PICTURE_TYPE_REGULAR = 0, - SCI_PICTURE_TYPE_SCI11 = 1, - SCI_PICTURE_TYPE_SCI32 = 2 -}; - class GfxPorts; class GfxScreen; class GfxPalette; @@ -63,7 +57,7 @@ private: void initData(GuiResourceId resourceId); void reset(); void drawSci11Vga(); - void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY); + void drawCelData(byte *inbuffer, int size, int headerPos, int rlePos, int literalPos, int16 callerX, int16 callerY, bool hasSci32Header); void drawVectorData(byte *data, int size); bool vectorIsNonOpcode(byte pixel); void vectorGetAbsCoords(byte *data, int &curPos, int16 &x, int16 &y); @@ -86,7 +80,6 @@ private: int16 _resourceId; Resource *_resource; - int _resourceType; int16 _animationNr; bool _mirroredFlag; diff --git a/engines/sci/graphics/ports.cpp b/engines/sci/graphics/ports.cpp index cdb6fe4ae1..ab3291dd79 100644 --- a/engines/sci/graphics/ports.cpp +++ b/engines/sci/graphics/ports.cpp @@ -54,7 +54,7 @@ GfxPorts::~GfxPorts() { delete _menuPort; } -void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16) { +void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16, Common::String gameId) { int16 offTop = 10; _usesOldGfxFunctions = usesOldGfxFunctions; @@ -88,7 +88,6 @@ void GfxPorts::init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, // Jones, Slater and Hoyle 3 were called with parameter -Nw 0 0 200 320. // Mother Goose (SCI1) uses -Nw 0 0 159 262. The game will later use SetPort so we don't need to set the other fields. // This actually meant not skipping the first 10 pixellines in windowMgrPort - Common::String gameId = g_sci->getGameID(); if (gameId == "jones" || gameId == "slater" || gameId == "hoyle3" || (gameId == "mothergoose" && getSciVersion() == SCI_VERSION_1_EARLY)) offTop = 0; diff --git a/engines/sci/graphics/ports.h b/engines/sci/graphics/ports.h index c8ce6b3470..0876d9e442 100644 --- a/engines/sci/graphics/ports.h +++ b/engines/sci/graphics/ports.h @@ -45,7 +45,7 @@ public: GfxPorts(SegManager *segMan, GfxScreen *screen); ~GfxPorts(); - void init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16); + void init(bool usesOldGfxFunctions, SciGui *gui, GfxPaint16 *paint16, GfxText16 *text16, Common::String gameId); void kernelSetActive(uint16 portId); Common::Rect kernelGetPicWindow(int16 &picTop, int16 &picLeft); diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 0e054d5a76..7ca9e33509 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -93,18 +93,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan, int16 width, int16 height, int ups } // Initialize the actual screen - - if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) { - // For SCI1.1 Mac, we need to expand the screen to accommodate for - // the icon bar. Of course, both KQ6 and QFG1 VGA differ in size. - if (!scumm_stricmp(g_sci->getGameID(), "kq6")) - initGraphics(_displayWidth, _displayHeight + 26, _displayWidth > 320); - else if (!scumm_stricmp(g_sci->getGameID(), "qfg1")) - initGraphics(_displayWidth, _displayHeight + 20, _displayWidth > 320); - else - error("Unknown SCI1.1 Mac game"); - } else - initGraphics(_displayWidth, _displayHeight, _displayWidth > 320); + initGraphics(_displayWidth, _displayHeight, _displayWidth > 320); } GfxScreen::~GfxScreen() { diff --git a/engines/sci/module.mk b/engines/sci/module.mk index a2cfd38f95..8c4d666ba7 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -6,7 +6,6 @@ MODULE_OBJS := \ detection.o \ event.o \ resource.o \ - resource_audio.o \ sci.o \ util.o \ engine/features.o \ @@ -45,7 +44,6 @@ MODULE_OBJS := \ graphics/font.o \ graphics/fontsjis.o \ graphics/gui.o \ - graphics/maciconbar.o \ graphics/menu.o \ graphics/paint.o \ graphics/paint16.o \ diff --git a/engines/sci/parser/grammar.cpp b/engines/sci/parser/grammar.cpp index 070e6767cf..1cfe84076f 100644 --- a/engines/sci/parser/grammar.cpp +++ b/engines/sci/parser/grammar.cpp @@ -258,11 +258,6 @@ void Vocabulary::freeRuleList(ParseRuleList *list) { static ParseRuleList *_vocab_add_rule(ParseRuleList *list, ParseRule *rule) { if (!rule) return list; - if (!rule->_data.size()) { - // Special case for qfg2 demo - warning("no rule contents on _vocab_add_rule()"); - return list; - } ParseRuleList *new_elem = new ParseRuleList(rule); diff --git a/engines/sci/parser/said.cpp b/engines/sci/parser/said.cpp index f49704372a..5cd1310ad3 100644 --- a/engines/sci/parser/said.cpp +++ b/engines/sci/parser/said.cpp @@ -2443,14 +2443,13 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai int said(EngineState *s, byte *spec, bool verbose) { int retval; - Vocabulary *voc = g_sci->getVocabulary(); - parse_tree_node_t *parse_tree_ptr = voc->_parserNodes; + parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes; - if (voc->parserIsValid) { + if (s->_voc->parserIsValid) { if (said_parse_spec(spec)) { printf("Offending spec was: "); - voc->decipherSaidBlock(spec); + s->_voc->decipherSaidBlock(spec); return SAID_NO_MATCH; } diff --git a/engines/sci/parser/said.y b/engines/sci/parser/said.y index cbb2ff3e62..27486c5794 100644 --- a/engines/sci/parser/said.y +++ b/engines/sci/parser/said.y @@ -799,14 +799,13 @@ static int augment_parse_nodes(parse_tree_node_t *parset, parse_tree_node_t *sai int said(EngineState *s, byte *spec, bool verbose) { int retval; - Vocabulary *voc = g_sci->getVocabulary(); - parse_tree_node_t *parse_tree_ptr = voc->_parserNodes; + parse_tree_node_t *parse_tree_ptr = s->_voc->_parserNodes; - if (voc->parserIsValid) { + if (s->_voc->parserIsValid) { if (said_parse_spec(spec)) { printf("Offending spec was: "); - voc->decipherSaidBlock(spec); + s->_voc->decipherSaidBlock(spec); return SAID_NO_MATCH; } diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 449effd737..4888dbd4cb 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -26,6 +26,7 @@ // Resource library #include "common/file.h" +#include "common/macresman.h" #include "sci/resource.h" #include "sci/util.h" @@ -44,6 +45,18 @@ struct resource_index_t { uint16 wSize; }; +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; + uint32 audioCompressionType; + int32 *audioCompressionOffsetMapping; + Common::MacResManager macResMan; +}; + ////////////////////////////////////////////////////////////////////// static SciVersion s_sciVersion = SCI_VERSION_NONE; // FIXME: Move this inside a suitable class, e.g. SciEngine @@ -183,7 +196,7 @@ ResourceSource *ResourceManager::addExternalMap(const char *file_name, int volum return newsrc; } -ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, int volume_nr) { +ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile) { ResourceSource *newsrc = new ResourceSource(); newsrc->source_type = kSourceExtMap; @@ -191,7 +204,7 @@ ResourceSource *ResourceManager::addExternalMap(const Common::FSNode *mapFile, i newsrc->resourceFile = mapFile; newsrc->scanned = false; newsrc->associated_map = NULL; - newsrc->volume_number = volume_nr; + newsrc->volume_number = 0; _sources.push_back(newsrc); return newsrc; @@ -237,7 +250,6 @@ ResourceSource *ResourceManager::addPatchDir(const char *dirname) { ResourceSource *newsrc = new ResourceSource(); newsrc->source_type = kSourceDirectory; - newsrc->resourceFile = 0; newsrc->scanned = false; newsrc->location_name = dirname; @@ -258,7 +270,37 @@ ResourceSource *ResourceManager::getVolume(ResourceSource *map, int volume_nr) { // Resource manager constructors and operations -bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *file) { +void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) { + Common::File *file = getVolumeFile(source->location_name.c_str()); + if (!file) { + warning("Failed to open %s", source->location_name.c_str()); + return; + } + file->seek(0, SEEK_SET); + uint32 compressionType = file->readUint32BE(); + switch (compressionType) { + case MKID_BE('MP3 '): + case MKID_BE('OGG '): + case MKID_BE('FLAC'): + // Detected a compressed audio volume + source->audioCompressionType = compressionType; + // Now read the whole offset mapping table for later usage + int32 recordCount = file->readUint32LE(); + if (!recordCount) + error("compressed audio volume doesn't contain any entries!"); + int32 *offsetMapping = new int32[(recordCount + 1) * 2]; + source->audioCompressionOffsetMapping = offsetMapping; + for (int recordNo = 0; recordNo < recordCount; recordNo++) { + *offsetMapping++ = file->readUint32LE(); + *offsetMapping++ = file->readUint32LE(); + } + // Put ending zero + *offsetMapping++ = 0; + *offsetMapping++ = file->size(); + } +} + +bool ResourceManager::loadPatch(Resource *res, Common::File &file) { // We assume that the resource type matches res->type // We also assume that the current file position is right at the actual data (behind resourceid/headersize byte) @@ -273,12 +315,12 @@ bool ResourceManager::loadPatch(Resource *res, Common::SeekableReadStream *file) unsigned int really_read; if (res->_headerSize > 0) { - really_read = file->read(res->_header, res->_headerSize); + really_read = file.read(res->_header, res->_headerSize); if (really_read != res->_headerSize) error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->_headerSize); } - really_read = file->read(res->data, res->size); + really_read = file.read(res->data, res->size); if (really_read != res->size) error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); @@ -296,7 +338,71 @@ bool ResourceManager::loadFromPatchFile(Resource *res) { } // Skip resourceid and header size byte file.seek(2, SEEK_SET); - return loadPatch(res, &file); + return loadPatch(res, file); +} + +bool ResourceManager::loadFromWaveFile(Resource *res, Common::File &file) { + res->data = new byte[res->size]; + + uint32 really_read = file.read(res->data, res->size); + if (really_read != res->size) + error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); + + res->_status = kResStatusAllocated; + return true; +} + +bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::File &file) { + // Check for WAVE files here + uint32 riffTag = file.readUint32BE(); + if (riffTag == MKID_BE('RIFF')) { + res->_headerSize = 0; + res->size = file.readUint32LE(); + file.seek(-8, SEEK_CUR); + return loadFromWaveFile(res, file); + } + file.seek(-4, SEEK_CUR); + + ResourceType type = (ResourceType)(file.readByte() & 0x7f); + if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio)) + || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) { + warning("Resource type mismatch loading %s from %s", res->_id.toString().c_str(), file.getName()); + res->unalloc(); + return false; + } + + res->_headerSize = file.readByte(); + + if (type == kResourceTypeAudio) { + if (res->_headerSize != 11 && res->_headerSize != 12) { + warning("Unsupported audio header"); + res->unalloc(); + return false; + } + + // Load sample size + file.seek(7, SEEK_CUR); + res->size = file.readUint32LE(); + // Adjust offset to point at the header data again + file.seek(-11, SEEK_CUR); + } + + return loadPatch(res, file); +} + +bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::File &file) { + res->data = new byte[res->size]; + + if (res->data == NULL) { + error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str()); + } + + unsigned int really_read = file.read(res->data, res->size); + if (really_read != res->size) + warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); + + res->_status = kResStatusAllocated; + return true; } Common::File *ResourceManager::getVolumeFile(const char *filename) { @@ -339,10 +445,11 @@ void ResourceManager::loadResource(Resource *res) { return; if (res->_source->source_type == kSourceMacResourceFork) { + //error("ResourceManager::loadResource(): TODO: Mac resource fork ;)"); Common::SeekableReadStream *stream = res->_source->macResMan.getResource(resTypeToMacTag(res->_id.type), res->_id.number); if (!stream) - error("Could not get Mac resource fork resource: %d %d", res->_id.type, res->_id.number); + error("Could not get Mac resource fork resource"); int error = decompress(res, stream); if (error) { @@ -353,15 +460,10 @@ void ResourceManager::loadResource(Resource *res) { return; } - Common::SeekableReadStream *fileStream; - + Common::File *file; // Either loading from volume or patch loading failed - if (res->_source->resourceFile) - fileStream = res->_source->resourceFile->createReadStream(); - else - fileStream = getVolumeFile(res->_source->location_name.c_str()); - - if (!fileStream) { + file = getVolumeFile(res->_source->location_name.c_str()); + if (!file) { warning("Failed to open %s", res->_source->location_name.c_str()); res->unalloc(); return; @@ -369,8 +471,8 @@ void ResourceManager::loadResource(Resource *res) { switch(res->_source->source_type) { case kSourceWave: - fileStream->seek(res->_fileOffset, SEEK_SET); - loadFromWaveFile(res, fileStream); + file->seek(res->_fileOffset, SEEK_SET); + loadFromWaveFile(res, *file); return; case kSourceAudioVolume: @@ -401,30 +503,30 @@ void ResourceManager::loadResource(Resource *res) { if (!compressedOffset) error("could not translate offset to compressed offset in audio volume"); - fileStream->seek(compressedOffset, SEEK_SET); + file->seek(compressedOffset, SEEK_SET); switch (res->_id.type) { case kResourceTypeAudio: case kResourceTypeAudio36: // Directly read the stream, compressed audio wont have resource type id and header size for SCI1.1 - loadFromAudioVolumeSCI1(res, fileStream); + loadFromAudioVolumeSCI1(res, *file); return; default: break; } } else { // original file, directly seek to given offset and get SCI1/SCI1.1 audio resource - fileStream->seek(res->_fileOffset, SEEK_SET); + file->seek(res->_fileOffset, SEEK_SET); } if (getSciVersion() < SCI_VERSION_1_1) - loadFromAudioVolumeSCI1(res, fileStream); + loadFromAudioVolumeSCI1(res, *file); else - loadFromAudioVolumeSCI11(res, fileStream); + loadFromAudioVolumeSCI11(res, *file); return; default: - fileStream->seek(res->_fileOffset, SEEK_SET); - int error = decompress(res, fileStream); + file->seek(res->_fileOffset, SEEK_SET); + int error = decompress(res, file); if (error) { warning("Error %d occured while reading %s from resource file: %s", error, res->_id.toString().c_str(), sci_error_types[error]); @@ -453,11 +555,11 @@ int sci0_get_compression_method(Common::ReadStream &stream) { int ResourceManager::addAppropriateSources() { Common::ArchiveMemberList files; - if (Common::File::exists("resource.map")) { + if (Common::File::exists("RESOURCE.MAP")) { // SCI0-SCI2 file naming scheme - ResourceSource *map = addExternalMap("resource.map"); + ResourceSource *map = addExternalMap("RESOURCE.MAP"); - SearchMan.listMatchingMembers(files, "resource.0??"); + SearchMan.listMatchingMembers(files, "RESOURCE.0??"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { const Common::String name = (*x)->getName(); @@ -468,12 +570,12 @@ int ResourceManager::addAppropriateSources() { } #ifdef ENABLE_SCI32 // GK1CD hires content - if (Common::File::exists("alt.map") && Common::File::exists("resource.alt")) - addSource(addExternalMap("alt.map", 10), kSourceVolume, "resource.alt", 10); + if (Common::File::exists("ALT.MAP") && Common::File::exists("RESOURCE.ALT")) + addSource(addExternalMap("ALT.MAP", 10), kSourceVolume, "RESOURCE.ALT", 10); #endif } else if (Common::File::exists("Data1")) { // Mac SCI1.1+ file naming scheme - SearchMan.listMatchingMembers(files, "Data?*"); + SearchMan.listMatchingMembers(files, "Data?"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { Common::String filename = (*x)->getName(); @@ -492,8 +594,8 @@ int ResourceManager::addAppropriateSources() { } else { // SCI2.1-SCI3 file naming scheme Common::ArchiveMemberList mapFiles; - SearchMan.listMatchingMembers(mapFiles, "resmap.0??"); - SearchMan.listMatchingMembers(files, "ressci.0??"); + SearchMan.listMatchingMembers(mapFiles, "RESMAP.0??"); + SearchMan.listMatchingMembers(files, "RESSCI.0??"); // We need to have the same number of maps as resource archives if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size()) @@ -515,9 +617,9 @@ int ResourceManager::addAppropriateSources() { } // SCI2.1 resource patches - if (Common::File::exists("resmap.pat") && Common::File::exists("ressci.pat")) { + if (Common::File::exists("RESMAP.PAT") && Common::File::exists("RESSCI.PAT")) { // We add this resource with a map which surely won't exist - addSource(addExternalMap("resmap.pat", 100), kSourceVolume, "ressci.pat", 100); + addSource(addExternalMap("RESMAP.PAT", 100), kSourceVolume, "RESSCI.PAT", 100); } } #else @@ -526,18 +628,14 @@ int ResourceManager::addAppropriateSources() { #endif addPatchDir("."); - if (Common::File::exists("message.map")) - addSource(addExternalMap("message.map"), kSourceVolume, "resource.msg", 0); + if (Common::File::exists("MESSAGE.MAP")) + addSource(addExternalMap("MESSAGE.MAP"), kSourceVolume, "RESOURCE.MSG", 0); return 1; } int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { ResourceSource *map = 0; -#ifdef ENABLE_SCI32 - ResourceSource *sci21PatchMap = 0; - const Common::FSNode *sci21PatchRes = 0; -#endif // First, find resource.map for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { @@ -547,33 +645,17 @@ int ResourceManager::addAppropriateSources(const Common::FSList &fslist) { Common::String filename = file->getName(); filename.toLowercase(); - if (filename.contains("resource.map")) + // TODO: Load the SCI2.1+ maps (resmap.*) in concurrence with the volumes to + // get the proper volume numbers from the maps. + if (filename.contains("resource.map") || filename.contains("resmap.000")) { map = addExternalMap(file); - - if (filename.contains("resmap.0")) { - const char *dot = strrchr(file->getName().c_str(), '.'); - int number = atoi(dot + 1); - map = addExternalMap(file, number); + break; } - -#ifdef ENABLE_SCI32 - // SCI2.1 resource patches - if (filename.contains("resmap.pat")) - sci21PatchMap = addExternalMap(file, 100); - - if (filename.contains("ressci.pat")) - sci21PatchRes = file; -#endif } if (!map) return 0; -#ifdef ENABLE_SCI32 - if (sci21PatchMap && sci21PatchRes) - addSource(sci21PatchMap, kSourceVolume, sci21PatchRes, 100); -#endif - // Now find all the resource.0?? files for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) { if (file->isDirectory()) @@ -614,6 +696,36 @@ int ResourceManager::addInternalSources() { return 1; } +void ResourceManager::addNewGMPatch(const Common::String &gameId) { + Common::String gmPatchFile; + + if (gameId == "ecoquest") + gmPatchFile = "ECO1GM.PAT"; + else if (gameId == "hoyle3") + gmPatchFile = "HOY3GM.PAT"; + else if (gameId == "hoyle3") + gmPatchFile = "HOY3GM.PAT"; + else if (gameId == "lsl1sci") + gmPatchFile = "LL1_GM.PAT"; + else if (gameId == "lsl5") + gmPatchFile = "LL5_GM.PAT"; + else if (gameId == "longbow") + gmPatchFile = "ROBNGM.PAT"; + else if (gameId == "sq1sci") + gmPatchFile = "SQ1_GM.PAT"; + else if (gameId == "sq4") + gmPatchFile = "SQ4_GM.PAT"; + else if (gameId == "fairytales") + gmPatchFile = "TALEGM.PAT"; + + if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) { + ResourceSource *psrcPatch = new ResourceSource; + psrcPatch->source_type = kSourcePatch; + psrcPatch->location_name = gmPatchFile; + processPatch(psrcPatch, kResourceTypePatch, 4); + } +} + void ResourceManager::scanNewSources() { for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { ResourceSource *source = *it; @@ -878,6 +990,7 @@ const char *ResourceManager::versionDescription(ResVersion version) const { ResourceManager::ResVersion ResourceManager::detectMapVersion() { Common::SeekableReadStream *fileStream = 0; + Common::File *file = 0; byte buff[6]; ResourceSource *rsrc= 0; @@ -888,7 +1001,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { if (rsrc->resourceFile) { fileStream = rsrc->resourceFile->createReadStream(); } else { - Common::File *file = new Common::File(); + file = new Common::File(); file->open(rsrc->location_name); if (file->isOpen()) fileStream = file; @@ -968,6 +1081,7 @@ ResourceManager::ResVersion ResourceManager::detectMapVersion() { ResourceManager::ResVersion ResourceManager::detectVolVersion() { Common::SeekableReadStream *fileStream = 0; + Common::File *file = 0; ResourceSource *rsrc; for (Common::List<ResourceSource *>::iterator it = _sources.begin(); it != _sources.end(); ++it) { @@ -977,7 +1091,7 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { if (rsrc->resourceFile) { fileStream = rsrc->resourceFile->createReadStream(); } else { - Common::File *file = new Common::File(); + file = new Common::File(); file->open(rsrc->location_name); if (file->isOpen()) fileStream = file; @@ -1067,7 +1181,7 @@ ResourceManager::ResVersion ResourceManager::detectVolVersion() { // version-agnostic patch application void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, int resnumber) { - Common::SeekableReadStream *fileStream = 0; + Common::File file; Resource *newrsc; ResourceId resId = ResourceId(restype, resnumber); byte patchtype, patch_data_offset; @@ -1075,27 +1189,18 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, if (resnumber == -1) return; - - if (source->resourceFile) { - fileStream = source->resourceFile->createReadStream(); - } else { - Common::File *file = new Common::File(); - if (!file->open(source->location_name)) { - warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str()); - return; - } - fileStream = file; + if (!file.open(source->location_name)) { + warning("ResourceManager::processPatch(): failed to open %s", source->location_name.c_str()); + return; } - fsize = fileStream->size(); + fsize = file.size(); if (fsize < 3) { debug("Patching %s failed - file too small", source->location_name.c_str()); return; } - patchtype = fileStream->readByte() & 0x7F; - patch_data_offset = fileStream->readByte(); - - delete fileStream; + patchtype = file.readByte() & 0x7F; + patch_data_offset = file.readByte(); if (patchtype != restype) { debug("Patching %s failed - resource type mismatch", source->location_name.c_str()); @@ -1110,12 +1215,8 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, case 1: patch_data_offset = 2; break; - case 4: - patch_data_offset = 8; - break; default: - warning("Resource patch unsupported special case %X", patch_data_offset & 0x7F); - return; + warning("Resource patch unsupported special case %X", patch_data_offset); } } @@ -1142,17 +1243,9 @@ void ResourceManager::processPatch(ResourceSource *source, ResourceType restype, void ResourceManager::readResourcePatches(ResourceSource *source) { - // Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files - // this function tries to read patch file with any supported naming scheme, - // regardless of s_sciVersion value - - // Note that audio36 and sync36 use a different naming scheme, because they cannot be described - // with a single resource number, but are a result of a <number, noun, verb, cond, seq> tuple. - // Please don't be confused with the normal audio patches (*.aud) and normal sync patches (*.syn). - // audio36 patches can be seen for example in the AUD folder of GK1CD, and are like this file: - // @0CS0M00.0X1. GK1CD is the first game where these have been observed. The actual audio36 and - // sync36 resources exist in SCI1.1 as well, but the first game where external patch files for - // them have been found is GK1CD +// Note: since some SCI1 games(KQ5 floppy, SQ4) might use SCI0 naming scheme for patch files +// this function tries to read patch file with any supported naming scheme, +// regardless of s_sciVersion value Common::String mask, name; Common::ArchiveMemberList files; @@ -1162,7 +1255,6 @@ void ResourceManager::readResourcePatches(ResourceSource *source) { for (int i = kResourceTypeView; i <= kResourceTypeRobot; ++i) { // TODO: add support for audio36 and sync36 files - // Such patches were introduced in SCI2, and didn't exist in SCI0-SCI1.1 if (i == kResourceTypeAudio36 || i == kResourceTypeSync36) continue; @@ -1197,13 +1289,55 @@ void ResourceManager::readResourcePatches(ResourceSource *source) { psrcPatch = new ResourceSource; psrcPatch->source_type = kSourcePatch; psrcPatch->location_name = name; - psrcPatch->resourceFile = 0; processPatch(psrcPatch, (ResourceType)i, number); } } } } +void ResourceManager::readWaveAudioPatches() { + // Here we do check for SCI1.1+ so we can patch wav files in as audio resources + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, "*.wav"); + + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { + Common::String name = (*x)->getName(); + + if (isdigit(name[0])) { + int number = atoi(name.c_str()); + ResourceSource *psrcPatch = new ResourceSource; + psrcPatch->source_type = kSourceWave; + psrcPatch->location_name = name; + psrcPatch->volume_number = 0; + psrcPatch->audioCompressionType = 0; + + ResourceId resId = ResourceId(kResourceTypeAudio, number); + + Resource *newrsc = NULL; + + // Prepare destination, if neccessary + if (_resMap.contains(resId) == false) { + newrsc = new Resource; + _resMap.setVal(resId, newrsc); + } else + newrsc = _resMap.getVal(resId); + + // Get the size of the file + Common::SeekableReadStream *stream = (*x)->createReadStream(); + uint32 fileSize = stream->size(); + delete stream; + + // Overwrite everything, because we're patching + newrsc->_id = resId; + newrsc->_status = kResStatusNoMalloc; + newrsc->_source = psrcPatch; + newrsc->size = fileSize; + newrsc->_headerSize = 0; + debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str()); + } + } +} + int ResourceManager::readResourceMapSCI0(ResourceSource *map) { Common::File file; Resource *res; @@ -1314,7 +1448,7 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { res->_id = resId; // NOTE: We add the map's volume number here to the specified volume number - // for SCI2.1 and SCI3 maps that are not resmap.000. The resmap.* files' numbers + // for SCI2.1 and SCI3 maps that are not RESMAP.000. The RESMAP.* files' numbers // need to be used in concurrence with the volume specified in the map to get // the actual resource file. res->_source = getVolume(map, volume_nr + map->volume_number); @@ -1342,11 +1476,7 @@ struct { { MKID_BE('PAL '), kResourceTypePalette }, { MKID_BE('snd '), kResourceTypeAudio }, { MKID_BE('MSG '), kResourceTypeMessage }, - { MKID_BE('HEP '), kResourceTypeHeap }, - { MKID_BE('IBIN'), kResourceTypeMacIconBarPictN }, - { MKID_BE('IBIS'), kResourceTypeMacIconBarPictS }, - { MKID_BE('PICT'), kResourceTypeMacPict }, - { MKID_BE('SYN '), kResourceTypeSync } + { MKID_BE('HEP '), kResourceTypeHeap } }; static uint32 resTypeToMacTag(ResourceType type) { @@ -1379,16 +1509,6 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) { Common::MacResIDArray idArray = source->macResMan.getResIDArray(tagArray[i]); for (uint32 j = 0; j < idArray.size(); j++) { - // Get the size of the file - Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]); - - // Some IBIS resources have a size of 0, so we skip them - if (!stream) - continue; - - uint32 fileSize = stream->size(); - delete stream; - ResourceId resId = ResourceId(type, idArray[j]); Resource *newrsc = NULL; @@ -1400,6 +1520,11 @@ int ResourceManager::readMacResourceFork(ResourceSource *source) { } else newrsc = _resMap.getVal(resId); + // Get the size of the file + Common::SeekableReadStream *stream = source->macResMan.getResource(tagArray[i], idArray[j]); + uint32 fileSize = stream->size(); + delete stream; + // Overwrite everything newrsc->_id = resId; newrsc->_status = kResStatusNoMalloc; @@ -1424,6 +1549,261 @@ void ResourceManager::addResource(ResourceId resId, ResourceSource *src, uint32 } } +void ResourceManager::removeAudioResource(ResourceId resId) { + // Remove resource, unless it was loaded from a patch + if (_resMap.contains(resId)) { + Resource *res = _resMap.getVal(resId); + + if (res->_source->source_type == kSourceAudioVolume) { + if (res->_status == kResStatusLocked) { + warning("Failed to remove resource %s (still in use)", resId.toString().c_str()); + } else { + if (res->_status == kResStatusEnqueued) + removeFromLRU(res); + + _resMap.erase(resId); + delete res; + } + } + } +} + +// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD): +// ========= +// 6-byte entries: +// w nEntry +// dw offset + +// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX): +// ========= +// 5-byte entries: +// w nEntry +// tb offset (cumulative) + +// Early SCI1.1 MAP structure: +// =============== +// 10-byte entries: +// b noun +// b verb +// b cond +// b seq +// dw offset +// w syncSize + syncAscSize + +// Late SCI1.1 MAP structure: +// =============== +// Header: +// dw baseOffset +// Followed by 7 or 11-byte entries: +// b noun +// b verb +// b cond +// b seq +// tb cOffset (cumulative offset) +// w syncSize (iff seq has bit 7 set) +// w syncAscSize (iff seq has bit 6 set) + +int ResourceManager::readAudioMapSCI11(ResourceSource *map) { + bool isEarly = true; + uint32 offset = 0; + Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false); + + if (!mapRes) { + warning("Failed to open %i.MAP", map->volume_number); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + ResourceSource *src = getVolume(map, 0); + + if (!src) + return SCI_ERROR_NO_RESOURCE_FILES_FOUND; + + byte *ptr = mapRes->data; + + if (map->volume_number == 65535) { + // Heuristic to detect late SCI1.1 map format + if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff)) + isEarly = false; + + while (ptr < mapRes->data + mapRes->size) { + uint16 n = READ_LE_UINT16(ptr); + ptr += 2; + + if (n == 0xffff) + break; + + if (isEarly) { + offset = READ_LE_UINT32(ptr); + ptr += 4; + } else { + offset += READ_LE_UINT24(ptr); + ptr += 3; + } + + addResource(ResourceId(kResourceTypeAudio, n), src, offset); + } + } else { + // Heuristic to detect late SCI1.1 map format + if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff)) + isEarly = false; + + if (!isEarly) { + offset = READ_LE_UINT32(ptr); + ptr += 4; + } + + while (ptr < mapRes->data + mapRes->size) { + uint32 n = READ_BE_UINT32(ptr); + int syncSize = 0; + ptr += 4; + + if (n == 0xffffffff) + break; + + if (isEarly) { + offset = READ_LE_UINT32(ptr); + ptr += 4; + } else { + offset += READ_LE_UINT24(ptr); + ptr += 3; + } + + if (isEarly || (n & 0x80)) { + syncSize = READ_LE_UINT16(ptr); + ptr += 2; + + if (syncSize > 0) + addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize); + } + + if (n & 0x40) { + syncSize += READ_LE_UINT16(ptr); + ptr += 2; + } + + addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize); + } + } + + return 0; +} + +// AUDIOnnn.MAP contains 10-byte entries: +// Early format: +// w 5 bits resource type and 11 bits resource number +// dw 7 bits volume number and 25 bits offset +// dw size +// Later format: +// w nEntry +// dw offset+volume (as in resource.map) +// dw size +// ending with 10 0xFFs +int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) { + Common::File file; + + if (!file.open(map->location_name)) + return SCI_ERROR_RESMAP_NOT_FOUND; + + bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio; + file.seek(0); + + while (1) { + uint16 n = file.readUint16LE(); + uint32 offset = file.readUint32LE(); + uint32 size = file.readUint32LE(); + + if (file.eos() || file.err()) { + warning("Error while reading %s", map->location_name.c_str()); + return SCI_ERROR_RESMAP_NOT_FOUND; + } + + if (n == 0xffff) + break; + + byte volume_nr; + + if (oldFormat) { + n &= 0x07ff; // Mask out resource type + volume_nr = offset >> 25; // most significant 7 bits + offset &= 0x01ffffff; // least significant 25 bits + } else { + volume_nr = offset >> 28; // most significant 4 bits + offset &= 0x0fffffff; // least significant 28 bits + } + + ResourceSource *src = getVolume(map, volume_nr); + + if (src) { + if (unload) + removeAudioResource(ResourceId(kResourceTypeAudio, n)); + else + addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); + } else { + warning("Failed to find audio volume %i", volume_nr); + } + } + + return 0; +} + +void ResourceManager::setAudioLanguage(int language) { + if (_audioMapSCI1) { + if (_audioMapSCI1->volume_number == language) { + // This language is already loaded + return; + } + + // We already have a map loaded, so we unload it first + readAudioMapSCI1(_audioMapSCI1, true); + + // Remove all volumes that use this map from the source list + Common::List<ResourceSource *>::iterator it = _sources.begin(); + while (it != _sources.end()) { + ResourceSource *src = *it; + if (src->associated_map == _audioMapSCI1) { + it = _sources.erase(it); + delete src; + } else { + ++it; + } + } + + // Remove the map itself from the source list + _sources.remove(_audioMapSCI1); + delete _audioMapSCI1; + + _audioMapSCI1 = NULL; + } + + char filename[9]; + snprintf(filename, 9, "AUDIO%03d", language); + + Common::String fullname = Common::String(filename) + ".MAP"; + if (!Common::File::exists(fullname)) { + warning("No audio map found for language %i", language); + return; + } + + _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language); + + // Search for audio volumes for this language and add them to the source list + Common::ArchiveMemberList files; + SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??"); + for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { + const Common::String name = (*x)->getName(); + const char *dot = strrchr(name.c_str(), '.'); + int number = atoi(dot + 1); + + addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number); + } + + scanNewSources(); +} + +int ResourceManager::getAudioLanguage() const { + return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0); +} + int ResourceManager::readResourceInfo(Resource *res, Common::SeekableReadStream *file, uint32&szPacked, ResourceCompression &compression) { // SCI0 volume format: {wResId wPacked+4 wUnpacked wCompression} = 8 bytes @@ -1949,98 +2329,248 @@ bool ResourceManager::hasSci1Voc900() { return offset == res->size; } -// Same function as Script::findBlock(). Slight code -// duplication here, but this has been done to keep the resource -// manager independent from the rest of the engine -static byte *findSci0ExportsBlock(byte *buffer) { - byte *buf = buffer; - bool oldScriptHeader = (getSciVersion() == SCI_VERSION_0_EARLY); +SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) { + Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true); + int trackNr, channelNr; + if (!resource) + return; - if (oldScriptHeader) - buf += 2; + _innerResource = resource; - do { - int seekerType = READ_LE_UINT16(buf); + byte *data, *data2; + byte *dataEnd; + Channel *channel, *sampleChannel; - if (seekerType == 0) - break; - if (seekerType == 7) // exports - return buf; + switch (_soundVersion) { + case SCI_VERSION_0_EARLY: + case SCI_VERSION_0_LATE: + // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards + _trackCount = 1; + _tracks = new Track[_trackCount]; + _tracks->digitalChannelNr = -1; + _tracks->type = 0; // Not used for SCI0 + _tracks->channelCount = 1; + // Digital sample data included? -> Add an additional channel + if (resource->data[0] == 2) + _tracks->channelCount++; + _tracks->channels = new Channel[_tracks->channelCount]; + memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount); + channel = &_tracks->channels[0]; + if (_soundVersion == SCI_VERSION_0_EARLY) { + channel->data = resource->data + 0x11; + channel->size = resource->size - 0x11; + } else { + channel->data = resource->data + 0x21; + channel->size = resource->size - 0x21; + } + if (_tracks->channelCount == 2) { + // Digital sample data included + _tracks->digitalChannelNr = 1; + sampleChannel = &_tracks->channels[1]; + // we need to find 0xFC (channel terminator) within the data + data = channel->data; + dataEnd = channel->data + channel->size; + while ((data < dataEnd) && (*data != 0xfc)) + data++; + // Skip any following 0xFCs as well + while ((data < dataEnd) && (*data == 0xfc)) + data++; + // Now adjust channels accordingly + sampleChannel->data = data; + sampleChannel->size = channel->size - (data - channel->data); + channel->size = data - channel->data; + // Read sample header information + //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer. + _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14); + _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32); + _tracks->digitalSampleStart = 0; + _tracks->digitalSampleEnd = 0; + sampleChannel->data += 44; // Skip over header + sampleChannel->size -= 44; + } + break; - int seekerSize = READ_LE_UINT16(buf + 2); - assert(seekerSize > 0); - buf += seekerSize; - } while (1); + case SCI_VERSION_1_EARLY: + case SCI_VERSION_1_LATE: + data = resource->data; + // Count # of tracks + _trackCount = 0; + while ((*data++) != 0xFF) { + _trackCount++; + while (*data != 0xFF) + data += 6; + data++; + } + _tracks = new Track[_trackCount]; + data = resource->data; + for (trackNr = 0; trackNr < _trackCount; trackNr++) { + // Track info starts with track type:BYTE + // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD + // 0xFF:BYTE as terminator to end that track and begin with another track type + // Track type 0xFF is the marker signifying the end of the tracks + + _tracks[trackNr].type = *data++; + // Counting # of channels used + data2 = data; + _tracks[trackNr].channelCount = 0; + while (*data2 != 0xFF) { + data2 += 6; + _tracks[trackNr].channelCount++; + } + _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount]; + _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated + _tracks[trackNr].digitalSampleRate = 0; + _tracks[trackNr].digitalSampleSize = 0; + _tracks[trackNr].digitalSampleStart = 0; + _tracks[trackNr].digitalSampleEnd = 0; + if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently + for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) { + channel = &_tracks[trackNr].channels[channelNr]; + channel->prio = READ_LE_UINT16(data); + channel->data = resource->data + READ_LE_UINT16(data + 2) + 2; + channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header + channel->number = *(channel->data - 2); + channel->poly = *(channel->data - 1); + channel->time = channel->prev = 0; + if (channel->number == 0xFE) { // Digital channel + _tracks[trackNr].digitalChannelNr = channelNr; + _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data); + _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2); + _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4); + _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6); + channel->data += 8; // Skip over header + channel->size -= 8; + } + data += 6; + } + } else { + // Skip over digital track + data += 6; + } + data++; // Skipping 0xFF that closes channels list + } + break; - return NULL; + default: + error("SoundResource: SCI version %d is unsupported", _soundVersion); + } } -reg_t ResourceManager::findGameObject(bool addSci11ScriptOffset) { - Resource *script = findResource(ResourceId(kResourceTypeScript, 0), false); - - if (!script) - return NULL_REG; +SoundResource::~SoundResource() { + for (int trackNr = 0; trackNr < _trackCount; trackNr++) + delete[] _tracks[trackNr].channels; + delete[] _tracks; - byte *offsetPtr = 0; + _resMan->unlockResource(_innerResource); +} - if (getSciVersion() < SCI_VERSION_1_1) { - byte *buf = (getSciVersion() == SCI_VERSION_0_EARLY) ? script->data + 2 : script->data; +#if 0 +SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) { + if (_soundVersion <= SCI_VERSION_0_LATE) + return &_tracks[0]; - // Check if the first block is the exports block (in most cases, it is) - bool exportsIsFirst = (READ_LE_UINT16(buf + 4) == 7); - if (exportsIsFirst) { - offsetPtr = buf + 4 + 2; - } else { - offsetPtr = findSci0ExportsBlock(script->data); - if (!offsetPtr) - error("Unable to find exports block from script 0"); - offsetPtr += 4 + 2; - } - } else { - offsetPtr = script->data + 4 + 2 + 2; - } - - int16 offset = !isSci11Mac() ? READ_LE_UINT16(offsetPtr) : READ_BE_UINT16(offsetPtr); + if (/*number >= 0 &&*/number < _trackCount) + return &_tracks[number]; + return NULL; +} +#endif - // In SCI1.1 and newer, the heap is appended at the end of the script, - // so adjust the offset accordingly - if (getSciVersion() >= SCI_VERSION_1_1 && addSci11ScriptOffset) { - offset += script->size; +SoundResource::Track *SoundResource::getTrackByType(byte type) { + if (_soundVersion <= SCI_VERSION_0_LATE) + return &_tracks[0]; - // Ensure that the start of the heap is word-aligned - same as in Script::init() - if (script->size & 2) - offset++; + for (int trackNr = 0; trackNr < _trackCount; trackNr++) { + if (_tracks[trackNr].type == type) + return &_tracks[trackNr]; } + return NULL; +} - return make_reg(1, offset); +SoundResource::Track *SoundResource::getDigitalTrack() { + for (int trackNr = 0; trackNr < _trackCount; trackNr++) { + if (_tracks[trackNr].digitalChannelNr != -1) + return &_tracks[trackNr]; + } + return NULL; } -Common::String ResourceManager::findSierraGameId() { - // In SCI0-SCI1, the heap is embedded in the script. In SCI1.1+, it's separated - Resource *heap = 0; - int nameSelector = 3; +// Gets the filter mask for SCI0 sound resources +int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) { + byte *data = _innerResource->data; + int channelMask = 0; - if (getSciVersion() < SCI_VERSION_1_1) { - heap = findResource(ResourceId(kResourceTypeScript, 0), false); - } else { - heap = findResource(ResourceId(kResourceTypeHeap, 0), false); - nameSelector += 5; + if (_soundVersion > SCI_VERSION_0_LATE) + return 0; + + data++; // Skip over digital sample flag + + for (int channelNr = 0; channelNr < 16; channelNr++) { + channelMask = channelMask >> 1; + + byte flags; + + if (_soundVersion == SCI_VERSION_0_EARLY) { + // Each channel is specified by a single byte + // Upper 4 bits of the byte is a voices count + // Lower 4 bits -> bit 0 set: use for AdLib + // bit 1 set: use for PCjr + // bit 2 set: use for PC speaker + // bit 3 set and bit 0 clear: control channel (15) + // bit 3 set and bit 0 set: rhythm channel (9) + // Note: control channel is dynamically assigned inside the drivers, + // but seems to be fixed at 15 in the song data. + flags = *data++; + + // Get device bits + flags &= 0x7; + } else { + // Each channel is specified by 2 bytes + // 1st byte is voices count + // 2nd byte is play mask, which specifies if the channel is supposed to be played + // by the corresponding hardware + + // Skip voice count + data++; + + flags = *data++; + } + + bool play; + switch (channelNr) { + case 15: + // Always play control channel + play = true; + break; + case 9: + // Play rhythm channel when requested + play = wantsRhythm; + break; + default: + // Otherwise check for flag + play = flags & hardwareMask; + } + + if (play) { + // This Channel is supposed to be played by the hardware + channelMask |= 0x8000; + } } - if (!heap) - return ""; + return channelMask; +} - int16 gameObjectOffset = findGameObject(false).offset; +byte SoundResource::getInitialVoiceCount(byte channel) { + byte *data = _innerResource->data; - if (!gameObjectOffset) - return ""; + if (_soundVersion > SCI_VERSION_0_LATE) + return 0; // TODO - // Seek to the name selector of the first export - byte *seeker = heap->data + READ_UINT16(heap->data + gameObjectOffset + nameSelector * 2); - Common::String sierraId; - sierraId += (const char *)seeker; + data++; // Skip over digital sample flag - return sierraId; + if (_soundVersion == SCI_VERSION_0_EARLY) + return data[channel] >> 4; + else + return data[channel * 2]; } } // End of namespace Sci diff --git a/engines/sci/resource.h b/engines/sci/resource.h index bc08154fed..48b5f095b1 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -26,9 +26,8 @@ #ifndef SCI_SCICORE_RESOURCE_H #define SCI_SCICORE_RESOURCE_H -#include "common/fs.h" -#include "common/macresman.h" #include "common/str.h" +#include "common/fs.h" #include "sci/graphics/helpers.h" // for ViewType #include "sci/decompressor.h" @@ -109,31 +108,14 @@ enum ResourceType { kResourceTypeUnknown1, // Translation, currently unsupported kResourceTypeUnknown2, kResourceTypeRobot, - kResourceTypeInvalid, - - // Mac-only resources, these resource types are self-defined - // Numbers subject to change - kResourceTypeMacIconBarPictN = -1, // IBIN resources (icon bar, not selected) - kResourceTypeMacIconBarPictS = -2, // IBIS resources (icon bar, selected) - kResourceTypeMacPict = -3 // PICT resources (inventory) + kResourceTypeInvalid }; const char *getResourceTypeName(ResourceType restype); class ResourceManager; - -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; - uint32 audioCompressionType; - int32 *audioCompressionOffsetMapping; - Common::MacResManager macResMan; -}; +struct ResourceSource; class ResourceId { public: @@ -145,7 +127,7 @@ public: ResourceId(ResourceType type_, uint16 number_, uint32 tuple_ = 0) : type(type_), number(number_), tuple(tuple_) { - if (type < kResourceTypeMacPict || type > kResourceTypeInvalid) + if ((type < kResourceTypeView) || (type > kResourceTypeInvalid)) type = kResourceTypeInvalid; } @@ -291,19 +273,6 @@ public: // Detects, if standard font of current game includes extended characters (>0x80) bool detectFontExtended(); - /** - * Finds the internal Sierra ID of the current game from script 0 - */ - Common::String findSierraGameId(); - - /** - * Finds the location of the game object from script 0 - * @param addSci11ScriptOffset: Adjust the return value for SCI1.1 and newer - * games. Needs to be false when the heap is accessed directly inside - * findSierraGameId(). - */ - reg_t findGameObject(bool addSci11ScriptOffset = true); - 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 @@ -321,8 +290,8 @@ protected: ResourceMap _resMap; Common::List<Common::File *> _volumeFiles; ///< list of opened volume files ResourceSource *_audioMapSCI1; ///< Currently loaded audio map for SCI1 - ResVersion _volVersion; ///< resource.0xx version - ResVersion _mapVersion; ///< resource.map version + ResVersion _volVersion; ///< RESOURCE.0xx version + ResVersion _mapVersion; ///< RESOURCE.MAP version /** * Initializes the resource manager @@ -358,7 +327,7 @@ protected: */ ResourceSource *addExternalMap(const char *file_name, int volume_nr = 0); - ResourceSource *addExternalMap(const Common::FSNode *mapFile, int volume_nr = 0); + ResourceSource *addExternalMap(const Common::FSNode *mapFile); /** * Add an internal (i.e., resource) map to the resource manager's list of sources. @@ -395,11 +364,11 @@ protected: Common::File *getVolumeFile(const char *filename); void loadResource(Resource *res); - bool loadPatch(Resource *res, Common::SeekableReadStream *file); + bool loadPatch(Resource *res, Common::File &file); bool loadFromPatchFile(Resource *res); - bool loadFromWaveFile(Resource *res, Common::SeekableReadStream *file); - bool loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file); - bool loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file); + bool loadFromWaveFile(Resource *res, Common::File &file); + bool loadFromAudioVolumeSCI1(Resource *res, Common::File &file); + bool loadFromAudioVolumeSCI11(Resource *res, Common::File &file); void freeOldResources(); int decompress(Resource *res, Common::SeekableReadStream *file); int readResourceInfo(Resource *res, Common::SeekableReadStream *file, uint32&szPacked, ResourceCompression &compression); @@ -512,7 +481,6 @@ public: Track *getDigitalTrack(); int getChannelFilterMask(int hardwareMask, bool wantsRhythm); byte getInitialVoiceCount(byte channel); - bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } private: SciVersion _soundVersion; @@ -520,9 +488,6 @@ private: Track *_tracks; Resource *_innerResource; ResourceManager *_resMan; - uint16 _channelsUsed; - - void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } }; } // End of namespace Sci diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp deleted file mode 100644 index 67bac974fc..0000000000 --- a/engines/sci/resource_audio.cpp +++ /dev/null @@ -1,706 +0,0 @@ -/* 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$ - * - */ - -// Resource library - -#include "common/file.h" - -#include "sci/resource.h" -#include "sci/util.h" - -namespace Sci { - -void ResourceManager::checkIfAudioVolumeIsCompressed(ResourceSource *source) { - Common::File *file = getVolumeFile(source->location_name.c_str()); - if (!file) { - warning("Failed to open %s", source->location_name.c_str()); - return; - } - file->seek(0, SEEK_SET); - uint32 compressionType = file->readUint32BE(); - switch (compressionType) { - case MKID_BE('MP3 '): - case MKID_BE('OGG '): - case MKID_BE('FLAC'): - // Detected a compressed audio volume - source->audioCompressionType = compressionType; - // Now read the whole offset mapping table for later usage - int32 recordCount = file->readUint32LE(); - if (!recordCount) - error("compressed audio volume doesn't contain any entries!"); - int32 *offsetMapping = new int32[(recordCount + 1) * 2]; - source->audioCompressionOffsetMapping = offsetMapping; - for (int recordNo = 0; recordNo < recordCount; recordNo++) { - *offsetMapping++ = file->readUint32LE(); - *offsetMapping++ = file->readUint32LE(); - } - // Put ending zero - *offsetMapping++ = 0; - *offsetMapping++ = file->size(); - } -} - -bool ResourceManager::loadFromWaveFile(Resource *res, Common::SeekableReadStream *file) { - res->data = new byte[res->size]; - - uint32 really_read = file->read(res->data, res->size); - if (really_read != res->size) - error("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); - - res->_status = kResStatusAllocated; - return true; -} - -bool ResourceManager::loadFromAudioVolumeSCI11(Resource *res, Common::SeekableReadStream *file) { - // Check for WAVE files here - uint32 riffTag = file->readUint32BE(); - if (riffTag == MKID_BE('RIFF')) { - res->_headerSize = 0; - res->size = file->readUint32LE(); - file->seek(-8, SEEK_CUR); - return loadFromWaveFile(res, file); - } - file->seek(-4, SEEK_CUR); - - ResourceType type = (ResourceType)(file->readByte() & 0x7f); - if (((res->_id.type == kResourceTypeAudio || res->_id.type == kResourceTypeAudio36) && (type != kResourceTypeAudio)) - || ((res->_id.type == kResourceTypeSync || res->_id.type == kResourceTypeSync36) && (type != kResourceTypeSync))) { - warning("Resource type mismatch loading %s", res->_id.toString().c_str()); - res->unalloc(); - return false; - } - - res->_headerSize = file->readByte(); - - if (type == kResourceTypeAudio) { - if (res->_headerSize != 11 && res->_headerSize != 12) { - warning("Unsupported audio header"); - res->unalloc(); - return false; - } - - // Load sample size - file->seek(7, SEEK_CUR); - res->size = file->readUint32LE(); - // Adjust offset to point at the header data again - file->seek(-11, SEEK_CUR); - } - - return loadPatch(res, file); -} - -bool ResourceManager::loadFromAudioVolumeSCI1(Resource *res, Common::SeekableReadStream *file) { - res->data = new byte[res->size]; - - if (res->data == NULL) { - error("Can't allocate %d bytes needed for loading %s", res->size, res->_id.toString().c_str()); - } - - unsigned int really_read = file->read(res->data, res->size); - if (really_read != res->size) - warning("Read %d bytes from %s but expected %d", really_read, res->_id.toString().c_str(), res->size); - - res->_status = kResStatusAllocated; - return true; -} - -void ResourceManager::addNewGMPatch(const Common::String &gameId) { - Common::String gmPatchFile; - - if (gameId == "ecoquest") - gmPatchFile = "ECO1GM.PAT"; - else if (gameId == "hoyle3") - gmPatchFile = "HOY3GM.PAT"; - else if (gameId == "hoyle3") - gmPatchFile = "HOY3GM.PAT"; - else if (gameId == "lsl1sci") - gmPatchFile = "LL1_GM.PAT"; - else if (gameId == "lsl5") - gmPatchFile = "LL5_GM.PAT"; - else if (gameId == "longbow") - gmPatchFile = "ROBNGM.PAT"; - else if (gameId == "sq1sci") - gmPatchFile = "SQ1_GM.PAT"; - else if (gameId == "sq4") - gmPatchFile = "SQ4_GM.PAT"; - else if (gameId == "fairytales") - gmPatchFile = "TALEGM.PAT"; - - if (!gmPatchFile.empty() && Common::File::exists(gmPatchFile)) { - ResourceSource *psrcPatch = new ResourceSource; - psrcPatch->source_type = kSourcePatch; - psrcPatch->resourceFile = 0; - psrcPatch->location_name = gmPatchFile; - processPatch(psrcPatch, kResourceTypePatch, 4); - } -} - -void ResourceManager::readWaveAudioPatches() { - // Here we do check for SCI1.1+ so we can patch wav files in as audio resources - Common::ArchiveMemberList files; - SearchMan.listMatchingMembers(files, "*.wav"); - - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - Common::String name = (*x)->getName(); - - if (isdigit(name[0])) { - int number = atoi(name.c_str()); - ResourceSource *psrcPatch = new ResourceSource; - psrcPatch->source_type = kSourceWave; - psrcPatch->resourceFile = 0; - psrcPatch->location_name = name; - psrcPatch->volume_number = 0; - psrcPatch->audioCompressionType = 0; - - ResourceId resId = ResourceId(kResourceTypeAudio, number); - - Resource *newrsc = NULL; - - // Prepare destination, if neccessary - if (_resMap.contains(resId) == false) { - newrsc = new Resource; - _resMap.setVal(resId, newrsc); - } else - newrsc = _resMap.getVal(resId); - - // Get the size of the file - Common::SeekableReadStream *stream = (*x)->createReadStream(); - uint32 fileSize = stream->size(); - delete stream; - - // Overwrite everything, because we're patching - newrsc->_id = resId; - newrsc->_status = kResStatusNoMalloc; - newrsc->_source = psrcPatch; - newrsc->size = fileSize; - newrsc->_headerSize = 0; - debugC(1, kDebugLevelResMan, "Patching %s - OK", psrcPatch->location_name.c_str()); - } - } -} - -void ResourceManager::removeAudioResource(ResourceId resId) { - // Remove resource, unless it was loaded from a patch - if (_resMap.contains(resId)) { - Resource *res = _resMap.getVal(resId); - - if (res->_source->source_type == kSourceAudioVolume) { - if (res->_status == kResStatusLocked) { - warning("Failed to remove resource %s (still in use)", resId.toString().c_str()); - } else { - if (res->_status == kResStatusEnqueued) - removeFromLRU(res); - - _resMap.erase(resId); - delete res; - } - } - } -} - -// Early SCI1.1 65535.MAP structure (uses RESOURCE.AUD): -// ========= -// 6-byte entries: -// w nEntry -// dw offset - -// Late SCI1.1 65535.MAP structure (uses RESOURCE.SFX): -// ========= -// 5-byte entries: -// w nEntry -// tb offset (cumulative) - -// Early SCI1.1 MAP structure: -// =============== -// 10-byte entries: -// b noun -// b verb -// b cond -// b seq -// dw offset -// w syncSize + syncAscSize - -// Late SCI1.1 MAP structure: -// =============== -// Header: -// dw baseOffset -// Followed by 7 or 11-byte entries: -// b noun -// b verb -// b cond -// b seq -// tb cOffset (cumulative offset) -// w syncSize (iff seq has bit 7 set) -// w syncAscSize (iff seq has bit 6 set) - -int ResourceManager::readAudioMapSCI11(ResourceSource *map) { - bool isEarly = true; - uint32 offset = 0; - Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->volume_number), false); - - if (!mapRes) { - warning("Failed to open %i.MAP", map->volume_number); - return SCI_ERROR_RESMAP_NOT_FOUND; - } - - ResourceSource *src = getVolume(map, 0); - - if (!src) - return SCI_ERROR_NO_RESOURCE_FILES_FOUND; - - byte *ptr = mapRes->data; - - if (map->volume_number == 65535) { - // Heuristic to detect late SCI1.1 map format - if ((mapRes->size >= 6) && (ptr[mapRes->size - 6] != 0xff)) - isEarly = false; - - while (ptr < mapRes->data + mapRes->size) { - uint16 n = READ_LE_UINT16(ptr); - ptr += 2; - - if (n == 0xffff) - break; - - if (isEarly) { - offset = READ_LE_UINT32(ptr); - ptr += 4; - } else { - offset += READ_LE_UINT24(ptr); - ptr += 3; - } - - addResource(ResourceId(kResourceTypeAudio, n), src, offset); - } - } else { - // Heuristic to detect late SCI1.1 map format - if ((mapRes->size >= 11) && (ptr[mapRes->size - 11] == 0xff)) - isEarly = false; - - if (!isEarly) { - offset = READ_LE_UINT32(ptr); - ptr += 4; - } - - while (ptr < mapRes->data + mapRes->size) { - uint32 n = READ_BE_UINT32(ptr); - int syncSize = 0; - ptr += 4; - - if (n == 0xffffffff) - break; - - if (isEarly) { - offset = READ_LE_UINT32(ptr); - ptr += 4; - } else { - offset += READ_LE_UINT24(ptr); - ptr += 3; - } - - if (isEarly || (n & 0x80)) { - syncSize = READ_LE_UINT16(ptr); - ptr += 2; - - if (syncSize > 0) - addResource(ResourceId(kResourceTypeSync36, map->volume_number, n & 0xffffff3f), src, offset, syncSize); - } - - if (n & 0x40) { - syncSize += READ_LE_UINT16(ptr); - ptr += 2; - } - - addResource(ResourceId(kResourceTypeAudio36, map->volume_number, n & 0xffffff3f), src, offset + syncSize); - } - } - - return 0; -} - -// AUDIOnnn.MAP contains 10-byte entries: -// Early format: -// w 5 bits resource type and 11 bits resource number -// dw 7 bits volume number and 25 bits offset -// dw size -// Later format: -// w nEntry -// dw offset+volume (as in resource.map) -// dw size -// ending with 10 0xFFs -int ResourceManager::readAudioMapSCI1(ResourceSource *map, bool unload) { - Common::File file; - - if (!file.open(map->location_name)) - return SCI_ERROR_RESMAP_NOT_FOUND; - - bool oldFormat = (file.readUint16LE() >> 11) == kResourceTypeAudio; - file.seek(0); - - while (1) { - uint16 n = file.readUint16LE(); - uint32 offset = file.readUint32LE(); - uint32 size = file.readUint32LE(); - - if (file.eos() || file.err()) { - warning("Error while reading %s", map->location_name.c_str()); - return SCI_ERROR_RESMAP_NOT_FOUND; - } - - if (n == 0xffff) - break; - - byte volume_nr; - - if (oldFormat) { - n &= 0x07ff; // Mask out resource type - volume_nr = offset >> 25; // most significant 7 bits - offset &= 0x01ffffff; // least significant 25 bits - } else { - volume_nr = offset >> 28; // most significant 4 bits - offset &= 0x0fffffff; // least significant 28 bits - } - - ResourceSource *src = getVolume(map, volume_nr); - - if (src) { - if (unload) - removeAudioResource(ResourceId(kResourceTypeAudio, n)); - else - addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); - } else { - warning("Failed to find audio volume %i", volume_nr); - } - } - - return 0; -} - -void ResourceManager::setAudioLanguage(int language) { - if (_audioMapSCI1) { - if (_audioMapSCI1->volume_number == language) { - // This language is already loaded - return; - } - - // We already have a map loaded, so we unload it first - readAudioMapSCI1(_audioMapSCI1, true); - - // Remove all volumes that use this map from the source list - Common::List<ResourceSource *>::iterator it = _sources.begin(); - while (it != _sources.end()) { - ResourceSource *src = *it; - if (src->associated_map == _audioMapSCI1) { - it = _sources.erase(it); - delete src; - } else { - ++it; - } - } - - // Remove the map itself from the source list - _sources.remove(_audioMapSCI1); - delete _audioMapSCI1; - - _audioMapSCI1 = NULL; - } - - char filename[9]; - snprintf(filename, 9, "AUDIO%03d", language); - - Common::String fullname = Common::String(filename) + ".MAP"; - if (!Common::File::exists(fullname)) { - warning("No audio map found for language %i", language); - return; - } - - _audioMapSCI1 = addSource(NULL, kSourceExtAudioMap, fullname.c_str(), language); - - // Search for audio volumes for this language and add them to the source list - Common::ArchiveMemberList files; - SearchMan.listMatchingMembers(files, Common::String(filename) + ".0??"); - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - const Common::String name = (*x)->getName(); - const char *dot = strrchr(name.c_str(), '.'); - int number = atoi(dot + 1); - - addSource(_audioMapSCI1, kSourceAudioVolume, name.c_str(), number); - } - - scanNewSources(); -} - -int ResourceManager::getAudioLanguage() const { - return (_audioMapSCI1 ? _audioMapSCI1->volume_number : 0); -} - -SoundResource::SoundResource(uint32 resNumber, ResourceManager *resMan, SciVersion soundVersion) : _resMan(resMan), _soundVersion(soundVersion) { - Resource *resource = _resMan->findResource(ResourceId(kResourceTypeSound, resNumber), true); - int trackNr, channelNr; - if (!resource) - return; - - _innerResource = resource; - - byte *data, *data2; - byte *dataEnd; - Channel *channel, *sampleChannel; - - _channelsUsed = 0; - - switch (_soundVersion) { - case SCI_VERSION_0_EARLY: - case SCI_VERSION_0_LATE: - // SCI0 only has a header of 0x11/0x21 byte length and the actual midi track follows afterwards - _trackCount = 1; - _tracks = new Track[_trackCount]; - _tracks->digitalChannelNr = -1; - _tracks->type = 0; // Not used for SCI0 - _tracks->channelCount = 1; - // Digital sample data included? -> Add an additional channel - if (resource->data[0] == 2) - _tracks->channelCount++; - _tracks->channels = new Channel[_tracks->channelCount]; - memset(_tracks->channels, 0, sizeof(Channel) * _tracks->channelCount); - channel = &_tracks->channels[0]; - if (_soundVersion == SCI_VERSION_0_EARLY) { - channel->data = resource->data + 0x11; - channel->size = resource->size - 0x11; - } else { - channel->data = resource->data + 0x21; - channel->size = resource->size - 0x21; - } - if (_tracks->channelCount == 2) { - // Digital sample data included - _tracks->digitalChannelNr = 1; - sampleChannel = &_tracks->channels[1]; - // we need to find 0xFC (channel terminator) within the data - data = channel->data; - dataEnd = channel->data + channel->size; - while ((data < dataEnd) && (*data != 0xfc)) - data++; - // Skip any following 0xFCs as well - while ((data < dataEnd) && (*data == 0xfc)) - data++; - // Now adjust channels accordingly - sampleChannel->data = data; - sampleChannel->size = channel->size - (data - channel->data); - channel->size = data - channel->data; - // Read sample header information - //Offset 14 in the header contains the frequency as a short integer. Offset 32 contains the sample length, also as a short integer. - _tracks->digitalSampleRate = READ_LE_UINT16(sampleChannel->data + 14); - _tracks->digitalSampleSize = READ_LE_UINT16(sampleChannel->data + 32); - _tracks->digitalSampleStart = 0; - _tracks->digitalSampleEnd = 0; - sampleChannel->data += 44; // Skip over header - sampleChannel->size -= 44; - } - break; - - case SCI_VERSION_1_EARLY: - case SCI_VERSION_1_LATE: - data = resource->data; - // Count # of tracks - _trackCount = 0; - while ((*data++) != 0xFF) { - _trackCount++; - while (*data != 0xFF) - data += 6; - data++; - } - _tracks = new Track[_trackCount]; - data = resource->data; - for (trackNr = 0; trackNr < _trackCount; trackNr++) { - // Track info starts with track type:BYTE - // Then the channel information gets appended Unknown:WORD, ChannelOffset:WORD, ChannelSize:WORD - // 0xFF:BYTE as terminator to end that track and begin with another track type - // Track type 0xFF is the marker signifying the end of the tracks - - _tracks[trackNr].type = *data++; - // Counting # of channels used - data2 = data; - _tracks[trackNr].channelCount = 0; - while (*data2 != 0xFF) { - data2 += 6; - _tracks[trackNr].channelCount++; - } - _tracks[trackNr].channels = new Channel[_tracks[trackNr].channelCount]; - _tracks[trackNr].digitalChannelNr = -1; // No digital sound associated - _tracks[trackNr].digitalSampleRate = 0; - _tracks[trackNr].digitalSampleSize = 0; - _tracks[trackNr].digitalSampleStart = 0; - _tracks[trackNr].digitalSampleEnd = 0; - if (_tracks[trackNr].type != 0xF0) { // Digital track marker - not supported currently - for (channelNr = 0; channelNr < _tracks[trackNr].channelCount; channelNr++) { - channel = &_tracks[trackNr].channels[channelNr]; - channel->prio = READ_LE_UINT16(data); - channel->data = resource->data + READ_LE_UINT16(data + 2) + 2; - channel->size = READ_LE_UINT16(data + 4) - 2; // Not counting channel header - channel->number = *(channel->data - 2); - setChannelUsed(channel->number); - channel->poly = *(channel->data - 1); - channel->time = channel->prev = 0; - if (channel->number == 0xFE) { // Digital channel - _tracks[trackNr].digitalChannelNr = channelNr; - _tracks[trackNr].digitalSampleRate = READ_LE_UINT16(channel->data); - _tracks[trackNr].digitalSampleSize = READ_LE_UINT16(channel->data + 2); - _tracks[trackNr].digitalSampleStart = READ_LE_UINT16(channel->data + 4); - _tracks[trackNr].digitalSampleEnd = READ_LE_UINT16(channel->data + 6); - channel->data += 8; // Skip over header - channel->size -= 8; - } - data += 6; - } - } else { - // Skip over digital track - data += 6; - } - data++; // Skipping 0xFF that closes channels list - } - break; - - default: - error("SoundResource: SCI version %d is unsupported", _soundVersion); - } -} - -SoundResource::~SoundResource() { - for (int trackNr = 0; trackNr < _trackCount; trackNr++) - delete[] _tracks[trackNr].channels; - delete[] _tracks; - - _resMan->unlockResource(_innerResource); -} - -#if 0 -SoundResource::Track* SoundResource::getTrackByNumber(uint16 number) { - if (_soundVersion <= SCI_VERSION_0_LATE) - return &_tracks[0]; - - if (/*number >= 0 &&*/number < _trackCount) - return &_tracks[number]; - return NULL; -} -#endif - -SoundResource::Track *SoundResource::getTrackByType(byte type) { - if (_soundVersion <= SCI_VERSION_0_LATE) - return &_tracks[0]; - - for (int trackNr = 0; trackNr < _trackCount; trackNr++) { - if (_tracks[trackNr].type == type) - return &_tracks[trackNr]; - } - return NULL; -} - -SoundResource::Track *SoundResource::getDigitalTrack() { - for (int trackNr = 0; trackNr < _trackCount; trackNr++) { - if (_tracks[trackNr].digitalChannelNr != -1) - return &_tracks[trackNr]; - } - return NULL; -} - -// Gets the filter mask for SCI0 sound resources -int SoundResource::getChannelFilterMask(int hardwareMask, bool wantsRhythm) { - byte *data = _innerResource->data; - int channelMask = 0; - - if (_soundVersion > SCI_VERSION_0_LATE) - return 0; - - data++; // Skip over digital sample flag - - for (int channelNr = 0; channelNr < 16; channelNr++) { - channelMask = channelMask >> 1; - - byte flags; - - if (_soundVersion == SCI_VERSION_0_EARLY) { - // Each channel is specified by a single byte - // Upper 4 bits of the byte is a voices count - // Lower 4 bits -> bit 0 set: use for AdLib - // bit 1 set: use for PCjr - // bit 2 set: use for PC speaker - // bit 3 set and bit 0 clear: control channel (15) - // bit 3 set and bit 0 set: rhythm channel (9) - // Note: control channel is dynamically assigned inside the drivers, - // but seems to be fixed at 15 in the song data. - flags = *data++; - - // Get device bits - flags &= 0x7; - } else { - // Each channel is specified by 2 bytes - // 1st byte is voices count - // 2nd byte is play mask, which specifies if the channel is supposed to be played - // by the corresponding hardware - - // Skip voice count - data++; - - flags = *data++; - } - - bool play; - switch (channelNr) { - case 15: - // Always play control channel - play = true; - break; - case 9: - // Play rhythm channel when requested - play = wantsRhythm; - break; - default: - // Otherwise check for flag - play = flags & hardwareMask; - } - - if (play) { - // This Channel is supposed to be played by the hardware - channelMask |= 0x8000; - } - } - - return channelMask; -} - -byte SoundResource::getInitialVoiceCount(byte channel) { - byte *data = _innerResource->data; - - if (_soundVersion > SCI_VERSION_0_LATE) - return 0; // TODO - - data++; // Skip over digital sample flag - - if (_soundVersion == SCI_VERSION_0_EARLY) - return data[channel] >> 4; - else - return data[channel * 2]; -} - -} // End of namespace Sci diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 1de42ab115..4862d0579a 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -43,7 +43,6 @@ #include "sci/sound/audio.h" #include "sci/sound/soundcmd.h" #include "sci/graphics/gui.h" -#include "sci/graphics/maciconbar.h" #include "sci/graphics/ports.h" #include "sci/graphics/palette.h" #include "sci/graphics/cursor.h" @@ -98,7 +97,6 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc) DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game"); _gamestate = 0; - _gfxMacIconBar = 0; const Common::FSNode gameDataDir(ConfMan.get("path")); @@ -126,7 +124,6 @@ SciEngine::~SciEngine() { delete _console; delete _resMan; delete _features; - delete _gfxMacIconBar; g_sci = 0; } @@ -173,9 +170,6 @@ Common::Error SciEngine::run() { else screen = new GfxScreen(_resMan, 320, 200, upscaledHires); - if (_resMan->isSci11Mac() && getSciVersion() == SCI_VERSION_1_1) - _gfxMacIconBar = new GfxMacIconBar(); - GfxPalette *palette = new GfxPalette(_resMan, screen); GfxCache *cache = new GfxCache(_resMan, screen, palette); GfxCursor *cursor = new GfxCursor(_resMan, palette, screen); @@ -190,7 +184,7 @@ Common::Error SciEngine::run() { _features = new GameFeatures(segMan, _kernel); - _gamestate = new EngineState(segMan); + _gamestate = new EngineState(_vocabulary, segMan); _gamestate->_event = new SciEvent(_resMan); @@ -228,11 +222,16 @@ Common::Error SciEngine::run() { } // Add the after market GM patches for the specified game, if they exist - _resMan->addNewGMPatch(getGameID()); + _resMan->addNewGMPatch(_gamestate->_gameId); script_adjust_opcode_formats(_gamestate); _kernel->loadKernelNames(getGameID()); + // Set the savegame dir (actually, we set it to a fake value, + // since we cannot let the game control where saves are stored) + assert(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value != 0); + strcpy(_gamestate->sys_strings->_strings[SYS_STRING_SAVEDIR]._value, ""); + SciVersion soundVersion = _features->detectDoSoundType(); _gamestate->_soundCmd = new SoundCommandParser(_resMan, segMan, _kernel, _audio, soundVersion); diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 685f05e685..fdd10bcd04 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -65,7 +65,7 @@ class GfxPalette; class GfxPorts; class GfxScreen; class SciGui; -class GfxMacIconBar; + #ifdef ENABLE_SCI32 class SciGui32; @@ -206,7 +206,6 @@ public: GfxPorts *_gfxPorts; // Port managment for 16-bit gfx GfxScreen *_gfxScreen; SciGui *_gui; /* Currently active Gui */ - GfxMacIconBar *_gfxMacIconBar; // Mac Icon Bar manager #ifdef ENABLE_SCI32 SciGui32 *_gui32; // GUI for SCI32 games diff --git a/engines/sci/sound/audio.cpp b/engines/sci/sound/audio.cpp index 7748c0505b..331561eea4 100644 --- a/engines/sci/sound/audio.cpp +++ b/engines/sci/sound/audio.cpp @@ -235,7 +235,6 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 uint32 audioCompressionType = audioRes->getAudioCompressionType(); if (audioCompressionType) { -#if (defined(USE_MAD) || defined(USE_VORBIS) || defined(USE_FLAC)) // Compressed audio made by our tool byte *compressedData = (byte *)malloc(audioRes->size); assert(compressedData); @@ -262,9 +261,6 @@ Audio::RewindableAudioStream *AudioPlayer::getAudioStream(uint32 number, uint32 #endif break; } -#else - error("Compressed audio file encountered, but no appropriate decoder is compiled in"); -#endif } else { // Original source file if (audioRes->_headerSize > 0) { @@ -336,11 +332,11 @@ void AudioPlayer::setSoundSync(ResourceId id, reg_t syncObjAddr, SegManager *seg _syncOffset = 0; if (_syncResource) { - writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), 0); + PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), 0); } else { warning("setSoundSync: failed to find resource %s", id.toString().c_str()); // Notify the scripts to stop sound sync - writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET); + PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), SIGNAL_OFFSET); } } @@ -356,8 +352,8 @@ void AudioPlayer::doSoundSync(reg_t syncObjAddr, SegManager *segMan) { _syncOffset += 2; } - writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncTime), syncTime); - writeSelectorValue(segMan, syncObjAddr, SELECTOR(syncCue), syncCue); + PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncTime), syncTime); + PUT_SEL32V(segMan, syncObjAddr, SELECTOR(syncCue), syncCue); } } diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 3ee8a3a83d..2068ea9a33 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -60,9 +60,6 @@ MidiParser_SCI::MidiParser_SCI(SciVersion soundVersion) : _dataincToAdd = 0; _resetOnPause = false; _channelsUsed = 0; - - for (int i = 0; i < 16; i++) - _channelRemap[i] = i; } MidiParser_SCI::~MidiParser_SCI() { @@ -88,6 +85,7 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in _tracks[0] = _mixedData; setTrack(0); _loopTick = 0; + _channelsUsed = 0; if (_soundVersion <= SCI_VERSION_0_LATE) { // Set initial voice count @@ -122,20 +120,17 @@ void MidiParser_SCI::unloadMusic() { // Center the pitch wheels and hold pedal in preparation for the next piece of music if (_driver) { for (int i = 0; i < 16; ++i) { - if (isChannelUsed(i)) { + if (_channelsUsed & (1 << i)) { _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel _driver->send(0xB0 | i, 0x40, 0); // Reset hold pedal } } } - - for (int i = 0; i < 16; i++) - _channelRemap[i] = i; } void MidiParser_SCI::parseNextEvent(EventInfo &info) { // Monitor which channels are used by this song - setChannelUsed(info.channel()); + _channelsUsed |= (1 << info.channel()); // Set signal AFTER waiting for delta, otherwise we would set signal too soon resulting in all sorts of bugs if (_dataincAdd) { @@ -327,7 +322,7 @@ byte MidiParser_SCI::midiGetNextChannel(long ticker) { for (int i = 0; i < _track->channelCount; i++) { if (_track->channels[i].time == -1) // channel ended continue; - next = *_track->channels[i].data; // when the next event should occur + next = *_track->channels[i].data; // when the next event shoudl occur if (next == 0xF8) // 0xF8 means 240 ticks delay next = 240; next += _track->channels[i].time; @@ -394,18 +389,9 @@ byte *MidiParser_SCI::midiMixChannels() { channel->time = -1; // FIXME break; default: // MIDI command - if (command & 0x80) { + if (command & 0x80) par1 = *channel->data++; - - // TODO: Fix remapping - -#if 0 - // Remap channel. Keep the upper 4 bits (command code) and change - // the lower 4 bits (channel) - byte remappedChannel = _channelRemap[par1 & 0xF]; - par1 = (par1 & 0xF0) | (remappedChannel & 0xF); -#endif - } else {// running status + else {// running status par1 = command; command = channel->prev; } diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 9d4b5a39da..f95c71ce2f 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -71,18 +71,7 @@ public: jumpToTick(0); } - void remapChannel(byte channel, byte newChannel) { - assert(channel < 0xF); // don't touch special SCI channel 15 - assert(newChannel < 0xF); // don't touch special SCI channel 15 - _channelRemap[channel] = newChannel; - } - - void clearUsedChannels() { _channelsUsed = 0; } - protected: - bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } - void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } - void parseNextEvent(EventInfo &info); byte *midiMixChannels(); byte *midiFilterChannels(int channelMask); @@ -104,8 +93,6 @@ protected: // A 16-bit mask, containing the channels used // by the currently parsed song uint16 _channelsUsed; - - byte _channelRemap[16]; }; } // End of namespace Sci diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp index fa5716e7cc..66f5ce9710 100644 --- a/engines/sci/sound/music.cpp +++ b/engines/sci/sound/music.cpp @@ -37,6 +37,9 @@ namespace Sci { +// When defined, volume fading immediately sets the final sound volume +#define DISABLE_VOLUME_FADING + SciMusic::SciMusic(SciVersion soundVersion) : _soundVersion(soundVersion), _soundOn(true), _masterVolume(0) { @@ -112,6 +115,8 @@ void SciMusic::clearPlayList() { } void SciMusic::pauseAll(bool pause) { + Common::StackLock lock(_mutex); + const MusicList::iterator end = _playList.end(); for (MusicList::iterator i = _playList.begin(); i != end; ++i) { soundToggle(*i, pause); @@ -165,29 +170,14 @@ void SciMusic::setReverb(byte reverb) { _pMidiDrv->setReverb(reverb); } -static bool musicEntryCompare(const MusicEntry *l, const MusicEntry *r) { - return (l->priority > r->priority); +static int f_compare(const void *arg1, const void *arg2) { + return ((const MusicEntry *)arg2)->priority - ((const MusicEntry *)arg1)->priority; } void SciMusic::sortPlayList() { - // Sort the play list in descending priority order - Common::sort(_playList.begin(), _playList.end(), musicEntryCompare); + MusicEntry ** pData = _playList.begin(); + qsort(pData, _playList.size(), sizeof(MusicEntry *), &f_compare); } - -void SciMusic::findUsedChannels() { - // Reset list - for (int k = 0; k < 16; k++) - _usedChannels[k] = false; - - const MusicList::const_iterator end = _playList.end(); - for (MusicList::const_iterator i = _playList.begin(); i != end; ++i) { - for (int channel = 0; channel < 16; channel++) { - if ((*i)->soundRes && (*i)->soundRes->isChannelUsed(channel)) - _usedChannels[channel] = true; - } - } -} - void SciMusic::soundInitSnd(MusicEntry *pSnd) { int channelFilterMask = 0; SoundResource::Track *track = pSnd->soundRes->getTrackByType(_pMidiDrv->getPlayId()); @@ -231,27 +221,6 @@ void SciMusic::soundInitSnd(MusicEntry *pSnd) { pSnd->pauseCounter = 0; - // TODO: Fix channel remapping. This doesn't quite work... (e.g. no difference in LSL1VGA) -#if 0 - // Remap channels - findUsedChannels(); - - pSnd->pMidiParser->clearUsedChannels(); - - for (int i = 0; i < 16; i++) { - if (_usedChannels[i] && pSnd->soundRes->isChannelUsed(i)) { - int16 newChannel = getNextUnusedChannel(); - if (newChannel >= 0) { - _usedChannels[newChannel] = true; - debug("Remapping channel %d to %d\n", i, newChannel); - pSnd->pMidiParser->remapChannel(i, newChannel); - } else { - warning("Attempt to remap channel %d, but no unused channels exist", i); - } - } - } -#endif - // Find out what channels to filter for SCI0 channelFilterMask = pSnd->soundRes->getChannelFilterMask(_pMidiDrv->getPlayId(), _pMidiDrv->hasRhythmChannel()); pSnd->pMidiParser->loadMusic(track, pSnd, channelFilterMask, _soundVersion); @@ -274,14 +243,14 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { uint playListCount = _playList.size(); uint playListNo = playListCount; - MusicEntry *alreadyPlaying = NULL; + bool alreadyPlaying = false; // searching if sound is already in _playList for (uint i = 0; i < playListCount; i++) { if (_playList[i] == pSnd) playListNo = i; if ((_playList[i]->status == kSoundPlaying) && (_playList[i]->pMidiParser)) - alreadyPlaying = _playList[i]; + alreadyPlaying = true; } if (playListNo == playListCount) { // not found _playList.push_back(pSnd); @@ -292,20 +261,13 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { if (pSnd->pMidiParser) { if ((_soundVersion <= SCI_VERSION_0_LATE) && (alreadyPlaying)) { - // Music already playing in SCI0? - if (pSnd->priority > alreadyPlaying->priority) { - // And new priority higher? pause previous music and play new one immediately - // Example of such case: lsl3, when getting points (jingle is played then) - soundPause(alreadyPlaying); - alreadyPlaying->isQueued = true; - } else { - // And new priority equal or lower? queue up music and play it afterwards done by - // SoundCommandParser::updateSci0Cues() - // Example of such case: iceman room 14 - pSnd->isQueued = true; - pSnd->status = kSoundPaused; - return; - } + // if any music is already playing, SCI0 queues music and plays it after the current music has finished + // done by SoundCommandParser::updateSci0Cues() + // Example of such case: iceman room 14 + // FIXME: this code is supposed to also take a look at priority and pause currently playing sound accordingly + pSnd->isQueued = true; + pSnd->status = kSoundPaused; + return; } } @@ -336,8 +298,6 @@ void SciMusic::soundPlay(MusicEntry *pSnd) { void SciMusic::soundStop(MusicEntry *pSnd) { pSnd->status = kSoundStopped; - if (_soundVersion <= SCI_VERSION_0_LATE) - pSnd->isQueued = false; if (pSnd->pStreamAud) _pMixer->stopHandle(pSnd->hCurrentAud); @@ -420,12 +380,7 @@ void SciMusic::soundResume(MusicEntry *pSnd) { return; if (pSnd->status != kSoundPaused) return; - if (pSnd->pStreamAud) { - _pMixer->pauseHandle(pSnd->hCurrentAud, false); - pSnd->status = kSoundPlaying; - } else { - soundPlay(pSnd); - } + soundPlay(pSnd); } void SciMusic::soundToggle(MusicEntry *pSnd, bool pause) { @@ -567,17 +522,15 @@ void MusicEntry::doFade() { fadeStep = 0; fadeCompleted = true; } -#ifdef ENABLE_SCI32 - // Disable fading for SCI32 - sound drivers have issues when fading in (gabriel knight 1 sierra title) - if (getSciVersion() >= SCI_VERSION_2) { + + // Only process MIDI streams in this thread, not digital sound effects + if (pMidiParser) { +#ifdef DISABLE_VOLUME_FADING + // Signal fading to stop... volume = fadeTo; fadeStep = 0; fadeCompleted = true; - } #endif - - // Only process MIDI streams in this thread, not digital sound effects - if (pMidiParser) { pMidiParser->setVolume(volume); } diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 83cd59e89b..8f08065b99 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -197,6 +197,7 @@ public: Common::Mutex _mutex; protected: + byte findAudEntry(uint16 nAud, byte&oVolume, uint32& oOffset, uint32&oSize); void sortPlayList(); SciVersion _soundVersion; @@ -210,20 +211,10 @@ protected: bool _bMultiMidi; private: static void miditimerCallback(void *p); - void findUsedChannels(); - int16 getNextUnusedChannel() const { - for (int i = 0; i < 16; i++) { - if (!_usedChannels[i]) - return i; - } - - return -1; - } MusicList _playList; bool _soundOn; byte _masterVolume; - bool _usedChannels[16]; }; } // End of namespace Sci diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index ece4c1430c..925f3b2e1a 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -50,9 +50,9 @@ namespace Sci { #ifdef USE_OLD_MUSIC_FUNCTIONS static void script_set_priority(ResourceManager *resMan, SegManager *segMan, SfxState *state, reg_t obj, int priority) { - int song_nr = readSelectorValue(segMan, obj, SELECTOR(number)); + int song_nr = GET_SEL32V(segMan, obj, SELECTOR(number)); Resource *song = resMan->findResource(ResourceId(kResourceTypeSound, song_nr), 0); - int flags = readSelectorValue(segMan, obj, SELECTOR(flags)); + int flags = GET_SEL32V(segMan, obj, SELECTOR(flags)); if (priority == -1) { if (song->data[0] == 0xf0) @@ -64,7 +64,7 @@ static void script_set_priority(ResourceManager *resMan, SegManager *segMan, Sfx } else flags |= SCI1_SOUND_FLAG_SCRIPTED_PRI; state->sfx_song_renice(FROBNICATE_HANDLE(obj), priority); - writeSelectorValue(segMan, obj, SELECTOR(flags), flags); + PUT_SEL32V(segMan, obj, SELECTOR(flags), flags); } SongIterator *build_iterator(ResourceManager *resMan, int song_nr, SongIteratorType type, songit_id_t id) { @@ -98,27 +98,27 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their case SI_LOOP: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x looped (to %d)", PRINT_REG(obj), cue); - /* writeSelectorValue(segMan, obj, SELECTOR(loops), readSelectorValue(segMan, obj, SELECTOR(loop));; - 1);*/ - writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + /* PUT_SEL32V(segMan, obj, SELECTOR(loops), GET_SEL32V(segMan, obj, SELECTOR(loop));; - 1);*/ + PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); break; case SI_RELATIVE_CUE: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received relative cue %d", PRINT_REG(obj), cue); - writeSelectorValue(segMan, obj, SELECTOR(signal), cue + 0x7f); + PUT_SEL32V(segMan, obj, SELECTOR(signal), cue + 0x7f); break; case SI_ABSOLUTE_CUE: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x received absolute cue %d", PRINT_REG(obj), cue); - writeSelectorValue(segMan, obj, SELECTOR(signal), cue); + PUT_SEL32V(segMan, obj, SELECTOR(signal), cue); break; case SI_FINISHED: debugC(2, kDebugLevelSound, "[process-sound] Song %04x:%04x finished", PRINT_REG(obj)); - writeSelectorValue(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); - writeSelectorValue(segMan, obj, SELECTOR(state), kSoundStopped); + PUT_SEL32V(segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(segMan, obj, SELECTOR(state), kSoundStopped); break; default: @@ -253,7 +253,7 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { if (!obj.segment) return; - int resourceId = readSelectorValue(_segMan, obj, SELECTOR(number)); + int resourceId = GET_SEL32V(_segMan, obj, SELECTOR(number)); #ifdef USE_OLD_MUSIC_FUNCTIONS @@ -267,7 +267,7 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { SongIteratorType type = (_soundVersion <= SCI_VERSION_0_LATE) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1; if (_soundVersion <= SCI_VERSION_0_LATE) { - if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr))) { + if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr))) { _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); _state->sfx_remove_song(handle); } @@ -281,11 +281,11 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { // Notify the engine if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized); + PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized); else - writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); + PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); - writeSelector(_segMan, obj, SELECTOR(handle), obj); + PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); #else @@ -302,10 +302,10 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { newSound->soundRes = 0; newSound->soundObj = obj; - newSound->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); - newSound->priority = readSelectorValue(_segMan, obj, SELECTOR(pri)) & 0xFF; - if (_soundVersion >= SCI_VERSION_1_EARLY) - newSound->volume = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); + newSound->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); + newSound->priority = GET_SEL32V(_segMan, obj, SELECTOR(pri)) & 0xFF; + if (_soundVersion >= SCI_VERSION_1_LATE) + newSound->volume = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, MUSIC_VOLUME_MAX); // In SCI1.1 games, sound effects are started from here. If we can find // a relevant audio resource, play it, otherwise switch to synthesized @@ -327,11 +327,11 @@ void SoundCommandParser::cmdInitSound(reg_t obj, int16 value) { if (newSound->soundRes || newSound->pStreamAud) { // Notify the engine if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundInitialized); + PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundInitialized); else - writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); + PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); - writeSelector(_segMan, obj, SELECTOR(handle), obj); + PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); } #endif @@ -346,30 +346,30 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { if (_soundVersion <= SCI_VERSION_0_LATE) { _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); - _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying); + _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop))); + PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying); } else if (_soundVersion == SCI_VERSION_1_EARLY) { _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); - _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); - _state->sfx_song_renice(handle, readSelectorValue(_segMan, obj, SELECTOR(pri))); + _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop))); + _state->sfx_song_renice(handle, GET_SEL32V(_segMan, obj, SELECTOR(pri))); RESTORE_BEHAVIOR rb = (RESTORE_BEHAVIOR) value; /* Too lazy to look up a default value for this */ _state->_songlib.setSongRestoreBehavior(handle, rb); - writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0); } else if (_soundVersion == SCI_VERSION_1_LATE) { - int looping = readSelectorValue(_segMan, obj, SELECTOR(loop)); - //int vol = readSelectorValue(_segMan, obj, SELECTOR(vol)); - int pri = readSelectorValue(_segMan, obj, SELECTOR(pri)); + int looping = GET_SEL32V(_segMan, obj, SELECTOR(loop)); + //int vol = GET_SEL32V(_segMan, obj, SELECTOR(vol)); + int pri = GET_SEL32V(_segMan, obj, SELECTOR(pri)); int sampleLen = 0; Song *song = _state->_songlib.findSong(handle); - int songNumber = readSelectorValue(_segMan, obj, SELECTOR(number)); + int songNumber = GET_SEL32V(_segMan, obj, SELECTOR(number)); - if (readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) { + if (GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && (song && songNumber != song->_resourceNum)) { _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); _state->sfx_remove_song(handle); - writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); + PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG); } - if (!readSelectorValue(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) { + if (!GET_SEL32V(_segMan, obj, SELECTOR(nodePtr)) && obj.segment) { // In SCI1.1 games, sound effects are started from here. If we can find // a relevant audio resource, play it, otherwise switch to synthesized // effects. If the resource exists, play it using map 65535 (sound @@ -387,7 +387,7 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { warning("Could not open song number %d", songNumber); // Send a "stop handle" event so that the engine won't wait forever here _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); return; } debugC(2, kDebugLevelSound, "Initializing song number %d", songNumber); @@ -395,15 +395,15 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { handle), 0, handle, songNumber); } - writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); - writeSelector(_segMan, obj, SELECTOR(handle), obj); + PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); + PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); } if (obj.segment) { _state->sfx_song_set_status(handle, SOUND_STATUS_PLAYING); _state->sfx_song_set_loops(handle, looping); _state->sfx_song_renice(handle, pri); - writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0); } } @@ -415,7 +415,7 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { return; } - int resourceId = obj.segment ? readSelectorValue(_segMan, obj, SELECTOR(number)) : -1; + int resourceId = obj.segment ? GET_SEL32V(_segMan, obj, SELECTOR(number)) : -1; if (musicSlot->resourceId != resourceId) { // another sound loaded into struct cmdDisposeSound(obj, value); @@ -423,25 +423,25 @@ void SoundCommandParser::cmdPlaySound(reg_t obj, int16 value) { // Find slot again :) musicSlot = _music->getSlot(obj); } - int16 loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); + int16 loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); debugC(2, kDebugLevelSound, "cmdPlaySound: resource number %d, loop %d", resourceId, loop); - writeSelector(_segMan, obj, SELECTOR(handle), obj); + PUT_SEL32(_segMan, obj, SELECTOR(handle), obj); if (_soundVersion >= SCI_VERSION_1_EARLY) { - writeSelector(_segMan, obj, SELECTOR(nodePtr), obj); - writeSelectorValue(_segMan, obj, SELECTOR(min), 0); - writeSelectorValue(_segMan, obj, SELECTOR(sec), 0); - writeSelectorValue(_segMan, obj, SELECTOR(frame), 0); - writeSelectorValue(_segMan, obj, SELECTOR(signal), 0); + PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), obj); + PUT_SEL32V(_segMan, obj, SELECTOR(min), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(sec), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(frame), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), 0); } else { - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundPlaying); + PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundPlaying); } - musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); - musicSlot->priority = readSelectorValue(_segMan, obj, SELECTOR(priority)); - if (_soundVersion >= SCI_VERSION_1_EARLY) - musicSlot->volume = readSelectorValue(_segMan, obj, SELECTOR(vol)); + musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); + musicSlot->priority = GET_SEL32V(_segMan, obj, SELECTOR(priority)); + if (_soundVersion >= SCI_VERSION_1_LATE) + musicSlot->volume = GET_SEL32V(_segMan, obj, SELECTOR(vol)); _music->soundPlay(musicSlot); #endif @@ -458,7 +458,7 @@ void SoundCommandParser::changeSoundStatus(reg_t obj, int newStatus) { if (obj.segment) { _state->sfx_song_set_status(handle, newStatus); if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(state), newStatus); + PUT_SEL32V(_segMan, obj, SELECTOR(state), newStatus); } } #endif @@ -475,7 +475,7 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) { _state->sfx_remove_song(handle); if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(handle), 0x0000); + PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0x0000); } #else @@ -489,11 +489,11 @@ void SoundCommandParser::cmdDisposeSound(reg_t obj, int16 value) { cmdStopSound(obj, value); _music->soundKill(musicSlot); - writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0); if (_soundVersion >= SCI_VERSION_1_EARLY) - writeSelector(_segMan, obj, SELECTOR(nodePtr), NULL_REG); + PUT_SEL32(_segMan, obj, SELECTOR(nodePtr), NULL_REG); else - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped); + PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped); #endif } @@ -509,7 +509,7 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin changeSoundStatus(obj, SOUND_STATUS_STOPPED); if (_soundVersion >= SCI_VERSION_1_EARLY) - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); #else MusicEntry *musicSlot = _music->getSlot(obj); if (!musicSlot) { @@ -518,9 +518,9 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin } if (_soundVersion <= SCI_VERSION_0_LATE) { - writeSelectorValue(_segMan, obj, SELECTOR(state), kSoundStopped); + PUT_SEL32V(_segMan, obj, SELECTOR(state), kSoundStopped); } else { - writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); + PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0); } // Set signal selector in sound SCI0 games only, when the sample has finished playing @@ -530,7 +530,7 @@ void SoundCommandParser::processStopSound(reg_t obj, int16 value, bool sampleFin // sfx drivers included // We need to set signal in sound SCI1+ games all the time if ((_soundVersion > SCI_VERSION_0_LATE) || sampleFinishedPlaying) - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); musicSlot->dataInc = 0; musicSlot->signal = 0; @@ -565,7 +565,7 @@ void SoundCommandParser::cmdPauseSound(reg_t obj, int16 value) { if (_soundVersion <= SCI_VERSION_0_LATE) { // Always pause the sound in SCI0 games. It's resumed in cmdResumeSound() - writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused); + PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPaused); _music->soundPause(musicSlot); } else { _music->soundToggle(musicSlot, value); @@ -590,7 +590,7 @@ void SoundCommandParser::cmdResumeSound(reg_t obj, int16 value) { return; } - writeSelectorValue(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); + PUT_SEL32V(_segMan, musicSlot->soundObj, SELECTOR(state), kSoundPlaying); _music->soundResume(musicSlot); #endif } @@ -630,12 +630,13 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { #ifdef USE_OLD_MUSIC_FUNCTIONS SongHandle handle = FROBNICATE_HANDLE(obj); if (_soundVersion != SCI_VERSION_1_LATE) { + /*s->sound_server->command(s, SOUND_COMMAND_FADE_HANDLE, obj, 120);*/ /* Fade out in 2 secs */ /* FIXME: The next couple of lines actually STOP the handle, rather ** than fading it! */ _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(state), SOUND_STATUS_STOPPED); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); } else { fade_params_t fade; fade.final_volume = _argv[2].toUint16(); @@ -650,11 +651,11 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { /* FIXME: The next couple of lines actually STOP the handle, rather ** than fading it! */ if (_argv[5].toUint16()) { - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); _state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED); } else { // FIXME: Support fade-and-continue. For now, send signal right away. - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); } } #else @@ -691,7 +692,7 @@ void SoundCommandParser::cmdFadeSound(reg_t obj, int16 value) { // If sound is not playing currently, set signal directly if (musicSlot->status != kSoundPlaying) { warning("cmdFadeSound: fading requested, but sound is currently not playing"); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); } debugC(2, kDebugLevelSound, "cmdFadeSound: to %d, step %d, ticker %d", musicSlot->fadeTo, musicSlot->fadeStep, musicSlot->fadeTickerStep); @@ -713,8 +714,8 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) { #ifdef USE_OLD_MUSIC_FUNCTIONS SongHandle handle = FROBNICATE_HANDLE(obj); if (_soundVersion <= SCI_VERSION_0_LATE && obj.segment) { - _state->sfx_song_set_loops(handle, readSelectorValue(_segMan, obj, SELECTOR(loop))); - script_set_priority(_resMan, _segMan, _state, obj, readSelectorValue(_segMan, obj, SELECTOR(pri))); + _state->sfx_song_set_loops(handle, GET_SEL32V(_segMan, obj, SELECTOR(loop))); + script_set_priority(_resMan, _segMan, _state, obj, GET_SEL32V(_segMan, obj, SELECTOR(pri))); } #else MusicEntry *musicSlot = _music->getSlot(obj); @@ -723,11 +724,11 @@ void SoundCommandParser::cmdUpdateSound(reg_t obj, int16 value) { return; } - musicSlot->loop = readSelectorValue(_segMan, obj, SELECTOR(loop)); - int16 objVol = CLIP<int>(readSelectorValue(_segMan, obj, SELECTOR(vol)), 0, 255); + musicSlot->loop = GET_SEL32V(_segMan, obj, SELECTOR(loop)); + int16 objVol = CLIP<int>(GET_SEL32V(_segMan, obj, SELECTOR(vol)), 0, 255); if (objVol != musicSlot->volume) _music->soundSetVolume(musicSlot, objVol); - uint32 objPrio = readSelectorValue(_segMan, obj, SELECTOR(pri)); + uint32 objPrio = GET_SEL32V(_segMan, obj, SELECTOR(pri)); if (objPrio != musicSlot->priority) _music->soundSetPriority(musicSlot, objPrio); @@ -754,7 +755,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { debugC(2, kDebugLevelSound, "--- [CUE] %04x:%04x Absolute Cue: %d", PRINT_REG(obj), signal); debugC(2, kDebugLevelSound, "abs-signal %04X", signal); - writeSelectorValue(_segMan, obj, SELECTOR(signal), signal); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal); break; case SI_RELATIVE_CUE: @@ -764,17 +765,17 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { /* FIXME to match commented-out semantics * below, with proper storage of dataInc and * signal in the iterator code. */ - writeSelectorValue(_segMan, obj, SELECTOR(dataInc), signal); + PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), signal); debugC(2, kDebugLevelSound, "rel-signal %04X", signal); if (_soundVersion == SCI_VERSION_1_EARLY) - writeSelectorValue(_segMan, obj, SELECTOR(signal), signal); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal); else - writeSelectorValue(_segMan, obj, SELECTOR(signal), signal + 127); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), signal + 127); break; case SI_FINISHED: debugC(2, kDebugLevelSound, "--- [FINISHED] %04x:%04x", PRINT_REG(obj)); - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); break; case SI_LOOP: @@ -783,30 +784,30 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { //switch (signal) { //case 0x00: - // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) { - // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc); - // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc+0x7f); + // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) { + // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc); + // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc+0x7f); // } else { - // writeSelectorValue(segMan, obj, SELECTOR(signal), signal); + // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal); // } // break; //case 0xFF: // May be unnecessary // s->_sound.sfx_song_set_status(handle, SOUND_STATUS_STOPPED); // break; //default : - // if (dataInc != readSelectorValue(segMan, obj, SELECTOR(dataInc))) { - // writeSelectorValue(segMan, obj, SELECTOR(dataInc), dataInc); - // writeSelectorValue(segMan, obj, SELECTOR(signal), dataInc + 0x7f); + // if (dataInc != GET_SEL32V(segMan, obj, SELECTOR(dataInc))) { + // PUT_SEL32V(segMan, obj, SELECTOR(dataInc), dataInc); + // PUT_SEL32V(segMan, obj, SELECTOR(signal), dataInc + 0x7f); // } else { - // writeSelectorValue(segMan, obj, SELECTOR(signal), signal); + // PUT_SEL32V(segMan, obj, SELECTOR(signal), signal); // } // break; //} if (_soundVersion == SCI_VERSION_1_EARLY) { - writeSelectorValue(_segMan, obj, SELECTOR(min), min); - writeSelectorValue(_segMan, obj, SELECTOR(sec), sec); - writeSelectorValue(_segMan, obj, SELECTOR(frame), frame); + PUT_SEL32V(_segMan, obj, SELECTOR(min), min); + PUT_SEL32V(_segMan, obj, SELECTOR(sec), sec); + PUT_SEL32V(_segMan, obj, SELECTOR(frame), frame); } #else MusicEntry *musicSlot = _music->getSlot(obj); @@ -827,7 +828,7 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { musicSlot->loop -= currentLoopCounter - musicSlot->sampleLoopCounter; musicSlot->sampleLoopCounter = currentLoopCounter; } - if ((!_music->soundIsActive(musicSlot)) && (musicSlot->status != kSoundPaused)) { + if (!_music->soundIsActive(musicSlot)) { processStopSound(obj, 0, true); } else { _music->updateAudioStreamTicker(musicSlot); @@ -840,14 +841,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { } else if (musicSlot->pMidiParser) { // Update MIDI slots if (musicSlot->signal == 0) { - if (musicSlot->dataInc != readSelectorValue(_segMan, obj, SELECTOR(dataInc))) { + if (musicSlot->dataInc != GET_SEL32V(_segMan, obj, SELECTOR(dataInc))) { if (_kernel->_selectorCache.dataInc > -1) - writeSelectorValue(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc); - writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127); + PUT_SEL32V(_segMan, obj, SELECTOR(dataInc), musicSlot->dataInc); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->dataInc + 127); } } else { // Sync the signal of the sound object - writeSelectorValue(_segMan, obj, SELECTOR(signal), musicSlot->signal); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), musicSlot->signal); // We need to do this especially because state selector needs to get updated if (musicSlot->signal == SIGNAL_OFFSET) cmdStopSound(obj, 0); @@ -855,14 +856,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { } else { // Slot actually has no data (which would mean that a sound-resource w/ unsupported data is used // (example lsl5 - sound resource 744 - it's roland exclusive - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); // If we don't set signal here, at least the switch to the mud wrestling room in lsl5 will not work } if (musicSlot->fadeCompleted) { musicSlot->fadeCompleted = false; // We need signal for sci0 at least in iceman as well (room 14, fireworks) - writeSelectorValue(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(signal), SIGNAL_OFFSET); if (_soundVersion <= SCI_VERSION_0_LATE) { cmdStopSound(obj, 0); } else { @@ -873,14 +874,14 @@ void SoundCommandParser::cmdUpdateCues(reg_t obj, int16 value) { // Sync loop selector for SCI0 if (_soundVersion <= SCI_VERSION_0_LATE) - writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop); + PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop); musicSlot->signal = 0; if (_soundVersion >= SCI_VERSION_1_EARLY) { - writeSelectorValue(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600); - writeSelectorValue(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60); - writeSelectorValue(_segMan, obj, SELECTOR(frame), musicSlot->ticker); + PUT_SEL32V(_segMan, obj, SELECTOR(min), musicSlot->ticker / 3600); + PUT_SEL32V(_segMan, obj, SELECTOR(sec), musicSlot->ticker % 3600 / 60); + PUT_SEL32V(_segMan, obj, SELECTOR(frame), musicSlot->ticker); } #endif @@ -926,13 +927,18 @@ void SoundCommandParser::cmdStopAllSounds(reg_t obj, int16 value) { #ifndef USE_OLD_MUSIC_FUNCTIONS Common::StackLock(_music->_mutex); + // FIXME: this can't be right, it's called in iceman (room 14) when the door sound has done playing + // stopping sounds can't be right, because music is starting afterwards in ssci. can't be resume queued + // song(s), because music is playing even when this call is nuked inside ssci. + return; + const MusicList::iterator end = _music->getPlayListEnd(); for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) { if (_soundVersion <= SCI_VERSION_0_LATE) { - writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped); + PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(state), kSoundStopped); } else { - writeSelectorValue(_segMan, obj, SELECTOR(handle), 0); - writeSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET); + PUT_SEL32V(_segMan, obj, SELECTOR(handle), 0); + PUT_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal), SIGNAL_OFFSET); } (*i)->dataInc = 0; @@ -963,7 +969,7 @@ void SoundCommandParser::cmdSetSoundVolume(reg_t obj, int16 value) { if (musicSlot->volume != value) { musicSlot->volume = value; _music->soundSetVolume(musicSlot, value); - writeSelectorValue(_segMan, obj, SELECTOR(vol), value); + PUT_SEL32V(_segMan, obj, SELECTOR(vol), value); } #endif } @@ -990,12 +996,12 @@ void SoundCommandParser::cmdSetSoundPriority(reg_t obj, int16 value) { warning("cmdSetSoundPriority: Attempt to unset song priority when there is no built-in value"); //pSnd->prio=0;field_15B=0 - writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) & 0xFD); + PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) & 0xFD); } else { // Scripted priority //pSnd->field_15B=1; - writeSelectorValue(_segMan, obj, SELECTOR(flags), readSelectorValue(_segMan, obj, SELECTOR(flags)) | 2); + PUT_SEL32V(_segMan, obj, SELECTOR(flags), GET_SEL32V(_segMan, obj, SELECTOR(flags)) | 2); //DoSOund(0xF,hobj,w) } #endif @@ -1006,7 +1012,7 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) { return; #ifdef USE_OLD_MUSIC_FUNCTIONS - if (!readSelector(_segMan, obj, SELECTOR(nodePtr)).isNull()) { + if (!GET_SEL32(_segMan, obj, SELECTOR(nodePtr)).isNull()) { SongHandle handle = FROBNICATE_HANDLE(obj); _state->sfx_song_set_loops(handle, value); } @@ -1031,7 +1037,7 @@ void SoundCommandParser::cmdSetSoundLoop(reg_t obj, int16 value) { musicSlot->loop = 1; // actually plays the music once } - writeSelectorValue(_segMan, obj, SELECTOR(loop), musicSlot->loop); + PUT_SEL32V(_segMan, obj, SELECTOR(loop), musicSlot->loop); #endif } @@ -1052,11 +1058,8 @@ void SoundCommandParser::updateSci0Cues() { for (MusicList::iterator i = _music->getPlayListStart(); i != end; ++i) { // Is the sound stopped, and the sound object updated too? If yes, skip // this sound, as SCI0 only allows one active song - if ((*i)->isQueued) { + if (((*i)->isQueued) && (!pWaitingForPlay)) { pWaitingForPlay = (*i); - // FIXME (?) - in iceman 2 songs are queued when playing the door sound - if we use the first song for resuming - // then it's the wrong one. Both songs have same priority. Maybe the new sound function in sci0 - // is somehow responsible continue; } if ((*i)->signal == 0 && (*i)->status != kSoundPlaying) @@ -1102,11 +1105,11 @@ void SoundCommandParser::reconstructPlayList(int savegame_version) { } if ((*i)->status == kSoundPlaying) { if (savegame_version < 14) { - (*i)->dataInc = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(dataInc)); - (*i)->signal = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(signal)); + (*i)->dataInc = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(dataInc)); + (*i)->signal = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(signal)); if (_soundVersion >= SCI_VERSION_1_LATE) - (*i)->volume = readSelectorValue(_segMan, (*i)->soundObj, SELECTOR(vol)); + (*i)->volume = GET_SEL32V(_segMan, (*i)->soundObj, SELECTOR(vol)); } cmdPlaySound((*i)->soundObj, 0); @@ -1142,7 +1145,7 @@ void SoundCommandParser::startNewSound(int number) { MusicEntry *song = *_music->getPlayListStart(); reg_t soundObj = song->soundObj; cmdDisposeSound(soundObj, 0); - writeSelectorValue(_segMan, soundObj, SELECTOR(number), number); + PUT_SEL32V(_segMan, soundObj, SELECTOR(number), number); cmdInitSound(soundObj, 0); cmdPlaySound(soundObj, 0); #endif diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 0e0c0e129e..757171b24c 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -845,7 +845,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { offsX = offsY = 0; } else { uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - assert(charOffs < 0x14000); + assert(charOffs < 0x10000); if (!charOffs) return; charPtr = _fontPtr + charOffs; diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index ea29e25a1f..a0975839d6 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -870,7 +870,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) { _detach_now = true; } else { - DebugPrintf("Current Passcode is %d \nUse 'passcode <SEGA CD Passcode>'\n",_vm->_scummVars[411]); + DebugPrintf("Use 'passcode <SEGA CD Passcode>'\n"); return true; } return false; diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index d9c24ddca2..1f153094c1 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -233,6 +233,19 @@ protected: #endif +class ConfigDialog : public GUI::OptionsDialog { +protected: +#ifdef SMALL_SCREEN_DEVICE + GUI::Dialog *_keysDialog; +#endif + +public: + ConfigDialog(); + ~ConfigDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +}; + #pragma mark - ScummDialog::ScummDialog(int x, int y, int w, int h) : GUI::Dialog(x, y, w, h) { @@ -245,31 +258,223 @@ ScummDialog::ScummDialog(String name) : GUI::Dialog(name) { #pragma mark - -#ifndef DISABLE_HELP +enum { + kSaveCmd = 'SAVE', + kLoadCmd = 'LOAD', + kPlayCmd = 'PLAY', + kOptionsCmd = 'OPTN', + kHelpCmd = 'HELP', + kAboutCmd = 'ABOU', + kQuitCmd = 'QUIT', + kChooseCmd = 'CHOS' +}; ScummMenuDialog::ScummMenuDialog(ScummEngine *scumm) - : MainMenuDialog(scumm) { + : ScummDialog("ScummMain"), _vm(scumm) { + + new GUI::ButtonWidget(this, "ScummMain.Resume", "Resume", kPlayCmd, 'P'); + + _loadButton = new GUI::ButtonWidget(this, "ScummMain.Load", "Load", kLoadCmd, 'L'); + _saveButton = new GUI::ButtonWidget(this, "ScummMain.Save", "Save", kSaveCmd, 'S'); + + new GUI::ButtonWidget(this, "ScummMain.Options", "Options", kOptionsCmd, 'O'); +#ifndef DISABLE_HELP + new GUI::ButtonWidget(this, "ScummMain.Help", "Help", kHelpCmd, 'H'); +#endif + new GUI::ButtonWidget(this, "ScummMain.About", "About", kAboutCmd, 'A'); + + new GUI::ButtonWidget(this, "ScummMain.Quit", "Quit", kQuitCmd, 'Q'); + + // + // Create the sub dialog(s) + // + _aboutDialog = new GUI::AboutDialog(); + _optionsDialog = new ConfigDialog(); +#ifndef DISABLE_HELP _helpDialog = new HelpDialog(scumm->_game); - _helpButton->setEnabled(true); +#endif + _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save"); + _saveDialog->setSaveMode(true); + _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load"); + _loadDialog->setSaveMode(false); } ScummMenuDialog::~ScummMenuDialog() { + delete _aboutDialog; + delete _optionsDialog; +#ifndef DISABLE_HELP delete _helpDialog; +#endif + delete _saveDialog; + delete _loadDialog; +} + +int ScummMenuDialog::runModal() { + _loadButton->setEnabled(_vm->canLoadGameStateCurrently()); + _saveButton->setEnabled(_vm->canSaveGameStateCurrently()); + return ScummDialog::runModal(); +} + +void ScummMenuDialog::reflowLayout() { + _loadButton->setEnabled(_vm->canLoadGameStateCurrently()); + _saveButton->setEnabled(_vm->canSaveGameStateCurrently()); + Dialog::reflowLayout(); } void ScummMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { switch (cmd) { + case kSaveCmd: + save(); + break; + case kLoadCmd: + load(); + break; + case kPlayCmd: + close(); + break; + case kOptionsCmd: + _optionsDialog->runModal(); + break; + case kAboutCmd: + _aboutDialog->runModal(); + break; +#ifndef DISABLE_HELP case kHelpCmd: _helpDialog->runModal(); break; +#endif + case kQuitCmd: + _vm->quitGame(); + close(); + break; default: - MainMenuDialog::handleCommand(sender, cmd, data); + ScummDialog::handleCommand(sender, cmd, data); + } +} + +void ScummMenuDialog::save() { + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int idx = _saveDialog->runModal(plugin, ConfMan.getActiveDomainName()); + if (idx >= 0) { + String result(_saveDialog->getResultString()); + char buffer[20]; + const char *str; + if (result.empty()) { + // If the user was lazy and entered no save name, come up with a default name. + sprintf(buffer, "Save %d", idx); + str = buffer; + } else + str = result.c_str(); + _vm->requestSave(idx, str); + close(); + } +} + +void ScummMenuDialog::load() { + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int idx = _loadDialog->runModal(plugin, ConfMan.getActiveDomainName()); + if (idx >= 0) { + _vm->requestLoad(idx); + close(); } } #pragma mark - enum { + kKeysCmd = 'KEYS' +}; + +// FIXME: We use the empty string as domain name here. This tells the +// ConfigManager to use the 'default' domain for all its actions. We do that +// to get as close as possible to editing the 'active' settings. +// +// However, that requires bad & evil hacks in the ConfigManager code, +// and even then still doesn't work quite correctly. +// For example, if the transient domain contains 'false' for the 'fullscreen' +// flag, but the user used a hotkey to switch to windowed mode, then the dialog +// will display the wrong value anyway. +// +// Proposed solution consisting of multiple steps: +// 1) Add special code to the open() code that reads out everything stored +// in the transient domain that is controlled by this dialog, and updates +// the dialog accordingly. +// 2) Even more code is added to query the backend for current settings, like +// the fullscreen mode flag etc., and also updates the dialog accordingly. +// 3) The domain being edited is set to the active game domain. +// 4) If the dialog is closed with the "OK" button, then we remove everything +// stored in the transient domain (or at least everything corresponding to +// switches in this dialog. +// If OTOH the dialog is closed with "Cancel" we do no such thing. +// +// These changes will achieve two things at once: Allow us to get rid of using +// "" as value for the domain, and in fact provide a somewhat better user +// experience at the same time. +ConfigDialog::ConfigDialog() + : GUI::OptionsDialog("", "ScummConfig") { + + // + // Sound controllers + // + + addVolumeControls(this, "ScummConfig."); + + // + // Some misc options + // + + // SCUMM has a talkspeed range of 0-9 + addSubtitleControls(this, "ScummConfig.", 9); + + // + // Add the buttons + // + + new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O'); + new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); +#ifdef SMALL_SCREEN_DEVICE + new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K'); + _keysDialog = NULL; +#endif +} + +ConfigDialog::~ConfigDialog() { +#ifdef SMALL_SCREEN_DEVICE + delete _keysDialog; +#endif +} + +void ConfigDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kKeysCmd: +#ifdef SMALL_SCREEN_DEVICE + // + // Create the sub dialog(s) + // + _keysDialog = new GUI::KeysDialog(); + _keysDialog->runModal(); + delete _keysDialog; + _keysDialog = NULL; +#endif + break; + default: + GUI::OptionsDialog::handleCommand (sender, cmd, data); + } +} + +#ifndef DISABLE_HELP + +#pragma mark - + +enum { kNextCmd = 'NEXT', kPrevCmd = 'PREV' }; diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index 41a8ec83c1..7889027dcf 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -27,8 +27,9 @@ #include "common/str.h" #include "gui/dialog.h" +#include "gui/options.h" #include "gui/widget.h" -#include "engines/dialogs.h" +#include "gui/saveload.h" #include "scumm/detection.h" @@ -51,17 +52,32 @@ protected: typedef Common::String String; }; -#ifndef DISABLE_HELP -class ScummMenuDialog : public MainMenuDialog { +class ScummMenuDialog : public ScummDialog { public: ScummMenuDialog(ScummEngine *scumm); ~ScummMenuDialog(); virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + virtual void reflowLayout(); + + int runModal(); protected: + ScummEngine *_vm; + + GUI::Dialog *_aboutDialog; + GUI::Dialog *_optionsDialog; +#ifndef DISABLE_HELP GUI::Dialog *_helpDialog; -}; #endif + GUI::SaveLoadChooser *_saveDialog; + GUI::SaveLoadChooser *_loadDialog; + + GUI::ButtonWidget *_loadButton; + GUI::ButtonWidget *_saveButton; + + void save(); + void load(); +}; /** * A dialog which displays an arbitrary message to the user and returns diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp index c259c3ffd2..886ee99e57 100644 --- a/engines/scumm/he/resource_he.cpp +++ b/engines/scumm/he/resource_he.cpp @@ -633,10 +633,8 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY); /* fill in wr->id, wr->numeric_id */ - if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) { - free(wr); + if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) return NULL; - } } return wr; diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index dc3a5d26b3..8a9570f534 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -508,7 +508,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0); - openMainMenuDialog(); // Display global main menu + scummMenuDialog(); // Display GUI if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0); diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index cc382d9621..9b7e0798eb 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Mon May 24 13:24:24 2010 + This file was generated by the md5table tool on Sun May 9 20:53:55 2010 DO NOT EDIT MANUALLY! */ @@ -170,13 +170,11 @@ static const MD5Table md5table[] = { { "3af61c5edf8e15b43dbafd285b2e9777", "puttcircus", "", "Demo", -1, Common::HE_ISR, Common::kPlatformWindows }, { "3b301b7892f883ce42ab4be6a274fea6", "samnmax", "Floppy", "Floppy", -1, Common::EN_ANY, Common::kPlatformPC }, { "3b832f4a90740bf22e9b8ed42ca0128c", "freddi4", "HE 99", "", -1, Common::EN_GRB, Common::kPlatformWindows }, - { "3c4c471342bd95505a42334367d8f127", "puttmoon", "HE 70", "", 12161, Common::RU_RUS, Common::kPlatformWindows }, { "3cce1913a3bc586b51a75c3892ff18dd", "indy3", "VGA", "VGA", -1, Common::RU_RUS, Common::kPlatformPC }, { "3d219e7546039543307b55a91282bf18", "funpack", "", "", -1, Common::EN_ANY, Common::kPlatformPC }, { "3de99ef0523f8ca7958faa3afccd035a", "spyfox", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "3df6ead57930488bc61e6e41901d0e97", "fbear", "HE 62", "", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "3e48298920fab9b7aec5a971e1bd1fab", "pajama3", "", "Demo", -1, Common::EN_GRB, Common::kPlatformWindows }, - { "3e861421f494711bc6f619d4aba60285", "airport", "", "", 93231, Common::RU_RUS, Common::kPlatformWindows }, { "40564ec47da48a67787d1f9bd043902a", "maniac", "V2 Demo", "V2 Demo", 1988, Common::EN_ANY, Common::kPlatformPC }, { "4167a92a1d46baa4f4127d918d561f88", "tentacle", "", "CD", 7932, Common::EN_ANY, Common::kPlatformUnknown }, { "41958e24d03181ff9a381a66d048a581", "ft", "", "", -1, Common::PT_BRA, Common::kPlatformUnknown }, @@ -245,7 +243,6 @@ static const MD5Table md5table[] = { { "5bd335265a61caa3d78956ad9f88ba23", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "5c21fc49aee8f46e58fef21579e614a1", "thinker1", "", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "5d88b9d6a88e6f8e90cded9d01b7f082", "loom", "VGA", "VGA", 8307, Common::EN_ANY, Common::kPlatformPC }, - { "5dda73606533d66a4c3f4f9ea6e842af", "farm", "", "", 87061, Common::RU_RUS, Common::kPlatformWindows }, { "5e8fb66971a60e523e5afbc4c129c0e8", "socks", "HE 85", "", -1, Common::EN_USA, Common::kPlatformUnknown }, { "5ebb57234b2fe5c5dff641e00184ad81", "freddi", "HE 73", "", -1, Common::FR_FRA, Common::kPlatformWindows }, { "5fbe557049892eb4b709d90916ec97ca", "indy3", "EGA", "EGA", 5361, Common::EN_ANY, Common::kPlatformPC }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index bb50ce7bb2..2359d4a04f 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -108,7 +108,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _language(dr.language), _debugger(0), _currentScript(0xFF), // Let debug() work on init stage - _messageDialog(0), _pauseDialog(0), _versionDialog(0) { + _messageDialog(0), _pauseDialog(0), _scummMenuDialog(0), _versionDialog(0) { if (_game.platform == Common::kPlatformNES) { _gdi = new GdiNES(this); @@ -140,6 +140,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _fileHandle = 0; + // Init all vars _v0ObjectIndex = false; _v0ObjectInInventory = false; @@ -151,6 +152,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _sound = NULL; memset(&vm, 0, sizeof(vm)); _pauseDialog = NULL; + _scummMenuDialog = NULL; _versionDialog = NULL; _fastMode = 0; _actors = NULL; @@ -550,12 +552,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) for (int i = 0; i < ARRAYSIZE(debugChannels); ++i) DebugMan.addDebugChannel(debugChannels[i].flag, debugChannels[i].channel, debugChannels[i].desc); -#ifndef DISABLE_HELP - // Create custom GMM dialog providing a help subdialog - assert(!_mainMenuDialog); - _mainMenuDialog = new ScummMenuDialog(this); -#endif - g_eventRec.registerRandomSource(_rnd, "scumm"); } @@ -576,6 +572,7 @@ ScummEngine::~ScummEngine() { delete _charset; delete _messageDialog; delete _pauseDialog; + delete _scummMenuDialog; delete _versionDialog; delete _fileHandle; @@ -2440,6 +2437,13 @@ void ScummEngine::versionDialog() { runDialog(*_versionDialog); } +void ScummEngine::scummMenuDialog() { + if (!_scummMenuDialog) + _scummMenuDialog = new ScummMenuDialog(this); + runDialog(*_scummMenuDialog); + syncSoundSettings(); +} + void ScummEngine::confirmExitDialog() { ConfirmDialog d(this, 6); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 42322ba5a2..885ab790de 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -530,6 +530,7 @@ protected: Dialog *_pauseDialog; Dialog *_messageDialog; Dialog *_versionDialog; + Dialog *_scummMenuDialog; virtual int runDialog(Dialog &dialog); void confirmExitDialog(); @@ -537,6 +538,7 @@ protected: void pauseDialog(); void messageDialog(const char *message); void versionDialog(); + void scummMenuDialog(); char displayMessage(const char *altButton, const char *message, ...) GCC_PRINTF(3, 4); diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index 441e622184..c0e7be7758 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -23,6 +23,7 @@ * */ + #include "common/file.h" #include "sword1/sword1.h" #include "sword1/animation.h" @@ -71,9 +72,6 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSys _bgSoundStream = NULL; _decoderType = decoderType; _decoder = decoder; - - _white = 255; - _black = 0; } MoviePlayer::~MoviePlayer() { @@ -256,35 +254,9 @@ bool MoviePlayer::playVideo() { if (frame) _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); - if (_decoder->hasDirtyPalette()) { + if (_decoder->hasDirtyPalette()) _decoder->setSystemPalette(); - uint32 maxWeight = 0; - uint32 minWeight = 0xFFFFFFFF; - uint32 weight; - byte r, g, b; - - byte *palette = _decoder->getPalette(); - - for (int i = 0; i < 256; i++) { - r = *palette++; - g = *palette++; - b = *palette++; - - weight = 3 * r * r + 6 * g * g + 2 * b * b; - - if (weight >= maxWeight) { - maxWeight = weight; - _white = i; - } - - if (weight <= minWeight) { - minWeight = weight; - _black = i; - } - } - } - Graphics::Surface *screen = _vm->_system->lockScreen(); performPostProcessing((byte *)screen->pixels); _vm->_system->unlockScreen(); @@ -295,19 +267,17 @@ bool MoviePlayer::playVideo() { while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) return false; - - _vm->_system->delayMillis(10); } return !_vm->shouldQuit(); } byte MoviePlayer::findBlackPalIndex() { - return _black; + return 0; } byte MoviePlayer::findWhitePalIndex() { - return _white; + return 0xff; } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index 193d5cf7ca..82343f2800 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -85,7 +85,6 @@ protected: OSystem *_system; Common::Array<MovieText *> _movieTexts; int _textX, _textY, _textWidth, _textHeight; - byte _white, _black; DecoderType _decoderType; Graphics::VideoDecoder *_decoder; diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 10895b2ec1..c3f3e796b2 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -51,9 +51,6 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, A _bgSoundStream = NULL; _decoderType = decoderType; _decoder = decoder; - - _white = 255; - _black = 0; } MoviePlayer:: ~MoviePlayer() { @@ -283,35 +280,9 @@ bool MoviePlayer::playVideo() { if (frame) _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); - if (_decoder->hasDirtyPalette()) { + if (_decoder->hasDirtyPalette()) _decoder->setSystemPalette(); - uint32 maxWeight = 0; - uint32 minWeight = 0xFFFFFFFF; - uint32 weight; - byte r, g, b; - - byte *palette = _decoder->getPalette(); - - for (int i = 0; i < 256; i++) { - r = *palette++; - g = *palette++; - b = *palette++; - - weight = 3 * r * r + 6 * g * g + 2 * b * b; - - if (weight >= maxWeight) { - maxWeight = weight; - _white = i; - } - - if (weight <= minWeight) { - minWeight = weight; - _black = i; - } - } - } - Graphics::Surface *screen = _vm->_system->lockScreen(); performPostProcessing((byte *)screen->pixels); _vm->_system->unlockScreen(); @@ -322,19 +293,17 @@ bool MoviePlayer::playVideo() { while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) return false; - - _vm->_system->delayMillis(10); } return !_vm->shouldQuit(); } byte MoviePlayer::findBlackPalIndex() { - return _black; + return 0; } byte MoviePlayer::findWhitePalIndex() { - return _white; + return 0xff; } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index ee32b1d5f2..bbf83e264c 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -87,7 +87,6 @@ protected: uint32 _currentMovieText; byte *_textSurface; int _textX, _textY; - byte _white, _black; DecoderType _decoderType; Graphics::VideoDecoder *_decoder; diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index 60eb08a2dd..de573feee2 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -288,8 +288,7 @@ void LoadFile(MEMHANDLE *pH) { } // extract and zero terminate the filename - memcpy(szFilename, pH->szName, sizeof(pH->szName)); - szFilename[sizeof(pH->szName)] = 0; + Common::strlcpy(szFilename, pH->szName, sizeof(pH->szName)); if (f.open(szFilename)) { // read the data diff --git a/graphics/conversion.h b/graphics/conversion.h index b6d230612e..149d7204c6 100644 --- a/graphics/conversion.h +++ b/graphics/conversion.h @@ -45,6 +45,13 @@ inline static void RGB2YUV(byte r, byte g, byte b, byte &y, byte &u, byte &v) { v = CLIP<int>( ((r * 512) >> 10) - ((g * 429) >> 10) - ((b * 83) >> 10) + 128, 0, 255); } +/** Converting a color from YUV to RGB colorspace, Cinepak style. */ +inline static void CPYUV2RGB(byte y, byte u, byte v, byte &r, byte &g, byte &b) { + r = CLIP<int>(y + 2 * (v - 128), 0, 255); + g = CLIP<int>(y - (u - 128) / 2 - (v - 128), 0, 255); + b = CLIP<int>(y + 2 * (u - 128), 0, 255); +} + // TODO: generic YUV to RGB blit /** diff --git a/graphics/module.mk b/graphics/module.mk index 75e3919370..ff6d7f8f60 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -13,7 +13,6 @@ MODULE_OBJS := \ iff.o \ imagedec.o \ jpeg.o \ - pict.o \ primitives.o \ scaler.o \ scaler/thumbnail_intern.o \ @@ -26,17 +25,10 @@ MODULE_OBJS := \ video/dxa_decoder.o \ video/flic_decoder.o \ video/mpeg_player.o \ - video/qt_decoder.o \ video/smk_decoder.o \ video/video_decoder.o \ - video/codecs/cinepak.o \ - video/codecs/mjpeg.o \ video/codecs/msrle.o \ video/codecs/msvideo1.o \ - video/codecs/qdm2.o \ - video/codecs/qtrle.o \ - video/codecs/rpza.o \ - video/codecs/smc.o \ video/coktelvideo/indeo3.o \ video/coktelvideo/coktelvideo.o diff --git a/graphics/video/avi_decoder.cpp b/graphics/video/avi_decoder.cpp index 4c3c770c60..944c9700bd 100644 --- a/graphics/video/avi_decoder.cpp +++ b/graphics/video/avi_decoder.cpp @@ -35,7 +35,6 @@ #include "graphics/video/avi_decoder.h" // Codecs -#include "graphics/video/codecs/cinepak.h" #include "graphics/video/codecs/msvideo1.h" #include "graphics/video/codecs/msrle.h" @@ -154,8 +153,10 @@ void AviDecoder::handleStreamHeader() { sHeader.bufferSize = _fileStream->readUint32LE(); sHeader.quality = _fileStream->readUint32LE(); sHeader.sampleSize = _fileStream->readUint32LE(); - - _fileStream->skip(sHeader.size - 48); // Skip over the remainder of the chunk (frame) + sHeader.frame.left = _fileStream->readSint16LE(); + sHeader.frame.top = _fileStream->readSint16LE(); + sHeader.frame.right = _fileStream->readSint16LE(); + sHeader.frame.bottom = _fileStream->readSint16LE(); if (_fileStream->readUint32BE() != ID_STRF) error("Could not find STRF tag"); @@ -186,14 +187,11 @@ void AviDecoder::handleStreamHeader() { _palette[i * 3 + 2] = _fileStream->readByte(); _palette[i * 3 + 1] = _fileStream->readByte(); _palette[i * 3] = _fileStream->readByte(); - _fileStream->readByte(); + /*_palette[i * 4 + 3] = */_fileStream->readByte(); } _dirtyPalette = true; } - - if (!_vidsHeader.streamHandler) - _vidsHeader.streamHandler = _bmInfo.compression; } else if (sHeader.streamType == ID_AUDS) { _audsHeader = sHeader; @@ -203,11 +201,6 @@ void AviDecoder::handleStreamHeader() { _wvInfo.avgBytesPerSec = _fileStream->readUint32LE(); _wvInfo.blockAlign = _fileStream->readUint16LE(); _wvInfo.size = _fileStream->readUint16LE(); - - // AVI seems to treat the sampleSize as including the second - // channel as well, so divide for our sake. - if (_wvInfo.channels == 2) - _audsHeader.sampleSize /= 2; } } @@ -331,9 +324,6 @@ Surface *AviDecoder::decodeNextFrame() { else flags |= Audio::FLAG_UNSIGNED; - if (_wvInfo.channels == 2) - flags |= Audio::FLAG_STEREO; - _audStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, flags); _fileStream->skip(chunkSize & 1); // Alignment } else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' || @@ -389,8 +379,6 @@ Codec *AviDecoder::createCodec() { return new MSVideo1Decoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); case ID_RLE : return new MSRLEDecoder(_bmInfo.width, _bmInfo.height, _bmInfo.bitCount); - case ID_CVID: - return new CinepakDecoder(); default: warning ("Unknown/Unhandled compression format \'%s\'", tag2str(_vidsHeader.streamHandler)); } @@ -405,7 +393,7 @@ PixelFormat AviDecoder::getPixelFormat() const { Audio::QueuingAudioStream *AviDecoder::createAudioStream() { if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM) - return Audio::makeQueuingAudioStream(AUDIO_RATE, _wvInfo.channels == 2); + return Audio::makeQueuingAudioStream(AUDIO_RATE, false); if (_wvInfo.tag != 0) // No sound warning ("Unsupported AVI audio format %d", _wvInfo.tag); diff --git a/graphics/video/codecs/mjpeg.cpp b/graphics/video/codecs/mjpeg.cpp deleted file mode 100644 index 76363036ee..0000000000 --- a/graphics/video/codecs/mjpeg.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* 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 "common/system.h" -#include "graphics/conversion.h" // For YUV2RGB - -#include "graphics/video/codecs/mjpeg.h" - -namespace Graphics { - -JPEGDecoder::JPEGDecoder() : Codec() { - _jpeg = new JPEG(); - _pixelFormat = g_system->getScreenFormat(); - _surface = NULL; -} - -JPEGDecoder::~JPEGDecoder() { - delete _jpeg; - - if (_surface) { - _surface->free(); - delete _surface; - } -} - -Surface *JPEGDecoder::decodeImage(Common::SeekableReadStream* stream) { - _jpeg->read(stream); - Surface *ySurface = _jpeg->getComponent(1); - Surface *uSurface = _jpeg->getComponent(2); - Surface *vSurface = _jpeg->getComponent(3); - - if (!_surface) { - _surface = new Surface(); - _surface->create(ySurface->w, ySurface->h, _pixelFormat.bytesPerPixel); - } - - for (uint16 i = 0; i < _surface->h; i++) { - for (uint16 j = 0; j < _surface->w; j++) { - byte r = 0, g = 0, b = 0; - YUV2RGB(*((byte *)ySurface->getBasePtr(j, i)), *((byte *)uSurface->getBasePtr(j, i)), *((byte *)vSurface->getBasePtr(j, i)), r, g, b); - if (_pixelFormat.bytesPerPixel == 2) - *((uint16 *)_surface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); - else - *((uint32 *)_surface->getBasePtr(j, i)) = _pixelFormat.RGBToColor(r, g, b); - } - } - - return _surface; -} - -} // End of namespace Graphics diff --git a/graphics/video/codecs/qdm2.h b/graphics/video/codecs/qdm2.h deleted file mode 100644 index c9a01a70ee..0000000000 --- a/graphics/video/codecs/qdm2.h +++ /dev/null @@ -1,54 +0,0 @@ -/* 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$ - * - */ - -// Only compile if Mohawk is enabled or if we're building dynamic modules -#if defined(ENABLE_MOHAWK) || defined(DYNAMIC_MODULES) - -#ifndef GRAPHICS_QDM2_H -#define GRAPHICS_QDM2_H - -namespace Common { - class SeekableReadStream; -} - -namespace Audio { - class AudioStream; -} - -namespace Graphics { - -/** - * Create a new AudioStream from the QDM2 data in the given stream. - * - * @param stream the SeekableReadStream from which to read the FLAC data - * @param extraData the QuickTime extra data stream - * @return a new AudioStream, or NULL, if an error occured - */ -Audio::AudioStream *makeQDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); - -} // End of namespace Graphics - -#endif // GRAPHICS_QDM2_H -#endif // Mohawk/Plugins guard diff --git a/graphics/video/smk_decoder.cpp b/graphics/video/smk_decoder.cpp index 0b7de774eb..3f200b5971 100644 --- a/graphics/video/smk_decoder.cpp +++ b/graphics/video/smk_decoder.cpp @@ -25,7 +25,7 @@ // Based on http://wiki.multimedia.cx/index.php?title=Smacker // and the FFmpeg Smacker decoder (libavcodec/smacker.c), revision 16143 -// http://git.ffmpeg.org/?p=ffmpeg;a=blob;f=libavcodec/smacker.c;hb=b8437a00a2f14d4a437346455d624241d726128e +// http://svn.ffmpeg.org/ffmpeg/trunk/libavcodec/smacker.c?revision=16143&view=markup #include "graphics/video/smk_decoder.h" @@ -361,7 +361,7 @@ SmackerDecoder::~SmackerDecoder() { } uint32 SmackerDecoder::getElapsedTime() const { - if (_audioStream && _audioStarted) + if (_audioStream) return _mixer->getSoundElapsedTime(_audioHandle); return VideoDecoder::getElapsedTime(); diff --git a/gui/ListWidget.cpp b/gui/ListWidget.cpp index 97dde29824..ad6c7e0c3a 100644 --- a/gui/ListWidget.cpp +++ b/gui/ListWidget.cpp @@ -36,7 +36,7 @@ namespace GUI { -ListWidget::ListWidget(Dialog *boss, const String &name, uint32 cmd) +ListWidget::ListWidget(GuiObject *boss, const String &name, uint32 cmd) : EditableWidget(boss, name), _cmd(cmd) { _scrollBar = NULL; @@ -68,7 +68,7 @@ ListWidget::ListWidget(Dialog *boss, const String &name, uint32 cmd) _editColor = ThemeEngine::kFontColorNormal; } -ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, uint32 cmd) +ListWidget::ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd) : EditableWidget(boss, x, y, w, h), _cmd(cmd) { _scrollBar = NULL; @@ -472,10 +472,6 @@ void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { if (_currentPos != (int)data) { _currentPos = data; draw(); - - // Scrollbar actions cause list focus (which triggers a redraw) - // NOTE: ListWidget's boss is always GUI::Dialog - ((GUI::Dialog *)_boss)->setFocusWidget(this); } break; } diff --git a/gui/ListWidget.h b/gui/ListWidget.h index a3fb825761..02f2c22e7c 100644 --- a/gui/ListWidget.h +++ b/gui/ListWidget.h @@ -87,8 +87,8 @@ protected: ThemeEngine::FontColor _editColor; public: - ListWidget(Dialog *boss, const String &name, uint32 cmd = 0); - ListWidget(Dialog *boss, int x, int y, int w, int h, uint32 cmd = 0); + ListWidget(GuiObject *boss, const String &name, uint32 cmd = 0); + ListWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd = 0); virtual ~ListWidget(); virtual Widget *findWidget(int x, int y); diff --git a/gui/about.cpp b/gui/about.cpp index 8b2f470bf6..74851caf94 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -81,10 +81,31 @@ AboutDialog::AboutDialog() : Dialog(10, 20, 300, 174), _scrollPos(0), _scrollTime(0), _willClose(false) { - reflowLayout(); - int i; + const int screenW = g_system->getOverlayWidth(); + const int screenH = g_system->getOverlayHeight(); + + _xOff = g_gui.xmlEval()->getVar("Globals.About.XOffset", 5); + _yOff = g_gui.xmlEval()->getVar("Globals.About.YOffset", 5); + int outerBorder = g_gui.xmlEval()->getVar("Globals.About.OuterBorder"); + + _w = screenW - 2 * outerBorder; + _h = screenH - 2 * outerBorder; + + _lineHeight = g_gui.getFontHeight() + 3; + + // Heuristic to compute 'optimal' dialog width + int maxW = _w - 2*_xOff; + _w = 0; + for (i = 0; i < ARRAYSIZE(credits); i++) { + int tmp = g_gui.getStringWidth(credits[i] + 5); + if (_w < tmp && tmp <= maxW) { + _w = tmp; + } + } + _w += 2*_xOff; + for (i = 0; i < 1; i++) _lines.push_back(""); @@ -130,6 +151,10 @@ AboutDialog::AboutDialog() for (i = 0; i < ARRAYSIZE(credits); i++) addLine(credits[i]); + + // Center the dialog + _x = (screenW - _w) / 2; + _y = (screenH - _h) / 2; } void AboutDialog::addLine(const char *str) { @@ -269,7 +294,6 @@ void AboutDialog::handleKeyUp(Common::KeyState state) { void AboutDialog::reflowLayout() { Dialog::reflowLayout(); - int i; const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); @@ -285,7 +309,7 @@ void AboutDialog::reflowLayout() { // Heuristic to compute 'optimal' dialog width int maxW = _w - 2*_xOff; _w = 0; - for (i = 0; i < ARRAYSIZE(credits); i++) { + for (int i = 0; i < ARRAYSIZE(credits); i++) { int tmp = g_gui.getStringWidth(credits[i] + 5); if (_w < tmp && tmp <= maxW) { _w = tmp; @@ -293,7 +317,8 @@ void AboutDialog::reflowLayout() { } _w += 2*_xOff; - // Center the dialog + _lineHeight = g_gui.getFontHeight() + 3; + _x = (screenW - _w) / 2; _y = (screenH - _h) / 2; } diff --git a/gui/editable.cpp b/gui/editable.cpp index 058f08e233..723384ed44 100644 --- a/gui/editable.cpp +++ b/gui/editable.cpp @@ -65,7 +65,7 @@ void EditableWidget::setEditString(const String &str) { // TODO: We probably should filter the input string here, // e.g. using tryInsertChar. _editString = str; - _caretPos = 0; + _caretPos = _editString.size(); } bool EditableWidget::tryInsertChar(byte c, int pos) { diff --git a/gui/themes/default.inc b/gui/themes/default.inc index 93897a7d88..75c4fb73de 100644 --- a/gui/themes/default.inc +++ b/gui/themes/default.inc @@ -1,397 +1,38 @@ "<?xml version = '1.0'?>" -"<render_info> " -"<palette> " -"<color name='black' " -"rgb='0,0,0' " -"/> " -"<color name='lightgrey' " -"rgb='104,104,104' " -"/> " -"<color name='darkgrey' " -"rgb='64,64,64' " -"/> " -"<color name='green' " -"rgb='32,160,32' " -"/> " -"<color name='green2' " -"rgb='0,255,0' " -"/> " -"</palette> " -"<fonts> " -"<font id='text_default' " -"file='default' " -"/> " -"<font id='text_button' " -"file='default' " -"/> " -"<font id='text_normal' " -"file='default' " -"/> " -"<text_color id='color_normal' " -"color='green' " -"/> " -"<text_color id='color_normal_inverted' " -"color='black' " -"/> " -"<text_color id='color_normal_hover' " -"color='green2' " -"/> " -"<text_color id='color_normal_disabled' " -"color='lightgrey' " -"/> " -"<text_color id='color_alternative' " -"color='lightgrey' " -"/> " -"<text_color id='color_alternative_inverted' " -"color='255,255,255' " -"/> " -"<text_color id='color_alternative_hover' " -"color='176,176,176' " -"/> " -"<text_color id='color_alternative_disabled' " -"color='darkgrey' " -"/> " -"<text_color id='color_button' " -"color='green' " -"/> " -"<text_color id='color_button_hover' " -"color='green2' " -"/> " -"<text_color id='color_button_disabled' " -"color='lightgrey' " -"/> " -"</fonts> " -"<defaults fill='foreground' fg_color='darkgrey' bg_color='black' shadow='0' bevel_color='lightgrey'/> " -"<drawdata id='text_selection' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='lightgrey' " -"/> " -"</drawdata> " -"<drawdata id='text_selection_focus' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='green' " -"/> " -"</drawdata> " -"<drawdata id='mainmenu_bg' cache='false'> " -"<drawstep func='fill' " -"fill='foreground' " -"fg_color='black' " -"/> " -"</drawdata> " -"<drawdata id='special_bg' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"/> " -"</drawdata> " -"<drawdata id='separator' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"height='2' " -"ypos='center' " -"fg_color='lightgrey' " -"/> " -"</drawdata> " -"<drawdata id='scrollbar_base' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"/> " -"</drawdata> " -"<drawdata id='scrollbar_handle_hover' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='green2' " -"/> " -"</drawdata> " -"<drawdata id='scrollbar_handle_idle' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='green' " -"/> " -"</drawdata> " -"<drawdata id='scrollbar_button_idle' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"<drawstep func='triangle' " -"fg_color='green' " -"fill='foreground' " -"width='auto' " -"height='auto' " -"xpos='center' " -"ypos='center' " -"orientation='top' " -"/> " -"</drawdata> " -"<drawdata id='scrollbar_button_hover' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"<drawstep func='triangle' " -"fg_color='green2' " -"fill='foreground' " -"width='auto' " -"height='auto' " -"xpos='center' " -"ypos='center' " -"orientation='top' " -"/> " -"</drawdata> " -"<drawdata id='tab_active' cache='false'> " -"<text font='text_default' " -"text_color='color_normal_hover' " -"vertical_align='center' " -"horizontal_align='center' " -"/> " -"<drawstep func='tab' " -"bevel='2' " -"radius='0' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='tab_inactive' cache='false'> " -"<text font='text_default' " -"text_color='color_normal' " -"vertical_align='center' " -"horizontal_align='center' " -"/> " -"<drawstep func='tab' " -"bevel='2' " -"radius='0' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='tab_background' cache='false'> " -"</drawdata> " -"<drawdata id='widget_slider' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='slider_disabled' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='lightgrey' " -"/> " -"</drawdata> " -"<drawdata id='slider_full' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='green' " -"/> " -"</drawdata> " -"<drawdata id='slider_hover' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='green2' " -"/> " -"</drawdata> " -"<drawdata id='widget_small' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='popup_idle' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"<drawstep func='triangle' " -"fg_color='green' " -"fill='foreground' " -"width='height' " -"height='auto' " -"xpos='right' " -"ypos='center' " -"orientation='bottom' " -"/> " -"<text font='text_default' " -"text_color='color_normal' " -"vertical_align='center' " -"horizontal_align='left' " -"/> " -"</drawdata> " -"<drawdata id='popup_disabled' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"<drawstep func='triangle' " -"fg_color='lightgrey' " -"fill='foreground' " -"width='height' " -"height='auto' " -"xpos='right' " -"ypos='center' " -"orientation='bottom' " -"/> " -"<text font='text_default' " -"text_color='color_normal_disabled' " -"vertical_align='center' " -"horizontal_align='left' " -"/> " -"</drawdata> " -"<drawdata id='popup_hover' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"<drawstep func='triangle' " -"fg_color='green2' " -"fill='foreground' " -"width='height' " -"height='auto' " -"xpos='right' " -"ypos='center' " -"orientation='bottom' " -"/> " -"<text font='text_default' " -"text_color='color_normal_hover' " -"vertical_align='center' " -"horizontal_align='left' " -"/> " -"</drawdata> " -"<drawdata id='widget_textedit' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='plain_bg' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"/> " -"</drawdata> " -"<drawdata id='caret' cache='false'> " -"<drawstep func='square' " -"fill='foreground' " -"fg_color='lightgrey' " -"/> " -"</drawdata> " -"<drawdata id='default_bg' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"/> " -"</drawdata> " -"<drawdata id='button_idle' cache='false'> " -"<text font='text_button' " -"text_color='color_button' " -"vertical_align='center' " -"horizontal_align='center' " -"/> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='button_hover' cache='false'> " -"<text font='text_button' " -"text_color='color_button_hover' " -"vertical_align='center' " -"horizontal_align='center' " -"/> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='button_disabled' cache='false'> " -"<text font='text_button' " -"text_color='color_button_disabled' " -"vertical_align='center' " -"horizontal_align='center' " -"/> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='checkbox_disabled' cache='false'> " -"<text font='text_default' " -"text_color='color_normal_disabled' " -"vertical_align='top' " -"horizontal_align='left' " -"/> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='checkbox_selected' cache='false'> " -"<text font='text_default' " -"text_color='color_normal' " -"vertical_align='top' " -"horizontal_align='left' " -"/> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"<drawstep func='cross' " -"fill='foreground' " -"stroke='2' " -"fg_color='green' " -"/> " -"</drawdata> " -"<drawdata id='checkbox_default' cache='false'> " -"<text font='text_default' " -"text_color='color_normal' " -"vertical_align='top' " -"horizontal_align='left' " -"/> " -"<drawstep func='bevelsq' " -"bevel='2' " -"fill='none' " -"/> " -"</drawdata> " -"<drawdata id='widget_default' cache='false'> " -"<drawstep func='bevelsq' " -"bevel='2' " -"/> " -"</drawdata> " -"</render_info> " -"<layout_info resolution='-320xY,-256x240,-Xx272'> " +"<layout_info resolution='320xY,256x240,Xx272'> " "<globals> " -"<def var='Line.Height' value='16' /> " -"<def var='Font.Height' value='16' /> " -"<def var='About.OuterBorder' value='80'/> " -"<def var='Layout.Spacing' value='8' /> " +"<def var='Line.Height' value='12' /> " +"<def var='Font.Height' value='10' /> " +"<def var='About.OuterBorder' value='10'/> " +"<def var='Layout.Spacing' value='8'/> " "<def var='ShowLauncherLogo' value='0'/> " "<def var='ShowGlobalMenuLogo' value='0'/> " "<def var='ShowSearchPic' value='0'/> " -"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> " -"<def var='KeyMapper.Spacing' value='10'/> " -"<def var='KeyMapper.LabelWidth' value='100'/> " -"<def var='KeyMapper.ButtonWidth' value='80'/> " +"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> " +"<def var='KeyMapper.Spacing' value='5'/> " +"<def var='KeyMapper.LabelWidth' value='80'/> " +"<def var='KeyMapper.ButtonWidth' value='60'/> " +"<widget name='Button' " +"size='72,16' " +"/> " +"<widget name='Slider' " +"size='85,12' " +"/> " "<widget name='OptionsLabel' " "size='110,Globals.Line.Height' " "textalign='right' " "/> " "<widget name='SmallLabel' " -"size='24,Globals.Line.Height' " -"/> " -"<widget name='ShortOptionsLabel' " -"size='60,Globals.Line.Height' " -"/> " -"<widget name='Button' " -"size='108,24' " -"/> " -"<widget name='Slider' " -"size='128,18' " +"size='18,Globals.Line.Height' " "/> " "<widget name='PopUp' " -"size='-1,19' " +"size='-1,15' " "/> " "<widget name='Checkbox' " -"size='-1,14' " +"size='-1,Globals.Line.Height' " "/> " "<widget name='ListWidget' " -"padding='5,0,8,0' " +"padding='5,0,0,0' " "/> " "<widget name='PopUpWidget' " "padding='7,5,0,0' " @@ -403,25 +44,25 @@ "padding='7,5,5,5' " "/> " "<widget name='Scrollbar' " -"size='15,0' " +"size='9,0' " "/> " "<widget name='TabWidget.Tab' " -"size='75,27' " -"padding='0,0,8,0' " +"size='45,16' " +"padding='0,0,2,0' " "/> " "<widget name='TabWidget.NavButton' " -"size='15,18' " +"size='32,18' " "padding='0,3,4,0' " "/> " "</globals> " "<dialog name='Launcher' overlays='screen'> " -"<layout type='vertical' center='true' padding='16,16,8,8'> " +"<layout type='vertical' center='true' padding='8,8,4,4'> " "<widget name='Version' " "height='Globals.Line.Height' " "/> " "<layout type='horizontal' spacing='5' padding='10,0,0,0'> " "<widget name='SearchDesc' " -"width='60' " +"width='50' " "height='Globals.Line.Height' " "textalign='right' " "/> " @@ -438,37 +79,36 @@ "<widget name='GameList'/> " "<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='LoadGameButton' " -"height='20' " +"height='12' " "/> " "<widget name='AddGameButton' " -"height='20' " +"height='12' " "/> " "<widget name='EditGameButton' " -"height='20' " +"height='12' " "/> " "<widget name='RemoveGameButton' " -"height='20' " +"height='12' " "/> " "</layout> " -"<space size='4'/> " "<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='QuitButton' " -"height='20' " +"height='12' " "/> " "<widget name='AboutButton' " -"height='20' " +"height='12' " "/> " "<widget name='OptionsButton' " -"height='20' " +"height='12' " "/> " "<widget name='StartButton' " -"height='20' " +"height='12' " "/> " "</layout> " "</layout> " "</dialog> " -"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " +"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> " +"<layout type='vertical' padding='8,8,0,4'> " "<widget name='Headline' " "height='Globals.Line.Height' " "/> " @@ -476,7 +116,7 @@ "height='Globals.Line.Height' " "/> " "<widget name='List'/> " -"<layout type='horizontal' padding='0,0,16,0'> " +"<layout type='horizontal' padding='0,0,8,0'> " "<widget name='Up' " "type='Button' " "/> " @@ -490,10 +130,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " +"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> " "<layout type='vertical' padding='0,0,0,0'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='16,16,16,16'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -506,7 +146,7 @@ "</dialog> " "<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='grModePopupDesc' " "type='OptionsLabel' " "/> " @@ -514,7 +154,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='grRenderPopupDesc' " "type='OptionsLabel' " "/> " @@ -532,7 +172,7 @@ "</dialog> " "<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auMidiPopupDesc' " "type='OptionsLabel' " "/> " @@ -540,7 +180,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auOPLPopupDesc' " "type='OptionsLabel' " "/> " @@ -548,7 +188,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='auSampleRatePopupDesc' " "type='OptionsLabel' " "/> " @@ -556,16 +196,16 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " "<widget name='subToggleButton' " -"width='150' " +"width='128' " "height='Globals.Slider.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -579,9 +219,8 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='horizontal' padding='16,16,16,16' spacing='8'> " -"<layout type='vertical' padding='0,0,0,0' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -592,7 +231,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -603,7 +242,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -614,8 +253,8 @@ "type='SmallLabel' " "/> " "</layout> " -"</layout> " -"<layout type='vertical' padding='24,0,24,0' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<space size='110' /> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " "/> " @@ -624,7 +263,7 @@ "</dialog> " "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='mcFontButton' " "type='Button' " "/> " @@ -645,7 +284,7 @@ "<widget name='mcGSCheckbox' " "type='Checkbox' " "/> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='mcMidiGainText' " "type='OptionsLabel' " "/> " @@ -661,7 +300,7 @@ "</dialog> " "<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='SaveButton' " "type='Button' " "/> " @@ -669,7 +308,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -677,7 +316,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='ExtraButton' " "type='Button' " "/> " @@ -697,7 +336,7 @@ "</dialog> " "<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -705,17 +344,21 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='RendererPopupDesc' " -"type='OptionsLabel' " +"width='80' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='RendererPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='AutosavePeriodPopupDesc' " -"type='OptionsLabel' " +"width='80' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='AutosavePeriodPopup' " "type='PopUp' " @@ -750,10 +393,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " +"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> " "<layout type='vertical' padding='0,0,0,0' spacing='16'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='16,16,16,4'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -765,7 +408,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -773,7 +416,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -781,7 +424,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -789,7 +432,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='8,8,8,8' spacing='6'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -797,34 +440,43 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='16,16,16,16'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='Id' " -"type='OptionsLabel' " +"width='35' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='Domain' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='Name' " -"type='OptionsLabel' " +"width='35' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='Desc' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<space size='8'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='LangPopupDesc' " -"type='OptionsLabel' " +"width='60' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='LangPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='PlatformPopupDesc' " -"type='OptionsLabel' " +"width='60' " +"height='Globals.Line.Height' " +"textalign='right' " "/> " "<widget name='PlatformPopup' " "type='PopUp' " @@ -833,8 +485,8 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='16,16,16,16'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='Savepath' " "type='Button' " "/> " @@ -842,7 +494,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='Extrapath' " "type='Button' " "/> " @@ -850,7 +502,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " "<widget name='Gamepath' " "type='Button' " "/> " @@ -861,57 +513,86 @@ "</layout> " "</dialog> " "<dialog name='GlobalMenu' overlays='screen_center'> " -"<layout type='vertical' padding='16,16,16,16' center='true'> " +"<layout type='vertical' padding='8,8,4,6' center='true'> " "<widget name='Title' " -"width='210' " -"height='Globals.Line.Height' " +"width='160' " +"height='4' " "/> " "<widget name='Version' " -"width='210' " -"height='Globals.Line.Height' " +"width='160' " +"height='4' " +"/> " +"<space size='1'/> " +"<widget name='Load' " +"width='120' " +"height='12' " "/> " +"<widget name='Save' " +"width='120' " +"height='12' " +"/> " +"<space size='1'/> " +"<widget name='Options' " +"width='120' " +"height='12' " +"/> " +"<widget name='About' " +"width='120' " +"height='12' " +"/> " +"<space size='1'/> " "<widget name='Resume' " -"width='150' " -"height='Globals.Button.Height' " +"width='120' " +"height='12' " "/> " -"<space size='10'/> " +"<widget name='RTL' " +"width='120' " +"height='12' " +"/> " +"<widget name='Quit' " +"width='120' " +"height='12' " +"/> " +"</layout> " +"</dialog> " +"<dialog name='ScummMain' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8'> " +"<widget name='Resume' " +"width='Globals.Button.Width' " +"height='14' " +"/> " +"<space size='2'/> " "<widget name='Load' " -"width='150' " -"height='Globals.Button.Height' " +"width='Globals.Button.Width' " +"height='14' " "/> " "<widget name='Save' " -"width='150' " -"height='Globals.Button.Height' " +"width='Globals.Button.Width' " +"height='14' " "/> " -"<space size='10'/> " +"<space size='2'/> " "<widget name='Options' " -"width='150' " -"height='Globals.Button.Height' " +"width='Globals.Button.Width' " +"height='14' " "/> " "<widget name='Help' " -"width='150' " -"height='Globals.Button.Height' " +"width='Globals.Button.Width' " +"height='14' " "/> " "<widget name='About' " -"width='150' " -"height='Globals.Button.Height' " -"/> " -"<space size='10'/> " -"<widget name='RTL' " -"width='150' " -"height='Globals.Button.Height' " +"width='Globals.Button.Width' " +"height='14' " "/> " +"<space size='2'/> " "<widget name='Quit' " -"width='150' " -"height='Globals.Button.Height' " +"width='Globals.Button.Width' " +"height='14' " "/> " "</layout> " "</dialog> " -"<dialog name='GlobalConfig' overlays='screen_center'> " +"<dialog name='ScummConfig' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0'> " -"<layout type='vertical' padding='0,0,0,0' center='true'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -922,7 +603,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -933,7 +614,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -944,25 +625,24 @@ "type='SmallLabel' " "/> " "</layout> " -"</layout> " -"<layout type='vertical' padding='24,24,24,24' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<space size='110' /> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " -"width='80' " +"width='80' " "/> " "</layout> " -"</layout> " -"<space size='8' /> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<space size='4' /> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " "<widget name='subToggleButton' " -"width='158' " +"width='128' " "height='Globals.Slider.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -973,8 +653,8 @@ "type='SmallLabel' " "/> " "</layout> " -"<space size='60'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " +"<space size='20'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='4'> " "<widget name='Keys' " "type='Button' " "/> " @@ -989,23 +669,15 @@ "</layout> " "</dialog> " "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> " -"<layout type='vertical' padding='8,8,8,32' center='true'> " -"<widget name='Title' " -"height='Globals.Line.Height' " -"/> " -"<layout type='horizontal' padding='0,0,0,16' spacing='16'> " +"<layout type='vertical' padding='8,8,8,8' center='true'> " +"<widget name='Title' height='Globals.Line.Height'/> " "<widget name='List' /> " -"<widget name='Thumbnail' " -"width='180' " -"height='200' " -"/> " -"</layout> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,16,0'> " "<space/> " "<widget name='Delete' " "type='Button' " "/> " -"<space size='32'/> " +"<space size='16'/> " "<widget name='Cancel' " "type='Button' " "/> " @@ -1015,16 +687,16 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='ScummHelp' overlays='screen_center'> " -"<layout type='vertical' padding='8,8,8,8' center='true'> " +"<dialog name='ScummHelp' overlays='screen'> " +"<layout type='vertical' padding='8,8,8,8'> " "<widget name='Title' " -"width='320' " +"width='180' " "height='Globals.Line.Height' " "/> " "<widget name='HelpText' " -"height='200' " +"height='140' " "/> " -"<layout type='horizontal' padding='0,0,16,0'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='Prev' " "type='Button' " "/> " @@ -1039,20 +711,20 @@ "</layout> " "</dialog> " "<dialog name='MassAdd' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='8,8,32,8' center='true'> " +"<layout type='vertical' padding='4,4,16,4' center='true'> " "<widget name='DirProgressText' " -"width='480' " +"width='280' " "height='Globals.Line.Height' " "/> " "<widget name='GameProgressText' " -"width='480' " +"width='280' " "height='Globals.Line.Height' " "/> " "<widget name='GameList' " -"width='480' " -"height='250' " +"width='280' " +"height='100' " "/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='4,4,4,4'> " "<widget name='Ok' " "type='Button' " "/> " @@ -1063,20 +735,20 @@ "</layout> " "</dialog> " "<dialog name='KeyMapper' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> " "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='PopupDesc' " "type='OptionsLabel' " "/> " "<widget name='Popup' " "type='PopUp' " -"width='400' " +"width='150' " "height='Globals.Line.Height' " "/> " "</layout> " "<widget name='KeymapArea' " -"width='600' " -"height='280' " +"width='300' " +"height='120' " "/> " "<widget name='Close' " "type='Button' " @@ -1084,40 +756,43 @@ "</layout> " "</dialog> " "</layout_info> " -"<layout_info resolution='320xY,256x240,Xx272'> " +"<layout_info resolution='-320xY,-256x240,-Xx272'> " "<globals> " -"<def var='Line.Height' value='12' /> " -"<def var='Font.Height' value='10' /> " -"<def var='About.OuterBorder' value='10'/> " -"<def var='Layout.Spacing' value='8'/> " +"<def var='Line.Height' value='16' /> " +"<def var='Font.Height' value='16' /> " +"<def var='About.OuterBorder' value='80'/> " +"<def var='Layout.Spacing' value='8' /> " "<def var='ShowLauncherLogo' value='0'/> " "<def var='ShowGlobalMenuLogo' value='0'/> " "<def var='ShowSearchPic' value='0'/> " -"<def var='SaveLoadChooser.ExtInfo.Visible' value='0'/> " -"<def var='KeyMapper.Spacing' value='5'/> " -"<def var='KeyMapper.LabelWidth' value='80'/> " -"<def var='KeyMapper.ButtonWidth' value='60'/> " -"<widget name='Button' " -"size='72,16' " -"/> " -"<widget name='Slider' " -"size='85,12' " -"/> " +"<def var='SaveLoadChooser.ExtInfo.Visible' value='1'/> " +"<def var='KeyMapper.Spacing' value='10'/> " +"<def var='KeyMapper.LabelWidth' value='100'/> " +"<def var='KeyMapper.ButtonWidth' value='80'/> " "<widget name='OptionsLabel' " "size='110,Globals.Line.Height' " "textalign='right' " "/> " "<widget name='SmallLabel' " -"size='18,Globals.Line.Height' " +"size='24,Globals.Line.Height' " +"/> " +"<widget name='ShortOptionsLabel' " +"size='60,Globals.Line.Height' " +"/> " +"<widget name='Button' " +"size='108,24' " +"/> " +"<widget name='Slider' " +"size='128,18' " "/> " "<widget name='PopUp' " -"size='-1,15' " +"size='-1,19' " "/> " "<widget name='Checkbox' " -"size='-1,Globals.Line.Height' " +"size='-1,14' " "/> " "<widget name='ListWidget' " -"padding='5,0,0,0' " +"padding='5,0,8,0' " "/> " "<widget name='PopUpWidget' " "padding='7,5,0,0' " @@ -1129,25 +804,25 @@ "padding='7,5,5,5' " "/> " "<widget name='Scrollbar' " -"size='9,0' " +"size='15,0' " "/> " "<widget name='TabWidget.Tab' " -"size='45,16' " -"padding='0,0,2,0' " +"size='75,27' " +"padding='0,0,8,0' " "/> " "<widget name='TabWidget.NavButton' " -"size='32,18' " +"size='15,18' " "padding='0,3,4,0' " "/> " "</globals> " "<dialog name='Launcher' overlays='screen'> " -"<layout type='vertical' center='true' padding='8,8,4,4'> " +"<layout type='vertical' center='true' padding='16,16,8,8'> " "<widget name='Version' " "height='Globals.Line.Height' " "/> " "<layout type='horizontal' spacing='5' padding='10,0,0,0'> " "<widget name='SearchDesc' " -"width='50' " +"width='60' " "height='Globals.Line.Height' " "textalign='right' " "/> " @@ -1164,36 +839,37 @@ "<widget name='GameList'/> " "<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='LoadGameButton' " -"height='12' " +"height='20' " "/> " "<widget name='AddGameButton' " -"height='12' " +"height='20' " "/> " "<widget name='EditGameButton' " -"height='12' " +"height='20' " "/> " "<widget name='RemoveGameButton' " -"height='12' " +"height='20' " "/> " "</layout> " +"<space size='4'/> " "<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='QuitButton' " -"height='12' " +"height='20' " "/> " "<widget name='AboutButton' " -"height='12' " +"height='20' " "/> " "<widget name='OptionsButton' " -"height='12' " +"height='20' " "/> " "<widget name='StartButton' " -"height='12' " +"height='20' " "/> " "</layout> " "</layout> " "</dialog> " -"<dialog name='Browser' overlays='screen' inset='8' shading='dim'> " -"<layout type='vertical' padding='8,8,0,4'> " +"<dialog name='Browser' overlays='Dialog.Launcher.GameList' shading='dim'> " +"<layout type='vertical' padding='8,8,8,8'> " "<widget name='Headline' " "height='Globals.Line.Height' " "/> " @@ -1201,7 +877,7 @@ "height='Globals.Line.Height' " "/> " "<widget name='List'/> " -"<layout type='horizontal' padding='0,0,8,0'> " +"<layout type='horizontal' padding='0,0,16,0'> " "<widget name='Up' " "type='Button' " "/> " @@ -1215,10 +891,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GlobalOptions' overlays='screen' inset='16' shading='dim'> " +"<dialog name='GlobalOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " "<layout type='vertical' padding='0,0,0,0'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='16,16,16,16'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -1231,7 +907,7 @@ "</dialog> " "<dialog name='GlobalOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='grModePopupDesc' " "type='OptionsLabel' " "/> " @@ -1239,7 +915,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='grRenderPopupDesc' " "type='OptionsLabel' " "/> " @@ -1257,7 +933,7 @@ "</dialog> " "<dialog name='GlobalOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auMidiPopupDesc' " "type='OptionsLabel' " "/> " @@ -1265,7 +941,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auOPLPopupDesc' " "type='OptionsLabel' " "/> " @@ -1273,7 +949,7 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='auSampleRatePopupDesc' " "type='OptionsLabel' " "/> " @@ -1281,16 +957,16 @@ "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " "<widget name='subToggleButton' " -"width='128' " +"width='150' " "height='Globals.Slider.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -1304,8 +980,9 @@ "</layout> " "</dialog> " "<dialog name='GlobalOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='16,16,16,16' spacing='8'> " +"<layout type='vertical' padding='0,0,0,0' spacing='8'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -1316,7 +993,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -1327,7 +1004,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -1338,8 +1015,8 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " -"<space size='110' /> " +"</layout> " +"<layout type='vertical' padding='24,0,24,0' center='true'> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " "/> " @@ -1348,7 +1025,7 @@ "</dialog> " "<dialog name='GlobalOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='mcFontButton' " "type='Button' " "/> " @@ -1369,7 +1046,7 @@ "<widget name='mcGSCheckbox' " "type='Checkbox' " "/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " "<widget name='mcMidiGainText' " "type='OptionsLabel' " "/> " @@ -1385,7 +1062,7 @@ "</dialog> " "<dialog name='GlobalOptions_Paths' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='SaveButton' " "type='Button' " "/> " @@ -1393,7 +1070,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -1401,7 +1078,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='ExtraButton' " "type='Button' " "/> " @@ -1421,7 +1098,7 @@ "</dialog> " "<dialog name='GlobalOptions_Misc' overlays='Dialog.GlobalOptions.TabWidget'> " "<layout type='vertical' padding='16,16,16,16' spacing='8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='ThemeButton' " "type='Button' " "/> " @@ -1429,21 +1106,17 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='RendererPopupDesc' " -"width='80' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='RendererPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='AutosavePeriodPopupDesc' " -"width='80' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='AutosavePeriodPopup' " "type='PopUp' " @@ -1478,10 +1151,10 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='GameOptions' overlays='screen' inset='16' shading='dim'> " +"<dialog name='GameOptions' overlays='Dialog.Launcher.GameList' shading='dim'> " "<layout type='vertical' padding='0,0,0,0' spacing='16'> " "<widget name='TabWidget'/> " -"<layout type='horizontal' padding='8,8,8,8'> " +"<layout type='horizontal' padding='16,16,16,4'> " "<space/> " "<widget name='Cancel' " "type='Button' " @@ -1493,7 +1166,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Graphics' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1501,7 +1174,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Audio' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1509,7 +1182,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_MIDI' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1517,7 +1190,7 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Volume' overlays='Dialog.GlobalOptions.TabWidget'> " -"<layout type='vertical' padding='8,8,8,8' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' spacing='8'> " "<widget name='EnableTabCheckbox' " "type='Checkbox' " "/> " @@ -1525,43 +1198,34 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Game' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='vertical' padding='16,16,16,16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Id' " -"width='35' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='Domain' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Name' " -"width='35' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='Desc' " "type='PopUp' " "/> " "</layout> " -"<space size='8'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='LangPopupDesc' " -"width='60' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='LangPopup' " "type='PopUp' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='PlatformPopupDesc' " -"width='60' " -"height='Globals.Line.Height' " -"textalign='right' " +"type='OptionsLabel' " "/> " "<widget name='PlatformPopup' " "type='PopUp' " @@ -1570,8 +1234,8 @@ "</layout> " "</dialog> " "<dialog name='GameOptions_Paths' overlays='Dialog.GameOptions.TabWidget' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='vertical' padding='16,16,16,16'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Savepath' " "type='Button' " "/> " @@ -1579,7 +1243,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Extrapath' " "type='Button' " "/> " @@ -1587,7 +1251,7 @@ "height='Globals.Line.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='16' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='Gamepath' " "type='Button' " "/> " @@ -1598,55 +1262,81 @@ "</layout> " "</dialog> " "<dialog name='GlobalMenu' overlays='screen_center'> " -"<layout type='vertical' padding='2,2,4,6' center='true' spacing='6'> " +"<layout type='vertical' padding='16,16,16,16' center='true'> " "<widget name='Title' " -"width='160' " -"height='4' " +"width='210' " +"height='Globals.Line.Height' " "/> " "<widget name='Version' " -"width='160' " -"height='4' " +"width='210' " +"height='Globals.Line.Height' " "/> " -"<space size='1'/> " +"<widget name='Resume' " +"width='150' " +"height='Globals.Button.Height' " +"/> " +"<space size='10'/> " "<widget name='Load' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "<widget name='Save' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " -"<space size='1'/> " +"<space size='10'/> " "<widget name='Options' " -"width='120' " -"height='12' " -"/> " -"<widget name='Help' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " "<widget name='About' " -"width='120' " -"height='12' " +"width='150' " +"height='Globals.Button.Height' " "/> " -"<space size='1'/> " +"<space size='10'/> " +"<widget name='RTL' " +"width='150' " +"height='Globals.Button.Height' " +"/> " +"<widget name='Quit' " +"width='150' " +"height='Globals.Button.Height' " +"/> " +"</layout> " +"</dialog> " +"<dialog name='ScummMain' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8'> " "<widget name='Resume' " -"width='120' " -"height='12' " +"type='Button' " "/> " -"<widget name='RTL' " -"width='120' " -"height='12' " +"<space size='15'/> " +"<widget name='Load' " +"type='Button' " +"/> " +"<widget name='Save' " +"type='Button' " +"/> " +"<space size='15'/> " +"<widget name='Options' " +"type='Button' " +"/> " +"<widget name='Help' " +"type='Button' " +"/> " +"<widget name='About' " +"type='Button' " "/> " +"<space size='15'/> " "<widget name='Quit' " -"width='120' " -"height='12' " +"type='Button' " "/> " "</layout> " "</dialog> " -"<dialog name='GlobalConfig' overlays='screen_center'> " +"<dialog name='ScummConfig' overlays='screen_center'> " "<layout type='vertical' padding='8,8,8,8'> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='vertical' padding='0,0,0,0' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='vcMusicText' " "type='OptionsLabel' " "/> " @@ -1657,7 +1347,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='vcSfxText' " "type='OptionsLabel' " "/> " @@ -1668,7 +1358,7 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='8'> " "<widget name='vcSpeechText' " "type='OptionsLabel' " "/> " @@ -1679,24 +1369,25 @@ "type='SmallLabel' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " -"<space size='110' /> " +"</layout> " +"<layout type='vertical' padding='24,24,24,24' center='true'> " "<widget name='vcMuteCheckbox' " "type='Checkbox' " -"width='80' " +"width='80' " "/> " "</layout> " -"<space size='4' /> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"</layout> " +"<space size='8' /> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subToggleDesc' " "type='OptionsLabel' " "/> " "<widget name='subToggleButton' " -"width='128' " +"width='158' " "height='Globals.Slider.Height' " "/> " "</layout> " -"<layout type='horizontal' padding='0,0,0,0' spacing='6' center='true'> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='subSubtitleSpeedDesc' " "type='OptionsLabel' " "/> " @@ -1707,8 +1398,8 @@ "type='SmallLabel' " "/> " "</layout> " -"<space size='20'/> " -"<layout type='horizontal' padding='0,0,0,0' spacing='4'> " +"<space size='60'/> " +"<layout type='horizontal' padding='0,0,0,0' spacing='10'> " "<widget name='Keys' " "type='Button' " "/> " @@ -1723,15 +1414,23 @@ "</layout> " "</dialog> " "<dialog name='SaveLoadChooser' overlays='screen' inset='8' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8' center='true'> " -"<widget name='Title' height='Globals.Line.Height'/> " +"<layout type='vertical' padding='8,8,8,32' center='true'> " +"<widget name='Title' " +"height='Globals.Line.Height' " +"/> " +"<layout type='horizontal' padding='0,0,0,16' spacing='16'> " "<widget name='List' /> " -"<layout type='horizontal' padding='0,0,16,0'> " +"<widget name='Thumbnail' " +"width='180' " +"height='200' " +"/> " +"</layout> " +"<layout type='horizontal' padding='0,0,0,0'> " "<space/> " "<widget name='Delete' " "type='Button' " "/> " -"<space size='16'/> " +"<space size='32'/> " "<widget name='Cancel' " "type='Button' " "/> " @@ -1741,16 +1440,16 @@ "</layout> " "</layout> " "</dialog> " -"<dialog name='ScummHelp' overlays='screen'> " -"<layout type='vertical' padding='8,8,8,8'> " +"<dialog name='ScummHelp' overlays='screen_center'> " +"<layout type='vertical' padding='8,8,8,8' center='true'> " "<widget name='Title' " -"width='180' " +"width='320' " "height='Globals.Line.Height' " "/> " "<widget name='HelpText' " -"height='140' " +"height='200' " "/> " -"<layout type='horizontal' padding='0,0,0,0'> " +"<layout type='horizontal' padding='0,0,16,0'> " "<widget name='Prev' " "type='Button' " "/> " @@ -1765,20 +1464,20 @@ "</layout> " "</dialog> " "<dialog name='MassAdd' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='4,4,16,4' center='true'> " +"<layout type='vertical' padding='8,8,32,8' center='true'> " "<widget name='DirProgressText' " -"width='280' " +"width='480' " "height='Globals.Line.Height' " "/> " "<widget name='GameProgressText' " -"width='280' " +"width='480' " "height='Globals.Line.Height' " "/> " "<widget name='GameList' " -"width='280' " -"height='100' " +"width='480' " +"height='250' " "/> " -"<layout type='horizontal' padding='4,4,4,4'> " +"<layout type='horizontal' padding='8,8,8,8'> " "<widget name='Ok' " "type='Button' " "/> " @@ -1789,20 +1488,20 @@ "</layout> " "</dialog> " "<dialog name='KeyMapper' overlays='screen_center' shading='dim'> " -"<layout type='vertical' padding='8,8,8,8' spacing='10' center='true'> " +"<layout type='vertical' padding='8,8,32,8' spacing='10' center='true'> " "<layout type='horizontal' padding='0,0,0,0' spacing='10' center='true'> " "<widget name='PopupDesc' " "type='OptionsLabel' " "/> " "<widget name='Popup' " "type='PopUp' " -"width='150' " +"width='400' " "height='Globals.Line.Height' " "/> " "</layout> " "<widget name='KeymapArea' " -"width='300' " -"height='120' " +"width='600' " +"height='280' " "/> " "<widget name='Close' " "type='Button' " @@ -1810,3 +1509,359 @@ "</layout> " "</dialog> " "</layout_info> " +"<render_info> " +"<palette> " +"<color name='black' " +"rgb='0,0,0' " +"/> " +"<color name='lightgrey' " +"rgb='104,104,104' " +"/> " +"<color name='darkgrey' " +"rgb='64,64,64' " +"/> " +"<color name='green' " +"rgb='32,160,32' " +"/> " +"<color name='green2' " +"rgb='0,255,0' " +"/> " +"</palette> " +"<fonts> " +"<font id='text_default' " +"file='default' " +"/> " +"<font id='text_button' " +"file='default' " +"/> " +"<font id='text_normal' " +"file='default' " +"/> " +"<text_color id='color_normal' " +"color='green' " +"/> " +"<text_color id='color_normal_inverted' " +"color='black' " +"/> " +"<text_color id='color_normal_hover' " +"color='green2' " +"/> " +"<text_color id='color_normal_disabled' " +"color='lightgrey' " +"/> " +"<text_color id='color_alternative' " +"color='lightgrey' " +"/> " +"<text_color id='color_alternative_inverted' " +"color='255,255,255' " +"/> " +"<text_color id='color_alternative_hover' " +"color='176,176,176' " +"/> " +"<text_color id='color_alternative_disabled' " +"color='darkgrey' " +"/> " +"<text_color id='color_button' " +"color='green' " +"/> " +"<text_color id='color_button_hover' " +"color='green2' " +"/> " +"<text_color id='color_button_disabled' " +"color='lightgrey' " +"/> " +"</fonts> " +"<defaults fill='foreground' fg_color='darkgrey' bg_color='black' shadow='0' bevel_color='lightgrey'/> " +"<drawdata id='text_selection' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='lightgrey' " +"/> " +"</drawdata> " +"<drawdata id='text_selection_focus' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green' " +"/> " +"</drawdata> " +"<drawdata id='mainmenu_bg' cache='false'> " +"<drawstep func='fill' " +"fill='foreground' " +"fg_color='black' " +"/> " +"</drawdata> " +"<drawdata id='special_bg' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"/> " +"</drawdata> " +"<drawdata id='separator' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"height='2' " +"ypos='center' " +"fg_color='lightgrey' " +"/> " +"</drawdata> " +"<drawdata id='scrollbar_base' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"/> " +"</drawdata> " +"<drawdata id='scrollbar_handle_hover' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green2' " +"/> " +"</drawdata> " +"<drawdata id='scrollbar_handle_idle' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green' " +"/> " +"</drawdata> " +"<drawdata id='scrollbar_button_idle' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"<drawstep func='triangle' " +"fg_color='green' " +"fill='foreground' " +"width='auto' " +"height='auto' " +"xpos='center' " +"ypos='center' " +"orientation='top' " +"/> " +"</drawdata> " +"<drawdata id='scrollbar_button_hover' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"<drawstep func='triangle' " +"fg_color='green2' " +"fill='foreground' " +"width='auto' " +"height='auto' " +"xpos='center' " +"ypos='center' " +"orientation='top' " +"/> " +"</drawdata> " +"<drawdata id='tab_active' cache='false'> " +"<text font='text_default' " +"text_color='color_normal_hover' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='tab' " +"bevel='2' " +"radius='0' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='tab_inactive' cache='false'> " +"<text font='text_default' " +"text_color='color_normal' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='tab' " +"bevel='2' " +"radius='0' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='tab_background' cache='false'> " +"</drawdata> " +"<drawdata id='widget_slider' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='slider_disabled' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='lightgrey' " +"/> " +"</drawdata> " +"<drawdata id='slider_full' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green' " +"/> " +"</drawdata> " +"<drawdata id='slider_hover' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='green2' " +"/> " +"</drawdata> " +"<drawdata id='widget_small' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='popup_idle' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"<drawstep func='triangle' " +"fg_color='green' " +"fill='foreground' " +"width='height' " +"height='auto' " +"xpos='right' " +"ypos='center' " +"orientation='bottom' " +"/> " +"<text font='text_default' " +"text_color='color_normal' " +"vertical_align='center' " +"horizontal_align='left' " +"/> " +"</drawdata> " +"<drawdata id='popup_disabled' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"<drawstep func='triangle' " +"fg_color='lightgrey' " +"fill='foreground' " +"width='height' " +"height='auto' " +"xpos='right' " +"ypos='center' " +"orientation='bottom' " +"/> " +"<text font='text_default' " +"text_color='color_normal_disabled' " +"vertical_align='center' " +"horizontal_align='left' " +"/> " +"</drawdata> " +"<drawdata id='popup_hover' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"<drawstep func='triangle' " +"fg_color='green2' " +"fill='foreground' " +"width='height' " +"height='auto' " +"xpos='right' " +"ypos='center' " +"orientation='bottom' " +"/> " +"<text font='text_default' " +"text_color='color_normal_hover' " +"vertical_align='center' " +"horizontal_align='left' " +"/> " +"</drawdata> " +"<drawdata id='widget_textedit' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='plain_bg' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"/> " +"</drawdata> " +"<drawdata id='caret' cache='false'> " +"<drawstep func='square' " +"fill='foreground' " +"fg_color='lightgrey' " +"/> " +"</drawdata> " +"<drawdata id='default_bg' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"/> " +"</drawdata> " +"<drawdata id='button_idle' cache='false'> " +"<text font='text_button' " +"text_color='color_button' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='button_hover' cache='false'> " +"<text font='text_button' " +"text_color='color_button_hover' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='button_disabled' cache='false'> " +"<text font='text_button' " +"text_color='color_button_disabled' " +"vertical_align='center' " +"horizontal_align='center' " +"/> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='checkbox_disabled' cache='false'> " +"<text font='text_default' " +"text_color='color_normal_disabled' " +"vertical_align='top' " +"horizontal_align='left' " +"/> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='checkbox_selected' cache='false'> " +"<text font='text_default' " +"text_color='color_normal' " +"vertical_align='top' " +"horizontal_align='left' " +"/> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"<drawstep func='cross' " +"fill='foreground' " +"stroke='2' " +"fg_color='green' " +"/> " +"</drawdata> " +"<drawdata id='checkbox_default' cache='false'> " +"<text font='text_default' " +"text_color='color_normal' " +"vertical_align='top' " +"horizontal_align='left' " +"/> " +"<drawstep func='bevelsq' " +"bevel='2' " +"fill='none' " +"/> " +"</drawdata> " +"<drawdata id='widget_default' cache='false'> " +"<drawstep func='bevelsq' " +"bevel='2' " +"/> " +"</drawdata> " +"</render_info> " diff --git a/gui/themes/scummclassic.zip b/gui/themes/scummclassic.zip Binary files differindex 67cbbf7990..9733c7a97e 100644 --- a/gui/themes/scummclassic.zip +++ b/gui/themes/scummclassic.zip diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx index 14f19a7151..27164673b4 100644 --- a/gui/themes/scummclassic/classic_layout.stx +++ b/gui/themes/scummclassic/classic_layout.stx @@ -582,10 +582,6 @@ width = '150' height = 'Globals.Button.Height' /> - <widget name = 'Help' - width = '150' - height = 'Globals.Button.Height' - /> <widget name = 'About' width = '150' height = 'Globals.Button.Height' @@ -602,7 +598,36 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'Resume' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Load' + type = 'Button' + /> + <widget name = 'Save' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Options' + type = 'Button' + /> + <widget name = 'Help' + type = 'Button' + /> + <widget name = 'About' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Quit' + type = 'Button' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <layout type = 'horizontal' padding = '0, 0, 0, 0'> <layout type = 'vertical' padding = '0, 0, 0, 0' center = 'true'> diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx index 8a0180db3c..c8ecb1abaf 100644 --- a/gui/themes/scummclassic/classic_layout_lowres.stx +++ b/gui/themes/scummclassic/classic_layout_lowres.stx @@ -565,7 +565,7 @@ </dialog> <dialog name = 'GlobalMenu' overlays = 'screen_center'> - <layout type = 'vertical' padding = '2, 2, 4, 6' center = 'true' spacing='6'> + <layout type = 'vertical' padding = '8, 8, 4, 6' center = 'true'> <widget name = 'Title' width = '160' height = '4' @@ -588,10 +588,6 @@ width = '120' height = '12' /> - <widget name = 'Help' - width = '120' - height = '12' - /> <widget name = 'About' width = '120' height = '12' @@ -612,7 +608,43 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'Resume' + width = 'Globals.Button.Width' + height = '14' + /> + <space size = '2'/> + <widget name = 'Load' + width = 'Globals.Button.Width' + height = '14' + /> + <widget name = 'Save' + width = 'Globals.Button.Width' + height = '14' + /> + <space size = '2'/> + <widget name = 'Options' + width = 'Globals.Button.Width' + height = '14' + /> + <widget name = 'Help' + width = 'Globals.Button.Width' + height = '14' + /> + <widget name = 'About' + width = 'Globals.Button.Width' + height = '14' + /> + <space size = '2'/> + <widget name = 'Quit' + width = 'Globals.Button.Width' + height = '14' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'> <widget name = 'vcMusicText' diff --git a/gui/themes/scummmodern.zip b/gui/themes/scummmodern.zip Binary files differindex b3dc16b67e..e6cda4ddbb 100644 --- a/gui/themes/scummmodern.zip +++ b/gui/themes/scummmodern.zip diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx index 25add37462..2fd14f37aa 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -595,10 +595,6 @@ width = '150' height = 'Globals.Button.Height' /> - <widget name = 'Help' - width = '150' - height = 'Globals.Button.Height' - /> <widget name = 'About' width = '150' height = 'Globals.Button.Height' @@ -615,7 +611,36 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'Resume' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Load' + type = 'Button' + /> + <widget name = 'Save' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Options' + type = 'Button' + /> + <widget name = 'Help' + type = 'Button' + /> + <widget name = 'About' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Quit' + type = 'Button' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '8'> <layout type = 'horizontal' padding = '0, 0, 0, 0'> <layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '8' center = 'true'> diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 1c2f83a3b7..e837e41e82 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -561,7 +561,7 @@ </dialog> <dialog name = 'GlobalMenu' overlays = 'screen_center'> - <layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true' spacing='2'> + <layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true'> <widget name = 'Title' width = '160' height = 'Globals.Line.Height' @@ -584,10 +584,6 @@ width = '120' height = 'Globals.Button.Height' /> - <widget name = 'Help' - width = '120' - height = 'Globals.Button.Height' - /> <widget name = 'About' width = '120' height = 'Globals.Button.Height' @@ -608,7 +604,36 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '4, 4, 4, 4'> + <widget name = 'Resume' + type = 'Button' + /> + <space size = '8'/> + <widget name = 'Load' + type = 'Button' + /> + <widget name = 'Save' + type = 'Button' + /> + <space size = '8'/> + <widget name = 'Options' + type = 'Button' + /> + <widget name = 'Help' + type = 'Button' + /> + <widget name = 'About' + type = 'Button' + /> + <space size = '8'/> + <widget name = 'Quit' + type = 'Button' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'> <widget name = 'vcMusicText' @@ -44,9 +44,7 @@ bundle: scummvm-static cp $(srcdir)/icons/scummvm.icns $(bundle_name)/Contents/Resources/ cp $(DIST_FILES_DOCS) $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/Contents/Resources/ -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/Contents/Resources/ -endif $(srcdir)/tools/credits.pl --rtf > $(bundle_name)/Contents/Resources/Credits.rtf chmod 644 $(bundle_name)/Contents/Resources/* cp scummvm-static $(bundle_name)/Contents/MacOS/scummvm @@ -58,9 +56,7 @@ iphonebundle: iphone cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/ cp $(DIST_FILES_DOCS) $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/ -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/ -endif $(STRIP) scummvm ldid -S scummvm chmod 755 scummvm @@ -155,9 +151,7 @@ win32dist: $(EXECUTABLE) mkdir -p $(WIN32PATH) $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) cp $(DIST_FILES_THEMES) $(WIN32PATH) -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH) -endif cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(WIN32PATH)/COPYING.txt cp $(srcdir)/COPYING.LGPL $(WIN32PATH)/COPYING.LGPL.txt @@ -178,9 +172,7 @@ aos4dist: $(EXECUTABLE) $(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE) cp icons/scummvm.info $(AOS4PATH)/$(EXECUTABLE).info cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/ -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/ -endif cp $(srcdir)/AUTHORS $(AOS4PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(AOS4PATH)/COPYING.txt cp $(srcdir)/COPYING.LGPL $(AOS4PATH)/COPYING.LGPL.txt diff --git a/sound/decoders/adpcm.cpp b/sound/decoders/adpcm.cpp index c8a907d13e..7a85bc24d5 100644 --- a/sound/decoders/adpcm.cpp +++ b/sound/decoders/adpcm.cpp @@ -724,10 +724,6 @@ int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) { } RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) { - // If size is 0, report the entire size of the stream - if (!size) - size = stream->size(); - switch (type) { case kADPCMOki: return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); diff --git a/sound/decoders/mp3.cpp b/sound/decoders/mp3.cpp index 732ae58b67..f66d6324ef 100644 --- a/sound/decoders/mp3.cpp +++ b/sound/decoders/mp3.cpp @@ -36,9 +36,7 @@ #include <mad.h> -#if defined(__PSP__) - #include "backends/platform/psp/mp3.h" -#endif + namespace Audio { @@ -349,18 +347,7 @@ int MP3Stream::readBuffer(int16 *buffer, const int numSamples) { SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { - -#if defined(__PSP__) - SeekableAudioStream *s = 0; - - if (Mp3PspStream::isOkToCreateStream()) - s = new Mp3PspStream(stream, disposeAfterUse); - - if (!s) // go to regular MAD mp3 stream if ME fails - s = new MP3Stream(stream, disposeAfterUse); -#else SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse); -#endif if (s && s->endOfData()) { delete s; return 0; diff --git a/sound/decoders/voc.cpp b/sound/decoders/voc.cpp index e9af7ece3f..5663861f05 100644 --- a/sound/decoders/voc.cpp +++ b/sound/decoders/voc.cpp @@ -384,7 +384,7 @@ AudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, uint SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) { #ifdef STREAM_AUDIO_FROM_DISK - return makeVOCDiskStreamNoLoop(stream, flags, disposeAfterUse); + return makeVOCDiskStreamNoLoop(*stream, flags, disposeAfterUse); #else int size, rate; diff --git a/sound/mods/rjp1.cpp b/sound/mods/rjp1.cpp index be376d61a4..fc1b49e9e9 100644 --- a/sound/mods/rjp1.cpp +++ b/sound/mods/rjp1.cpp @@ -422,7 +422,7 @@ void Rjp1::setupNote(Rjp1Channel *channel, int16 period) { channel->envelopeMode = 4; channel->data = channel->waveData; channel->pos = READ_BE_UINT16(note + 16); - channel->len = channel->pos + READ_BE_UINT16(note + 18); + channel->len = READ_BE_UINT16(note + 18); channel->setupNewNote = true; } } diff --git a/test/common/str.h b/test/common/str.h index 6581c37cdb..16fb0859db 100644 --- a/test/common/str.h +++ b/test/common/str.h @@ -118,30 +118,6 @@ class StringTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(foo3, "fooasdkadklasdjklasdjlkasjdlkasjdklasjdlkjasdasd""fooasdkadklasdjklasdjlkasjdlkasjdklasjdlkjasdasd"); } - void test_refCount5() { - // using external storage - Common::String foo1("HelloHelloHelloHelloAndHi"); - Common::String foo2(foo1); - - for (Common::String::iterator i = foo2.begin(); i != foo2.end(); ++i) - *i = 'h'; - - TS_ASSERT_EQUALS(foo1, "HelloHelloHelloHelloAndHi"); - TS_ASSERT_EQUALS(foo2, "hhhhhhhhhhhhhhhhhhhhhhhhh"); - } - - void test_refCount6() { - // using internal storage - Common::String foo1("Hello"); - Common::String foo2(foo1); - - for (Common::String::iterator i = foo2.begin(); i != foo2.end(); ++i) - *i = 'h'; - - TS_ASSERT_EQUALS(foo1, "Hello"); - TS_ASSERT_EQUALS(foo2, "hhhhh"); - } - void test_self_asignment() { Common::String foo1("12345678901234567890123456789012"); foo1 = foo1.c_str() + 2; diff --git a/tools/create_msvc/create_msvc.cpp b/tools/create_msvc/create_msvc.cpp index f418971239..a2636dab21 100644 --- a/tools/create_msvc/create_msvc.cpp +++ b/tools/create_msvc/create_msvc.cpp @@ -511,6 +511,8 @@ int main(int argc, char *argv[]) { // 4103 (alignment changed after including header, may be due to missing #pragma pack(pop)) // used by pack-start / pack-end // + // 4121 (alignment of a member was sensitive to packing) + // // 4127 (conditional expression is constant) // used in a lot of engines // @@ -564,6 +566,7 @@ int main(int argc, char *argv[]) { projectWarnings["lure"] = "4189;4355"; projectWarnings["kyra"] = "4355"; projectWarnings["m4"] = "4355"; + projectWarnings["mohawk"] = "4121"; ProjectProvider *provider = NULL; diff --git a/tools/md5table.c b/tools/md5table.c index 6823607b27..b69f3957cd 100644 --- a/tools/md5table.c +++ b/tools/md5table.c @@ -221,7 +221,7 @@ int main(int argc, char *argv[]) const int entrySize = 256; int numEntries = 0, maxEntries = 1; - char *entriesBuffer = (char *)malloc(maxEntries * entrySize); + char *entriesBuffer = malloc(maxEntries * entrySize); typedef enum { kCPPOutput, @@ -294,7 +294,7 @@ int main(int argc, char *argv[]) } else if (entry.md5) { if (numEntries >= maxEntries) { maxEntries *= 2; - entriesBuffer = (char *)realloc(entriesBuffer, maxEntries * entrySize); + entriesBuffer = realloc(entriesBuffer, maxEntries * entrySize); } if (0 == strcmp(entry.variant, "-")) entry.variant = ""; diff --git a/tools/module.mk b/tools/module.mk index 896a2e504b..4117ceac77 100644 --- a/tools/module.mk +++ b/tools/module.mk @@ -36,15 +36,15 @@ clean-tools: tools/convbdf$(EXEEXT): $(srcdir)/tools/convbdf.c $(QUIET)$(MKDIR) tools/$(DEPDIR) - $(QUIET_LINK)$(LD) $(CFLAGS) -Wall -o $@ $< + $(QUIET_LINK)$(CC) $(CFLAGS) -Wall -o $@ $< tools/md5table$(EXEEXT): $(srcdir)/tools/md5table.c $(QUIET)$(MKDIR) tools/$(DEPDIR) - $(QUIET_LINK)$(LD) $(CFLAGS) -Wall -o $@ $< + $(QUIET_LINK)$(CC) $(CFLAGS) -Wall -o $@ $< tools/make-scumm-fontdata$(EXEEXT): $(srcdir)/tools/make-scumm-fontdata.c $(QUIET)$(MKDIR) tools/$(DEPDIR) - $(QUIET_LINK)$(LD) $(CFLAGS) -Wall -o $@ $< + $(QUIET_LINK)$(CC) $(CFLAGS) -Wall -o $@ $< # # Rules to explicitly rebuild the credits / MD5 tables. diff --git a/tools/scumm-md5.txt b/tools/scumm-md5.txt index 0637c386d7..7b9918ba43 100644 --- a/tools/scumm-md5.txt +++ b/tools/scumm-md5.txt @@ -584,7 +584,6 @@ catalog Humongous Interactive Catalog airport Let's Explore the Airport with Buzzy d6334a5a9b61afe18c368540fdf522ca -1 en Mac - - - Joachim Eberhard 07433205acdca3bc553d0e731588b35f -1 en Windows - - - Kirben - 3e861421f494711bc6f619d4aba60285 93231 ru Windows - - - sev 7ea2da67ebabea4ac20cee9f4f9d2934 -1 en Mac - Demo - khalek 8ffd618a776a4c0d8922bb28b09f8ce8 -1 en Windows - Demo - khalek @@ -596,7 +595,6 @@ farm Let's Explore the Farm with Buzzy a5c5388da9bf0e6662fdca8813a79d13 86962 en Windows - - - George Kormendi a85856675429fe88051744f755b72f93 -1 en Windows - - - Kirben a2386da005672cbd5136f4f27a626c5f 87061 nl Windows - - - George Kormendi - 5dda73606533d66a4c3f4f9ea6e842af 87061 ru Windows - - - sev 39fd6db10d0222d817025c4d3346e3b4 -1 en Mac - Demo - Joachim Eberhard bf8b52fdd9a69c67f34e8e9fec72661c -1 en Windows HE 71 Demo - khalek, sev @@ -708,7 +706,6 @@ puttmoon Putt-Putt Goes to the Moon 697c9b7c55a05d8199c48b48e379d2c8 -1 he DOS - - - sev 9dc02577bf50d4cfaf3de3fbac06fbe2 -1 en Mac - - - khalek 9c92eeaf517a31b7221ec2546ab669fd -1 en Windows HE 70 - - khalek - 3c4c471342bd95505a42334367d8f127 12161 ru Windows HE 70 - - sev aa6a91b7f6f119d1b7b1f2a4c9e24d59 6233 en DOS - Demo - 4af4a6b248103c1fe9edef619677f540 -1 en Mac - Demo - khalek |