diff options
257 files changed, 6481 insertions, 3110 deletions
@@ -285,6 +285,9 @@ ScummVM Team Dreamcast: Marcus Comstedt + GCW0: + Eugene Sandulenko + GPH Devices (GP2X, GP2XWiz & Caanoo): John Willis @@ -1,7 +1,53 @@ For a more comprehensive changelog of the latest experimental code, see: https://github.com/scummvm/scummvm/commits/ -1.8.0 (????-??-??) +1.9.0 (XXXX-XX-XX) + AGI: + - Added support for Hercules rendering. Both green and amber modes are + supported. + - Added support for the Hercules high resolution font. The font is also + usable outside of Hercules rendering. + - Added optional "pause, when entering commands" feature, that was only + available in the original interpreter for Hercules rendering. + +1.8.1 (XXXX-XX-XX) + General: + - Removed TESTING flag from several supported games. + + BBVS: + - Fixed game restart. + + CinE: + - Fixed sound effect loading. + + Gob: + - Fixed lock up for some games during sound initialization. + + Lab: + - Fixed lock-up during ending sequence. + - Improved internal game controls. + + SAGA: + - Fixed user interface colors in the French and German versions of I Have No + Mouth and I Must Scream. + + SCUMM: + - Fixed detection of Maniac Mansion from Day of the Tentacle in the Windows + version of ScummVM. + - Fixed a sound effect not stopping in Loom EGA with AdLib. + + Broken Sword 2.5: + - Added option to use English speech instead of German one when no speech is + available for the selected language. + - Fixed resource releasing on game exit. + - Fixed game restart after language change in-game. + - Fixed flickering in main Menu. + + Windows port: + - Fixed bug in MIDI device listing affecting cases where MIDI devices were + not usable. + +1.8.0 (2016-03-04) New Games: - Added support for Rex Nebular and the Cosmic Gender Bender. - Added support for Sfinx. @@ -18,13 +64,13 @@ For a more comprehensive changelog of the latest experimental code, see: New Ports: - Added Raspberry Pi port. + - Added GCW0 port. General: - Updated Munt MT-32 emulation code to version 1.5.0. SDL: - - Alt-x no longer quits ScummVM on platforms exhibiting this behavior - before. A notable example is our Windows port. + - Alt-x no longer quits ScummVM. Use Cmd-q/Ctrl-q/Ctrl-z instead; see README. - On POSIX systems we now follow the XDG Base Directory Specification for placement of files for users. This effectively results in new locations for our configuration file, our log file, and our default savegame path. @@ -38,19 +84,17 @@ For a more comprehensive changelog of the latest experimental code, see: AGI: - It is now possible to disable mouse support (except for Amiga versions and fanmade games, that require a mouse). - - Fix incorrect volume attenuation in PCjr sound code (bug #6858). + - Fixed PCjr sound volumes. - Major rewrite of graphics subsystem. - Support for Apple IIgs, Amiga + Atari ST transitions, fonts and mouse cursors. The Atari ST 8x8 system font is not included with ScummVM. - Added ability to make for example a PC version look like an Apple IIgs version. This includes palette, cursor, transition and even font. Just set corresponding render mode. - - Support for simplified automatic saving/restoring used by Mixed Up Mother - Goose. - - Removed forced 2 second delay on every room change, replaced with - heuristic. - - Now saving controllers (key bindings by scripts) when saving games - (bug #5858). + - Fixed Apple IIgs game versions running too fast. + - Added support for automatic saving/restoring used by Mixed Up Mother Goose. + - Removed forced two second delay on room changes; replaced with heuristic. + - Fixed certain key bindings breaking after saving/reloading. AGOS: - Fixed arpeggio effect used in music of Amiga version of Elvira 1. @@ -62,11 +106,8 @@ For a more comprehensive changelog of the latest experimental code, see: output and makes it closer to the original. Broken Sword 1: - - Fix speech endianness detection on big endian systems for the Macintosh - version (bug #6720). - - Fix crash when reloading a game from the Main Menu while in the bull's - head scene (bug #6728). It may have been happening in other scenes as - well. + - Fixed Macintosh version speech when running on big endian systems. + - Fixed loading from Main Menu in bull's head scene, and maybe other scenes. CinE: - Added support for music in CD version of Future Wars. @@ -84,7 +125,7 @@ For a more comprehensive changelog of the latest experimental code, see: KQ6 (Dual Mode), LSL5, PQ1, QfG1 (EGA), QfG1 (VGA), QfG2, QfG3, SQ1, SQ4 (CD). - Restoring from the ScummVM in-game menu should now work all the time. - - Improve support for Japanese PC-9801 games. + - Improved support for Japanese PC-9801 games. - Default to hi res version of KQ6, changeable using engine option. SCUMM: @@ -93,6 +134,7 @@ For a more comprehensive changelog of the latest experimental code, see: - It is now possible to play Maniac Mansion from within Day of the Tentacle, with a few caveats. See README for details. - Alt-x can now be used to quit SCUMM games on all platforms. + - Improved lip sync animation in later HE games. Tinsel: - Improved AdLib music support in Discworld 1. diff --git a/backends/midi/windows.cpp b/backends/midi/windows.cpp index e2b327ffa7..52a46200cb 100644 --- a/backends/midi/windows.cpp +++ b/backends/midi/windows.cpp @@ -185,6 +185,9 @@ MusicDevices WindowsMusicPlugin::getDevices() const { deviceNames.push_back(tmp.szPname); } + // Limit us to the number of actually retrieved devices. + numDevs = deviceNames.size(); + // Check for non-unique device names. This may happen if someone has devices with identical // names (e. g. more than one USB device of the exact same hardware type). It seems that this // does happen in reality sometimes. We generate index numbers for these devices. diff --git a/backends/platform/dc/vmsave.cpp b/backends/platform/dc/vmsave.cpp index d7602bd43e..75fc1ed0df 100644 --- a/backends/platform/dc/vmsave.cpp +++ b/backends/platform/dc/vmsave.cpp @@ -182,7 +182,7 @@ static void tryList(const Common::String &glob, int vm, Common::StringArray &lis char buf[16]; strncpy(buf, (char *)de.entry+4, 12); buf[12] = 0; - if (glob.matchString(buf)) + if (Common::matchString(buf, glob.c_str())) list.push_back(buf); } } diff --git a/backends/platform/dingux/README.GCW0 b/backends/platform/dingux/README.GCW0 new file mode 100644 index 0000000000..1875e5323a --- /dev/null +++ b/backends/platform/dingux/README.GCW0 @@ -0,0 +1,26 @@ +[ScummVM-GCW0 README] + +Controls +======== +- Dpad/analog joy: move mouse cursor +- A: left mouse button click +- B: right mouse button click +- X: '0' key +- Y: '.' key (skips dialogue line in some engines) +- Left Trigger: open global menu +- Right Trigger: opens virtual keyboard +- Select: ESC button, scene skip in some engines +- Start: F5 key, game menu in some engines + +Installation from binaries +========================== +Copy over scummvm.opk file + +Building from binaries +====================== +It's pretty simple if you are running Linux on an x86/amd64 machine: +1. Download and install the GCW0 toolchain (http://www.gcw-zero.com/develop) +2. Download ScummVM sources and uncompress them +3. Run backends/platform/dingux/build.gcw0.sh script +4. Copy the resulting file scummvm.opk to your device +5. Enjoy diff --git a/backends/platform/dingux/build.gcw0.sh b/backends/platform/dingux/build.gcw0.sh index 62e084f911..c1a4fa29c2 100755 --- a/backends/platform/dingux/build.gcw0.sh +++ b/backends/platform/dingux/build.gcw0.sh @@ -3,4 +3,4 @@ export PATH=/opt/gcw0-toolchain/usr/bin:$PATH # Disable high resolution engines since we have 320x240 hardware -./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release --disable-engine=mohawk,neverhood,sword25,toltecs,wintermute,zvision --disable-mt32emu --disable-scalers && make -j6 gcw-opk && ls -l scummvm.opk +./configure --host=gcw0 --enable-plugins --default-dynamic --enable-release --disable-mt32emu --disable-hq-scalers && make -j6 gcw-opk && ls -l scummvm.opk diff --git a/backends/platform/dingux/dingux.mk b/backends/platform/dingux/dingux.mk index f357e0bffa..56c26c3be1 100644 --- a/backends/platform/dingux/dingux.mk +++ b/backends/platform/dingux/dingux.mk @@ -33,7 +33,7 @@ endif $(CP) $(srcdir)/backends/platform/dingux/scummvm.png $(bundle_name)/ # Special target for generationg GCW-Zero OPK bundle -$(gcw0_bundle): all GeneralUser\ GS\ FluidSynth\ v1.44.sf2 +$(gcw0_bundle): all $(MKDIR) $(gcw0_bundle) $(CP) $(DIST_FILES_DOCS) $(gcw0_bundle)/ $(MKDIR) $(gcw0_bundle)/themes @@ -55,8 +55,13 @@ endif $(CP) $(srcdir)/dists/gcw0/default.gcw0.desktop $(gcw0_bundle)/ $(CP) $(srcdir)/dists/gcw0/scummvmrc $(gcw0_bundle)/ $(CP) $(srcdir)/dists/gcw0/scummvm.sh $(gcw0_bundle)/ + $(CP) $(srcdir)/backends/platform/dingux/README.GCW0 $(gcw0_bundle)/README.man.txt + echo >> $(gcw0_bundle)/README.man.txt + echo '[General README]' >> $(gcw0_bundle)/README.man.txt + echo >> $(gcw0_bundle)/README.man.txt + cat README >> $(gcw0_bundle)/README.man.txt - $(CP) GeneralUser\ GS\ FluidSynth\ v1.44.sf2 $(gcw0_bundle)/ +# $(CP) GeneralUser\ GS\ FluidSynth\ v1.44.sf2 $(gcw0_bundle)/ gcw0-opk-unstripped: $(gcw0_bundle) $(CP) $(PLUGINS) $(gcw0_bundle)/plugins/ diff --git a/backends/platform/ios7/ios7_osys_main.cpp b/backends/platform/ios7/ios7_osys_main.cpp index c1280a2969..25d9cbed15 100644 --- a/backends/platform/ios7/ios7_osys_main.cpp +++ b/backends/platform/ios7/ios7_osys_main.cpp @@ -79,6 +79,33 @@ AQCallbackStruct OSystem_iOS7::s_AudioQueue; SoundProc OSystem_iOS7::s_soundCallback = NULL; void *OSystem_iOS7::s_soundParam = NULL; +#ifdef IPHONE_SANDBOXED +class SandboxedSaveFileManager : public DefaultSaveFileManager { + Common::String _sandboxRootPath; +public: + + SandboxedSaveFileManager(Common::String sandboxRootPath, Common::String defaultSavepath) + : DefaultSaveFileManager(defaultSavepath), _sandboxRootPath(sandboxRootPath) { + } + + virtual bool removeSavefile(const Common::String &filename) override { + Common::String chrootedFile = getSavePath() + "/" + filename; + Common::String realFilePath = _sandboxRootPath + chrootedFile; + + if (remove(realFilePath.c_str()) != 0) { + if (errno == EACCES) + setError(Common::kWritePermissionDenied, "Search or write permission denied: "+chrootedFile); + + if (errno == ENOENT) + setError(Common::kPathDoesNotExist, "removeSavefile: '"+chrootedFile+"' does not exist or path is invalid"); + return false; + } else { + return true; + } + } +}; +#endif + OSystem_iOS7::OSystem_iOS7() : _mixer(NULL), _lastMouseTap(0), _queuedEventTime(0), _mouseNeedTextureUpdate(false), _secondaryTapped(false), _lastSecondaryTap(0), @@ -89,7 +116,8 @@ OSystem_iOS7::OSystem_iOS7() : _queuedInputEvent.type = Common::EVENT_INVALID; _touchpadModeEnabled = !iOS7_isBigDevice(); #ifdef IPHONE_SANDBOXED - _fsFactory = new ChRootFilesystemFactory(iOS7_getDocumentsDir()); + _chrootBasePath = iOS7_getDocumentsDir(); + _fsFactory = new ChRootFilesystemFactory(_chrootBasePath); #else _fsFactory = new POSIXFilesystemFactory(); #endif @@ -124,7 +152,7 @@ int OSystem_iOS7::timerHandler(int t) { void OSystem_iOS7::initBackend() { #ifdef IPHONE_SANDBOXED - _savefileManager = new DefaultSaveFileManager("/Savegames"); + _savefileManager = new SandboxedSaveFileManager(_chrootBasePath, "/Savegames"); #else _savefileManager = new DefaultSaveFileManager(SCUMMVM_SAVE_PATH); #endif diff --git a/backends/platform/ios7/ios7_osys_main.h b/backends/platform/ios7/ios7_osys_main.h index cc2f1ccc06..174c160bd6 100644 --- a/backends/platform/ios7/ios7_osys_main.h +++ b/backends/platform/ios7/ios7_osys_main.h @@ -111,6 +111,10 @@ protected: char *_lastErrorMessage; +#ifdef IPHONE_SANDBOXED + Common::String _chrootBasePath; +#endif + public: OSystem_iOS7(); diff --git a/backends/platform/maemo/debian/changelog b/backends/platform/maemo/debian/changelog index 8975871203..6b6d1aebd8 100644 --- a/backends/platform/maemo/debian/changelog +++ b/backends/platform/maemo/debian/changelog @@ -1,8 +1,14 @@ -scummvm (1.8.0~git) unstable; urgency=low +scummvm (1.9.0~git) unstable; urgency=low * Development snapshot - -- Tarek Soliman <tsoliman@scummvm.org> Mon, 01 Feb 2016 22:37:44 -0600 + -- Tarek Soliman <tsoliman@scummvm.org> Fri, 26 Feb 2016 21:11:20 -0600 + +scummvm (1.8.0) unstable; urgency=low + + * 1.8.0 release + + -- Tarek Soliman <tsoliman@scummvm.org> Fri, 26 Feb 2016 21:11:20 -0600 scummvm (1.7.0) unstable; urgency=low diff --git a/backends/platform/sdl/sdl-sys.h b/backends/platform/sdl/sdl-sys.h index 219755219f..551605a4b4 100644 --- a/backends/platform/sdl/sdl-sys.h +++ b/backends/platform/sdl/sdl-sys.h @@ -52,6 +52,21 @@ typedef struct { int FAKE; } FAKE_FILE; #define strncasecmp FAKE_strncasecmp #endif +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_exit) +#undef exit +#define exit FAKE_exit +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_abort) +#undef abort +#define abort FAKE_abort +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_system) +#undef system +#define system FAKE_system +#endif + // HACK: SDL might include windows.h which defines its own ARRAYSIZE. // However, we want to use the version from common/util.h. Thus, we make sure // that we actually have this definition after including the SDL headers. @@ -146,6 +161,21 @@ typedef struct { int FAKE; } FAKE_FILE; #define strncasecmp FORBIDDEN_SYMBOL_REPLACEMENT #endif +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_exit) +#undef exit +#define exit(a) FORBIDDEN_SYMBOL_REPLACEMENT +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_abort) +#undef abort +#define abort() FORBIDDEN_SYMBOL_REPLACEMENT +#endif + +#if !defined(FORBIDDEN_SYMBOL_ALLOW_ALL) && !defined(FORBIDDEN_SYMBOL_EXCEPTION_system) +#undef system +#define system(a) FORBIDDEN_SYMBOL_REPLACEMENT +#endif + // SDL 2 has major API changes. We redefine constants which got renamed to // ease the transition. This is sometimes dangerous because the values changed // too! diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 19702ea36d..105d810460 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -373,6 +373,12 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha // We defer checking whether this is a valid target to a later point. return s; } else { + // On MacOS X prior to 10.9 the OS is sometimes adding a -psn_X_XXXXXX argument (where X are digits) + // to pass the process serial number. We need to ignore it to avoid an error. +#ifdef MACOSX + if (strncmp(s, "-psn_", 5) == 0) + continue; +#endif bool isLongCmd = (s[0] == '-' && s[1] == '-'); diff --git a/common/macresman.cpp b/common/macresman.cpp index d83bde8fd8..adca1ea10b 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -29,6 +29,7 @@ #include "common/md5.h" #include "common/substream.h" #include "common/textconsole.h" +#include "common/archive.h" #ifdef MACOSX #include "common/config-manager.h" @@ -261,6 +262,76 @@ bool MacResManager::exists(const String &fileName) { return false; } +void MacResManager::listFiles(StringArray &files, const String &pattern) { + // Base names discovered so far. + typedef HashMap<String, bool, IgnoreCase_Hash, IgnoreCase_EqualTo> BaseNameSet; + BaseNameSet baseNames; + + // List files itself. + ArchiveMemberList memberList; + SearchMan.listMatchingMembers(memberList, pattern); + SearchMan.listMatchingMembers(memberList, pattern + ".rsrc"); + SearchMan.listMatchingMembers(memberList, pattern + ".bin"); + SearchMan.listMatchingMembers(memberList, constructAppleDoubleName(pattern)); + + for (ArchiveMemberList::const_iterator i = memberList.begin(), end = memberList.end(); i != end; ++i) { + String filename = (*i)->getName(); + + // For raw resource forks and MacBinary files we strip the extension + // here to obtain a valid base name. + int lastDotPos = filename.size() - 1; + for (; lastDotPos >= 0; --lastDotPos) { + if (filename[lastDotPos] == '.') { + break; + } + } + + if (lastDotPos != -1) { + const char *extension = filename.c_str() + lastDotPos + 1; + bool removeExtension = false; + + // TODO: Should we really keep filenames suggesting raw resource + // forks or MacBinary files but not being such around? This might + // depend on the pattern the client requests... + if (!scumm_stricmp(extension, "rsrc")) { + SeekableReadStream *stream = (*i)->createReadStream(); + removeExtension = stream && isRawFork(*stream); + delete stream; + } else if (!scumm_stricmp(extension, "bin")) { + SeekableReadStream *stream = (*i)->createReadStream(); + removeExtension = stream && isMacBinary(*stream); + delete stream; + } + + if (removeExtension) { + filename.erase(lastDotPos); + } + } + + // Strip AppleDouble '._' prefix if applicable. + bool isAppleDoubleName = false; + const String filenameAppleDoubleStripped = disassembleAppleDoubleName(filename, &isAppleDoubleName); + + if (isAppleDoubleName) { + SeekableReadStream *stream = (*i)->createReadStream(); + if (stream->readUint32BE() == 0x00051607) { + filename = filenameAppleDoubleStripped; + } + // TODO: Should we really keep filenames suggesting AppleDouble + // but not being AppleDouble around? This might depend on the + // pattern the client requests... + delete stream; + } + + baseNames[filename] = true; + } + + // Append resulting base names to list to indicate found files. + for (BaseNameSet::const_iterator i = baseNames.begin(), end = baseNames.end(); i != end; ++i) { + files.push_back(i->_key); + } +} + bool MacResManager::loadFromAppleDouble(SeekableReadStream &stream) { if (stream.readUint32BE() != 0x00051607) // tag return false; @@ -314,6 +385,18 @@ bool MacResManager::isMacBinary(SeekableReadStream &stream) { return true; } +bool MacResManager::isRawFork(SeekableReadStream &stream) { + // TODO: Is there a better way to detect whether this is a raw fork? + const uint32 dataOffset = stream.readUint32BE(); + const uint32 mapOffset = stream.readUint32BE(); + const uint32 dataLength = stream.readUint32BE(); + const uint32 mapLength = stream.readUint32BE(); + + return !stream.eos() && !stream.err() + && dataOffset < (uint32)stream.size() && dataOffset + dataLength <= (uint32)stream.size() + && mapOffset < (uint32)stream.size() && mapOffset + mapLength <= (uint32)stream.size(); +} + bool MacResManager::loadFromMacBinary(SeekableReadStream &stream) { byte infoHeader[MBI_INFOHDR]; stream.read(infoHeader, MBI_INFOHDR); @@ -592,4 +675,32 @@ String MacResManager::constructAppleDoubleName(String name) { return name; } +String MacResManager::disassembleAppleDoubleName(String name, bool *isAppleDouble) { + if (isAppleDouble) { + *isAppleDouble = false; + } + + // Remove "._" before the last portion of a path name. + for (int i = name.size() - 1; i >= 0; --i) { + if (i == 0) { + if (name.size() > 2 && name[0] == '.' && name[1] == '_') { + name.erase(0, 2); + if (isAppleDouble) { + *isAppleDouble = true; + } + } + } else if (name[i] == '/') { + if ((uint)(i + 2) < name.size() && name[i + 1] == '.' && name[i + 2] == '_') { + name.erase(i + 1, 2); + if (isAppleDouble) { + *isAppleDouble = true; + } + } + break; + } + } + + return name; +} + } // End of namespace Common diff --git a/common/macresman.h b/common/macresman.h index 43ec8d8e2c..05b2a875f4 100644 --- a/common/macresman.h +++ b/common/macresman.h @@ -33,6 +33,7 @@ #include "common/array.h" #include "common/fs.h" #include "common/str.h" +#include "common/str-array.h" #ifndef COMMON_MACRESMAN_H #define COMMON_MACRESMAN_H @@ -82,6 +83,16 @@ public: static bool exists(const String &fileName); /** + * List all filenames matching pattern for opening with open(). + * + * @param files Array containing all matching filenames discovered. Only + * adds to the list. + * @param pattern Pattern to match against. Taking String::matchPattern's + * format. + */ + static void listFiles(StringArray &files, const String &pattern); + + /** * Close the Mac data/resource fork pair. */ void close(); @@ -176,6 +187,7 @@ private: bool loadFromAppleDouble(SeekableReadStream &stream); static String constructAppleDoubleName(String name); + static String disassembleAppleDoubleName(String name, bool *isAppleDouble); /** * Check if the given stream is in the MacBinary format. @@ -183,6 +195,13 @@ private: */ static bool isMacBinary(SeekableReadStream &stream); + /** + * Do a sanity check whether the given stream is a raw resource fork. + * + * @param stream Stream object to check. Will not preserve its position. + */ + static bool isRawFork(SeekableReadStream &stream); + enum { kResForkNone = 0, kResForkRaw, diff --git a/common/rect.h b/common/rect.h index 32424d3e6a..e6534e55d3 100644 --- a/common/rect.h +++ b/common/rect.h @@ -163,7 +163,8 @@ struct Rect { * * @param r the rectangle to check * - * @return true if the given rectangle is inside the rectangle, false otherwise + * @return true if the given rectangle has a non-empty intersection with + * this rectangle, false otherwise */ bool intersects(const Rect &r) const { return (left < r.right) && (r.left < right) && (top < r.bottom) && (r.top < bottom); @@ -162,6 +162,7 @@ _translation=yes # Default platform settings _backend=sdl _16bit=auto +_highres=auto _savegame_timestamp=auto _dynamic_modules=no _elf_loader=no @@ -181,6 +182,7 @@ _stagingpath="staging" _win32path="c:/scummvm" _amigaospath="Games:ScummVM" _staticlibpath= +_xcodetoolspath= _sdlconfig=sdl-config _freetypeconfig=freetype-config _sdlpath="$PATH" @@ -201,6 +203,7 @@ add_feature 16bit "16bit color" "_16bit" add_feature faad "libfaad" "_faad" add_feature flac "FLAC" "_flac" add_feature freetype2 "FreeType2" "_freetype2" +add_feature highres "high resolution" "_highres" add_feature mad "MAD" "_mad" add_feature jpeg "JPEG" "_jpeg" add_feature png "PNG" "_png" @@ -922,6 +925,7 @@ Optional Features: --default-dynamic make plugins dynamic by default --disable-mt32emu don't enable the integrated MT-32 emulator --disable-16bit don't enable 16bit color support + --disable-highres don't enable support for high resolution engines >320x240 --disable-savegame-timestamp don't use timestamps for blank savegame descriptions --disable-scalers exclude scalers --disable-hq-scalers exclude HQ2x and HQ3x scalers @@ -1019,6 +1023,8 @@ done # for parm in ... for ac_option in $@; do case "$ac_option" in --disable-16bit) _16bit=no ;; + --enable-highres) _highres=yes ;; + --disable-highres) _highres=no ;; --disable-savegame-timestamp) _savegame_timestamp=no ;; --disable-scalers) _build_scalers=no ;; --disable-hq-scalers) _build_hq_scalers=no ;; @@ -1230,6 +1236,9 @@ for ac_option in $@; do --with-staticlib-prefix=*) _staticlibpath=`echo $ac_option | cut -d '=' -f 2` ;; + --with-xcodetools-path=*) + _xcodetoolspath=`echo $ac_option | cut -d '=' -f 2` + ;; --host=*) _host=`echo $ac_option | cut -d '=' -f 2` ;; @@ -2345,6 +2354,17 @@ case $_host_os in echo "Could not determine prefix for static libraries" fi fi + + # If _xcodetoolspath is not set yet use xcode-select to get the path + if test -z "$_xcodetoolspath"; then + _xcodetoolspath=`xcode-select -print-path`/Tools + if test -d "$_xcodetoolspath"; then + echo "Set xcodetools-path to ${_xcodetoolspath}" + else + _xcodetoolspath= + echo "Could not determine path for Xcode Tools" + fi + fi ;; dreamcast) append_var DEFINES "-D__DC__" @@ -2438,6 +2458,10 @@ case $_host_os in mint*) append_var DEFINES "-DSYSTEM_NOT_SUPPORTING_D_TYPE" ;; + msys) + echo ERROR: Using the MSYS shell in msys mode is not supported. Please use the MSYS shell in mingw mode instead. + exit 1 + ;; n64) append_var DEFINES "-D__N64__" append_var DEFINES "-DLIMIT_FPS" @@ -3210,6 +3234,26 @@ case $_backend in esac # +# Enable High resolution engines (>320x240) support only for backends which support it +# +case $_host in + gcw0) + if test "$_highres" = yes ; then + _highres=yes + else + _highres=no + fi + ;; + *) + if test "$_highres" = no ; then + _highres=no + else + _highres=yes + fi + ;; +esac + +# # Enable Event Recorder only for backends that support it # case $_backend in @@ -3522,6 +3566,11 @@ define_in_config_if_yes "$_mt32emu" 'USE_MT32EMU' define_in_config_if_yes "$_16bit" 'USE_RGB_COLOR' # +# Check whether High resolution graphics support is requested +# +define_in_config_if_yes "$_highres" 'USE_HIGHRES' + +# # Check whether save games use the current time as default description # define_in_config_if_yes "$_savegame_timestamp" 'USE_SAVEGAME_TIMESTAMP' @@ -3908,10 +3957,7 @@ echocheck "FluidSynth" append_var FLUIDSYNTH_LIBS "-lfluidsynth" case $_host_os in mingw*) - # NOTE: Windows builds use an older FluidSynth version (1.0.9) - # which doesn't require glib, to avoid bundling the complete glib - # libraries with Windows builds. - FLUIDSYNTH_STATIC_LIBS="$FLUIDSYNTH_LIBS -ldsound -lwinmm" + FLUIDSYNTH_STATIC_LIBS="$FLUIDSYNTH_LIBS -lglib-2.0 -lintl -liconv -lws2_32 -lole32 -lshlwapi -lpcre -ldsound -lwinmm" ;; darwin*) @@ -4424,6 +4470,10 @@ if test "$_16bit" = yes ; then echo_n ", 16bit color" fi +if test "$_highres" = yes ; then + echo_n ", high resolution" +fi + if test "$_savegame_timestamp" = yes ; then echo_n ", savegame timestamp" fi @@ -4437,7 +4487,7 @@ if test "$_build_scalers" = yes ; then fi if test "$_mt32emu" = yes ; then - echo_n ", MT-32 emu" + echo_n ", MT-32 emulator" fi if test "$_text_console" = yes ; then @@ -4694,6 +4744,7 @@ STAGINGPATH=$_stagingpath WIN32PATH=$_win32path AMIGAOSPATH=$_amigaospath STATICLIBPATH=$_staticlibpath +XCODETOOLSPATH=$_xcodetoolspath SDLCONFIG=$_sdlconfig ABI := $ABI diff --git a/devtools/create_project/create_project.cpp b/devtools/create_project/create_project.cpp index 65b7601a54..aa450f1461 100644 --- a/devtools/create_project/create_project.cpp +++ b/devtools/create_project/create_project.cpp @@ -939,7 +939,7 @@ const Feature s_features[] = { { "mad", "USE_MAD", "libmad", true, "libmad (MP3) support" }, { "vorbis", "USE_VORBIS", "libvorbisfile_static libvorbis_static libogg_static", true, "Ogg Vorbis support" }, { "flac", "USE_FLAC", "libFLAC_static win_utf8_io_static", true, "FLAC support" }, - { "png", "USE_PNG", "libpng", true, "libpng support" }, + { "png", "USE_PNG", "libpng16", true, "libpng support" }, { "faad", "USE_FAAD", "libfaad", false, "AAC support" }, { "mpeg2", "USE_MPEG2", "libmpeg2", false, "MPEG-2 support" }, { "theora", "USE_THEORADEC", "libtheora_static", true, "Theora decoding support" }, diff --git a/devtools/create_project/msbuild.cpp b/devtools/create_project/msbuild.cpp index a326bd721a..a804205c42 100644 --- a/devtools/create_project/msbuild.cpp +++ b/devtools/create_project/msbuild.cpp @@ -319,12 +319,6 @@ void MSBuildProvider::outputProjectSettings(std::ofstream &project, const std::s for (StringList::const_iterator i = setup.libraries.begin(); i != setup.libraries.end(); ++i) libraries += *i + ".lib;"; - if (_version == 14) { - std::string debug = isRelease ? "" : "d"; - libraries += "libvcruntime" + debug + ".lib;"; - libraries += "libucrt" + debug + ".lib;"; - } - project << "\t\t<Link>\n" "\t\t\t<OutputFile>$(OutDir)" << ((setup.devTools || setup.tests) ? name : setup.projectName) << ".exe</OutputFile>\n" "\t\t\t<AdditionalDependencies>" << libraries << "%(AdditionalDependencies)</AdditionalDependencies>\n" @@ -370,17 +364,17 @@ void MSBuildProvider::outputGlobalPropFile(const BuildSetup &setup, std::ofstrea "<Project DefaultTargets=\"Build\" ToolsVersion=\"" << (_version >= 12 ? _version : 4) << ".0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n" "\t<PropertyGroup>\n" "\t\t<_PropertySheetDisplayName>" << setup.projectDescription << "_Global</_PropertySheetDisplayName>\n" - "\t\t<ExecutablePath>$(" << LIBS_DEFINE << ")\\bin;$(ExecutablePath)</ExecutablePath>\n" - "\t\t<LibraryPath>$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << ";$(LibraryPath)</LibraryPath>\n" + "\t\t<ExecutablePath>$(" << LIBS_DEFINE << ")\\bin;$(" << LIBS_DEFINE << ")\\bin\\" << (bits == 32 ? "x86" : "x64") << ";$(ExecutablePath)</ExecutablePath>\n" + "\t\t<LibraryPath>$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << ";$(" << LIBS_DEFINE << ")\\lib\\" << (bits == 32 ? "x86" : "x64") << "\\$(Configuration);$(LibraryPath)</LibraryPath>\n" "\t\t<IncludePath>$(" << LIBS_DEFINE << ")\\include;$(" << LIBS_DEFINE << ")\\include\\SDL;$(IncludePath)</IncludePath>\n" "\t\t<OutDir>$(Configuration)" << bits << "\\</OutDir>\n" - "\t\t<IntDir>$(Configuration)" << bits << "/$(ProjectName)\\</IntDir>\n" + "\t\t<IntDir>$(Configuration)" << bits << "\\$(ProjectName)\\</IntDir>\n" "\t</PropertyGroup>\n" "\t<ItemDefinitionGroup>\n" "\t\t<ClCompile>\n" "\t\t\t<DisableLanguageExtensions>true</DisableLanguageExtensions>\n" "\t\t\t<DisableSpecificWarnings>" << warnings << ";%(DisableSpecificWarnings)</DisableSpecificWarnings>\n" - "\t\t\t<AdditionalIncludeDirectories>$(" << LIBS_DEFINE << ")\\include;.;" << prefix << ";" << prefix << "\\engines;" << (setup.tests ? prefix + "\\test\\cxxtest;" : "") << "$(TargetDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n" + "\t\t\t<AdditionalIncludeDirectories>.;" << prefix << ";" << prefix << "\\engines;" << (setup.tests ? prefix + "\\test\\cxxtest;" : "") << "$(TargetDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\n" "\t\t\t<PreprocessorDefinitions>" << definesList << "%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" "\t\t\t<ExceptionHandling>" << ((setup.devTools || setup.tests) ? "Sync" : "") << "</ExceptionHandling>\n"; @@ -437,10 +431,14 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t\t<StringPooling>true</StringPooling>\n" "\t\t\t<BufferSecurityCheck>false</BufferSecurityCheck>\n" "\t\t\t<DebugInformationFormat></DebugInformationFormat>\n" - "\t\t\t<RuntimeLibrary>MultiThreaded</RuntimeLibrary>\n" + "\t\t\t<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\n" "\t\t\t<EnablePREfast>" << (configuration == "Analysis" ? "true" : "false") << "</EnablePREfast>\n" "\t\t</ClCompile>\n" + "\t\t<Lib>\n" + "\t\t\t<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>\n" + "\t\t</Lib>\n" "\t\t<Link>\n" + "\t\t\t<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>\n" "\t\t\t<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n" "\t\t\t<SetChecksum>true</SetChecksum>\n"; } else { @@ -448,11 +446,17 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b "\t\t\t<PreprocessorDefinitions>WIN32;" << (configuration == "LLVM" ? "_CRT_SECURE_NO_WARNINGS;" : "") << "%(PreprocessorDefinitions)</PreprocessorDefinitions>\n" "\t\t\t<MinimalRebuild>true</MinimalRebuild>\n" "\t\t\t<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\n" - "\t\t\t<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>\n" + "\t\t\t<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\n" "\t\t\t<FunctionLevelLinking>true</FunctionLevelLinking>\n" - "\t\t\t<TreatWarningAsError>false</TreatWarningAsError>\n" - "\t\t\t<DebugInformationFormat>" << (isWin32 ? "EditAndContinue" : "ProgramDatabase") << "</DebugInformationFormat>\n" // For x64 format Edit and continue is not supported, thus we default to Program Database - "\t\t\t<EnablePREfast>" << (configuration == "Analysis" ? "true" : "false") << "</EnablePREfast>\n"; + "\t\t\t<TreatWarningAsError>false</TreatWarningAsError>\n"; + if (_version >= 14) { + // Since MSVC 2015 Edit and Continue is support for x64 too. + properties << "\t\t\t<DebugInformationFormat>" << "EditAndContinue" << "</DebugInformationFormat>\n"; + } else { + // Older MSVC versions did not support Edit and Continue for x64, thus we do not use it. + properties << "\t\t\t<DebugInformationFormat>" << (isWin32 ? "EditAndContinue" : "ProgramDatabase") << "</DebugInformationFormat>\n"; + } + properties << "\t\t\t<EnablePREfast>" << (configuration == "Analysis" ? "true" : "false") << "</EnablePREfast>\n"; if (configuration == "LLVM") { // FIXME The LLVM cl wrapper does not seem to work properly with the $(TargetDir) path so we hard-code the build folder until the issue is resolved @@ -463,8 +467,7 @@ void MSBuildProvider::createBuildProp(const BuildSetup &setup, bool isRelease, b properties << "\t\t</ClCompile>\n" "\t\t<Link>\n" "\t\t\t<GenerateDebugInformation>true</GenerateDebugInformation>\n" - "\t\t\t<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>\n" - "\t\t\t<IgnoreSpecificDefaultLibraries>libcmt.lib;%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>\n"; + "\t\t\t<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>\n"; } properties << "\t\t</Link>\n" diff --git a/devtools/credits.pl b/devtools/credits.pl index d7cd26fbdc..c67793cbfa 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -287,7 +287,7 @@ sub begin_section { # headlines... my $ascii_title = html_entities_to_ascii($title); $title = html_entities_to_cpp($title); - if ($ascii_title ne $title) { + if ($ascii_title ne $title) { print '"A1""'.$ascii_title.'",' . "\n"; } print '"C1""'.$title.'",' . "\n"; @@ -295,7 +295,7 @@ sub begin_section { } else { my $ascii_title = html_entities_to_ascii($title); $title = html_entities_to_cpp($title); - if ($ascii_title ne $title) { + if ($ascii_title ne $title) { print '"A1""'.$ascii_title.'",' . "\n"; } print '"C1""'.$title.'",' . "\n"; @@ -428,7 +428,7 @@ sub add_person { if (length $desc > 0) { my $ascii_desc = html_entities_to_ascii($desc); $desc = html_entities_to_cpp($desc); - if ($ascii_desc ne $desc) { + if ($ascii_desc ne $desc) { print '"A2""'.$ascii_desc.'",' . "\n"; } print '"C2""'.$desc.'",' . "\n"; @@ -572,7 +572,7 @@ begin_credits("Credits"); add_person("Arnaud Boutonné", "Strangerke", ""); add_person("Paul Gilbert", "dreammaster", ""); end_section(); - + begin_section("CGE2"); add_person("Peter Bozsó", "uruk", ""); add_person("Arnaud Boutonné", "Strangerke", ""); @@ -803,7 +803,7 @@ begin_credits("Credits"); add_person("Einar Johan T. Sømåen", "somaen", ""); add_person("Tobia Tesan", "t0by", ""); end_section(); - + begin_section("Z-Vision"); add_person("Adrian Astley", "RichieSams", ""); add_person("Filippos Karapetis", "[md5]", ""); @@ -823,6 +823,10 @@ begin_credits("Credits"); add_person("Marcus Comstedt", "", ""); end_section(); + begin_section("GCW0"); + add_person("Eugene Sandulenko", "", ""); + end_section(); + begin_section("GPH Devices (GP2X, GP2XWiz & Caanoo)"); add_person("John Willis", "DJWillis", ""); end_section(); diff --git a/dists/gcw0/default.gcw0.desktop b/dists/gcw0/default.gcw0.desktop index 46bd2be092..890852184f 100644 --- a/dists/gcw0/default.gcw0.desktop +++ b/dists/gcw0/default.gcw0.desktop @@ -11,6 +11,6 @@ Exec=scummvm.sh Icon=scummvm Terminal=false Type=Application -Categories=games +Categories=games; StartupNotify=false -X-OD-Manual=README +X-OD-Manual=README.man.txt diff --git a/dists/gcw0/scummvmrc b/dists/gcw0/scummvmrc index f75a56f287..c2087c222a 100644 --- a/dists/gcw0/scummvmrc +++ b/dists/gcw0/scummvmrc @@ -7,7 +7,3 @@ browser_lastpath=/media extrapath=./engine-data pluginspath=./plugins joystick_num=0 -soundfont=./GeneralUser GS FluidSynth v1.44.sf2 -mt32_device=fluidsynth -music_driver=fluidsynth -gm_device=fluidsynth diff --git a/dists/macosx/DS_Store b/dists/macosx/DS_Store Binary files differindex 7ad5a19d61..164e7beb9a 100644 --- a/dists/macosx/DS_Store +++ b/dists/macosx/DS_Store diff --git a/dists/openpandora/pnd_make.sh b/dists/openpandora/pnd_make.sh index 202f137acc..a24beaf5d4 100755 --- a/dists/openpandora/pnd_make.sh +++ b/dists/openpandora/pnd_make.sh @@ -42,14 +42,14 @@ cecho () # Color-echo. Argument $1 = message, Argument $2 = color local default_msg="No message passed." # Doesn't really need to be a local variable. message=${1:-$default_msg} # Defaults to default message. - # We only output colors when a TERM environment variable is set. This - # fixes execution of the script on buildbot, which does not have this set. - if [ -z "$TERM" ]; then - echo "$message" - else + # We only output colors when stdout is outputting to a terminal. + # This avoids color codes being output in log files created on buildbot. + if [ -t 1 -a -n "$TERM" ]; then color=${2:-$black} # Defaults to black, if not specified. echo -e "$color$message" - tput sgr0 # Reset to normal. + tput -T"$TERM" sgr0 # Reset to normal. + else + echo "$message" fi return } diff --git a/dists/samsungtv/README-SamsungTV b/dists/samsungtv/README-SamsungTV index 26ded6c7e6..7c171747dc 100644 --- a/dists/samsungtv/README-SamsungTV +++ b/dists/samsungtv/README-SamsungTV @@ -3,7 +3,7 @@ Notes: - Should works on 2009 B series TVs (Full HD): LExxBE65x, LExxBE75x, PSxxB65x, UExxB7xxx, UExxB8xxx, PSxxB85x, LAxxB65x, LAxxB75x, UNxxB7xxx, UAxxB8xxx - To allow use mouse and keyboard you need load extension first: "SamyGO Mouse And Keyboard" Download from SamyGO project and run from Content Library: - http://sourceforge.net/projects/samygo/files/SamyGO%20Kernel%20Modules/SamyGO%20Mouse%20and%20Keyboard%20Modules%20v0.01.zip/download + http://download.samygo.tv/B%20Series/Content%20Library%20Applications/SamyGO%20Mouse%20and%20Keyboard%20Modules%20v0.01.zip - Buttons on remote controler: EXIT, SOURCE, P+, P-, TV, POWER, CONTENT - cause immediately exit from ScummVM - Config file is in /mtd_rwarea/.scummvmrc - Saves are stored in '/mtd_wiselink/scummvm savegames' directory diff --git a/doc/de/Liesmich b/doc/de/Liesmich index 5807c8383a..f37fc7d396 100644 --- a/doc/de/Liesmich +++ b/doc/de/Liesmich @@ -48,10 +48,10 @@ Inhaltsverzeichnis: 4.0) UnterstÃŒtzte Plattformen 5.0) ScummVM verwenden * 5.1 Kommandozeilenoptionen - * 5.2 Sprachoptionen + * 5.2 Globales MenÃŒ * 5.3 Grafikfilter - * 5.4 Globales MenÃŒ - * 5.5 TastenkÃŒrzel + * 5.4 TastenkÃŒrzel + * 5.5 Sprachoptionen 6.0) SpielstÀnde * 6.1 Automatisches Speichern von SpielstÀnden * 6.2 SpielstÀnde umwandeln @@ -1593,6 +1593,7 @@ zwischen SCUMM-Spielen und anderen Spielen. Strg+F5 - Zeigt globales MenÃŒ. Cmd+q - Beenden (Mac OS X) Strg+q - Beenden (andere UNIX-Systeme einschlieÃlich Linux) + Alt+F4 - Beenden (Windows) Strg+z ODER Alt+x - Beenden (andere Plattformen) Strg+u - Allen Ton abschalten - EIN/AUS Strg+m - Mausbegrenzung in Fenster EIN/AUS diff --git a/doc/de/Neues b/doc/de/Neues index 0a67cb77df..ef462c189c 100644 --- a/doc/de/Neues +++ b/doc/de/Neues @@ -2,7 +2,17 @@ Umfangreichere Informationen ÃŒber die Ãnderungen des aktuellen experimentellen Programmcodes finden Sie auf Englisch unter: https://github.com/scummvm/scummvm/commits/ -1.8.0 (26.02.2016) +1.9.0 (DD.MM.YYYY) + AGI: + - UnterstÃŒtzung fÃŒr Hercules-Darstellung (GrÃŒn + Bernstein) hinzugefÃŒgt + - UnterstÃŒtzung fÃŒr hochauflösende Hercules-Schriftart hinzugefÃŒgt + (auch auÃerhalb der Hercules-Darstellung nutzbar) + - Optionale Funktion "Pause, wenn Befehle eingegeben werden" hinzugefÃŒgt. + Diese Funktion war im originalen Interpreter nur im Hercules-Darstellungsmodus + verfÃŒgbar. + + +1.8.0 (04.03.2016) Neue Spiele: - UnterstÃŒtzung fÃŒr Rex Nebular and the Cosmic Gender Bender hinzugefÃŒgt. - UnterstÃŒtzung fÃŒr Sfinx hinzugefÃŒgt. @@ -20,14 +30,14 @@ Programmcodes finden Sie auf Englisch unter: Neue Portierungen: - Portierung fÃŒr den Raspberry Pi hinzugefÃŒgt. + - Portierung fÃŒr den GCW Zero (GCW0) hinzugefÃŒgt. Allgemein: - Code fÃŒr Munt-MT-32-Emulation auf Version 1.5.0 aktualisiert. SDL: - - Alt+x beendet ScummVM nicht mehr auf Plattformen, fÃŒr welche dies - vorher der Fall war. Ein bedeutendes Beispiel dafÃŒr ist unsere Portierung - fÃŒr Windows. + - Alt+x beendet ScummVM nicht mehr. Verwenden Sie stattdessen + Cmd+q/Strg+q/Strg+z und beachten Sie die Hinweise in der Liesmich-Datei. - Auf POSIX-Systemen befolgen wir nun die Spezifikation XDG Base Directory fÃŒr die Speicherung von Benutzerdaten. Dies fÃŒhrt zu neuen Speicherorten fÃŒr unsere Konfigurationsdatei, unsere Log-Datei sowie fÃŒr @@ -43,20 +53,20 @@ Neue Portierungen: AGI: - Es ist nun möglich, die Maus-UnterstÃŒtzung zu deaktivieren (auÃer bei Amiga-Versionen und Fan-Spielen, die eine Maus benötigen). - - Fehlerhafte LautstÀrke-DÀmpfung im PCjr-Sound-Code behoben (Fehler #6858). - - Umfangreiche Ãnderung im Grafik-Subsystem + - Fehlerhafte LautstÀrke in PCjr-Spielen korrigiert. + - Umfangreiche Ãnderung im Grafik-Subsystem. - UnterstÃŒtzung fÃŒr ÃbergÀnge, Schriftarten und Mauszeigern fÃŒr Apple IIgs, Amiga und Atari (die Systemschriftart Atari ST 8x8 ist nicht in ScummVM enthalten) - Eine PC-Version kann jetzt wie eine Apple IIgs-Version dargestellt werden (inklusive Farbpalette, Cursor, ÃbergÀnge und Schriftart). Sie mÃŒssen lediglich den gewÃŒnschten Darstellungsmodus auswÀhlen. - - UnterstÃŒtzung fÃŒr vereinfachtes automatisches Speichern / Laden - hinzugefÃŒgt (verwendet von Mixed Up Mother Goose). + - Apple IIgs-Spiele laufen nicht mehr zu schnell. + - UnterstÃŒtzung fÃŒr automatisches Speichern / Laden hinzugefÃŒgt + (verwendet von Mixed Up Mother Goose). - Feste Verzögerung von 2 Sekunden bei Raumwechseln entfernt und durch Heuristik ersetzt. - - Controller (skriptgesteuerte Tastenbelegungen) werden jetzt mit - abgespeichert (Fehler #5858). + - Fehlerhafte Tastenbelegungen nach abspeichern/laden behoben AGOS: - Arpeggio-Effekt in der Musik der Amiga-Version von Elvira 1 repariert. @@ -68,11 +78,11 @@ Neue Portierungen: AdLib-Ausgabe erheblich und erhöht die Originaltreue. Baphomets Fluch 1: - - Erkennung der Byte-Reihenfolge der Sprachausgabe auf Big-Endian-Systemen - fÃŒr die Macintosh-Version (Fehler #6720) repariert. - - Absturz beim Neuladen eines Spiels aus dem HauptmenÃŒ heraus, wÀhrend sich - das Spiel in der Szene am Bull's Head Hill befindet, behoben - (Fehler #6728). Dieser Fehler trat womöglich auch in anderen Szenen auf. + - Sprachausgabe in Macintosh-Versionen korrigiert, wenn ScumMVM + auf Big-Endian-Systemen ausgefÃŒhrt wird. + - Fehler beim Laden eines Spielstandes aus dem HauptmenÃŒ in der + Bull's Head Hill-Szene korrigiert. Dieser Fehler trat womöglich auch + in anderen Szenen auf. CinE: - UnterstÃŒtzung fÃŒr Musik in der CD-Version von Future Wars hinzugefÃŒgt. @@ -86,11 +96,14 @@ Neue Portierungen: SCI: - Behandlung der Musik-PrioritÀt extrem verbessert. - Viele Fehler in den originalen Skripten behoben, die auch bei - Verwendung des originalen Interpreters auftreten: - KQ6 (Sprache und Untertitel), LSL5, QfG1 (EGA), QfG (VGA), QfG2, QfG3, - SQ1, SQ4 (CD) + Verwendung des originalen Interpreters auftreten. + Folgende Spiele sind davon betroffen: + KQ6 (Sprache und Untertitel), LSL5, PQ1, QfG1 (EGA), QfG (VGA), + QfG2, QfG3, SQ1, SQ4 (CD) - RÃŒckkehr aus dem ScummVM-MenÃŒ im Spiel sollte nun immer funktionieren. - Verbesserte UnterstÃŒtzung fÃŒr japanische PC-9801-Spiele + - Verwende standardmÀÃig die hochauflösende Version von KQ6 + (kann in den Spieloptionen umgeschaltet werden) SCUMM: - Umfangreiche Verbesserung der Textdarstellung in koreanischen Versionen @@ -99,6 +112,7 @@ Neue Portierungen: Tentacle zu spielen. Bitte Liesmich-Datei fÃŒr weitere Details lesen. - Alt+x kann jetzt auf allen Plattformen dazu verwendet werden, SCUMM-Spiele zu beenden. + - Lippensynchronisation in neueren Spielen von Humongous Entertainment verbessert. Tinsel: - UnterstÃŒtzung fÃŒr AdLib-Musik in Discworld 1 verbessert. diff --git a/engines/access/detection_tables.h b/engines/access/detection_tables.h index 9556cd9f67..7d9509ca43 100644 --- a/engines/access/detection_tables.h +++ b/engines/access/detection_tables.h @@ -49,7 +49,7 @@ static const AccessGameDescription gameDescriptions[] = { AD_ENTRY1s("c00.ap", "aeb429ff015596144c0df06886c84825", 303753), Common::ES_ESP, Common::kPlatformDOS, - ADGF_NO_FLAGS, + ADGF_UNSTABLE, GUIO1(GUIO_NONE) }, GType_Amazon, diff --git a/engines/advancedDetector.cpp b/engines/advancedDetector.cpp index 72629a833e..7a09f662d1 100644 --- a/engines/advancedDetector.cpp +++ b/engines/advancedDetector.cpp @@ -41,8 +41,8 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa title = g.extra; extra = ""; } else { - while (sg->gameid) { - if (!scumm_stricmp(g.gameid, sg->gameid)) + while (sg->gameId) { + if (!scumm_stricmp(g.gameId, sg->gameId)) title = sg->description; sg++; } @@ -56,7 +56,7 @@ static GameDescriptor toGameDescriptor(const ADGameDescription &g, const PlainGa else if (g.flags & ADGF_TESTING) gsl = kTestingGame; - GameDescriptor gd(g.gameid, title, g.language, g.platform, 0, gsl); + GameDescriptor gd(g.gameId, title, g.language, g.platform, 0, gsl); gd.updateDesc(extra); return gd; } @@ -89,21 +89,38 @@ static Common::String generatePreferredTarget(const Common::String &id, const AD return res; } +static Common::String sanitizeName(const char *name) { + Common::String res; + + while (*name) { + if (Common::isAlnum(*name)) + res += tolower(*name); + name++; + } + + return res; +} + void AdvancedMetaEngine::updateGameDescriptor(GameDescriptor &desc, const ADGameDescription *realDesc) const { - if (_singleid != NULL) { + if (_singleId != NULL) { desc["preferredtarget"] = desc["gameid"]; - desc["gameid"] = _singleid; + desc["gameid"] = _singleId; } if (!desc.contains("preferredtarget")) desc["preferredtarget"] = desc["gameid"]; + if (realDesc->flags & ADGF_AUTOGENTARGET) { + if (*realDesc->extra) + desc["preferredtarget"] = sanitizeName(realDesc->extra); + } + desc["preferredtarget"] = generatePreferredTarget(desc["preferredtarget"], realDesc); if (_flags & kADFlagUseExtraAsHint) desc["extra"] = realDesc->extra; - desc.setGUIOptions(realDesc->guioptions + _guioptions); + desc.setGUIOptions(realDesc->guiOptions + _guiOptions); desc.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(realDesc->language)); if (realDesc->flags & ADGF_ADDENGLISH) @@ -149,7 +166,7 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { // Use fallback detector if there were no matches by other means const ADGameDescription *fallbackDesc = fallbackDetect(allFiles, fslist); if (fallbackDesc != 0) { - GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameids)); + GameDescriptor desc(toGameDescriptor(*fallbackDesc, _gameIds)); updateGameDescriptor(desc, fallbackDesc); detectedGames.push_back(desc); } @@ -157,7 +174,7 @@ GameList AdvancedMetaEngine::detectGames(const Common::FSList &fslist) const { // Otherwise use the found matches cleanupPirated(matches); for (uint i = 0; i < matches.size(); i++) { - GameDescriptor desc(toGameDescriptor(*matches[i], _gameids)); + GameDescriptor desc(toGameDescriptor(*matches[i], _gameIds)); updateGameDescriptor(desc, matches[i]); detectedGames.push_back(desc); } @@ -252,10 +269,10 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) if (cleanupPirated(matches)) return Common::kNoGameDataFoundError; - if (_singleid == NULL) { + if (_singleId == NULL) { // Find the first match with correct gameid. for (uint i = 0; i < matches.size(); i++) { - if (matches[i]->gameid == gameid) { + if (matches[i]->gameId == gameid) { agdDesc = matches[i]; break; } @@ -270,7 +287,7 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) if (agdDesc != 0) { // Seems we found a fallback match. But first perform a basic // sanity check: the gameid must match. - if (_singleid == NULL && agdDesc->gameid != gameid) + if (_singleId == NULL && agdDesc->gameId != gameid) agdDesc = 0; } } @@ -284,9 +301,9 @@ Common::Error AdvancedMetaEngine::createInstance(OSystem *syst, Engine **engine) if (agdDesc->flags & ADGF_ADDENGLISH) lang += " " + getGameGUIOptionsDescriptionLanguage(Common::EN_ANY); - Common::updateGameGUIOptions(agdDesc->guioptions + _guioptions, lang); + Common::updateGameGUIOptions(agdDesc->guiOptions + _guiOptions, lang); - GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameids); + GameDescriptor gameDescriptor = toGameDescriptor(*agdDesc, _gameIds); bool showTestingWarning = false; @@ -407,7 +424,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // Check which files are included in some ADGameDescription *and* are present. // Compute MD5s and file sizes for these files. - for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += _descItemSize) { + for (descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize) { g = (const ADGameDescription *)descPtr; for (fileDesc = g->filesDescriptions; fileDesc->fileName; fileDesc++) { @@ -430,7 +447,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons // MD5 based matching uint i; - for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameid != 0; descPtr += _descItemSize, ++i) { + for (i = 0, descPtr = _gameDescriptors; ((const ADGameDescription *)descPtr)->gameId != 0; descPtr += _descItemSize, ++i) { g = (const ADGameDescription *)descPtr; bool fileMissing = false; @@ -487,7 +504,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons gotAnyMatchesWithAllFiles = true; if (!fileMissing) { - debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameid, g->extra, + debug(2, "Found game: %s (%s %s/%s) (%d)", g->gameId, g->extra, getPlatformDescription(g->platform), getLanguageDescription(g->language), i); if (curFilesMatched > maxFilesMatched) { @@ -503,7 +520,7 @@ ADGameDescList AdvancedMetaEngine::detectGame(const Common::FSNode &parent, cons } } else { - debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameid, g->extra, + debug(5, "Skipping game: %s (%s %s/%s) (%d)", g->gameId, g->extra, getPlatformDescription(g->platform), getLanguageDescription(g->language), i); } } @@ -543,7 +560,7 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap & } if (!fileMissing) { - debug(4, "Matched: %s", agdesc->gameid); + debug(4, "Matched: %s", agdesc->gameId); if (numMatchedFiles > maxNumMatchedFiles) { matchedDesc = agdesc; @@ -568,27 +585,27 @@ const ADGameDescription *AdvancedMetaEngine::detectGameFilebased(const FileMap & } GameList AdvancedMetaEngine::getSupportedGames() const { - if (_singleid != NULL) { + if (_singleId != NULL) { GameList gl; - const PlainGameDescriptor *g = _gameids; - while (g->gameid) { - if (0 == scumm_stricmp(_singleid, g->gameid)) { - gl.push_back(GameDescriptor(g->gameid, g->description)); + const PlainGameDescriptor *g = _gameIds; + while (g->gameId) { + if (0 == scumm_stricmp(_singleId, g->gameId)) { + gl.push_back(GameDescriptor(g->gameId, g->description)); return gl; } g++; } - error("Engine %s doesn't have its singleid specified in ids list", _singleid); + error("Engine %s doesn't have its singleid specified in ids list", _singleId); } - return GameList(_gameids); + return GameList(_gameIds); } -GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const { +GameDescriptor AdvancedMetaEngine::findGame(const char *gameId) const { // First search the list of supported gameids for a match. - const PlainGameDescriptor *g = findPlainGameDescriptor(gameid, _gameids); + const PlainGameDescriptor *g = findPlainGameDescriptor(gameId, _gameIds); if (g) return GameDescriptor(*g); @@ -596,14 +613,14 @@ GameDescriptor AdvancedMetaEngine::findGame(const char *gameid) const { return GameDescriptor(); } -AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids, const ADExtraGuiOptionsMap *extraGuiOptions) - : _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameids(gameids), +AdvancedMetaEngine::AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions) + : _gameDescriptors((const byte *)descs), _descItemSize(descItemSize), _gameIds(gameIds), _extraGuiOptions(extraGuiOptions) { _md5Bytes = 5000; - _singleid = NULL; + _singleId = NULL; _flags = 0; - _guioptions = GUIO_NONE; + _guiOptions = GUIO_NONE; _maxScanDepth = 1; _directoryGlobs = NULL; } diff --git a/engines/advancedDetector.h b/engines/advancedDetector.h index ad551698f6..ab3ec22bdc 100644 --- a/engines/advancedDetector.h +++ b/engines/advancedDetector.h @@ -81,6 +81,7 @@ typedef Common::HashMap<Common::String, ADFileProperties, Common::IgnoreCase_Has enum ADGameFlags { ADGF_NO_FLAGS = 0, + ADGF_AUTOGENTARGET = (1 << 20), // automatically generate gameid from extra ADGF_UNSTABLE = (1 << 21), // flag to designate not yet officially-supported games that are not fit for public testing ADGF_TESTING = (1 << 22), // flag to designate not yet officially-supported games that are fit for public testing ADGF_PIRATED = (1 << 23), ///< flag to designate well known pirated versions with cracks @@ -94,7 +95,7 @@ enum ADGameFlags { }; struct ADGameDescription { - const char *gameid; + const char *gameId; const char *extra; ADGameFileDescription filesDescriptions[14]; Common::Language language; @@ -107,7 +108,7 @@ struct ADGameDescription { */ uint32 flags; - const char *guioptions; + const char *guiOptions; }; /** @@ -191,7 +192,7 @@ protected: * A list of all gameids (and their corresponding descriptions) supported * by this engine. */ - const PlainGameDescriptor *_gameids; + const PlainGameDescriptor *_gameIds; /** * A map containing all the extra game GUI options the engine supports. @@ -219,7 +220,7 @@ protected: * address a more generic problem. We should find a better way to * disambiguate gameids. */ - const char *_singleid; + const char *_singleId; /** * A bitmask of flags which can be used to configure the behavior @@ -233,7 +234,7 @@ protected: * entry in addition to per-game options. Refer to GameGUIOption * enum for the list. */ - Common::String _guioptions; + Common::String _guiOptions; /** * Maximum depth of directories to look up. @@ -251,7 +252,7 @@ protected: const char * const *_directoryGlobs; public: - AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameids, const ADExtraGuiOptionsMap *extraGuiOptions = 0); + AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions = 0); /** * Returns list of targets supported by the engine. @@ -259,7 +260,7 @@ public: */ virtual GameList getSupportedGames() const; - virtual GameDescriptor findGame(const char *gameid) const; + virtual GameDescriptor findGame(const char *gameId) const; virtual GameList detectGames(const Common::FSList &fslist) const; diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index ec10344cfb..6e63cd3e71 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -283,18 +283,7 @@ void AgiBase::initRenderMode() { switch (platform) { case Common::kPlatformDOS: - switch (configRenderMode) { - case Common::kRenderCGA: - _renderMode = Common::kRenderCGA; - break; - // Hercules is not supported atm - //case Common::kRenderHercA: - //case Common::kRenderHercG: - // _renderMode = Common::kRenderHercG; - // break; - default: - break; - } + // Keep EGA break; case Common::kPlatformAmiga: _renderMode = Common::kRenderAmiga; @@ -323,6 +312,12 @@ void AgiBase::initRenderMode() { case Common::kRenderVGA: _renderMode = Common::kRenderVGA; break; + case Common::kRenderHercG: + _renderMode = Common::kRenderHercG; + break; + case Common::kRenderHercA: + _renderMode = Common::kRenderHercA; + break; case Common::kRenderAmiga: _renderMode = Common::kRenderAmiga; break; @@ -466,7 +461,7 @@ void AgiEngine::initialize() { _console = new Console(this); _words = new Words(this); _font = new GfxFont(this); - _gfx = new GfxMgr(this); + _gfx = new GfxMgr(this, _font); _sound = new SoundMgr(this, _mixer); _picture = new PictureMgr(this, _gfx); _sprites = new SpritesMgr(this, _gfx); @@ -474,9 +469,9 @@ void AgiEngine::initialize() { _systemUI = new SystemUI(this, _gfx, _text); _inventory = new InventoryMgr(this, _gfx, _text, _systemUI); + _font->init(); _gfx->initVideo(); - _font->init(); _text->init(_systemUI); _game.gameFlags = 0; @@ -512,19 +507,6 @@ void AgiEngine::redrawScreen() { _text->promptRedraw(); } -// Adjust a given coordinate to the local game screen -// Used on mouse cursor coordinates before passing them to scripts -void AgiEngine::adjustPosToGameScreen(int16 &x, int16 &y) { - x = x / 2; // 320 -> 160 - y = y - _gfx->getRenderStartOffsetY(); // remove status bar line - if (y < 0) { - y = 0; - } - if (y >= SCRIPT_HEIGHT) { - y = SCRIPT_HEIGHT + 1; // 1 beyond - } -} - AgiEngine::~AgiEngine() { agiDeinit(); delete _loader; diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 98d6bcff8a..b288557f57 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -148,17 +148,17 @@ enum BooterDisks { // position and position.v. // enum AgiGameFeatures { - GF_AGIMOUSE = (1 << 0), + GF_AGIMOUSE = (1 << 0), // this disables "Click-to-walk mouse interface" GF_AGDS = (1 << 1), - GF_AGI256 = (1 << 2), - GF_AGI256_2 = (1 << 3), - GF_AGIPAL = (1 << 4), - GF_MACGOLDRUSH = (1 << 5), - GF_FANMADE = (1 << 6), - GF_MENUS = (1 << 7), - GF_ESCPAUSE = (1 << 8), + GF_AGI256 = (1 << 2), // marks fanmade AGI-256 games + GF_AGI256_2 = (1 << 3), // marks fanmade AGI-256-2 games + GF_AGIPAL = (1 << 4), // marks game using fanmade AGIPAL extension + GF_MACGOLDRUSH = (1 << 5), // use "grdir" instead of "dir" for volume loading + GF_FANMADE = (1 << 6), // marks fanmade games + GF_MENUS = (1 << 7), // not used anymore + GF_ESCPAUSE = (1 << 8), // not used anymore, we detect this internally GF_OLDAMIGAV20 = (1 << 9), - GF_CLIPCOORDS = (1 << 10), + GF_CLIPCOORDS = (1 << 10), // not used atm GF_2GSOLDSOUND = (1 << 11) }; @@ -739,8 +739,6 @@ public: Common::Error loadGameState(int slot); Common::Error saveGameState(int slot, const Common::String &description); - void adjustPosToGameScreen(int16 &x, int16 &y); - private: int _keyQueue[KEY_QUEUE_SIZE]; int _keyQueueStart; diff --git a/engines/agi/cycle.cpp b/engines/agi/cycle.cpp index 211513ca36..19aca6f2c4 100644 --- a/engines/agi/cycle.cpp +++ b/engines/agi/cycle.cpp @@ -497,7 +497,13 @@ int AgiEngine::runGame() { break; case Common::kRenderHercA: case Common::kRenderHercG: - setVar(VM_VAR_MONITOR, kAgiMonitorHercules); + // Set EGA for now. Some games place text differently, when this is set to kAgiMonitorHercules. + // Text placement was different for Hercules rendering (16x12 instead of 16x16). There also was + // not enough space left for the prompt at the bottom. This was caused by the Hercules resolution. + // We don't have this restriction and we also support the regular prompt for Hercules mode. + // In theory Sierra could have had special Hercules code inside their games. + // TODO: check this. + setVar(VM_VAR_MONITOR, kAgiMonitorEga); break; // Don't know if Amiga AGI games use a different value than kAgiMonitorEga // for vMonitor so I just use kAgiMonitorEga for them (As was done before too). diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 3efd6bde65..9f66d78d80 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -171,6 +171,26 @@ static const ADExtraGuiOptionsMap optionsList[] = { } }, + { + GAMEOPTION_USE_HERCULES_FONT, + { + _s("Use Hercules hires font"), + _s("Uses Hercules hires font, when font file is available."), + "herculesfont", + false + } + }, + + { + GAMEOPTION_COMMAND_PROMPT_WINDOW, + { + _s("Pause when entering commands"), + _s("Shows a command prompt window and pauses the game (like in SCI) instead of a real-time prompt."), + "commandpromptwindow", + false + } + }, + AD_EXTRA_GUI_OPTIONS_TERMINATOR }; @@ -182,8 +202,8 @@ class AgiMetaEngine : public AdvancedMetaEngine { public: AgiMetaEngine() : AdvancedMetaEngine(Agi::gameDescriptions, sizeof(Agi::AGIGameDescription), agiGames, optionsList) { - _singleid = "agi"; - _guioptions = GUIO1(GUIO_NOSPEECH); + _singleId = "agi"; + _guiOptions = GUIO1(GUIO_NOSPEECH); } virtual const char *getName() const { @@ -547,13 +567,13 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX // Override the gameid & extra values in g_fallbackDesc.desc. This only works // until the fallback detector is called again, and while the MetaEngine instance // is alive (as else the string storage is modified/deleted). - g_fallbackDesc.desc.gameid = _gameid.c_str(); + g_fallbackDesc.desc.gameId = _gameid.c_str(); g_fallbackDesc.desc.extra = _extra.c_str(); Common::String fallbackWarning; fallbackWarning = "Your game version has been detected using fallback matching as a\n"; - fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra); + fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameId, g_fallbackDesc.desc.extra); fallbackWarning += "If this is an original and unmodified version or new made Fanmade game,\n"; fallbackWarning += "please report any, information previously printed by ScummVM to the team.\n"; diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index 5c3e814e74..0938e9d4d6 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -24,12 +24,14 @@ namespace Agi { #define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1 #define GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE GUIO_GAMEOPTIONS2 -#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3 +#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3 +#define GAMEOPTION_USE_HERCULES_FONT GUIO_GAMEOPTIONS4 +#define GAMEOPTION_COMMAND_PROMPT_WINDOW GUIO_GAMEOPTIONS5 // TODO: properly implement GAMEOPTIONs -#define GAMEOPTIONS_DEFAULT GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE) -#define GAMEOPTIONS_AMIGA GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE) -#define GAMEOPTIONS_FANMADE_MOUSE GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD) +#define GAMEOPTIONS_DEFAULT GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW) +#define GAMEOPTIONS_AMIGA GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW) +#define GAMEOPTIONS_FANMADE_MOUSE GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_USE_HERCULES_FONT,GAMEOPTION_COMMAND_PROMPT_WINDOW) #define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp,guioptions) { \ { \ diff --git a/engines/agi/font.cpp b/engines/agi/font.cpp index 896befca04..f9605e4a3d 100644 --- a/engines/agi/font.cpp +++ b/engines/agi/font.cpp @@ -20,6 +20,7 @@ * */ +#include "common/config-manager.h" #include "agi/agi.h" #include "agi/font.h" #include "agi/text.h" @@ -31,6 +32,7 @@ GfxFont::GfxFont(AgiBase *vm) { _fontData = nullptr; _fontDataAllocated = nullptr; + _fontIsHires = false; } GfxFont::~GfxFont() { @@ -619,52 +621,71 @@ static const uint8 fontData_ExtendedRussian[] = { }; void GfxFont::init() { - switch (_vm->_renderMode) { - case Common::kRenderAmiga: - // Try user-file first, if that fails use our internal inaccurate topaz font - loadFontScummVMFile("agi-font-amiga.bin"); - if (!_fontData) { - loadFontAmigaPseudoTopaz(); + if (ConfMan.getBool("herculesfont")) { + // User wants, that we use Hercules hires font, try to load it + loadFontHercules(); + } else { + switch (_vm->_renderMode) { + case Common::kRenderHercA: + case Common::kRenderHercG: + // Render mode is Hercules, we try to load Hercules hires font + loadFontHercules(); + break; + default: + break; } - break; - case Common::kRenderApple2GS: - // Special font, stored in file AGIFONT - loadFontAppleIIgs(); - break; - case Common::kRenderAtariST: - // TODO: Atari ST uses another font - // Seems to be the standard Atari ST 8x8 system font - loadFontScummVMFile("agi-font-atarist.bin"); - if (!_fontData) { - loadFontAtariST("agi-font-atarist-system.fnt"); + } + + if (!_fontData) { + switch (_vm->_renderMode) { + case Common::kRenderAmiga: + // Try user-file first, if that fails use our internal inaccurate topaz font + loadFontScummVMFile("agi-font-amiga.bin"); if (!_fontData) { - // TODO: in case we find a recreation of the font, add it in here + loadFontAmigaPseudoTopaz(); + } + break; + case Common::kRenderApple2GS: + // Special font, stored in file AGIFONT + loadFontAppleIIgs(); + break; + case Common::kRenderAtariST: + // TODO: Atari ST uses another font + // Seems to be the standard Atari ST 8x8 system font + loadFontScummVMFile("agi-font-atarist.bin"); + if (!_fontData) { + loadFontAtariST("agi-font-atarist-system.fnt"); + if (!_fontData) { + // TODO: in case we find a recreation of the font, add it in here + } } - } - break; - case Common::kRenderCGA: - case Common::kRenderEGA: - case Common::kRenderVGA: - switch (_vm->getGameID()) { - case GID_MICKEY: - // load mickey mouse font from interpreter file - loadFontMickey(); break; + case Common::kRenderHercA: + case Common::kRenderHercG: + case Common::kRenderCGA: + case Common::kRenderEGA: + case Common::kRenderVGA: + switch (_vm->getGameID()) { + case GID_MICKEY: + // load mickey mouse font from interpreter file + loadFontMickey(); + break; + default: + loadFontScummVMFile("agi-font-dos.bin"); + break; + } + break; + default: - loadFontScummVMFile("agi-font-dos.bin"); break; } - break; - default: - break; - } - - if (!_fontData) { - // no font assigned? - // use regular PC-BIOS font (taken from Dos-Box with a few modifications) - _fontData = fontData_PCBIOS; - debug("AGI: Using PC-BIOS font"); + if (!_fontData) { + // no font assigned? + // use regular PC-BIOS font (taken from Dos-Box with a few modifications) + _fontData = fontData_PCBIOS; + debug("AGI: Using PC-BIOS font"); + } } if (_vm->getLanguage() == Common::RU_RUS) { @@ -678,6 +699,10 @@ const byte *GfxFont::getFontData() { return _fontData; } +bool GfxFont::isFontHires() { + return _fontIsHires; +} + void GfxFont::overwriteSaveRestoreDialogCharacter() { // overwrite character 0x1A with the standard Sierra arrow to the right character // required for the original save/restore dialogs @@ -686,6 +711,11 @@ void GfxFont::overwriteSaveRestoreDialogCharacter() { // Overwrite extended character set (0x80-0xFF) with Russian characters void GfxFont::overwriteExtendedWithRussianSet() { + if (_fontIsHires) { + // TODO: Implement overwriting hires font characters too + return; + } + if (!_fontDataAllocated) { // nothing allocated, we need to allocate space ourselves to be able to modify an internal font _fontDataAllocated = (uint8 *)calloc(256, 8); @@ -1165,4 +1195,102 @@ void GfxFont::loadFontAtariST(Common::String fontFilename) { debug("AGI: Using Atari ST 8x8 system font"); } +// Loads a Sierra Hercules font file +void GfxFont::loadFontHercules() { + Common::File fontFile; + int32 fontFileSize = 0; + byte *fontData = nullptr; + byte *rawData = nullptr; + + uint16 rawDataPos = 0; + uint16 curCharNr = 0; + uint16 curCharLine = 0; + + if (fontFile.open("hgc_font")) { + // hgc_font file found, this is interleaved font data 16x12, should be 3072 bytes + // 24 bytes per character, 128 characters + fontFileSize = fontFile.size(); + if (fontFileSize == (128 * 24)) { + // size seems to be fine + fontData = (uint8 *)calloc(256, 32); + _fontDataAllocated = fontData; + + rawData = (byte *)calloc(128, 24); + fontFile.read(rawData, 128 * 24); + + // convert interleaved 16x12 -> non-interleaved 16x16 + for (curCharNr = 0; curCharNr < 128; curCharNr++) { + fontData += 4; // skip the first 2 lines + for (curCharLine = 0; curCharLine < 6; curCharLine++) { + fontData[0] = rawData[rawDataPos + 2 + 0]; + fontData[1] = rawData[rawDataPos + 2 + 1]; + fontData[2] = rawData[rawDataPos + 0 + 0]; + fontData[3] = rawData[rawDataPos + 0 + 1]; + rawDataPos += 4; + fontData += 4; + } + fontData += 4; // skip the last 2 lines + } + + free(rawData); + } else { + warning("Fontfile 'hgc_font': unexpected file size"); + } + fontFile.close(); + + } + + // It seems hgc_graf.ovl holds a low-res font. It makes no real sense to use it. + // This was only done to AGI3 games and those rendered differently (2 pixel lines -> 3 pixel lines instead of 4) + // User could copy hgc_font from another AGI game over to get the hires font working. +#if 0 + if (!_fontDataAllocated) { + if (fontFile.open("hgc_graf.ovl")) { + // hgc_graf.ovl file found, this is font data + code. non-interleaved font data, should be 3075 bytes + // 16 bytes per character, 128 characters, 2048 bytes of font data, starting offset 21 + fontFileSize = fontFile.size(); + if (fontFileSize == 3075) { + // size seems to be fine + fontData = (uint8 *)calloc(256, 32); + _fontDataAllocated = fontData; + + fontFile.seek(21); + rawData = (byte *)calloc(128, 16); + fontFile.read(rawData, 128 * 16); + + // repeat every line 2 times to get 16x16 pixels + for (curCharNr = 0; curCharNr < 128; curCharNr++) { + for (curCharLine = 0; curCharLine < 8; curCharLine++) { + fontData[0] = rawData[rawDataPos + 0]; + fontData[1] = rawData[rawDataPos + 1]; + fontData[2] = rawData[rawDataPos + 0]; + fontData[3] = rawData[rawDataPos + 1]; + rawDataPos += 2; + fontData += 4; + } + } + + free(rawData); + + } else { + warning("Fontfile 'hgc_graf.ovl': unexpected file size"); + } + fontFile.close(); + } + } +#endif + + if (_fontDataAllocated) { + // font loaded + _fontData = _fontDataAllocated; + _fontIsHires = true; + + debug("AGI: Using Hercules hires font"); + + } else { + // Continue, if no file was not found + warning("Could not open/use file 'hgc_font' for Hercules hires font"); + } +} + } // End of namespace Agi diff --git a/engines/agi/font.h b/engines/agi/font.h index 0bb1bbb18d..485b139858 100644 --- a/engines/agi/font.h +++ b/engines/agi/font.h @@ -36,6 +36,7 @@ private: public: void init(); const byte *getFontData(); + bool isFontHires(); private: void overwriteSaveRestoreDialogCharacter(); @@ -46,9 +47,11 @@ private: void loadFontAmigaPseudoTopaz(); void loadFontAppleIIgs(); void loadFontAtariST(Common::String fontFilename); + void loadFontHercules(); const uint8 *_fontData; // pointer to the currently used font uint8 *_fontDataAllocated; + bool _fontIsHires; }; } // End of namespace Agi diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index 6f4b272e2b..6d3563a451 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -39,7 +39,7 @@ namespace Agi { #include "agi/font.h" -GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) { +GfxMgr::GfxMgr(AgiBase *vm, GfxFont *font) : _vm(vm), _font(font) { _agipalFileNum = 0; memset(&_paletteGfxMode, 0, sizeof(_paletteGfxMode)); @@ -50,7 +50,17 @@ GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) { initPriorityTable(); - _renderStartOffsetY = 0; + _renderStartVisualOffsetY = 0; + _renderStartDisplayOffsetY = 0; + + _upscaledHires = DISPLAY_UPSCALED_DISABLED; + _displayScreenWidth = DISPLAY_DEFAULT_WIDTH; + _displayScreenHeight = DISPLAY_DEFAULT_HEIGHT; + _displayFontWidth = 8; + _displayFontHeight = 8; + + _displayWidthMulAdjust = 0; // visualPos * (2+0) = displayPos + _displayHeightMulAdjust = 0; // visualPos * (1+0) = displayPos } /** @@ -59,6 +69,8 @@ GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) { * @see deinit_video() */ int GfxMgr::initVideo() { + bool forceHires = false; + // Set up palettes initPalette(_paletteTextMode, PALETTE_EGA); @@ -72,6 +84,14 @@ int GfxMgr::initVideo() { case Common::kRenderVGA: initPalette(_paletteGfxMode, PALETTE_VGA, 256, 8); break; + case Common::kRenderHercG: + initPalette(_paletteGfxMode, PALETTE_HERCULES_GREEN, 2, 8); + forceHires = true; + break; + case Common::kRenderHercA: + initPalette(_paletteGfxMode, PALETTE_HERCULES_AMBER, 2, 8); + forceHires = true; + break; case Common::kRenderAmiga: if (!ConfMan.getBool("altamigapalette")) { // Set the correct Amiga palette depending on AGI interpreter version @@ -125,31 +145,47 @@ int GfxMgr::initVideo() { break; } + //bool forcedUpscale = true; + + if (_font->isFontHires() || forceHires) { + // Upscaling enable + _upscaledHires = DISPLAY_UPSCALED_640x400; + _displayScreenWidth = 640; + _displayScreenHeight = 400; + _displayFontWidth = 16; + _displayFontHeight = 16; + + _displayWidthMulAdjust = 2; + _displayHeightMulAdjust = 1; + } + // set up mouse cursors switch (_vm->_renderMode) { case Common::kRenderEGA: case Common::kRenderCGA: case Common::kRenderVGA: - initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 1, 1); + case Common::kRenderHercG: + case Common::kRenderHercA: + initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; case Common::kRenderAmiga: - initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_AMIGA_BUSY, 13, 16, 7, 8); break; case Common::kRenderApple2GS: // had no special busy mouse cursor - initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; case Common::kRenderAtariST: - initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; case Common::kRenderMacintosh: // It looks like Atari ST + Macintosh used the same standard mouse cursor // TODO: Verify by checking actual hardware - initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_MACINTOSH_BUSY, 10, 14, 7, 8); break; default: @@ -158,15 +194,15 @@ int GfxMgr::initVideo() { } _pixels = SCRIPT_WIDTH * SCRIPT_HEIGHT; - _visualScreen = (byte *)calloc(_pixels, 1); + _gameScreen = (byte *)calloc(_pixels, 1); _priorityScreen = (byte *)calloc(_pixels, 1); - _activeScreen = _visualScreen; + _activeScreen = _gameScreen; //_activeScreen = _priorityScreen; - _displayPixels = DISPLAY_WIDTH * DISPLAY_HEIGHT; + _displayPixels = _displayScreenWidth * _displayScreenHeight; _displayScreen = (byte *)calloc(_displayPixels, 1); - initGraphics(DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH > 320); + initGraphics(_displayScreenWidth, _displayScreenHeight, _displayScreenWidth > 320); setPalette(true); // set gfx-mode palette @@ -183,27 +219,151 @@ int GfxMgr::initVideo() { * @see init_video() */ int GfxMgr::deinitVideo() { + // Free mouse cursors in case they were allocated + if (_mouseCursor.bitmapDataAllocated) + free(_mouseCursor.bitmapDataAllocated); + if (_mouseCursorBusy.bitmapDataAllocated) + free(_mouseCursorBusy.bitmapDataAllocated); + free(_displayScreen); - free(_visualScreen); + free(_gameScreen); free(_priorityScreen); return errOK; } void GfxMgr::setRenderStartOffset(uint16 offsetY) { - if (offsetY >= (DISPLAY_HEIGHT - SCRIPT_HEIGHT)) + if (offsetY >= (VISUAL_HEIGHT - SCRIPT_HEIGHT)) error("invalid render start offset"); - _renderStartOffsetY = offsetY; + _renderStartVisualOffsetY = offsetY; + _renderStartDisplayOffsetY = offsetY * (1 + _displayHeightMulAdjust); +} +uint16 GfxMgr::getRenderStartDisplayOffsetY() { + return _renderStartDisplayOffsetY; +} + +// Translates a game screen coordinate to a display screen coordinate +// Game screen to 320x200 -> x * 2, y + renderStart +// Game screen to 640x400 -> x * 4, (y * 2) + renderStart +void GfxMgr::translateGamePosToDisplayScreen(int16 &x, int16 &y) { + x = x * (2 + _displayWidthMulAdjust); + y = y * (1 + _displayHeightMulAdjust) + _renderStartDisplayOffsetY; +} + +// Translates a visual coordinate to a display screen coordinate +// Visual to 320x200 -> x * 2, y +// Visual to 640x400 -> x * 4, y * 2 +void GfxMgr::translateVisualPosToDisplayScreen(int16 &x, int16 &y) { + x = x * (2 + _displayWidthMulAdjust); + y = y * (1 + _displayHeightMulAdjust); +} + +// Translates a display screen coordinate to a game screen coordinate +// Display screen to 320x200 -> x / 2, y - renderStart +// Display screen to 640x400 -> x / 4, (y / 2) - renderStart +void GfxMgr::translateDisplayPosToGameScreen(int16 &x, int16 &y) { + y -= _renderStartDisplayOffsetY; // remove status bar line + x = x / (2 + _displayWidthMulAdjust); + y = y / (1 + _displayHeightMulAdjust); + if (y < 0) + y = 0; + if (y >= SCRIPT_HEIGHT) + y = SCRIPT_HEIGHT + 1; // 1 beyond +} + +// Translates dimension from visual screen to display screen +void GfxMgr::translateVisualDimensionToDisplayScreen(int16 &width, int16 &height) { + width = width * (2 + _displayWidthMulAdjust); + height = height * (1 + _displayHeightMulAdjust); +} + +// Translates dimension from display screen to visual screen +void GfxMgr::translateDisplayDimensionToVisualScreen(int16 &width, int16 &height) { + width = width / (2 + _displayWidthMulAdjust); + height = height / (1 + _displayHeightMulAdjust); +} + +// Translates a rect from game screen to display screen +void GfxMgr::translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) { + translateGamePosToDisplayScreen(x, y); + translateVisualDimensionToDisplayScreen(width, height); +} + +// Translates a rect from visual screen to display screen +void GfxMgr::translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) { + translateVisualPosToDisplayScreen(x, y); + translateVisualDimensionToDisplayScreen(width, height); +} + +uint32 GfxMgr::getDisplayOffsetToGameScreenPos(int16 x, int16 y) { + translateGamePosToDisplayScreen(x, y); + return (y * _displayScreenWidth) + x; +} + +uint32 GfxMgr::getDisplayOffsetToVisualScreenPos(int16 x, int16 y) { + translateVisualPosToDisplayScreen(x, y); + return (y * _displayScreenWidth) + x; +} + +// Attention: uses display screen coordinates! +void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) { + g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + break; + case DISPLAY_UPSCALED_640x400: + adjX *= 2; adjY *= 2; + adjWidth *= 2; adjHeight *= 2; + break; + default: + assert(0); + break; + } + x += adjX; y += adjY; + width += adjWidth; height += adjHeight; + g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height) { + translateGameRectToDisplayScreen(x, y, width, height); + g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height); } -uint16 GfxMgr::getRenderStartOffsetY() { - return _renderStartOffsetY; +void GfxMgr::copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height) { + translateVisualRectToDisplayScreen(x, y, width, height); + g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayToScreen() { + g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, 0, 0, _displayScreenWidth, _displayScreenHeight); +} + +void GfxMgr::translateFontPosToDisplayScreen(int16 &x, int16 &y) { + x *= _displayFontWidth; + y *= _displayFontHeight; +} +void GfxMgr::translateDisplayPosToFontScreen(int16 &x, int16 &y) { + x /= _displayFontWidth; + y /= _displayFontHeight; +} +void GfxMgr::translateFontDimensionToDisplayScreen(int16 &width, int16 &height) { + width *= _displayFontWidth; + height *= _displayFontHeight; +} +void GfxMgr::translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) { + translateFontPosToDisplayScreen(x, y); + translateFontDimensionToDisplayScreen(width, height); +} +Common::Rect GfxMgr::getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height) { + Common::Rect displayRect(width * _displayFontWidth, height * _displayFontHeight); + displayRect.moveTo(column * _displayFontWidth, row * _displayFontHeight); + return displayRect; } void GfxMgr::debugShowMap(int mapNr) { switch (mapNr) { case 0: - _activeScreen = _visualScreen; + _activeScreen = _gameScreen; break; case 1: _activeScreen = _priorityScreen; @@ -212,11 +372,11 @@ void GfxMgr::debugShowMap(int mapNr) { break; } - render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT); + render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT); } void GfxMgr::clear(byte color, byte priority) { - memset(_visualScreen, color, _pixels); + memset(_gameScreen, color, _pixels); memset(_priorityScreen, priority, _pixels); } @@ -224,7 +384,7 @@ void GfxMgr::clearDisplay(byte color, bool copyToScreen) { memset(_displayScreen, color, _displayPixels); if (copyToScreen) { - g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); + copyDisplayToScreen(); } } @@ -232,7 +392,7 @@ void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority int offset = y * SCRIPT_WIDTH + x; if (drawMask & GFX_SCREEN_MASK_VISUAL) { - _visualScreen[offset] = color; + _gameScreen[offset] = color; } if (drawMask & GFX_SCREEN_MASK_PRIORITY) { _priorityScreen[offset] = priority; @@ -240,15 +400,72 @@ void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority } void GfxMgr::putPixelOnDisplay(int16 x, int16 y, byte color) { - int offset = y * DISPLAY_WIDTH + x; + uint32 offset = 0; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + offset = y * _displayScreenWidth + x; + + _displayScreen[offset] = color; + break; + case DISPLAY_UPSCALED_640x400: + offset = (y * _displayScreenWidth) + x; + + _displayScreen[offset + 0] = color; + _displayScreen[offset + 1] = color; + _displayScreen[offset + _displayScreenWidth + 0] = color; + _displayScreen[offset + _displayScreenWidth + 1] = color; + break; + default: + break; + } +} - _displayScreen[offset] = color; +void GfxMgr::putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + break; + case DISPLAY_UPSCALED_640x400: + adjX *= 2; adjY *= 2; + break; + default: + assert(0); + break; + } + x += adjX; + y += adjY; + putPixelOnDisplay(x, y, color); +} + +void GfxMgr::putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires) { + uint32 offset = 0; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX); + _displayScreen[offset] = color; + break; + case DISPLAY_UPSCALED_640x400: + if (isHires) { + offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX); + _displayScreen[offset] = color; + } else { + offset = ((baseY + addY * 2) * _displayScreenWidth) + (baseX + addX * 2); + _displayScreen[offset + 0] = color; + _displayScreen[offset + 1] = color; + _displayScreen[offset + _displayScreenWidth + 0] = color; + _displayScreen[offset + _displayScreenWidth + 1] = color; + } + break; + default: + break; + } } byte GfxMgr::getColor(int16 x, int16 y) { int offset = y * SCRIPT_WIDTH + x; - return _visualScreen[offset]; + return _gameScreen[offset]; } byte GfxMgr::getPriority(int16 x, int16 y) { @@ -288,12 +505,17 @@ byte GfxMgr::getCGAMixtureColor(byte color) { return CGA_MixtureColorTable[color & 0x0F]; } -// Attention: y-coordinate points to the LOWER left! +// Attention: in our implementation, y-coordinate is upper left. +// Sierra passed the lower left instead. We changed it to make upscaling easier. void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { if (!render_Clip(x, y, width, height)) return; switch (_vm->_renderMode) { + case Common::kRenderHercG: + case Common::kRenderHercA: + render_BlockHercules(x, y, width, height, copyToScreen); + break; case Common::kRenderCGA: render_BlockCGA(x, y, width, height, copyToScreen); break; @@ -304,17 +526,26 @@ void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copy } if (copyToScreen) { - int16 upperY = y - height + 1 + _renderStartOffsetY; - g_system->copyRectToScreen(_displayScreen + upperY * DISPLAY_WIDTH + x * 2, DISPLAY_WIDTH, x * 2, upperY, width * 2, height); + copyDisplayRectToScreenUsingGamePos(x, y, width, height); } } bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 clipAgainstWidth, int16 clipAgainstHeight) { if ((x >= clipAgainstWidth) || ((x + width - 1) < 0) || - (y < 0) || ((y - (height - 1)) >= clipAgainstHeight)) { + (y < 0) || ((y + (height - 1)) >= clipAgainstHeight)) { return false; } + if (y < 0) { + height += y; + y = 0; + } + + if ((y + height - 1) >= clipAgainstHeight) { + height = clipAgainstHeight - y; + } + +#if 0 if ((y - height + 1) < 0) height = y + 1; @@ -322,6 +553,7 @@ bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 height -= y - (clipAgainstHeight - 1); y = clipAgainstHeight - 1; } +#endif if (x < 0) { width += x; @@ -335,52 +567,212 @@ bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 } void GfxMgr::render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { - int offsetVisual = SCRIPT_WIDTH * y + x; - int offsetDisplay = (DISPLAY_WIDTH * (y + _renderStartOffsetY)) + x * 2; + uint32 offsetVisual = SCRIPT_WIDTH * y + x; + uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y); int16 remainingWidth = width; int16 remainingHeight = height; byte curColor = 0; + int16 displayWidth = width * (2 + _displayWidthMulAdjust); while (remainingHeight) { remainingWidth = width; - while (remainingWidth) { - curColor = _activeScreen[offsetVisual++]; - _displayScreen[offsetDisplay++] = curColor; - _displayScreen[offsetDisplay++] = curColor; - remainingWidth--; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + _displayScreen[offsetDisplay++] = curColor; + _displayScreen[offsetDisplay++] = curColor; + remainingWidth--; + } + break; + case DISPLAY_UPSCALED_640x400: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + memset(&_displayScreen[offsetDisplay], curColor, 4); + memset(&_displayScreen[offsetDisplay + _displayScreenWidth], curColor, 4); + offsetDisplay += 4; + remainingWidth--; + } + break; + default: + assert(0); + break; + } + + offsetVisual += SCRIPT_WIDTH - width; + offsetDisplay += _displayScreenWidth - displayWidth; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_640x400: + offsetDisplay += _displayScreenWidth;; + break; + default: + break; } - offsetVisual -= SCRIPT_WIDTH + width; - offsetDisplay -= DISPLAY_WIDTH + width * 2; remainingHeight--; } } void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { - int offsetVisual = SCRIPT_WIDTH * y + x; - int offsetDisplay = (DISPLAY_WIDTH * (y + _renderStartOffsetY)) + x * 2; + uint32 offsetVisual = SCRIPT_WIDTH * y + x; + uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y); int16 remainingWidth = width; int16 remainingHeight = height; byte curColor = 0; + int16 displayWidth = width * (2 + _displayWidthMulAdjust); while (remainingHeight) { remainingWidth = width; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + _displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture + _displayScreen[offsetDisplay++] = curColor >> 2; + remainingWidth--; + } + break; + case DISPLAY_UPSCALED_640x400: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + _displayScreen[offsetDisplay + 0] = curColor & 0x03; // we process CGA mixture + _displayScreen[offsetDisplay + 1] = curColor >> 2; + _displayScreen[offsetDisplay + 2] = curColor & 0x03; + _displayScreen[offsetDisplay + 3] = curColor >> 2; + _displayScreen[offsetDisplay + _displayScreenWidth + 0] = curColor & 0x03; + _displayScreen[offsetDisplay + _displayScreenWidth + 1] = curColor >> 2; + _displayScreen[offsetDisplay + _displayScreenWidth + 2] = curColor & 0x03; + _displayScreen[offsetDisplay + _displayScreenWidth + 3] = curColor >> 2; + offsetDisplay += 4; + remainingWidth--; + } + break; + default: + assert(0); + break; + } + + offsetVisual += SCRIPT_WIDTH - width; + offsetDisplay += _displayScreenWidth - displayWidth; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_640x400: + offsetDisplay += _displayScreenWidth;; + break; + default: + break; + } + + remainingHeight--; + } +} + +static const uint8 herculesColorMapping[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04, + 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, + 0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF, + 0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA, + 0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, + 0x77, 0xFF, 0xFF, 0xFF, 0xDD, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +// Sierra actually seems to have rendered the whole screen all the time +void GfxMgr::render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { + uint32 offsetVisual = SCRIPT_WIDTH * y + x; + uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y); + int16 remainingWidth = width; + int16 remainingHeight = height; + byte curColor = 0; + int16 displayWidth = width * (2 + _displayWidthMulAdjust); + + assert(_upscaledHires == DISPLAY_UPSCALED_640x400); + + uint16 lookupOffset1 = (y * 2 & 0x07); + uint16 lookupOffset2 = 0; + bool getUpperNibble = false; + byte herculesColors1 = 0; + byte herculesColors2 = 0; + + while (remainingHeight) { + remainingWidth = width; + + lookupOffset1 = (lookupOffset1 + 0) & 0x07; + lookupOffset2 = (lookupOffset1 + 1) & 0x07; + + getUpperNibble = (x & 1) ? false : true; while (remainingWidth) { - curColor = _activeScreen[offsetVisual++]; - _displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture - _displayScreen[offsetDisplay++] = curColor >> 2; + curColor = _activeScreen[offsetVisual++] & 0x0F; + + if (getUpperNibble) { + herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] & 0x0F; + herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] & 0x0F; + } else { + herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] >> 4; + herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] >> 4; + } + getUpperNibble ^= true; + + _displayScreen[offsetDisplay + 0] = (herculesColors1 & 0x08) ? 1 : 0; + _displayScreen[offsetDisplay + 1] = (herculesColors1 & 0x04) ? 1 : 0; + _displayScreen[offsetDisplay + 2] = (herculesColors1 & 0x02) ? 1 : 0; + _displayScreen[offsetDisplay + 3] = (herculesColors1 & 0x01) ? 1 : 0; + + _displayScreen[offsetDisplay + _displayScreenWidth + 0] = (herculesColors2 & 0x08) ? 1 : 0; + _displayScreen[offsetDisplay + _displayScreenWidth + 1] = (herculesColors2 & 0x04) ? 1 : 0; + _displayScreen[offsetDisplay + _displayScreenWidth + 2] = (herculesColors2 & 0x02) ? 1 : 0; + _displayScreen[offsetDisplay + _displayScreenWidth + 3] = (herculesColors2 & 0x01) ? 1 : 0; + + offsetDisplay += 4; remainingWidth--; } - offsetVisual -= SCRIPT_WIDTH + width; - offsetDisplay -= DISPLAY_WIDTH + width * 2; + + lookupOffset1 += 2; + + offsetVisual += SCRIPT_WIDTH - width; + offsetDisplay += _displayScreenWidth - displayWidth; + offsetDisplay += _displayScreenWidth;; remainingHeight--; } } +// Table used for at least Manhunter 2, it renders 2 lines -> 3 lines instead of 4 +// Manhunter 1 is shipped with a broken Hercules font +// King's Quest 4 aborts right at the start, when Hercules rendering is active +#if 0 +static const uint8 herculesCoordinateOffset[] = { + 0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x01, 0x02, + 0x04, 0x05, 0x07, 0x00, 0x02, 0x03, 0x05, 0x06 +}; + +static const uint8 herculesColorMapping[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x08, 0x00, + 0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF, + 0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA, 0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, + 0x7F, 0xEF, 0xFB, 0xBF, 0xEF, 0xFE, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +#endif + void GfxMgr::transition_Amiga() { uint16 screenPos = 1; - uint16 screenStepPos = 1; + uint32 screenStepPos = 1; int16 posY = 0, posX = 0; int16 stepCount = 0; @@ -402,15 +794,29 @@ void GfxMgr::transition_Amiga() { posY = screenStepPos / SCRIPT_WIDTH; posX = screenStepPos - (posY * SCRIPT_WIDTH); - posY += _renderStartOffsetY; // adjust to only update the main area, not the status bar - posX *= 2; // adjust for display screen - - screenStepPos = (screenStepPos * 2) + (_renderStartOffsetY * DISPLAY_WIDTH); // adjust here too for display screen - for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) { - g_system->copyRectToScreen(_displayScreen + screenStepPos, DISPLAY_WIDTH, posX, posY, 2, 1); - screenStepPos += (0x1A40 * 2); // 6720d - posY += 42; + // Adjust to only update the game screen, not the status bar + translateGamePosToDisplayScreen(posX, posY); + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 1); + posY += 42; + } + break; + case DISPLAY_UPSCALED_640x400: + for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 4, 2); + posY += 42 * 2; + } + break; + default: + assert(0); + break; } + stepCount++; if (stepCount == 220) { // 30 times for the whole transition, so should take around 0.5 seconds @@ -433,7 +839,7 @@ void GfxMgr::transition_Amiga() { // Atari ST definitely had a hi-res transition using the full resolution unlike the Amiga transition. void GfxMgr::transition_AtariSt() { uint16 screenPos = 1; - uint16 screenStepPos = 1; + uint32 screenStepPos = 1; int16 posY = 0, posX = 0; int16 stepCount = 0; @@ -452,17 +858,31 @@ void GfxMgr::transition_AtariSt() { if ((screenPos < 13440) && (screenPos & 1)) { screenStepPos = screenPos >> 1; - posY = screenStepPos / DISPLAY_WIDTH; - posX = screenStepPos - (posY * DISPLAY_WIDTH); - - posY += _renderStartOffsetY; // adjust to only update the main area, not the status bar - - screenStepPos = screenStepPos + (_renderStartOffsetY * DISPLAY_WIDTH); // adjust here too for display screen - for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) { - g_system->copyRectToScreen(_displayScreen + screenStepPos, DISPLAY_WIDTH, posX, posY, 1, 1); - screenStepPos += 0x1a40; // 6720d - posY += 21; + posY = screenStepPos / DISPLAY_DEFAULT_WIDTH; + posX = screenStepPos - (posY * DISPLAY_DEFAULT_WIDTH); + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar + for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 1, 1); + posY += 21; + } + break; + case DISPLAY_UPSCALED_640x400: + posX *= 2; posY *= 2; + posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar + for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 2); + posY += 21 * 2; + } + break; + default: + break; } + stepCount++; if (stepCount == 168) { // 40 times for the whole transition, so should take around 0.7 seconds @@ -493,7 +913,7 @@ void GfxMgr::block_save(int16 x, int16 y, int16 width, int16 height, byte *buffe //warning("block_save: %d, %d -> %d, %d", x, y, width, height); while (remainingHeight) { - memcpy(curBufferPtr, _visualScreen + offset, width); + memcpy(curBufferPtr, _gameScreen + offset, width); offset += SCRIPT_WIDTH; curBufferPtr += width; remainingHeight--; @@ -519,7 +939,7 @@ void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bu //warning("block_restore: %d, %d -> %d, %d", x, y, width, height); while (remainingHeight) { - memcpy(_visualScreen + offset, curBufferPtr, width); + memcpy(_gameScreen + offset, curBufferPtr, width); offset += SCRIPT_WIDTH; curBufferPtr += width; remainingHeight--; @@ -535,15 +955,8 @@ void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bu } } -// Attention: uses visual screen coordinates! -void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) { - g_system->copyRectToScreen(_displayScreen + y * DISPLAY_WIDTH + x, DISPLAY_WIDTH, x, y, width, height); -} -void GfxMgr::copyDisplayToScreen() { - g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); -} - // coordinates are for visual screen, but are supposed to point somewhere inside the playscreen +// x, y is the upper left. Sierra passed them as lower left. We change that to make upscaling easier. // attention: Clipping is done here against 160x200 instead of 160x168 // Original interpreter didn't do any clipping, we do it for security. // Clipping against the regular script width/height must not be done, @@ -551,13 +964,13 @@ void GfxMgr::copyDisplayToScreen() { // Going beyond 160x168 will result in messageboxes not getting fully removed // In KQ4's case, the scripts clear the screen that's why it works. void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor) { - if (!render_Clip(x, y, width, height, SCRIPT_WIDTH, DISPLAY_HEIGHT - _renderStartOffsetY)) + if (!render_Clip(x, y, width, height, VISUAL_WIDTH, VISUAL_HEIGHT - _renderStartVisualOffsetY)) return; // coordinate translation: visual-screen -> display-screen - x = x * 2; - y = y + _renderStartOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen - width = width * 2; // width was given as visual width, we need display width + translateVisualRectToDisplayScreen(x, y, width, height); + + y = y + _renderStartDisplayOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen // draw box background drawDisplayRect(x, y, width, height, backgroundColor); @@ -567,27 +980,31 @@ void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroun case Common::kRenderApple2GS: case Common::kRenderAmiga: // Slightly different window frame, and actually using 1-pixel width, which is "hi-res" - drawDisplayRect(x + 2, y - 2, width - 4, 1, lineColor); - drawDisplayRect(x + width - 3, y - 2, 1, height - 4, lineColor); - drawDisplayRect(x + 2, y - height + 3, width - 4, 1, lineColor); - drawDisplayRect(x + 2, y - 2, 1, height - 4, lineColor); + drawDisplayRect(x, +2, y, +2, width, -4, 0, 1, lineColor); + drawDisplayRect(x + width, -3, y, +2, 0, 1, height, -4, lineColor); + drawDisplayRect(x, +2, y + height, -3, width, -4, 0, 1, lineColor); + drawDisplayRect(x, +2, y, +2, 0, 1, height, -4, lineColor); break; case Common::kRenderMacintosh: // 1 pixel between box and frame lines. Frame lines were black - drawDisplayRect(x + 1, y - 1, width - 2, 1, 0); - drawDisplayRect(x + width - 2, y - 1, 1, height - 2, 0); - drawDisplayRect(x + 1, y - height + 2, width - 2, 1, 0); - drawDisplayRect(x + 1, y - 1, 1, height - 2, 0); + drawDisplayRect(x, +1, y, +1, width, -2, 0, 1, 0); + drawDisplayRect(x + width, -2, y, +1, 0, 1, height, -2, 0); + drawDisplayRect(x, +1, y + height, -2, width, -2, 0, 1, 0); + drawDisplayRect(x, +1, y, +1, 0, 1, height, -2, 0); break; + case Common::kRenderHercA: + case Common::kRenderHercG: + lineColor = 0; // change linecolor to black + // supposed to fall through case Common::kRenderCGA: case Common::kRenderEGA: case Common::kRenderVGA: case Common::kRenderAtariST: default: - drawDisplayRect(x + 2, y - 1, width - 4, 1, lineColor); - drawDisplayRect(x + width - 4, y - 2, 2, height - 4, lineColor); - drawDisplayRect(x + 2, y - height + 2, width - 4, 1, lineColor); - drawDisplayRect(x + 2, y - 2, 2, height - 4, lineColor); + drawDisplayRect(x, +2, y, +1, width, -4, 0, 1, lineColor); + drawDisplayRect(x + width, -4, y, +2, 0, 2, height, -4, lineColor); + drawDisplayRect(x, +2, y + height, -2, width, -4, 0, 1, lineColor); + drawDisplayRect(x, +2, y, +2, 0, 2, height, -4, lineColor); break; } } @@ -598,31 +1015,52 @@ void GfxMgr::drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte c case Common::kRenderCGA: drawDisplayRectCGA(x, y, width, height, color); break; + case Common::kRenderHercG: + case Common::kRenderHercA: + if (color) + color = 1; // change any color except black to green/amber + // supposed to fall through case Common::kRenderEGA: default: drawDisplayRectEGA(x, y, width, height, color); break; } if (copyToScreen) { - int16 upperY = y - height + 1; - g_system->copyRectToScreen(_displayScreen + upperY * DISPLAY_WIDTH + x, DISPLAY_WIDTH, x, upperY, width, height); + copyDisplayRectToScreen(x, y, width, height); } } +void GfxMgr::drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + x += adjX; y += adjY; + width += adjWidth; height += adjHeight; + break; + case DISPLAY_UPSCALED_640x400: + x += adjX * 2; y += adjY * 2; + width += adjWidth * 2; height += adjHeight * 2; + break; + default: + assert(0); + break; + } + drawDisplayRect(x, y, width, height, color, copyToScreen); +} + void GfxMgr::drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color) { - int offsetDisplay = (DISPLAY_WIDTH * y) + x; + uint32 offsetDisplay = (y * _displayScreenWidth) + x; int16 remainingHeight = height; while (remainingHeight) { memset(_displayScreen + offsetDisplay, color, width); - offsetDisplay -= DISPLAY_WIDTH; + offsetDisplay += _displayScreenWidth; remainingHeight--; } } void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color) { - int offsetDisplay = (DISPLAY_WIDTH * y) + x; + uint32 offsetDisplay = (y * _displayScreenWidth) + x; int16 remainingHeight = height; int16 remainingWidth = width; byte CGAMixtureColor = getCGAMixtureColor(color); @@ -643,18 +1081,20 @@ void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byt remainingWidth -= 2; } - offsetDisplay -= DISPLAY_WIDTH; + offsetDisplay += _displayScreenWidth; remainingHeight--; } } // row + column are text-coordinates void GfxMgr::drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook) { - int16 x = column * FONT_DISPLAY_WIDTH; - int16 y = row * FONT_DISPLAY_HEIGHT; + int16 x = column; + int16 y = row; byte transformXOR = 0; byte transformOR = 0; + translateFontPosToDisplayScreen(x, y); + // Now figure out, if special handling needs to be done if (_vm->_game.gfxMode) { if (background & 0x08) { @@ -675,22 +1115,43 @@ void GfxMgr::drawStringOnDisplay(int16 x, int16 y, const char *text, byte foregr while (*text) { drawCharacterOnDisplay(x, y, *text, foregroundColor, backgroundColor); text++; - x += FONT_DISPLAY_WIDTH; + x += _displayFontWidth; } } +void GfxMgr::drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + x += adjX; + y += adjY; + break; + case DISPLAY_UPSCALED_640x400: + x += adjX * 2; + y += adjY * 2; + break; + default: + assert(0); + break; + } + drawStringOnDisplay(x, y, text, foregroundColor, backgroundColor); +} + void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte foreground, byte background, byte transformXOR, byte transformOR) { int16 curX, curY; const byte *fontData; + bool fontIsHires = _font->isFontHires(); + int16 fontHeight = fontIsHires ? 16 : FONT_DISPLAY_HEIGHT; + int16 fontWidth = fontIsHires ? 16 : FONT_DISPLAY_WIDTH; + int16 fontBytesPerCharacter = fontIsHires ? 32 : FONT_BYTES_PER_CHARACTER; byte curByte = 0; uint16 curBit; // get font data of specified character - fontData = _vm->getFontData() + character * FONT_BYTES_PER_CHARACTER; + fontData = _font->getFontData() + character * fontBytesPerCharacter; curBit = 0; - for (curY = 0; curY < FONT_DISPLAY_HEIGHT; curY++) { - for (curX = 0; curX < FONT_DISPLAY_WIDTH; curX++) { + for (curY = 0; curY < fontHeight; curY++) { + for (curX = 0; curX < fontWidth; curX++) { if (!curBit) { curByte = *fontData; // do transformations in case they are needed (invert/disabled look) @@ -700,9 +1161,9 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte curBit = 0x80; } if (curByte & curBit) { - putPixelOnDisplay(x + curX, y + curY, foreground); + putFontPixelOnDisplay(x, y, curX, curY, foreground, fontIsHires); } else { - putPixelOnDisplay(x + curX, y + curY, background); + putFontPixelOnDisplay(x, y, curX, curY, background, fontIsHires); } curBit = curBit >> 1; } @@ -710,18 +1171,20 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte transformOR ^= 0xFF; } - copyDisplayRectToScreen(x, y, FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT); + copyDisplayRectToScreen(x, y, _displayFontWidth, _displayFontHeight); } #define SHAKE_VERTICAL_PIXELS 4 -#define SHAKE_HORIZONTAL_PIXELS 8 +#define SHAKE_HORIZONTAL_PIXELS 4 // Sierra used some EGA port trickery to do it, we have to do it by copying pixels around void GfxMgr::shakeScreen(int16 repeatCount) { int shakeNr, shakeCount; uint8 *blackSpace; + int16 shakeHorizontalPixels = SHAKE_HORIZONTAL_PIXELS * (2 + _displayWidthMulAdjust); + int16 shakeVerticalPixels = SHAKE_VERTICAL_PIXELS * (1 + _displayHeightMulAdjust); - if ((blackSpace = (uint8 *)calloc(SHAKE_HORIZONTAL_PIXELS * DISPLAY_WIDTH, 1)) == NULL) + if ((blackSpace = (uint8 *)calloc(shakeVerticalPixels * _displayScreenWidth, 1)) == NULL) return; shakeCount = repeatCount * 8; // effectively 4 shakes per repeat @@ -733,10 +1196,10 @@ void GfxMgr::shakeScreen(int16 repeatCount) { // move back copyDisplayToScreen(); } else { - g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, SHAKE_HORIZONTAL_PIXELS, SHAKE_VERTICAL_PIXELS, DISPLAY_WIDTH - SHAKE_HORIZONTAL_PIXELS, DISPLAY_HEIGHT - SHAKE_VERTICAL_PIXELS); + g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, shakeHorizontalPixels, shakeVerticalPixels, _displayScreenWidth - shakeHorizontalPixels, _displayScreenHeight - shakeVerticalPixels); // additionally fill the remaining space with black - g_system->copyRectToScreen(blackSpace, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, SHAKE_VERTICAL_PIXELS); - g_system->copyRectToScreen(blackSpace, SHAKE_HORIZONTAL_PIXELS, 0, 0, SHAKE_HORIZONTAL_PIXELS, DISPLAY_HEIGHT); + g_system->copyRectToScreen(blackSpace, _displayScreenWidth, 0, 0, _displayScreenWidth, shakeVerticalPixels); + g_system->copyRectToScreen(blackSpace, shakeHorizontalPixels, 0, 0, shakeHorizontalPixels, _displayScreenHeight); } g_system->updateScreen(); g_system->delayMillis(66); // Sierra waited for 4 V'Syncs, which is around 66 milliseconds @@ -956,7 +1419,38 @@ int GfxMgr::getAGIPalFileNum() { } void GfxMgr::initMouseCursor(MouseCursorData *mouseCursor, const byte *bitmapData, uint16 width, uint16 height, int hotspotX, int hotspotY) { - mouseCursor->bitmapData = bitmapData; + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + mouseCursor->bitmapData = bitmapData; + break; + case DISPLAY_UPSCALED_640x400: { + mouseCursor->bitmapDataAllocated = (byte *)malloc(width * height * 4); + mouseCursor->bitmapData = mouseCursor->bitmapDataAllocated; + + // Upscale mouse cursor + byte *upscaledData = mouseCursor->bitmapDataAllocated; + + for (uint16 y = 0; y < height; y++) { + for (uint16 x = 0; x < width; x++) { + byte curColor = *bitmapData++; + upscaledData[x * 2 + 0] = curColor; + upscaledData[x * 2 + 1] = curColor; + upscaledData[x * 2 + (width * 2) + 0] = curColor; + upscaledData[x * 2 + (width * 2) + 1] = curColor; + } + upscaledData += width * 2 * 2; + } + + width *= 2; + height *= 2; + hotspotX *= 2; + hotspotY *= 2; + break; + } + default: + assert(0); + break; + } mouseCursor->width = width; mouseCursor->height = height; mouseCursor->hotspotX = hotspotX; diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h index 3f94b3e950..1cb595cdfa 100644 --- a/engines/agi/graphics.h +++ b/engines/agi/graphics.h @@ -29,8 +29,15 @@ namespace Agi { #define SCRIPT_WIDTH 160 #define SCRIPT_HEIGHT 168 -#define DISPLAY_WIDTH 320 -#define DISPLAY_HEIGHT 200 +#define VISUAL_WIDTH 160 +#define VISUAL_HEIGHT 200 +#define DISPLAY_DEFAULT_WIDTH 320 +#define DISPLAY_DEFAULT_HEIGHT 200 + +enum GfxScreenUpscaledMode { + DISPLAY_UPSCALED_DISABLED = 0, + DISPLAY_UPSCALED_640x400 = 1 +}; class AgiEngine; @@ -42,6 +49,7 @@ enum GfxScreenMasks { struct MouseCursorData { const byte *bitmapData; + byte *bitmapDataAllocated; uint16 width; uint16 height; int hotspotX; @@ -51,6 +59,7 @@ struct MouseCursorData { class GfxMgr { private: AgiBase *_vm; + GfxFont *_font; uint8 _paletteGfxMode[256 * 3]; uint8 _paletteTextMode[256 * 3]; @@ -59,7 +68,7 @@ private: int _agipalFileNum; public: - GfxMgr(AgiBase *vm); + GfxMgr(AgiBase *vm, GfxFont *font); int initVideo(); int deinitVideo(); @@ -73,18 +82,58 @@ public: void setMouseCursor(bool busy = false); void setRenderStartOffset(uint16 offsetY); - uint16 getRenderStartOffsetY(); + uint16 getRenderStartDisplayOffsetY(); + + void translateGamePosToDisplayScreen(int16 &x, int16 &y); + void translateVisualPosToDisplayScreen(int16 &x, int16 &y); + void translateDisplayPosToGameScreen(int16 &x, int16 &y); + + void translateVisualDimensionToDisplayScreen(int16 &width, int16 &height); + void translateDisplayDimensionToVisualScreen(int16 &width, int16 &height); + + void translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height); + void translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height); + void translateDisplayRectToVisualScreen(int16 &x, int16 &y, int16 &width, int16 &height); + + uint32 getDisplayOffsetToGameScreenPos(int16 x, int16 y); + uint32 getDisplayOffsetToVisualScreenPos(int16 x, int16 y); + + void copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height); + void copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight); + void copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height); + void copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height); + void copyDisplayToScreen(); + + void translateFontPosToDisplayScreen(int16 &x, int16 &y); + void translateDisplayPosToFontScreen(int16 &x, int16 &y); + void translateFontDimensionToDisplayScreen(int16 &width, int16 &height); + void translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height); + Common::Rect getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height); private: uint _pixels; - //uint16 _displayWidth; - //uint16 _displayHeight; uint _displayPixels; byte *_activeScreen; - byte *_visualScreen; // 160x168 - byte *_priorityScreen; // 160x168 - byte *_displayScreen; // 320x200 + byte *_gameScreen; // 160x168 - screen, where the actual game content is drawn to (actual graphics, not including status line, prompt, etc.) + byte *_priorityScreen; // 160x168 - screen contains priority information of the game screen + // the term "visual screen" is effectively the display screen, but at 160x200 resolution. Used for coordinate translation + byte *_displayScreen; // 320x200 or 640x400 - screen, that the game is rendered to and which is then copied to framebuffer + + uint16 _displayScreenWidth; + uint16 _displayScreenHeight; + + uint16 _displayFontWidth; + uint16 _displayFontHeight; + + uint16 _displayWidthMulAdjust; + uint16 _displayHeightMulAdjust; + + /** + * This variable defines, if upscaled hires is active and what upscaled mode + * is used. + */ + GfxScreenUpscaledMode _upscaledHires; bool _priorityTableSet; uint8 _priorityTable[SCRIPT_HEIGHT]; /**< priority table */ @@ -92,15 +141,32 @@ private: MouseCursorData _mouseCursor; MouseCursorData _mouseCursorBusy; - uint16 _renderStartOffsetY; + uint16 _renderStartVisualOffsetY; + uint16 _renderStartDisplayOffsetY; public: + uint16 getDisplayScreenWidth() { + return _displayScreenWidth; + } + uint16 getDisplayFontWidth() { + return _displayFontWidth; + } + uint16 getDisplayFontHeight() { + return _displayFontHeight; + } + + GfxScreenUpscaledMode getUpscaledHires() { + return _upscaledHires; + } + void debugShowMap(int mapNr); void clear(byte color, byte priority); void clearDisplay(byte color, bool copyToScreen = true); void putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority); void putPixelOnDisplay(int16 x, int16 y, byte color); + void putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color); + void putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires); byte getColor(int16 x, int16 y); byte getPriority(int16 x, int16 y); @@ -114,6 +180,7 @@ public: private: void render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen); void render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen); + void render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen); public: void transition_Amiga(); @@ -122,11 +189,9 @@ public: void block_save(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr); void block_restore(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr); - void copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height); - void copyDisplayToScreen(); - void drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor); void drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte color, bool copyToScreen = true); + void drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen = true); private: void drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color); void drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color); @@ -134,6 +199,7 @@ private: public: void drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook); void drawStringOnDisplay(int16 x, int16 y, const char *text, byte foreground, byte background); + void drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor); void drawCharacterOnDisplay(int16 x, int16 y, byte character, byte foreground, byte background, byte transformXOR = 0, byte transformOR = 0); void shakeScreen(int16 repeatCount); diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp index ba1b2f5729..3bc45af5d4 100644 --- a/engines/agi/keyboard.cpp +++ b/engines/agi/keyboard.cpp @@ -313,7 +313,8 @@ bool AgiEngine::handleMouseClicks(uint16 &key) { if (!cycleInnerLoopIsActive()) { // Only do this, when no inner loop is currently active - Common::Rect displayLineRect(DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT); + Common::Rect displayLineRect = _gfx->getFontRectForDisplayScreen(0, 0, FONT_COLUMN_CHARACTERS, 1); +// Common::Rect displayLineRect(_gfx->getDisplayScreenWidth(), _gfx->getDisplayFontHeight()); if (displayLineRect.contains(_mouse.pos)) { // Mouse is inside first line of the screen @@ -328,7 +329,7 @@ bool AgiEngine::handleMouseClicks(uint16 &key) { // Prompt is currently enabled int16 promptRow = _text->promptRow_Get(); - displayLineRect.moveTo(0, promptRow * FONT_DISPLAY_HEIGHT); + displayLineRect.moveTo(0, promptRow * _gfx->getDisplayFontHeight()); if (displayLineRect.contains(_mouse.pos)) { // and user clicked within the line of the prompt @@ -351,9 +352,7 @@ bool AgiEngine::handleMouseClicks(uint16 &key) { _text->stringPos_Get(stringRow, stringColumn); stringMaxLen = _text->stringGetMaxLen(); - Common::Rect displayRect(stringMaxLen * FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT); - displayRect.moveTo(stringColumn * FONT_DISPLAY_WIDTH, stringRow * FONT_DISPLAY_HEIGHT); - + Common::Rect displayRect = _gfx->getFontRectForDisplayScreen(stringColumn, stringRow, stringMaxLen, 1); if (displayRect.contains(_mouse.pos)) { // user clicked inside the input space showPredictiveDialog(); @@ -493,7 +492,7 @@ bool AgiEngine::handleController(uint16 key) { // in case you walked to the log by using the mouse, so don't!!! int16 egoDestinationX = _mouse.pos.x; int16 egoDestinationY = _mouse.pos.y; - adjustPosToGameScreen(egoDestinationX, egoDestinationY); + _gfx->translateDisplayPosToGameScreen(egoDestinationX, egoDestinationY); screenObjEgo->motionType = kMotionEgo; if (egoDestinationX < (screenObjEgo->xSize / 2)) { diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index cef60ca161..49c2d0eeab 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -49,8 +49,8 @@ GfxMenu::GfxMenu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture, TextMgr *text) _drawnMenuNr = -1; _drawnMenuHeight = 0; _drawnMenuWidth = 0; - _drawnMenuRow = 0; - _drawnMenuColumn = 0; + _drawnMenuY = 0; + _drawnMenuX = 0; } GfxMenu::~GfxMenu() { @@ -323,8 +323,9 @@ void GfxMenu::execute() { // Unless we are in "via mouse" mode. In that case check current mouse position if (viaMouse) { - int16 mouseRow = _vm->_mouse.pos.y / FONT_DISPLAY_HEIGHT; - int16 mouseColumn = _vm->_mouse.pos.x / FONT_DISPLAY_WIDTH; + int16 mouseRow = _vm->_mouse.pos.y; + int16 mouseColumn = _vm->_mouse.pos.x; + _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow); mouseFindMenuSelection(mouseRow, mouseColumn, _drawnMenuNr, _mouseModeItemNr); } @@ -368,7 +369,7 @@ void GfxMenu::execute() { // WORKAROUND: Playarea starts right at the stop, so instead of clearing that part, render it from playarea // Required for at least Donald Duck // This was not done by original AGI, which means the upper pixel line were cleared in this case. - _gfx->render_Block(0, (1 * FONT_VISUAL_HEIGHT) - 1, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT); } else { _text->clearLine(0, 0); } @@ -427,10 +428,11 @@ void GfxMenu::drawMenu(int16 selectedMenuNr, int16 selectedMenuItemNr) { // calculate active menu dimensions _drawnMenuHeight = (menuEntry->itemCount + 2) * FONT_VISUAL_HEIGHT; _drawnMenuWidth = (menuEntry->maxItemTextLen * FONT_VISUAL_WIDTH) + 8; - _drawnMenuRow = (menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1; - _drawnMenuColumn = (itemEntry->column - 1) * FONT_VISUAL_WIDTH; + _drawnMenuY = (1 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT; + //(menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1; + _drawnMenuX = (itemEntry->column - 1) * FONT_VISUAL_WIDTH; - _gfx->drawBox(_drawnMenuColumn, _drawnMenuRow, _drawnMenuWidth, _drawnMenuHeight, 15, 0); + _gfx->drawBox(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight, 15, 0); while (itemCount) { if (itemNr == selectedMenuItemNr) { @@ -448,7 +450,7 @@ void GfxMenu::removeActiveMenu(int16 selectedMenuNr) { drawMenuName(selectedMenuNr, false); // overwrite actual menu items by rendering play screen - _gfx->render_Block(_drawnMenuColumn, _drawnMenuRow, _drawnMenuWidth, _drawnMenuHeight); + _gfx->render_Block(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight); } void GfxMenu::keyPress(uint16 newKey) { @@ -549,8 +551,10 @@ void GfxMenu::keyPress(uint16 newKey) { // In "via mouse" mode, we check if user let go of the left mouse button and then select the item that way void GfxMenu::mouseEvent(uint16 newKey) { // Find out, where current mouse cursor actually is - int16 mouseRow = _vm->_mouse.pos.y / FONT_DISPLAY_HEIGHT; - int16 mouseColumn = _vm->_mouse.pos.x / FONT_DISPLAY_WIDTH; + int16 mouseRow = _vm->_mouse.pos.y; + int16 mouseColumn = _vm->_mouse.pos.x; + + _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow); int16 activeMenuNr, activeItemNr; mouseFindMenuSelection(mouseRow, mouseColumn, activeMenuNr, activeItemNr); @@ -638,7 +642,7 @@ void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &a if (mouseRow == menuEntry->row) { // line match - if ((mouseColumn >= menuEntry->column) && (mouseColumn <= (menuEntry->column + menuEntry->textLen))) { + if ((mouseColumn >= menuEntry->column) && (mouseColumn < (menuEntry->column + menuEntry->textLen))) { // full match activeMenuNr = menuNr; activeMenuItemNr = -1; // no item selected @@ -660,7 +664,7 @@ void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &a if (mouseRow == itemEntry->row) { // line match - if ((mouseColumn >= itemEntry->column) && (mouseColumn <= (itemEntry->column + itemEntry->textLen))) { + if ((mouseColumn >= itemEntry->column) && (mouseColumn < (itemEntry->column + itemEntry->textLen))) { // full match if (itemEntry->enabled) { // Only see it, when it's currently enabled diff --git a/engines/agi/menu.h b/engines/agi/menu.h index a621d7f0f2..b47289180b 100644 --- a/engines/agi/menu.h +++ b/engines/agi/menu.h @@ -111,8 +111,8 @@ private: uint16 _drawnMenuHeight; uint16 _drawnMenuWidth; - int16 _drawnMenuRow; - int16 _drawnMenuColumn; + int16 _drawnMenuY; + int16 _drawnMenuX; // Following variables are used in "via mouse" mode int16 _mouseModeItemNr; diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index 0e0f49b542..fed07ea986 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -2227,7 +2227,7 @@ void cmdMousePosn(AgiGame *state, AgiEngine *vm, uint8 *parameter) { int16 mouseX = vm->_mouse.pos.x; int16 mouseY = vm->_mouse.pos.y; - state->_vm->adjustPosToGameScreen(mouseX, mouseY); + vm->_gfx->translateDisplayPosToGameScreen(mouseX, mouseY); vm->setVar(destVarNr1, mouseX); vm->setVar(destVarNr2, mouseY); diff --git a/engines/agi/palette.h b/engines/agi/palette.h index e0db81ed7b..40c31da425 100644 --- a/engines/agi/palette.h +++ b/engines/agi/palette.h @@ -60,6 +60,22 @@ static const uint8 PALETTE_CGA[4 * 3] = { }; /** + * 2 color Hercules (green) palette. Using 8-bit RGB values. + */ +static const uint8 PALETTE_HERCULES_GREEN[2 * 3] = { + 0x00, 0x00, 0x00, // black + 0x00, 0xdc, 0x28 // green +}; + +/** + * 2 color Hercules (amber) palette. Using 8-bit RGB values. + */ +static const uint8 PALETTE_HERCULES_AMBER[2 * 3] = { + 0x00, 0x00, 0x00, // black + 0xdc, 0xb4, 0x00 // amber +}; + +/** * Atari ST AGI palette. * Used by all of the tested Atari ST AGI games * from Donald Duck's Playground (1986) to Manhunter II (1989). diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp index 0be2de7089..a80e811f44 100644 --- a/engines/agi/picture.cpp +++ b/engines/agi/picture.cpp @@ -1001,7 +1001,7 @@ void PictureMgr::clear() { void PictureMgr::showPic() { debugC(8, kDebugLevelMain, "Show picture!"); - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT); } /** @@ -1014,8 +1014,7 @@ void PictureMgr::showPic(int16 x, int16 y, int16 pic_width, int16 pic_height) { debugC(8, kDebugLevelMain, "Show picture!"); - // render block requires lower left coordinate! - _gfx->render_Block(x, pic_height + y - 1, pic_width, pic_height); + _gfx->render_Block(x, y, pic_width, pic_height); } void PictureMgr::showPicWithTransition() { @@ -1038,13 +1037,13 @@ void PictureMgr::showPicWithTransition() { case Common::kRenderAmiga: case Common::kRenderApple2GS: // Platform Amiga/Apple II GS -> render and do Amiga transition - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); _gfx->transition_Amiga(); return; break; case Common::kRenderAtariST: // Platform Atari ST used a different transition, looks "high-res" (full 320x168) - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); _gfx->transition_AtariSt(); return; default: @@ -1054,7 +1053,7 @@ void PictureMgr::showPicWithTransition() { } } - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT); } // preagi needed functions (for plotPattern) diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp index cbd15f2666..bb5d3b8896 100644 --- a/engines/agi/preagi.cpp +++ b/engines/agi/preagi.cpp @@ -58,7 +58,7 @@ void PreAgiEngine::initialize() { initRenderMode(); _font = new GfxFont(this); - _gfx = new GfxMgr(this); + _gfx = new GfxMgr(this, _font); _picture = new PictureMgr(this, _gfx); _font->init(); @@ -112,7 +112,7 @@ void PreAgiEngine::clearScreen(int attr, bool overrideDefault) { } void PreAgiEngine::clearGfxScreen(int attr) { - _gfx->drawDisplayRect(0, 0, DISPLAY_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10); + _gfx->drawDisplayRect(0, 0, DISPLAY_DEFAULT_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10); } // String functions diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp index 434cf1b30e..8263ea12ac 100644 --- a/engines/agi/sprite.cpp +++ b/engines/agi/sprite.cpp @@ -354,7 +354,8 @@ void SpritesMgr::showSprite(ScreenObjEntry *screenObj) { } // render this block - _gfx->render_Block(x, y, width, height); + int16 upperY = y - height + 1; + _gfx->render_Block(x, upperY, width, height); } void SpritesMgr::showSprites(SpriteList &spriteList) { diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp index f618459823..aeb1ded4a2 100644 --- a/engines/agi/systemui.cpp +++ b/engines/agi/systemui.cpp @@ -44,6 +44,8 @@ SystemUI::SystemUI(AgiEngine *vm, GfxMgr *gfx, TextMgr *text) { _textStatusSoundOn = "Sound:on"; _textStatusSoundOff = "Sound:off"; + _textEnterCommand = "Enter input\n\n"; + _textPause = " Game paused.\nPress Enter to continue."; _textPauseButton = nullptr; @@ -214,6 +216,47 @@ const char *SystemUI::getInventoryTextReturnToGame() { return _textInventoryReturnToGame; } +bool SystemUI::askForCommand(Common::String &commandText) { + // Let user enter the command (this was originally only available for Hercules rendering, we allow it everywhere) + bool previousEditState = _text->inputGetEditStatus(); + byte previousEditCursor = _text->inputGetCursorChar(); + + _text->drawMessageBox(_textEnterCommand, 0, 36, true); + + _text->inputEditOn(); + + _text->charPos_Push(); + _text->charAttrib_Push(); + + _text->charPos_SetInsideWindow(2, 0); + _text->charAttrib_Set(15, 0); + _text->clearBlockInsideWindow(2, 0, 36, 0); // input line is supposed to be black + _text->inputSetCursorChar('_'); + + _text->stringSet(commandText.c_str()); // Set current command text (may be a command recall) + + _vm->cycleInnerLoopActive(CYCLE_INNERLOOP_GETSTRING); + _text->stringEdit(35); // only allow up to 35 characters + + _text->charAttrib_Pop(); + _text->charPos_Pop(); + _text->inputSetCursorChar(previousEditCursor); + if (!previousEditState) { + _text->inputEditOff(); + } + + _text->closeWindow(); + + if (!_text->stringWasEntered()) { + // User cancelled? exit now + return false; + } + + commandText.clear(); + commandText += (char *)_text->_inputString; + return true; +} + int16 SystemUI::figureOutAutomaticSaveGameSlot(const char *automaticSaveDescription) { int16 matchedGameSlotId = -1; int16 freshGameSlotId = -1; @@ -751,21 +794,23 @@ bool SystemUI::askForVerification(const char *verifyText, const char *button1Tex // Buttons enabled, calculate button coordinates int16 msgBoxX = 0, msgBoxY = 0, msgBoxLowerY = 0; int16 msgBoxWidth = 0, msgBoxHeight = 0; + int16 fontHeight = _gfx->getDisplayFontHeight(); + int16 fontWidth = _gfx->getDisplayFontWidth(); _text->getMessageBoxInnerDisplayDimensions(msgBoxX, msgBoxY, msgBoxWidth, msgBoxHeight); - // Adjust Y coordinate to lower edge + // Calculate lower Y msgBoxLowerY = msgBoxY + (msgBoxHeight - 1); buttonEntry.active = false; if (button1Text) { buttonEntry.text = button1Text; - buttonEntry.textWidth = strlen(button1Text) * FONT_DISPLAY_WIDTH; + buttonEntry.textWidth = strlen(button1Text) * _gfx->getDisplayFontWidth(); buttonEntry.isDefault = true; _buttonArray.push_back(buttonEntry); } if (button2Text) { buttonEntry.text = button2Text; - buttonEntry.textWidth = strlen(button2Text) * FONT_DISPLAY_WIDTH; + buttonEntry.textWidth = strlen(button2Text) * _gfx->getDisplayFontWidth(); buttonEntry.isDefault = false; _buttonArray.push_back(buttonEntry); } @@ -773,37 +818,30 @@ bool SystemUI::askForVerification(const char *verifyText, const char *button1Tex // Render-Mode specific calculations switch (_vm->_renderMode) { case Common::kRenderApple2GS: - _buttonArray[0].rect = Common::Rect(14 + _buttonArray[0].textWidth, FONT_DISPLAY_HEIGHT + 6); - _buttonArray[0].rect.moveTo(msgBoxX + 2, msgBoxLowerY - (8 + FONT_DISPLAY_HEIGHT + 2)); - + _buttonArray[0].rect = createRect(msgBoxX, +2, msgBoxLowerY - fontHeight, -(8 + 2), _buttonArray[0].textWidth, +14, fontHeight, +6); + if (_buttonArray.size() > 1) { - int16 adjustedX = msgBoxX + msgBoxWidth - 10; - _buttonArray[1].rect = Common::Rect(14 + _buttonArray[1].textWidth, FONT_DISPLAY_HEIGHT + 6); - adjustedX -= _buttonArray[1].rect.width(); - _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - (8 + FONT_DISPLAY_HEIGHT + 2)); + int16 adjustedX = msgBoxX + msgBoxWidth - _buttonArray[1].textWidth; // - 10; + _buttonArray[1].rect = createRect(adjustedX, -(14 + 10), _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, +14, fontHeight, +6); } break; - case Common::kRenderAmiga: - _buttonArray[0].rect = Common::Rect(4 + _buttonArray[0].textWidth + 4, 2 + FONT_DISPLAY_HEIGHT + 2); - _buttonArray[0].rect.moveTo(msgBoxX, msgBoxLowerY - _buttonArray[0].rect.height()); + case Common::kRenderAmiga: { + _buttonArray[0].rect = createRect(msgBoxX, 0, msgBoxLowerY - fontHeight, -(2 + 2), _buttonArray[0].textWidth, +(4 + 4), fontHeight, +(2 + 2)); if (_buttonArray.size() > 1) { - int16 adjustedX = msgBoxX + msgBoxWidth; - _buttonArray[1].rect = Common::Rect(4 + _buttonArray[1].textWidth + 4, 2 + FONT_DISPLAY_HEIGHT + 2); - adjustedX -= _buttonArray[1].rect.width(); - _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - _buttonArray[1].rect.height()); + int16 adjustedX = msgBoxX + msgBoxWidth - _buttonArray[1].textWidth; + _buttonArray[1].rect = createRect(adjustedX, -(4 + 4), _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, +(4 + 4), fontHeight, +(2 + 2)); } break; + } case Common::kRenderAtariST: - _buttonArray[0].rect = Common::Rect(_buttonArray[0].textWidth, FONT_DISPLAY_HEIGHT); - _buttonArray[0].rect.moveTo(msgBoxX + (5 * FONT_DISPLAY_WIDTH), msgBoxLowerY - FONT_DISPLAY_HEIGHT); + _buttonArray[0].rect = createRect(msgBoxX + (5 * fontWidth), 0, msgBoxLowerY - fontHeight, 0, _buttonArray[0].textWidth, 0, fontHeight, 0); + if (_buttonArray.size() > 1) { - int16 adjustedX = msgBoxX + msgBoxWidth - (5 * FONT_DISPLAY_WIDTH); - _buttonArray[1].rect = Common::Rect(_buttonArray[1].textWidth, FONT_DISPLAY_HEIGHT); - adjustedX -= _buttonArray[1].rect.width(); - _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - _buttonArray[1].rect.height()); + int16 adjustedX = msgBoxX + msgBoxWidth - (5 * fontWidth + _buttonArray[1].textWidth); + _buttonArray[1].rect = createRect(adjustedX, 0, _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, 0, fontHeight, 0); } break; @@ -951,6 +989,25 @@ void SystemUI::askForVerificationKeyPress(uint16 newKey) { } } +Common::Rect SystemUI::createRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) { + switch (_gfx->getUpscaledHires()) { + case DISPLAY_UPSCALED_DISABLED: + break; + case DISPLAY_UPSCALED_640x400: + adjX *= 2; adjY *= 2; + adjWidth *= 2; adjHeight *= 2; + break; + default: + assert(0); + break; + } + x += adjX; y += adjY; + width += adjWidth; height += adjHeight; + Common::Rect newRect(width, height); + newRect.moveTo(x, y); + return newRect; +} + #define SYSTEMUI_BUTTONEDGE_APPLEIIGS_WIDTH 8 #define SYSTEMUI_BUTTONEDGE_APPLEIIGS_HEIGHT 5 @@ -998,20 +1055,20 @@ void SystemUI::drawButtonAppleIIgs(SystemUIButtonEntry *button) { } // draw base box for it - _gfx->drawDisplayRect(button->rect.left, button->rect.bottom - 1, button->rect.width(), button->rect.height(), backgroundColor, false); + _gfx->drawDisplayRect(button->rect.left, button->rect.top, button->rect.width(), button->rect.height(), backgroundColor, false); // draw inner lines - _gfx->drawDisplayRect(button->rect.left + 1, button->rect.top - 1, button->rect.width() - 2, 1, 0, false); // upper horizontal - _gfx->drawDisplayRect(button->rect.left - 2, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // left vertical - _gfx->drawDisplayRect(button->rect.right, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // right vertical - _gfx->drawDisplayRect(button->rect.left + 1, button->rect.bottom, button->rect.width() - 2, 1, 0, false); // lower horizontal + _gfx->drawDisplayRect(button->rect.left, +1, button->rect.top, -1, button->rect.width(), -2, 0, 1, 0, false); // lower horizontal + _gfx->drawDisplayRect(button->rect.left, -2, button->rect.top, +1, 0, 2, button->rect.height(), -2, 0, false); // left vertical + _gfx->drawDisplayRect(button->rect.right, 0, button->rect.top, +1, 0, 2, button->rect.height(), -2, 0, false); // right vertical + _gfx->drawDisplayRect(button->rect.left, +1, button->rect.bottom, 0, button->rect.width(), -2, 0, 1, 0, false); // upper horizontal if (button->isDefault) { // draw outer lines - _gfx->drawDisplayRect(button->rect.left, button->rect.top - 3, button->rect.width(), 1, 0, false); // upper horizontal - _gfx->drawDisplayRect(button->rect.left - 5, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // left vertical - _gfx->drawDisplayRect(button->rect.right + 3, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // right vertical - _gfx->drawDisplayRect(button->rect.left, button->rect.bottom + 2, button->rect.width(), 1, 0, false); // lower horizontal + _gfx->drawDisplayRect(button->rect.left, 0, button->rect.top, -3, button->rect.width(), 0, 0, 1, 0, false); // upper horizontal + _gfx->drawDisplayRect(button->rect.left, -5, button->rect.top, +2, 0, 2, button->rect.height(), -2, 0, false); // left vertical + _gfx->drawDisplayRect(button->rect.right, +3, button->rect.top, +2, 0, 2, button->rect.height(), -2, 0, false); // right vertical + _gfx->drawDisplayRect(button->rect.left, 0, button->rect.bottom, +2, button->rect.width(), 0, 0, 1, 0, false); // lower horizontal if (button->active) edgeBitmap = buttonEdgeAppleIIgsDefaultActive; @@ -1026,18 +1083,18 @@ void SystemUI::drawButtonAppleIIgs(SystemUIButtonEntry *button) { } // draw edge graphics - drawButtonAppleIIgsEdgePixels(button->rect.left - 5, button->rect.top - 3, edgeBitmap, false, false); - drawButtonAppleIIgsEdgePixels(button->rect.right + 4, button->rect.top - 3, edgeBitmap, true, false); - drawButtonAppleIIgsEdgePixels(button->rect.left - 5, button->rect.bottom + 2, edgeBitmap, false, true); - drawButtonAppleIIgsEdgePixels(button->rect.right + 4, button->rect.bottom + 2, edgeBitmap, true, true); + drawButtonAppleIIgsEdgePixels(button->rect.left, -5, button->rect.top, -3, edgeBitmap, false, false); + drawButtonAppleIIgsEdgePixels(button->rect.right, +4, button->rect.top, -3, edgeBitmap, true, false); + drawButtonAppleIIgsEdgePixels(button->rect.left, -5, button->rect.bottom, +2, edgeBitmap, false, true); + drawButtonAppleIIgsEdgePixels(button->rect.right, +4, button->rect.bottom, +2, edgeBitmap, true, true); // Button text - _gfx->drawStringOnDisplay(button->rect.left + 7, button->rect.top + 3, button->text, foregroundColor, backgroundColor); + _gfx->drawStringOnDisplay(button->rect.left, +7, button->rect.top, +3, button->text, foregroundColor, backgroundColor); - _gfx->copyDisplayRectToScreen(button->rect.left - 5, button->rect.top - 3, button->rect.width() + 10, button->rect.height() + 6); + _gfx->copyDisplayRectToScreen(button->rect.left, -5, button->rect.top, -3, button->rect.width(), +10, button->rect.height(), +6); } -void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, bool mirrored, bool upsideDown) { +void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 adjX, int16 y, int16 adjY, byte *edgeBitmap, bool mirrored, bool upsideDown) { int8 directionY = upsideDown ? -1 : +1; int8 directionX = mirrored ? -1 : +1; int8 curY = 0; @@ -1055,9 +1112,9 @@ void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, while (widthLeft) { if (curBitmapByte & curBitmapBit) { - _gfx->putPixelOnDisplay(x + curX, y + curY, 0); + _gfx->putPixelOnDisplay(x, adjX + curX, y, adjY + curY, 0); } else { - _gfx->putPixelOnDisplay(x + curX, y + curY, 15); + _gfx->putPixelOnDisplay(x, adjX + curX, y, adjY + curY, 15); } curBitmapBit = curBitmapBit >> 1; @@ -1092,12 +1149,11 @@ void SystemUI::drawButtonAmiga(SystemUIButtonEntry *button) { } // draw base box for it - _gfx->drawDisplayRect(button->rect.left, button->rect.bottom - 1, button->rect.width(), button->rect.height(), backgroundColor, false); + _gfx->drawDisplayRect(button->rect.left, button->rect.top, button->rect.width(), button->rect.height(), backgroundColor, false); // Button text - _gfx->drawStringOnDisplay(button->rect.left + 4, button->rect.top + 2, button->text, foregroundColor, backgroundColor); + _gfx->drawStringOnDisplay(button->rect.left, +4, button->rect.top, +2, button->text, foregroundColor, backgroundColor); - // draw base box for it _gfx->copyDisplayRectToScreen(button->rect.left, button->rect.top, button->rect.width(), button->rect.height()); } diff --git a/engines/agi/systemui.h b/engines/agi/systemui.h index ceb78935eb..283de8794c 100644 --- a/engines/agi/systemui.h +++ b/engines/agi/systemui.h @@ -77,6 +77,8 @@ public: const char *getInventoryTextSelectItems(); const char *getInventoryTextReturnToGame(); + bool askForCommand(Common::String &commandText); + int16 figureOutAutomaticSaveGameSlot(const char *automaticSaveDescription); int16 figureOutAutomaticRestoreGameSlot(const char *automaticSaveDescription); @@ -107,9 +109,12 @@ private: private: SystemUIButtonArray _buttonArray; + Common::Rect createRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight); + //void moveRect(int16 x, int16 adjX, int16 y, int16 adjY); + void drawButton(SystemUIButtonEntry *button); void drawButtonAppleIIgs(SystemUIButtonEntry *buttonEntry); - void drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, bool mirrored, bool upsideDown); + void drawButtonAppleIIgsEdgePixels(int16 x, int16 adjX, int16 y, int16 adjY, byte *edgeBitmap, bool mirrored, bool upsideDown); void drawButtonAmiga(SystemUIButtonEntry *buttonEntry); void drawButtonAtariST(SystemUIButtonEntry *buttonEntry); @@ -127,6 +132,8 @@ private: const char *_textStatusSoundOn; const char *_textStatusSoundOff; + const char *_textEnterCommand; + const char *_textPause; const char *_textPauseButton; const char *_textRestart; diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 611bd135eb..0cacce2421 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -20,6 +20,7 @@ * */ +#include "common/config-manager.h" #include "agi/agi.h" #include "agi/sprite.h" // for commit_both() #include "agi/graphics.h" @@ -74,6 +75,12 @@ TextMgr::TextMgr(AgiEngine *vm, Words *words, GfxMgr *gfx) { configureScreen(2); _messageBoxCancelled = false; + + _optionCommandPromptWindow = false; + + if (ConfMan.getBool("commandpromptwindow")) { + _optionCommandPromptWindow = true; + } } TextMgr::~TextMgr() { @@ -88,7 +95,7 @@ void TextMgr::configureScreen(uint16 row_Min) { _window_Row_Max = row_Min + 21; // forward data to GfxMgr as well - _gfx->setRenderStartOffset(row_Min * FONT_DISPLAY_HEIGHT); + _gfx->setRenderStartOffset(row_Min * FONT_VISUAL_HEIGHT); } uint16 TextMgr::getWindowRowMin() { return _window_Row_Min; @@ -179,6 +186,16 @@ void TextMgr::charAttrib_Set(byte foreground, byte background) { _textAttrib.combinedBackground = 0; } break; + case Common::kRenderHercA: + case Common::kRenderHercG: + if (background) { + _textAttrib.combinedForeground = 0; + _textAttrib.combinedBackground = 1; + } else { + _textAttrib.combinedForeground = 1; + _textAttrib.combinedBackground = 0; + } + break; default: // EGA-handling: if (background) { @@ -466,7 +483,8 @@ void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 want _messageState.backgroundSize_Width = (_messageState.textSize_Width * FONT_VISUAL_WIDTH) + 10; _messageState.backgroundSize_Height = (_messageState.textSize_Height * FONT_VISUAL_HEIGHT) + 10; _messageState.backgroundPos_x = (_messageState.textPos.column * FONT_VISUAL_WIDTH) - 5; - _messageState.backgroundPos_y = (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4; + _messageState.backgroundPos_y = (startingRow * FONT_VISUAL_HEIGHT) - 5; + // original AGI used lowerY here, calculated using (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4; // Hardcoded colors: white background and red lines _gfx->drawBox(_messageState.backgroundPos_x, _messageState.backgroundPos_y, _messageState.backgroundSize_Width, _messageState.backgroundSize_Height, 15, 4); @@ -487,10 +505,11 @@ void TextMgr::getMessageBoxInnerDisplayDimensions(int16 &x, int16 &y, int16 &wid if (!_messageState.window_Active) return; - y = _messageState.textPos.row * FONT_DISPLAY_HEIGHT; - x = _messageState.textPos.column * FONT_DISPLAY_WIDTH; - width = _messageState.textSize_Width * FONT_DISPLAY_WIDTH; - height = _messageState.textSize_Height * FONT_DISPLAY_HEIGHT; + y = _messageState.textPos.row; + x = _messageState.textPos.column; + width = _messageState.textSize_Width; + height = _messageState.textSize_Height; + _gfx->translateFontRectToDisplayScreen(x, y, width, height); } bool TextMgr::isMouseWithinMessageBox() { @@ -499,10 +518,10 @@ bool TextMgr::isMouseWithinMessageBox() { int16 mouseX = _vm->_mouse.pos.x; if (_messageState.window_Active) { - _vm->adjustPosToGameScreen(mouseX, mouseY); + _gfx->translateDisplayPosToGameScreen(mouseX, mouseY); - if ((mouseX >= _messageState.backgroundPos_x) && (mouseX <= (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) { - if ((mouseY >= _messageState.backgroundPos_y - _messageState.backgroundSize_Height) && (mouseY <= (_messageState.backgroundPos_y))) { + if ((mouseX >= _messageState.backgroundPos_x) && (mouseX < (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) { + if ((mouseY >= _messageState.backgroundPos_y) && (mouseY < (_messageState.backgroundPos_y + _messageState.backgroundSize_Height))) { return true; } } @@ -581,12 +600,12 @@ void TextMgr::clearBlock(int16 row_Upper, int16 column_Upper, int16 row_Lower, i charPos_Clip(row_Upper, column_Upper); charPos_Clip(row_Lower, column_Lower); - int16 x = column_Upper * FONT_DISPLAY_WIDTH; - int16 y = row_Upper * FONT_DISPLAY_HEIGHT; - int16 width = (column_Lower + 1 - column_Upper) * FONT_DISPLAY_WIDTH; - int16 height = (row_Lower + 1 - row_Upper) * FONT_DISPLAY_HEIGHT; + int16 x = column_Upper; + int16 y = row_Upper; + int16 width = (column_Lower + 1 - column_Upper); + int16 height = (row_Lower + 1 - row_Upper); + _gfx->translateFontRectToDisplayScreen(x, y, width, height); - y = y + height - 1; // drawDisplayRect wants lower Y-coordinate _gfx->drawDisplayRect(x, y, width, height, color); } @@ -659,6 +678,30 @@ void TextMgr::promptKeyPress(uint16 newKey) { int16 maxChars = 0; int16 scriptsInputLen = _vm->getVar(VM_VAR_MAX_INPUT_CHARACTERS); + bool acceptableInput = false; + + // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.) + // In text-mode this sort of worked at least with the DOS interpreter + // but as soon as invalid characters were used in graphics mode they weren't properly shown + switch (_vm->getLanguage()) { + case Common::RU_RUS: + if (newKey >= 0x20) + acceptableInput = true; + break; + default: + if ((newKey >= 0x20) && (newKey <= 0x7f)) + acceptableInput = true; + break; + } + + if (_optionCommandPromptWindow) { + // Forward to command prompt window, using last command + if (acceptableInput) { + promptCommandWindow(false, newKey); + } + return; + } + if (_messageState.dialogue_Open) { maxChars = TEXT_STRING_MAX_SIZE - 4; } else { @@ -703,22 +746,6 @@ void TextMgr::promptKeyPress(uint16 newKey) { } default: if (maxChars > _promptCursorPos) { - bool acceptableInput = false; - - // FEATURE: Sierra didn't check for valid characters (filtered out umlauts etc.) - // In text-mode this sort of worked at least with the DOS interpreter - // but as soon as invalid characters were used in graphics mode they weren't properly shown - switch (_vm->getLanguage()) { - case Common::RU_RUS: - if (newKey >= 0x20) - acceptableInput = true; - break; - default: - if ((newKey >= 0x20) && (newKey <= 0x7f)) - acceptableInput = true; - break; - } - if (acceptableInput) { _prompt[_promptCursorPos] = newKey; _promptCursorPos++; @@ -735,6 +762,11 @@ void TextMgr::promptKeyPress(uint16 newKey) { } void TextMgr::promptCancelLine() { + if (_optionCommandPromptWindow) { + // Abort, in case command prompt window is active + return; + } + while (_promptCursorPos) { promptKeyPress(0x08); // Backspace until prompt is empty } @@ -743,6 +775,12 @@ void TextMgr::promptCancelLine() { void TextMgr::promptEchoLine() { int16 previousLen = strlen((char *)_promptPrevious); + if (_optionCommandPromptWindow) { + // Forward to command prompt window, using last command + promptCommandWindow(true, 0); + return; + } + if (_promptCursorPos < previousLen) { inputEditOn(); @@ -759,6 +797,11 @@ void TextMgr::promptRedraw() { char *textPtr = nullptr; if (_promptEnabled) { + if (_optionCommandPromptWindow) { + // Abort, in case command prompt window is active + return; + } + inputEditOn(); clearLine(_promptRow, _textAttrib.background); charPos_Set(_promptRow, 0); @@ -776,6 +819,10 @@ void TextMgr::promptRedraw() { // for AGI1 void TextMgr::promptClear() { + if (_optionCommandPromptWindow) { + // Abort, in case command prompt window is active + return; + } clearLine(_promptRow, _textAttrib.background); } @@ -785,6 +832,35 @@ void TextMgr::promptRememberForAutoComplete(bool entered) { #endif } +void TextMgr::promptCommandWindow(bool recallLastCommand, uint16 newKey) { + Common::String commandText; + + if (recallLastCommand) { + commandText += Common::String((char *)_promptPrevious); + } + if (newKey) { + if (newKey != ' ') { + // Only add char, when it's not a space. + // Original AGI did not filter space, but it makes no sense to start with a space. + // Space would get filtered anyway during dictionary parsing. + commandText += newKey; + } + } + + if (_systemUI->askForCommand(commandText)) { + if (commandText.size()) { + // Something actually was entered? + strncpy((char *)&_prompt, commandText.c_str(), sizeof(_prompt)); + promptRememberForAutoComplete(true); + memcpy(&_promptPrevious, &_prompt, sizeof(_prompt)); + // parse text + _vm->_words->parseUsingDictionary((char *)&_prompt); + + _prompt[0] = 0; + } + } +} + bool TextMgr::stringWasEntered() { return _inputStringEntered; } diff --git a/engines/agi/text.h b/engines/agi/text.h index 72d012b917..f0aeab7762 100644 --- a/engines/agi/text.h +++ b/engines/agi/text.h @@ -55,7 +55,7 @@ struct MessageState_Struct { uint16 printed_Height; int16 backgroundPos_x; - int16 backgroundPos_y; + int16 backgroundPos_y; // original AGI used lowerY here, we use upperY so that upscaling is easier int16 backgroundSize_Width; int16 backgroundSize_Height; }; @@ -163,6 +163,8 @@ public: bool _inputEditEnabled; byte _inputCursorChar; + bool _optionCommandPromptWindow; + bool _promptEnabled; int16 _promptRow; int16 _promptCursorPos; @@ -189,6 +191,8 @@ public: void promptClear(); // for AGI1 void promptRememberForAutoComplete(bool entered = false); // for auto-completion + void promptCommandWindow(bool recallLastCommand, uint16 newKey); + int16 _inputStringRow; int16 _inputStringColumn; bool _inputStringEntered; diff --git a/engines/agi/words.cpp b/engines/agi/words.cpp index 32fa4cbff4..7072f003c2 100644 --- a/engines/agi/words.cpp +++ b/engines/agi/words.cpp @@ -293,7 +293,7 @@ int16 Words::findWordInDictionary(const Common::String &userInputLowcased, uint1 return wordId; } -void Words::parseUsingDictionary(char *rawUserInput) { +void Words::parseUsingDictionary(const char *rawUserInput) { Common::String userInput; Common::String userInputLowcased; const char *userInputPtr = nullptr; diff --git a/engines/agi/words.h b/engines/agi/words.h index c7bf4829c3..96dafae275 100644 --- a/engines/agi/words.h +++ b/engines/agi/words.h @@ -57,7 +57,7 @@ public: void unloadDictionary(); void clearEgoWords(); - void parseUsingDictionary(char *rawUserInput); + void parseUsingDictionary(const char *rawUserInput); private: void cleanUpInput(const char *userInput, Common::String &cleanInput); diff --git a/engines/agos/configure.engine b/engines/agos/configure.engine index 3ae1fb16f2..cd7fcf9d78 100644 --- a/engines/agos/configure.engine +++ b/engines/agos/configure.engine @@ -1,4 +1,4 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine agos "AGOS" yes "agos2" "AGOS 1 games" -add_engine agos2 "AGOS 2 games" yes +add_engine agos2 "AGOS 2 games" yes "" "" "highres" diff --git a/engines/agos/detection.cpp b/engines/agos/detection.cpp index 82f8ad4bf2..2c89522089 100644 --- a/engines/agos/detection.cpp +++ b/engines/agos/detection.cpp @@ -94,13 +94,13 @@ using namespace AGOS; class AgosMetaEngine : public AdvancedMetaEngine { public: AgosMetaEngine() : AdvancedMetaEngine(AGOS::gameDescriptions, sizeof(AGOS::AGOSGameDescription), agosGames) { - _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); + _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD); _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } - virtual GameDescriptor findGame(const char *gameid) const { - return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); + virtual GameDescriptor findGame(const char *gameId) const { + return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } virtual const char *getName() const { diff --git a/engines/avalanche/configure.engine b/engines/avalanche/configure.engine index 28d6a558db..9b913ff053 100644 --- a/engines/avalanche/configure.engine +++ b/engines/avalanche/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine avalanche "Lord Avalot d'Argent" no +add_engine avalanche "Lord Avalot d'Argent" no "" "" "highres" diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp index 1ea72b613a..e35c5d2cac 100644 --- a/engines/avalanche/detection.cpp +++ b/engines/avalanche/detection.cpp @@ -40,7 +40,7 @@ uint32 AvalancheEngine::getFeatures() const { } const char *AvalancheEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } static const PlainGameDescriptor avalancheGames[] = { diff --git a/engines/bbvs/bbvs.cpp b/engines/bbvs/bbvs.cpp index d40d5e482f..6ae663479d 100644 --- a/engines/bbvs/bbvs.cpp +++ b/engines/bbvs/bbvs.cpp @@ -137,6 +137,21 @@ BbvsEngine::~BbvsEngine() { } void BbvsEngine::newGame() { + memset(_easterEggInput, 0, sizeof(_easterEggInput)); + _gameTicks = 0; + _playVideoNumber = 0; + memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus)); + memset(_gameVars, 0, sizeof(_gameVars)); + memset(_sceneVisited, 0, sizeof(_sceneVisited)); + + _mouseX = 160; + _mouseY = 120; + _mouseButtons = 0; + + _currVerbNum = kVerbLook; + _currTalkObjectIndex = -1; + _currSceneNum = 0; + _currInventoryItem = -1; _newSceneNum = 32; } @@ -162,24 +177,10 @@ Common::Error BbvsEngine::run() { _sound = new SoundMan(); allocSnapshot(); - memset(_easterEggInput, 0, sizeof(_easterEggInput)); - _gameTicks = 0; - _playVideoNumber = 0; - _bootSaveSlot = -1; - - memset(_inventoryItemStatus, 0, sizeof(_inventoryItemStatus)); - memset(_gameVars, 0, sizeof(_gameVars)); - memset(_sceneVisited, 0, sizeof(_sceneVisited)); - - _mouseX = 160; - _mouseY = 120; - _mouseButtons = 0; + newGame(); - _currVerbNum = kVerbLook; - _currInventoryItem = -1; - _currTalkObjectIndex = -1; - _currSceneNum = 0; + _bootSaveSlot = -1; _newSceneNum = 31; if (ConfMan.hasKey("save_slot")) diff --git a/engines/bbvs/detection.cpp b/engines/bbvs/detection.cpp index d2da3861bc..7c0045ee73 100644 --- a/engines/bbvs/detection.cpp +++ b/engines/bbvs/detection.cpp @@ -43,7 +43,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("vspr0001.vnm", "7ffe9b9e7ca322db1d48e86f5130578e", 1166628), Common::EN_ANY, Common::kPlatformWindows, - ADGF_NO_FLAGS | ADGF_TESTING, + ADGF_NO_FLAGS, GUIO0() }, { @@ -52,7 +52,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("vspr0001.vnm", "91c76b1048f93208cd7b1a05ebccb408", 1176976), Common::RU_RUS, Common::kPlatformWindows, - GF_GUILANGSWITCH | ADGF_TESTING, + GF_GUILANGSWITCH | ADGF_NO_FLAGS, GUIO0() }, @@ -69,7 +69,7 @@ static const char * const directoryGlobs[] = { class BbvsMetaEngine : public AdvancedMetaEngine { public: BbvsMetaEngine() : AdvancedMetaEngine(Bbvs::gameDescriptions, sizeof(ADGameDescription), bbvsGames) { - _singleid = "bbvs"; + _singleId = "bbvs"; _maxScanDepth = 3; _directoryGlobs = directoryGlobs; } diff --git a/engines/bbvs/dialogs.cpp b/engines/bbvs/dialogs.cpp index ef7f3c9320..c8470f8eef 100644 --- a/engines/bbvs/dialogs.cpp +++ b/engines/bbvs/dialogs.cpp @@ -102,7 +102,7 @@ void MainMenu::reflowLayout() { _w = 2 * buttonWidth + buttonPadding; _h = 3 * buttonHeight + 3 * buttonPadding; _x = (screenW - _w) / 2; - _y = screenH - _h; + _y = screenH - _h - 2; int x = 0, y = 0; diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index 95705cbebc..52168dc934 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -115,7 +115,7 @@ static const ADExtraGuiOptionsMap optionsList[] = { class CGEMetaEngine : public AdvancedMetaEngine { public: CGEMetaEngine() : AdvancedMetaEngine(CGE::gameDescriptions, sizeof(ADGameDescription), CGEGames, optionsList) { - _singleid = "soltys"; + _singleId = "soltys"; } virtual const char *getName() const { diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp index 3ccfffe43f..01119bb1fd 100644 --- a/engines/cge2/detection.cpp +++ b/engines/cge2/detection.cpp @@ -111,7 +111,7 @@ static const ADExtraGuiOptionsMap optionsList[] = { class CGE2MetaEngine : public AdvancedMetaEngine { public: CGE2MetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(ADGameDescription), CGE2Games, optionsList) { - _singleid = "sfinx"; + _singleId = "sfinx"; } virtual const char *getName() const { diff --git a/engines/cge2/vga13h.cpp b/engines/cge2/vga13h.cpp index eb111c3255..54f5c00d93 100644 --- a/engines/cge2/vga13h.cpp +++ b/engines/cge2/vga13h.cpp @@ -141,7 +141,7 @@ Sprite::Sprite(CGE2Engine *vm) memset(_actionCtrl, 0, sizeof(_actionCtrl)); memset(_file, 0, sizeof(_file)); memset(&_flags, 0, sizeof(_flags)); - _flags._frnt = 1; + _flags._frnt = true; } Sprite::Sprite(CGE2Engine *vm, BitmapPtr shpP, int cnt) @@ -152,7 +152,7 @@ Sprite::Sprite(CGE2Engine *vm, BitmapPtr shpP, int cnt) memset(_actionCtrl, 0, sizeof(_actionCtrl)); memset(_file, 0, sizeof(_file)); memset(&_flags, 0, sizeof(_flags)); - _flags._frnt = 1; + _flags._frnt = true; setShapeList(shpP, cnt); } diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index c6099447d8..6ecf07fe15 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -535,7 +535,7 @@ int loadSpl(const char *resourceName, int16 idx) { entry = idx < 0 ? emptyAnimSpace() : idx; assert(entry >= 0); - g_cine->_animDataTable[entry].load(dataPtr + 0x16, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize - 0x16, 1, foundFileIdx, 0, currentPartName); + g_cine->_animDataTable[entry].load(dataPtr, ANIM_RAW, g_cine->_partBuffer[foundFileIdx].unpackedSize, 1, foundFileIdx, 0, currentPartName); free(dataPtr); return entry + 1; diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index 5e1ffadb1b..ec01e8734d 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -80,12 +80,12 @@ static const ADExtraGuiOptionsMap optionsList[] = { class CineMetaEngine : public AdvancedMetaEngine { public: CineMetaEngine() : AdvancedMetaEngine(Cine::gameDescriptions, sizeof(Cine::CINEGameDescription), cineGames, optionsList) { - _singleid = "cine"; - _guioptions = GUIO2(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD); + _singleId = "cine"; + _guiOptions = GUIO2(GUIO_NOSPEECH, GAMEOPTION_ORIGINAL_SAVELOAD); } - virtual GameDescriptor findGame(const char *gameid) const { - return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); + virtual GameDescriptor findGame(const char *gameId) const { + return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } virtual const char *getName() const { diff --git a/engines/cine/saveload.cpp b/engines/cine/saveload.cpp index 1f4f286694..dfd3a1f4bc 100644 --- a/engines/cine/saveload.cpp +++ b/engines/cine/saveload.cpp @@ -691,6 +691,11 @@ bool CineEngine::loadPlainSaveFW(Common::SeekableReadStream &in, CineSaveGameFor } if (strlen(bgName)) { + if (g_cine->getGameType() == GType_FW && (g_cine->getFeatures() & GF_CD)) { + char buffer[20]; + removeExtention(buffer, bgName); + g_sound->setBgMusic(atoi(buffer + 1)); + } loadBg(bgName); } diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index 6ad38f4433..86eb709d5a 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -1858,7 +1858,9 @@ int FWScript::o1_playSample() { if (g_cine->getGameType() == Cine::GType_OS && size == 0) { return 0; } - g_sound->stopMusic(); + // The DOS CD version of Future Wars uses CD audio for music + if (!(g_cine->getGameType() == Cine::GType_FW && (g_cine->getFeatures() & GF_CD))) + g_sound->stopMusic(); if (size == 0xFFFF) { g_sound->playSound(channel, 0, data, 0, 0, 0, volume, 0); } else { diff --git a/engines/composer/configure.engine b/engines/composer/configure.engine index 71a79acb5d..17120a3a3d 100644 --- a/engines/composer/configure.engine +++ b/engines/composer/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine composer "Magic Composer" yes +add_engine composer "Magic Composer" yes "" "" "highres" diff --git a/engines/composer/detection.cpp b/engines/composer/detection.cpp index e250996371..a3ab18ae54 100644 --- a/engines/composer/detection.cpp +++ b/engines/composer/detection.cpp @@ -38,7 +38,7 @@ int ComposerEngine::getGameType() const { } const char *ComposerEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } uint32 ComposerEngine::getFeatures() const { @@ -386,7 +386,7 @@ static const char *directoryGlobs[] = { class ComposerMetaEngine : public AdvancedMetaEngine { public: ComposerMetaEngine() : AdvancedMetaEngine(Composer::gameDescriptions, sizeof(Composer::ComposerGameDescription), composerGames) { - _singleid = "composer"; + _singleId = "composer"; _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } diff --git a/engines/cruise/detection.cpp b/engines/cruise/detection.cpp index 69e1bc4ad0..6f5d236173 100644 --- a/engines/cruise/detection.cpp +++ b/engines/cruise/detection.cpp @@ -35,7 +35,7 @@ struct CRUISEGameDescription { }; const char *CruiseEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } Common::Language CruiseEngine::getLanguage() const { @@ -196,8 +196,8 @@ static const CRUISEGameDescription gameDescriptions[] = { class CruiseMetaEngine : public AdvancedMetaEngine { public: CruiseMetaEngine() : AdvancedMetaEngine(Cruise::gameDescriptions, sizeof(Cruise::CRUISEGameDescription), cruiseGames) { - _singleid = "cruise"; - _guioptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI); + _singleId = "cruise"; + _guiOptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI); } virtual const char *getName() const { diff --git a/engines/draci/detection.cpp b/engines/draci/detection.cpp index 82b6935d72..65427bd8cd 100644 --- a/engines/draci/detection.cpp +++ b/engines/draci/detection.cpp @@ -84,7 +84,7 @@ const ADGameDescription gameDescriptions[] = { class DraciMetaEngine : public AdvancedMetaEngine { public: DraciMetaEngine() : AdvancedMetaEngine(Draci::gameDescriptions, sizeof(ADGameDescription), draciGames) { - _singleid = "draci"; + _singleId = "draci"; } virtual const char *getName() const { diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp index 9a2da1d732..ffec393a0a 100644 --- a/engines/drascula/detection.cpp +++ b/engines/drascula/detection.cpp @@ -310,8 +310,8 @@ SaveStateDescriptor loadMetaData(Common::ReadStream *s, int slot, bool setPlayTi class DrasculaMetaEngine : public AdvancedMetaEngine { public: DrasculaMetaEngine() : AdvancedMetaEngine(Drascula::gameDescriptions, sizeof(Drascula::DrasculaGameDescription), drasculaGames) { - _singleid = "drascula"; - _guioptions = GUIO1(GUIO_NOMIDI); + _singleId = "drascula"; + _guiOptions = GUIO1(GUIO_NOMIDI); } virtual const char *getName() const { diff --git a/engines/dreamweb/detection.cpp b/engines/dreamweb/detection.cpp index 853047ccc7..764171bcb0 100644 --- a/engines/dreamweb/detection.cpp +++ b/engines/dreamweb/detection.cpp @@ -70,8 +70,8 @@ public: AdvancedMetaEngine(DreamWeb::gameDescriptions, sizeof(DreamWeb::DreamWebGameDescription), dreamWebGames, gameGuiOptions) { - _singleid = "dreamweb"; - _guioptions = GUIO1(GUIO_NOMIDI); + _singleId = "dreamweb"; + _guiOptions = GUIO1(GUIO_NOMIDI); } virtual const char *getName() const { diff --git a/engines/fullpipe/configure.engine b/engines/fullpipe/configure.engine index a9042449db..611d0188dc 100644 --- a/engines/fullpipe/configure.engine +++ b/engines/fullpipe/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine fullpipe "Full Pipe" no "" "" "16bit" +add_engine fullpipe "Full Pipe" no "" "" "16bit highres" diff --git a/engines/fullpipe/detection.cpp b/engines/fullpipe/detection.cpp index 99fbdc4b82..6f92f19f24 100644 --- a/engines/fullpipe/detection.cpp +++ b/engines/fullpipe/detection.cpp @@ -32,7 +32,7 @@ namespace Fullpipe { const char *FullpipeEngine::getGameId() const { - return _gameDescription->gameid; + return _gameDescription->gameId; } } @@ -76,7 +76,7 @@ static const ADGameDescription gameDescriptions[] = { class FullpipeMetaEngine : public AdvancedMetaEngine { public: FullpipeMetaEngine() : AdvancedMetaEngine(Fullpipe::gameDescriptions, sizeof(ADGameDescription), fullpipeGames) { - _singleid = "fullpipe"; + _singleId = "fullpipe"; } virtual const char *getName() const { diff --git a/engines/game.cpp b/engines/game.cpp index 85ad6fe2e8..7ff51a99cc 100644 --- a/engines/game.cpp +++ b/engines/game.cpp @@ -26,8 +26,8 @@ const PlainGameDescriptor *findPlainGameDescriptor(const char *gameid, const PlainGameDescriptor *list) { const PlainGameDescriptor *g = list; - while (g->gameid) { - if (0 == scumm_stricmp(gameid, g->gameid)) + while (g->gameId) { + if (0 == scumm_stricmp(gameid, g->gameId)) return g; g++; } @@ -40,7 +40,7 @@ GameDescriptor::GameDescriptor() { } GameDescriptor::GameDescriptor(const PlainGameDescriptor &pgd, Common::String guioptions) { - setVal("gameid", pgd.gameid); + setVal("gameid", pgd.gameId); setVal("description", pgd.description); if (!guioptions.empty()) diff --git a/engines/game.h b/engines/game.h index a9bec8f9e0..e01e5c6885 100644 --- a/engines/game.h +++ b/engines/game.h @@ -36,7 +36,7 @@ * consisting of PlainGameDescriptors. */ struct PlainGameDescriptor { - const char *gameid; + const char *gameId; const char *description; }; @@ -108,7 +108,7 @@ public: GameList() {} GameList(const GameList &list) : Common::Array<GameDescriptor>(list) {} GameList(const PlainGameDescriptor *g) { - while (g->gameid) { + while (g->gameId) { push_back(GameDescriptor(*g)); g++; } diff --git a/engines/gob/detection/detection.cpp b/engines/gob/detection/detection.cpp index 3b26f63c39..b0aa78f416 100644 --- a/engines/gob/detection/detection.cpp +++ b/engines/gob/detection/detection.cpp @@ -33,7 +33,7 @@ class GobMetaEngine : public AdvancedMetaEngine { public: GobMetaEngine(); - virtual GameDescriptor findGame(const char *gameid) const; + virtual GameDescriptor findGame(const char *gameId) const; virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; @@ -55,12 +55,12 @@ private: GobMetaEngine::GobMetaEngine() : AdvancedMetaEngine(Gob::gameDescriptions, sizeof(Gob::GOBGameDescription), gobGames) { - _singleid = "gob"; - _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); + _singleId = "gob"; + _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD); } -GameDescriptor GobMetaEngine::findGame(const char *gameid) const { - return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); +GameDescriptor GobMetaEngine::findGame(const char *gameId) const { + return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } const ADGameDescription *GobMetaEngine::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { diff --git a/engines/gob/sound/adlib.cpp b/engines/gob/sound/adlib.cpp index 1e024d5a50..2a9a716058 100644 --- a/engines/gob/sound/adlib.cpp +++ b/engines/gob/sound/adlib.cpp @@ -283,7 +283,12 @@ void AdLib::initOPL() { _voiceOn [i] = 0; } - _opl->reset(); + /* NOTE: We used to completely reset the OPL here, via _opl->reset(). However, + * with the OPL timer change in 73e8ac2a, reset() must not be called while + * the callback is still active. With the Gob AdLib rewrite in 03ef6689, + * this reset shouldn't be necessary anymore either, since this function + * here cleans everything properly anyway. If suddenly a certain piece of + * music in a Gob game sounds weird, we need to re-examine that. */ initOperatorVolumes(); resetFreqs(); diff --git a/engines/groovie/configure.engine b/engines/groovie/configure.engine index 212a49bec8..f283731a58 100644 --- a/engines/groovie/configure.engine +++ b/engines/groovie/configure.engine @@ -1,4 +1,4 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine groovie "Groovie" yes "groovie2" "7th Guest" +add_engine groovie "Groovie" yes "groovie2" "7th Guest" "highres" add_engine groovie2 "Groovie 2 games" no "" "" "jpeg 16bit" diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp index a249b66858..b12e264a57 100644 --- a/engines/groovie/detection.cpp +++ b/engines/groovie/detection.cpp @@ -322,7 +322,7 @@ static const ADExtraGuiOptionsMap optionsList[] = { class GroovieMetaEngine : public AdvancedMetaEngine { public: GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames, optionsList) { - _singleid = "groovie"; + _singleId = "groovie"; // Use kADFlagUseExtraAsHint in order to distinguish the 11th hour from // its "Making of" as well as the Clandestiny Trailer; they all share @@ -333,7 +333,7 @@ public: // to the detection entries. In the latter case, this TODO should be // replaced with an according explanation. _flags = kADFlagUseExtraAsHint; - _guioptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT); + _guiOptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT); // Need MIDI directory to detect 11H Mac Installed _maxScanDepth = 2; diff --git a/engines/hopkins/configure.engine b/engines/hopkins/configure.engine index c38ecd4cd2..cd9f50a5f9 100644 --- a/engines/hopkins/configure.engine +++ b/engines/hopkins/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine hopkins "Hopkins FBI" yes "" "" "16bit" +add_engine hopkins "Hopkins FBI" yes "" "" "16bit highres" diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp index c48a26b405..4e4746c002 100644 --- a/engines/hugo/detection.cpp +++ b/engines/hugo/detection.cpp @@ -41,7 +41,7 @@ uint32 HugoEngine::getFeatures() const { } const char *HugoEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h index 2ee0262ef2..773d491423 100644 --- a/engines/kyra/detection_tables.h +++ b/engines/kyra/detection_tables.h @@ -1546,7 +1546,7 @@ const KYRAGameDescription adGameDescs[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS) }, EOB_FLAGS @@ -1562,7 +1562,7 @@ const KYRAGameDescription adGameDescs[] = { }, Common::DE_DEU, Common::kPlatformDOS, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS) }, EOB_FLAGS @@ -1576,10 +1576,10 @@ const KYRAGameDescription adGameDescs[] = { { "EOBDATA3.PAK", 0, "3ed915ab5b94d60dbfe1b55379889c51", -1 }, { 0, 0, 0, 0 } }, - Common::IT_ITA, - Common::kPlatformDOS, - ADGF_TESTING, - GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS) + Common::IT_ITA, + Common::kPlatformDOS, + ADGF_NO_FLAGS, + GUIO7(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GUIO_RENDERCGA, GAMEOPTION_EOB_HPGRAPHS) }, EOB_FLAGS }, @@ -1594,7 +1594,7 @@ const KYRAGameDescription adGameDescs[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO6(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GAMEOPTION_EOB_HPGRAPHS) }, EOB2_FLAGS @@ -1610,7 +1610,7 @@ const KYRAGameDescription adGameDescs[] = { }, Common::DE_DEU, Common::kPlatformDOS, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO6(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK, GUIO_RENDERVGA, GUIO_RENDEREGA, GAMEOPTION_EOB_HPGRAPHS) }, EOB2_FLAGS diff --git a/engines/lab/detection.cpp b/engines/lab/detection.cpp index b080a80f68..1fd3ca8944 100644 --- a/engines/lab/detection.cpp +++ b/engines/lab/detection.cpp @@ -48,7 +48,7 @@ static const ADGameDescription labDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO0() }, { @@ -61,7 +61,7 @@ static const ADGameDescription labDescriptions[] = { }, Common::EN_ANY, Common::kPlatformDOS, - Lab::GF_LOWRES | ADGF_TESTING, + Lab::GF_LOWRES | ADGF_NO_FLAGS, GUIO0() }, { @@ -75,7 +75,7 @@ static const ADGameDescription labDescriptions[] = { }, Common::EN_ANY, Common::kPlatformWindows, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO0() }, { @@ -115,7 +115,7 @@ uint32 LabEngine::getFeatures() const { class LabMetaEngine : public AdvancedMetaEngine { public: LabMetaEngine() : AdvancedMetaEngine(labDescriptions, sizeof(ADGameDescription), lab_setting) { - _singleid = "lab"; + _singleId = "lab"; _maxScanDepth = 4; _directoryGlobs = directoryGlobs; @@ -197,26 +197,7 @@ int LabMetaEngine::getMaximumSaveSlot() const { void LabMetaEngine::removeSaveState(const char *target, int slot) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); - Common::String filename = Common::String::format("%s.%03u", target, slot); - - saveFileMan->removeSavefile(filename.c_str()); - - Common::StringArray filenames; - Common::String pattern = target; - pattern += ".###"; - filenames = saveFileMan->listSavefiles(pattern.c_str()); - Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) - - for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { - // Obtain the last 3 digits of the filename, since they correspond to the save slot - int slotNum = atoi(file->c_str() + file->size() - 3); - - // Rename every slot greater than the deleted slot, - if (slotNum > slot) { - saveFileMan->renameSavefile(file->c_str(), filename.c_str()); - filename = Common::String::format("%s.%03u", target, ++slot); - } - } + saveFileMan->removeSavefile(Common::String::format("%s.%03u", target, slot)); } SaveStateDescriptor LabMetaEngine::querySaveMetaInfos(const char *target, int slot) const { diff --git a/engines/lab/processroom.cpp b/engines/lab/processroom.cpp index 44c8d65d7c..5093e8ef85 100644 --- a/engines/lab/processroom.cpp +++ b/engines/lab/processroom.cpp @@ -238,6 +238,8 @@ void LabEngine::doActions(const ActionList &actionList) { ActionList::const_iterator action; for (action = actionList.begin(); action != actionList.end(); ++action) { updateEvents(); + if (_quitLab || shouldQuit()) + return; switch (action->_actionType) { case kActionPlaySound: @@ -381,6 +383,8 @@ void LabEngine::doActions(const ActionList &actionList) { while (_system->getMillis() < targetMillis) { updateEvents(); + if (_quitLab || shouldQuit()) + return; _anim->diffNextFrame(); } } @@ -409,6 +413,8 @@ void LabEngine::doActions(const ActionList &actionList) { case kActionWaitSound: // used in scene 44 (heart of the labyrinth / ending) while (_music->isSoundEffectActive()) { updateEvents(); + if (_quitLab || shouldQuit()) + return; _anim->diffNextFrame(); waitTOF(); } diff --git a/engines/lastexpress/configure.engine b/engines/lastexpress/configure.engine index 807b1a088b..66bac55dea 100644 --- a/engines/lastexpress/configure.engine +++ b/engines/lastexpress/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine lastexpress "The Last Express" no "" "" "16bit" +add_engine lastexpress "The Last Express" no "" "" "16bit highres" diff --git a/engines/lastexpress/detection.cpp b/engines/lastexpress/detection.cpp index d790582104..52224e4ea5 100644 --- a/engines/lastexpress/detection.cpp +++ b/engines/lastexpress/detection.cpp @@ -199,8 +199,8 @@ static const ADGameDescription gameDescriptions[] = { class LastExpressMetaEngine : public AdvancedMetaEngine { public: LastExpressMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(ADGameDescription), lastExpressGames) { - _singleid = "lastexpress"; - _guioptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOSFX); + _singleId = "lastexpress"; + _guiOptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOSFX); } const char *getName() const { diff --git a/engines/lure/detection.cpp b/engines/lure/detection.cpp index d0a8ee0b4a..690a358bc3 100644 --- a/engines/lure/detection.cpp +++ b/engines/lure/detection.cpp @@ -193,12 +193,12 @@ class LureMetaEngine : public AdvancedMetaEngine { public: LureMetaEngine() : AdvancedMetaEngine(Lure::gameDescriptions, sizeof(Lure::LureGameDescription), lureGames) { _md5Bytes = 1024; - _singleid = "lure"; + _singleId = "lure"; // Use kADFlagUseExtraAsHint to distinguish between EGA and VGA versions // of italian Lure when their datafiles sit in the same directory. _flags = kADFlagUseExtraAsHint; - _guioptions = GUIO1(GUIO_NOSPEECH); + _guiOptions = GUIO1(GUIO_NOSPEECH); } virtual const char *getName() const { diff --git a/engines/made/detection.cpp b/engines/made/detection.cpp index 7adc3e1f98..636c2d147c 100644 --- a/engines/made/detection.cpp +++ b/engines/made/detection.cpp @@ -521,7 +521,7 @@ static MadeGameDescription g_fallbackDesc = { class MadeMetaEngine : public AdvancedMetaEngine { public: MadeMetaEngine() : AdvancedMetaEngine(Made::gameDescriptions, sizeof(Made::MadeGameDescription), madeGames) { - _singleid = "made"; + _singleId = "made"; } virtual const char *getName() const { diff --git a/engines/metaengine.h b/engines/metaengine.h index 3f8f56c1dd..e7bfebab71 100644 --- a/engines/metaengine.h +++ b/engines/metaengine.h @@ -96,8 +96,8 @@ public: /** * Return a list of all save states associated with the given target. * - * The list return is guarnateed to be sorted by slot numbers. That means - * lower slot numbers are always stored before bigger slot numbers. + * The returned list is guaranteed to be sorted by slot numbers. That + * means smaller slot numbers are always stored before bigger slot numbers. * * The caller has to ensure that this (Meta)Engine is responsible * for the specified target (by using findGame on it respectively diff --git a/engines/mohawk/configure.engine b/engines/mohawk/configure.engine index 47402c4560..ccb9499ef0 100644 --- a/engines/mohawk/configure.engine +++ b/engines/mohawk/configure.engine @@ -1,6 +1,6 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" +add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" "highres" add_engine cstime "Where in Time is Carmen Sandiego?" no add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit" -add_engine myst "Myst" no +add_engine myst "Myst" yes diff --git a/engines/mohawk/detection.cpp b/engines/mohawk/detection.cpp index 986b35c85e..a64d7ff7df 100644 --- a/engines/mohawk/detection.cpp +++ b/engines/mohawk/detection.cpp @@ -26,6 +26,7 @@ #include "common/savefile.h" #include "common/system.h" #include "common/textconsole.h" +#include "common/translation.h" #include "mohawk/livingbooks.h" @@ -53,7 +54,7 @@ struct MohawkGameDescription { }; const char* MohawkEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } uint32 MohawkEngine::getFeatures() const { @@ -160,10 +161,24 @@ static const char *directoryGlobs[] = { 0 }; +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_PLAY_MYST_FLYBY, + { + _s("Play the Myst fly by movie"), + _s("The Myst fly by movie was not played by the original engine."), + "playmystflyby", + false + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR +}; + class MohawkMetaEngine : public AdvancedMetaEngine { public: - MohawkMetaEngine() : AdvancedMetaEngine(Mohawk::gameDescriptions, sizeof(Mohawk::MohawkGameDescription), mohawkGames) { - _singleid = "mohawk"; + MohawkMetaEngine() : AdvancedMetaEngine(Mohawk::gameDescriptions, sizeof(Mohawk::MohawkGameDescription), mohawkGames, optionsList) { + _singleId = "mohawk"; _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 97d2932d57..7941a0d51a 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -22,6 +22,13 @@ namespace Mohawk { +#define GAMEOPTION_PLAY_MYST_FLYBY GUIO_GAMEOPTIONS1 + +#define GUI_OPTIONS_MYST GUIO3(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI) +#define GUI_OPTIONS_MYST_ME GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GAMEOPTION_PLAY_MYST_FLYBY) +#define GUI_OPTIONS_MYST_DEMO GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GUIO_NOLAUNCHLOAD) +#define GUI_OPTIONS_MYST_MAKING_OF GUIO4(GUIO_NOASPECT, GUIO_NOSUBTITLES, GUIO_NOMIDI, GUIO_NOLAUNCHLOAD) + static const MohawkGameDescription gameDescriptions[] = { // Myst // English Windows 3.11 @@ -34,7 +41,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -52,7 +59,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO | ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_DEMO }, GType_MYST, GF_DEMO, @@ -70,7 +77,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -88,7 +95,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -106,7 +113,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::ES_ESP, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -124,7 +131,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::IT_ITA, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -142,7 +149,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::JA_JPN, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -160,7 +167,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -178,7 +185,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_MAKING_OF }, GType_MAKINGOF, 0, @@ -196,7 +203,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::JA_JPN, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_MAKING_OF }, GType_MAKINGOF, 0, @@ -214,7 +221,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -232,7 +239,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::DE_DEU, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -250,7 +257,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -268,7 +275,7 @@ static const MohawkGameDescription gameDescriptions[] = { Common::PL_POL, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, @@ -2698,7 +2705,7 @@ static const MohawkGameDescription fallbackDescs[] = { Common::UNK_LANG, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST }, GType_MYST, 0, @@ -2713,7 +2720,7 @@ static const MohawkGameDescription fallbackDescs[] = { Common::UNK_LANG, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_MAKING_OF }, GType_MAKINGOF, 0, @@ -2728,7 +2735,7 @@ static const MohawkGameDescription fallbackDescs[] = { Common::UNK_LANG, Common::kPlatformWindows, ADGF_UNSTABLE, - GUIO1(GUIO_NOASPECT) + GUI_OPTIONS_MYST_ME }, GType_MYST, GF_ME, diff --git a/engines/mohawk/dialogs.cpp b/engines/mohawk/dialogs.cpp index f8aaf0f4af..6c6ae9e77f 100644 --- a/engines/mohawk/dialogs.cpp +++ b/engines/mohawk/dialogs.cpp @@ -207,9 +207,13 @@ void MystOptionsDialog::handleCommand(GUI::CommandSender *sender, uint32 cmd, ui save(); break; case kQuitCmd: { - Common::Event eventQ; - eventQ.type = Common::EVENT_QUIT; - g_system->getEventManager()->pushEvent(eventQ); + if (_vm->getGameType() != GType_MAKINGOF) { + _vm->_needsShowCredits = true; + } else { + Common::Event eventQ; + eventQ.type = Common::EVENT_QUIT; + g_system->getEventManager()->pushEvent(eventQ); + } close(); } break; diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index ad803cca7c..c16fab9131 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -66,11 +66,6 @@ MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription DebugMan.addDebugChannel(kDebugHelp, "Help", "Track Help File (HELP) Parsing"); DebugMan.addDebugChannel(kDebugCache, "Cache", "Track Resource Cache Accesses"); - // Engine tweaks - // Disabling this makes engine behavior as per - // original, including bugs, missing bits etc. :) - _tweaksEnabled = true; - _currentCursor = 0; _mainCursor = kDefaultMystCursor; _showResourceRects = false; @@ -235,7 +230,7 @@ Common::Error MohawkEngine_Myst::run() { _cursor->showCursor(); // Load game from launcher/command line if requested - if (ConfMan.hasKey("save_slot") && canLoadGameStateCurrently()) { + if (ConfMan.hasKey("save_slot") && hasGameSaveSupport()) { uint32 gameToLoad = ConfMan.getInt("save_slot"); Common::StringArray savedGamesList = MystGameState::generateSaveGameList(); if (gameToLoad > savedGamesList.size()) @@ -312,6 +307,7 @@ Common::Error MohawkEngine_Myst::run() { _needsPageDrop = false; _needsShowMap = false; _needsShowDemoMenu = false; + _needsShowCredits = false; _canSafelySaveLoad = true; runDialog(*_optionsDialog); @@ -331,6 +327,12 @@ Common::Error MohawkEngine_Myst::run() { changeToStack(kDemoStack, 2002, 0, 0); _needsShowDemoMenu = false; } + + if (_needsShowCredits) { + _cursor->hideCursor(); + changeToStack(kCreditsStack, 10000, 0, 0); + _needsShowCredits = false; + } break; default: break; @@ -503,8 +505,9 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS flyby = "stoneship flyby"; break; // Myst Flyby Movie not used in Original Masterpiece Edition Engine + // We play it when first arriving on Myst, and if the user has chosen so. case kMystStack: - if (_tweaksEnabled) + if (ConfMan.getBool("playmystflyby") && card == 4134) flyby = "myst flyby"; break; case kMechanicalStack: @@ -599,10 +602,12 @@ void MohawkEngine_Myst::changeToCard(uint16 card, TransitionType transition) { // Make sure the screen is updated if (transition != kNoTransition) { - if (!_gameState->_globals.transitions) - transition = kTransitionCopy; - - _gfx->runTransition(transition, Common::Rect(544, 333), 10, 0); + if (_gameState->_globals.transitions) { + _gfx->runTransition(transition, Common::Rect(544, 333), 10, 0); + } else { + _gfx->copyBackBufferToScreen(Common::Rect(544, 333)); + _needsUpdate = true; + } } // Make sure we have the right cursor showing @@ -1093,9 +1098,13 @@ Common::Error MohawkEngine_Myst::saveGameState(int slot, const Common::String &d return _gameState->save(desc) ? Common::kNoError : Common::kUnknownError; } +bool MohawkEngine_Myst::hasGameSaveSupport() const { + return !(getFeatures() & GF_DEMO) && getGameType() != GType_MAKINGOF; +} + bool MohawkEngine_Myst::canLoadGameStateCurrently() { // No loading in the demo/makingof - return _canSafelySaveLoad && !(getFeatures() & GF_DEMO) && getGameType() != GType_MAKINGOF; + return _canSafelySaveLoad && hasGameSaveSupport(); } bool MohawkEngine_Myst::canSaveGameStateCurrently() { diff --git a/engines/mohawk/myst.h b/engines/mohawk/myst.h index 79bd0819cb..0b249e5499 100644 --- a/engines/mohawk/myst.h +++ b/engines/mohawk/myst.h @@ -192,11 +192,11 @@ public: MystSoundBlock readSoundBlock(Common::ReadStream *stream) const; void applySoundBlock(const MystSoundBlock &block); - bool _tweaksEnabled; bool _needsUpdate; bool _needsPageDrop; bool _needsShowMap; bool _needsShowDemoMenu; + bool _needsShowCredits; bool _showResourceRects; @@ -245,6 +245,7 @@ private: * Saving / Loading is only allowed from the main event loop */ bool _canSafelySaveLoad; + bool hasGameSaveSupport() const; bool pollEvent(Common::Event &event); diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp index 59871ed49f..4b9cf546fa 100644 --- a/engines/mohawk/myst_areas.cpp +++ b/engines/mohawk/myst_areas.cpp @@ -240,6 +240,7 @@ VideoHandle MystAreaVideo::playMovie() { } else { // Resume the video handle->pause(false); + handle->start(); } if (_playBlocking) { @@ -250,6 +251,20 @@ VideoHandle MystAreaVideo::playMovie() { return handle; } +VideoHandle MystAreaVideo::getMovieHandle() { + // If the video is already in the manager, just return the handle + VideoHandle handle = _vm->_video->findVideoHandle(_videoFile); + if (!handle) { + // If the video has not been loaded yet, do it but don't start playing it + handle = _vm->_video->playMovie(_videoFile); + if (!handle) + error("Failed to open '%s'", _videoFile.c_str()); + handle->stop(); + } + + return handle; +} + void MystAreaVideo::handleCardChange() { if (_playOnCardChange) playMovie(); diff --git a/engines/mohawk/myst_areas.h b/engines/mohawk/myst_areas.h index 09ec6a2742..b19a2df9e2 100644 --- a/engines/mohawk/myst_areas.h +++ b/engines/mohawk/myst_areas.h @@ -108,6 +108,8 @@ public: MystAreaVideo(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystArea *parent); VideoHandle playMovie(); + VideoHandle getMovieHandle(); + void handleCardChange() override; bool isPlaying(); void setDirection(int16 direction) { _direction = direction; } diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index 6c93f980ac..5db9697a78 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -227,9 +227,8 @@ void MystGraphics::copyBackBufferToScreen(Common::Rect r) { void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay) { - // Do not artificially delay during transitions - int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation; - _enableDrawingTimeSimulation = 0; + // Transitions are barely visible without adding delays between the draw calls + enableDrawingTimeSimulation(true); switch (type) { case kTransitionLeftToRight: { @@ -290,7 +289,10 @@ void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 debugC(kDebugView, "Dissolve"); for (int16 step = 0; step < 8; step++) { - simulatePreviousDrawDelay(rect); + // Only one eighth of the rect pixels are updated by a draw step, + // delay by one eighth of the regular time + simulatePreviousDrawDelay(Common::Rect(rect.width() / 8, rect.height())); + transitionDissolve(rect, step); } } @@ -369,7 +371,7 @@ void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 error("Unknown transition %d", type); } - _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation; + enableDrawingTimeSimulation(false); } void MystGraphics::transitionDissolve(Common::Rect rect, uint step) { @@ -641,8 +643,10 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { // Do not draw anything new too quickly after the previous draw call // so that images stay at least a little while on screen // This is enabled only for scripted draw calls - if (time < _nextAllowedDrawTime) + if (time < _nextAllowedDrawTime) { + debugC(kDebugView, "Delaying draw call by %d ms", _nextAllowedDrawTime - time); _vm->_system->delayMillis(_nextAllowedDrawTime - time); + } } // Next draw call allowed at DELAY + AERA * COEFF milliseconds from now diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 659c5dcdf2..21c3042359 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -621,24 +621,32 @@ void Channelwood::o_hologramMonitor(uint16 op, uint16 var, uint16 argc, uint16 * switch (button) { case 0: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monalgh", kChannelwoodStack)); + if (!handle) + error("Failed to open monalgh movie"); + handle->moveTo(227, 70); break; case 1: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monamth", kChannelwoodStack)); + if (!handle) + error("Failed to open monamth movie"); + handle->moveTo(227, 70); break; case 2: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monasirs", kChannelwoodStack)); + if (!handle) + error("Failed to open monasirs movie"); + handle->moveTo(227, 70); break; case 3: handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monsmsg", kChannelwoodStack)); + if (!handle) + error("Failed to open monsmsg movie"); + handle->moveTo(226, 68); break; default: warning("Opcode %d Control Variable Out of Range", op); break; } - - // Move the video to the right location - if (handle) - handle->moveTo(227, 70); } } diff --git a/engines/mohawk/myst_stacks/credits.cpp b/engines/mohawk/myst_stacks/credits.cpp index 6548dd3171..b4a2076528 100644 --- a/engines/mohawk/myst_stacks/credits.cpp +++ b/engines/mohawk/myst_stacks/credits.cpp @@ -67,8 +67,10 @@ void Credits::runPersistentScripts() { _curImage++; // After the 6th image has shown, it's time to quit - if (_curImage == 7) + if (_curImage == 7) { _vm->quitGame(); + return; + } // Draw next image _vm->drawCardBackground(); diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index 3214c643a5..3324c9a22d 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -667,7 +667,7 @@ void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 * void Mechanical::o_fortressRotationSetPosition(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Set fortress position", op); - VideoHandle gears = _fortressRotationGears->playMovie(); + VideoHandle gears = _fortressRotationGears->getMovieHandle(); uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames(); // Myst ME short movie workaround, explained in o_fortressRotation_init @@ -801,7 +801,7 @@ void Mechanical::o_elevatorRotation_init(uint16 op, uint16 var, uint16 argc, uin } void Mechanical::fortressRotation_run() { - VideoHandle gears = _fortressRotationGears->playMovie(); + VideoHandle gears = _fortressRotationGears->getMovieHandle(); double oldRate = gears->getRate().toDouble(); uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames(); @@ -949,7 +949,7 @@ void Mechanical::fortressSimulation_run() { _fortressSimulationInit = false; } else { - VideoHandle holo = _fortressSimulationHolo->playMovie(); + VideoHandle holo = _fortressSimulationHolo->getMovieHandle(); double oldRate = holo->getRate().toDouble(); diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index 5033f0fef8..9d23d2fb10 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -52,6 +52,7 @@ Myst::Myst(MohawkEngine_Myst *vm) : _cabinDoorOpened = 0; _cabinHandleDown = 0; _cabinMatchState = 2; + _cabinGaugeMovieEnabled = false; _matchBurning = false; _tree = nullptr; _treeAlcove = nullptr; @@ -2142,7 +2143,7 @@ void Myst::tree_run() { // Check if alcove is accessible treeSetAlcoveAccessible(); - if (_cabinGaugeMovie) { + if (_cabinGaugeMovieEnabled) { Common::Rational rate = boilerComputeGaugeRate(pressure, delay); boilerResetGauge(rate); } @@ -2313,7 +2314,7 @@ void Myst::o_rocketPianoStart(uint16 op, uint16 var, uint16 argc, uint16 *argv) dest.bottom = 332 - rect.top; // Draw pressed piano key - _vm->_gfx->copyImageSectionToScreen(key->getSubImage(0).wdib, src, dest); + _vm->_gfx->copyImageSectionToScreen(key->getSubImage(1).wdib, src, dest); _vm->_system->updateScreen(); // Play note @@ -3703,6 +3704,8 @@ void Myst::boilerGaugeInit() { frame = Audio::Timestamp(0, 0, 600); _vm->_video->drawVideoFrame(_cabinGaugeMovie, frame); + + _cabinGaugeMovieEnabled = true; } void Myst::o_rocketSliders_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -3842,6 +3845,8 @@ void Myst::o_boiler_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { _cabinGaugeMovie = VideoHandle(); _cabinFireMovie = VideoHandle(); + + _cabinGaugeMovieEnabled = false; } void Myst::o_generatorControlRoom_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/myst_stacks/myst.h b/engines/mohawk/myst_stacks/myst.h index 9d66b798c5..6e2f7cc1c8 100644 --- a/engines/mohawk/myst_stacks/myst.h +++ b/engines/mohawk/myst_stacks/myst.h @@ -261,6 +261,8 @@ protected: uint32 _matchGoOutTime; // 144 VideoHandle _cabinFireMovie; // 240 + + bool _cabinGaugeMovieEnabled; VideoHandle _cabinGaugeMovie; // 244 bool _boilerPressureIncreasing; diff --git a/engines/mohawk/myst_stacks/selenitic.cpp b/engines/mohawk/myst_stacks/selenitic.cpp index 2617bd04aa..5402e5a581 100644 --- a/engines/mohawk/myst_stacks/selenitic.cpp +++ b/engines/mohawk/myst_stacks/selenitic.cpp @@ -672,6 +672,11 @@ void Selenitic::soundReceiverUpdate() { } void Selenitic::soundReceiverDrawView() { + soundReceiverSetSubimageRect(); + soundReceiverDrawAngle(); +} + +void Selenitic::soundReceiverSetSubimageRect() const { uint32 left = ((*_soundReceiverPosition) * 1800) / 3600; Common::Rect rect = _soundReceiverViewer->getSubImage(0).rect; @@ -681,8 +686,6 @@ void Selenitic::soundReceiverDrawView() { _soundReceiverViewer->setSubImageRect(0, rect); _soundReceiverViewer->drawConditionalDataToScreen(0); - - soundReceiverDrawAngle(); } void Selenitic::soundReceiverDrawAngle() { @@ -948,10 +951,13 @@ void Selenitic::soundReceiver_run() { if (_soundReceiverDirection) { uint32 currentTime = _vm->_system->getMillis(); - if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500) - soundReceiverIncreaseSpeed(); - else if (currentTime > _soundReceiverStartTime + 1000) - soundReceiverIncreaseSpeed(); + if (_soundReceiverSpeed == 50 && currentTime > _soundReceiverStartTime + 500) { + soundReceiverIncreaseSpeed(); + _soundReceiverStartTime = currentTime; + } else if (currentTime > _soundReceiverStartTime + 1000) { + soundReceiverIncreaseSpeed(); + _soundReceiverStartTime = currentTime; + } if (currentTime > _soundReceiverStartTime + 100) soundReceiverUpdate(); @@ -1080,6 +1086,8 @@ void Selenitic::o_soundReceiver_init(uint16 op, uint16 var, uint16 argc, uint16 _soundReceiverPosition = &_state.soundReceiverPositions[currentSource]; _soundReceiverCurrentSource = _soundReceiverSources[currentSource]; + soundReceiverSetSubimageRect(); + _soundReceiverSigmaPressed = false; } diff --git a/engines/mohawk/myst_stacks/selenitic.h b/engines/mohawk/myst_stacks/selenitic.h index ffd8941f2c..fc9649755d 100644 --- a/engines/mohawk/myst_stacks/selenitic.h +++ b/engines/mohawk/myst_stacks/selenitic.h @@ -117,6 +117,7 @@ private: void soundReceiverLeftRight(uint direction); void soundReceiverUpdate(); + void soundReceiverSetSubimageRect() const; void soundReceiverDrawView(); void soundReceiverDrawAngle(); void soundReceiverIncreaseSpeed(); diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index 522dd5ecdd..eec543235e 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -146,7 +146,7 @@ VideoHandle::VideoHandle(const VideoHandle &handle) : _ptr(handle._ptr) { VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { // Set dithering enabled, if required - _enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME); + _enableDither = (_vm->getGameType() == GType_MYST || _vm->getGameType() == GType_MAKINGOF) && !(_vm->getFeatures() & GF_ME); } VideoManager::~VideoManager() { @@ -317,69 +317,8 @@ bool VideoManager::updateMovies() { // Check if we need to draw a frame if (video->needsUpdate()) { - const Graphics::Surface *frame = video->decodeNextFrame(); - Graphics::Surface *convertedFrame = 0; - - if (frame && (*it)->isEnabled()) { - Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); - - if (frame->format != pixelFormat) { - // We don't support downconverting to 8bpp without having - // support in the codec. Set _enableDither if shows up. - if (pixelFormat.bytesPerPixel == 1) { - warning("Cannot convert high color video frame to 8bpp"); - (*it)->close(); - it = _videos.erase(it); - continue; - } - - // Convert to the current screen format - convertedFrame = frame->convertTo(pixelFormat, video->getPalette()); - frame = convertedFrame; - } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) { - // Set the palette when running in 8bpp mode only - // Don't do this for Myst, which has its own per-stack handling - if (_vm->getGameType() != GType_MYST) - _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256); - } - - // Clip the video to make sure it stays on the screen (Myst does this a few times) - Common::Rect targetRect = Common::Rect(video->getWidth(), video->getHeight()); - targetRect.translate((*it)->getX(), (*it)->getY()); - - Common::Rect frameRect = Common::Rect(video->getWidth(), video->getHeight()); - - if (targetRect.left < 0) { - frameRect.left -= targetRect.left; - targetRect.left = 0; - } - - if (targetRect.top < 0) { - frameRect.top -= targetRect.top; - targetRect.top = 0; - } - - if (targetRect.right > _vm->_system->getWidth()) { - frameRect.right -= targetRect.right - _vm->_system->getWidth(); - targetRect.right = _vm->_system->getWidth(); - } - - if (targetRect.bottom > _vm->_system->getHeight()) { - frameRect.bottom -= targetRect.bottom - _vm->_system->getHeight(); - targetRect.bottom = _vm->_system->getHeight(); - } - - _vm->_system->copyRectToScreen(frame->getBasePtr(frameRect.left, frameRect.top), frame->pitch, - targetRect.left, targetRect.top, targetRect.width(), targetRect.height()); - - // We've drawn something to the screen, make sure we update it + if (drawNextFrame(*it)) { updateScreen = true; - - // Delete 8bpp conversion surface - if (convertedFrame) { - convertedFrame->free(); - delete convertedFrame; - } } } @@ -394,6 +333,74 @@ bool VideoManager::updateMovies() { return updateScreen; } +bool VideoManager::drawNextFrame(VideoEntryPtr videoEntry) { + Video::VideoDecoder *video = videoEntry->_video; + const Graphics::Surface *frame = video->decodeNextFrame(); + + if (!frame || !videoEntry->isEnabled()) { + return false; + } + + Graphics::Surface *convertedFrame = 0; + Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); + + if (frame->format != pixelFormat) { + // We don't support downconverting to 8bpp without having + // support in the codec. Set _enableDither if shows up. + if (pixelFormat.bytesPerPixel == 1) { + warning("Cannot convert high color video frame to 8bpp"); + return false; + } + + // Convert to the current screen format + convertedFrame = frame->convertTo(pixelFormat, video->getPalette()); + frame = convertedFrame; + } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) { + // Set the palette when running in 8bpp mode only + // Don't do this for Myst, which has its own per-stack handling + if (_vm->getGameType() != GType_MYST) + _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256); + } + + // Clip the video to make sure it stays on the screen (Myst does this a few times) + Common::Rect targetRect = Common::Rect(video->getWidth(), video->getHeight()); + targetRect.translate(videoEntry->getX(), videoEntry->getY()); + + Common::Rect frameRect = Common::Rect(video->getWidth(), video->getHeight()); + + if (targetRect.left < 0) { + frameRect.left -= targetRect.left; + targetRect.left = 0; + } + + if (targetRect.top < 0) { + frameRect.top -= targetRect.top; + targetRect.top = 0; + } + + if (targetRect.right > _vm->_system->getWidth()) { + frameRect.right -= targetRect.right - _vm->_system->getWidth(); + targetRect.right = _vm->_system->getWidth(); + } + + if (targetRect.bottom > _vm->_system->getHeight()) { + frameRect.bottom -= targetRect.bottom - _vm->_system->getHeight(); + targetRect.bottom = _vm->_system->getHeight(); + } + + _vm->_system->copyRectToScreen(frame->getBasePtr(frameRect.left, frameRect.top), frame->pitch, + targetRect.left, targetRect.top, targetRect.width(), targetRect.height()); + + // Delete 8bpp conversion surface + if (convertedFrame) { + convertedFrame->free(); + delete convertedFrame; + } + + // We've drawn something to the screen, make sure we update it + return true; +} + void VideoManager::activateMLST(uint16 mlstId, uint16 card) { Common::SeekableReadStream *mlstStream = _vm->getResource(ID_MLST, card); uint16 recordCount = mlstStream->readUint16BE(); @@ -582,11 +589,9 @@ bool VideoManager::isVideoPlaying() { } void VideoManager::drawVideoFrame(VideoHandle handle, const Audio::Timestamp &time) { - // FIXME: This should be done separately from the "playing" - // videos eventually. assert(handle); handle->seek(time); - updateMovies(); + drawNextFrame(handle._ptr); handle->stop(); } diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index c5a3dc7041..d0edab9def 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -350,6 +350,8 @@ private: VideoList::iterator findEntry(VideoEntryPtr ptr); void removeEntry(VideoEntryPtr ptr); + bool drawNextFrame(VideoEntryPtr videoEntry); + // Dithering control bool _enableDither; void checkEnableDither(VideoEntryPtr &entry); diff --git a/engines/mortevielle/configure.engine b/engines/mortevielle/configure.engine index a7fb2ccda6..0fe89acc99 100644 --- a/engines/mortevielle/configure.engine +++ b/engines/mortevielle/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine mortevielle "Mortevielle" yes +add_engine mortevielle "Mortevielle" yes "" "" "highres" diff --git a/engines/mortevielle/detection.cpp b/engines/mortevielle/detection.cpp index b6c27e6b12..6791707dd5 100644 --- a/engines/mortevielle/detection.cpp +++ b/engines/mortevielle/detection.cpp @@ -55,7 +55,7 @@ public: MortevielleMetaEngine() : AdvancedMetaEngine(Mortevielle::MortevielleGameDescriptions, sizeof(Mortevielle::MortevielleGameDescription), MortevielleGame) { _md5Bytes = 512; - _singleid = "mortevielle"; + _singleId = "mortevielle"; // Use kADFlagUseExtraAsHint to distinguish between original and improved versions // (i.e. use or not of the game data file). _flags = kADFlagUseExtraAsHint; diff --git a/engines/neverhood/configure.engine b/engines/neverhood/configure.engine index 46910e293e..f04e6c69f6 100644 --- a/engines/neverhood/configure.engine +++ b/engines/neverhood/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine neverhood "Neverhood" yes +add_engine neverhood "Neverhood" yes "" "" "highres" diff --git a/engines/neverhood/detection.cpp b/engines/neverhood/detection.cpp index c663d5d3a4..cfddc2d6b4 100644 --- a/engines/neverhood/detection.cpp +++ b/engines/neverhood/detection.cpp @@ -41,7 +41,7 @@ struct NeverhoodGameDescription { }; const char *NeverhoodEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } uint32 NeverhoodEngine::getFeatures() const { @@ -181,8 +181,8 @@ static const ExtraGuiOption neverhoodExtraGuiOption3 = { class NeverhoodMetaEngine : public AdvancedMetaEngine { public: NeverhoodMetaEngine() : AdvancedMetaEngine(Neverhood::gameDescriptions, sizeof(Neverhood::NeverhoodGameDescription), neverhoodGames) { - _singleid = "neverhood"; - _guioptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOMIDI); + _singleId = "neverhood"; + _guiOptions = GUIO2(GUIO_NOSUBTITLES, GUIO_NOMIDI); } virtual const char *getName() const { diff --git a/engines/parallaction/detection.cpp b/engines/parallaction/detection.cpp index 0c7ebb2b05..4c52990874 100644 --- a/engines/parallaction/detection.cpp +++ b/engines/parallaction/detection.cpp @@ -220,7 +220,7 @@ static const PARALLACTIONGameDescription gameDescriptions[] = { class ParallactionMetaEngine : public AdvancedMetaEngine { public: ParallactionMetaEngine() : AdvancedMetaEngine(Parallaction::gameDescriptions, sizeof(Parallaction::PARALLACTIONGameDescription), parallactionGames) { - _guioptions = GUIO1(GUIO_NOLAUNCHLOAD); + _guiOptions = GUIO1(GUIO_NOLAUNCHLOAD); } virtual const char *getName() const { diff --git a/engines/pegasus/configure.engine b/engines/pegasus/configure.engine index ed7e295287..650d57a602 100644 --- a/engines/pegasus/configure.engine +++ b/engines/pegasus/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit" +add_engine pegasus "The Journeyman Project: Pegasus Prime" yes "" "" "16bit highres" diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp index 721c382d4f..161a133c8b 100644 --- a/engines/pegasus/detection.cpp +++ b/engines/pegasus/detection.cpp @@ -134,7 +134,7 @@ static const PegasusGameDescription gameDescriptions[] = { class PegasusMetaEngine : public AdvancedMetaEngine { public: PegasusMetaEngine() : AdvancedMetaEngine(Pegasus::gameDescriptions, sizeof(Pegasus::PegasusGameDescription), pegasusGames) { - _singleid = "pegasus"; + _singleId = "pegasus"; } virtual const char *getName() const { diff --git a/engines/prince/configure.engine b/engines/prince/configure.engine index 50740d9f41..827579b00d 100644 --- a/engines/prince/configure.engine +++ b/engines/prince/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine prince "The Prince and The Coward" no +add_engine prince "The Prince and The Coward" no "" "" "highres" diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp index 3fe7993fdb..1c6f63aff3 100644 --- a/engines/prince/detection.cpp +++ b/engines/prince/detection.cpp @@ -29,7 +29,7 @@ int PrinceEngine::getGameType() const { } const char *PrinceEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } uint32 PrinceEngine::getFeatures() const { diff --git a/engines/prince/detection.h b/engines/prince/detection.h index 7e5bdd6b7b..3076253cf5 100644 --- a/engines/prince/detection.h +++ b/engines/prince/detection.h @@ -104,7 +104,7 @@ const static char *directoryGlobs[] = { class PrinceMetaEngine : public AdvancedMetaEngine { public: PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) { - _singleid = "prince"; + _singleId = "prince"; _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } diff --git a/engines/queen/detection.cpp b/engines/queen/detection.cpp index 4e87b7e5c2..81e0767836 100644 --- a/engines/queen/detection.cpp +++ b/engines/queen/detection.cpp @@ -401,7 +401,7 @@ static const QueenGameDescription gameDescriptions[] = { class QueenMetaEngine : public AdvancedMetaEngine { public: QueenMetaEngine() : AdvancedMetaEngine(Queen::gameDescriptions, sizeof(Queen::QueenGameDescription), queenGames, optionsList) { - _singleid = "queen"; + _singleId = "queen"; } virtual const char *getName() const { @@ -443,25 +443,25 @@ const ADGameDescription *QueenMetaEngine::fallbackDetect(const FileMap &allFiles } Queen::DetectedGameVersion version; if (Queen::Resource::detectVersion(&version, &dataFile)) { - desc.gameid = "queen"; + desc.gameId = "queen"; desc.language = version.language; desc.platform = version.platform; desc.flags = ADGF_NO_FLAGS; - desc.guioptions = GUIO0(); + desc.guiOptions = GUIO0(); if (version.features & Queen::GF_DEMO) { desc.extra = "Demo"; desc.flags = ADGF_DEMO; - desc.guioptions = GUIO_NOSPEECH; + desc.guiOptions = GUIO_NOSPEECH; } else if (version.features & Queen::GF_INTERVIEW) { desc.extra = "Interview"; desc.flags = ADGF_DEMO; - desc.guioptions = GUIO_NOSPEECH; + desc.guiOptions = GUIO_NOSPEECH; } else if (version.features & Queen::GF_FLOPPY) { desc.extra = "Floppy"; - desc.guioptions = GUIO_NOSPEECH; + desc.guiOptions = GUIO_NOSPEECH; } else if (version.features & Queen::GF_TALKIE) { desc.extra = "Talkie"; - desc.guioptions = GAMEOPTION_ALT_INTRO; + desc.guiOptions = GAMEOPTION_ALT_INTRO; } return (const ADGameDescription *)&desc; } diff --git a/engines/saga/configure.engine b/engines/saga/configure.engine index 99e2ab367b..adb904a6dd 100644 --- a/engines/saga/configure.engine +++ b/engines/saga/configure.engine @@ -1,5 +1,5 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine saga "SAGA" yes "ihnm saga2" "ITE" -add_engine ihnm "IHNM" yes -add_engine saga2 "SAGA 2 games" no +add_engine ihnm "IHNM" yes "" "" "highres" +add_engine saga2 "SAGA 2 games" no "" "" "highres" diff --git a/engines/saga/detection.cpp b/engines/saga/detection.cpp index e201612beb..0677e84d67 100644 --- a/engines/saga/detection.cpp +++ b/engines/saga/detection.cpp @@ -102,11 +102,11 @@ static const Engines::ObsoleteGameID obsoleteGameIDsTable[] = { class SagaMetaEngine : public AdvancedMetaEngine { public: SagaMetaEngine() : AdvancedMetaEngine(Saga::gameDescriptions, sizeof(Saga::SAGAGameDescription), sagaGames) { - _singleid = "saga"; + _singleId = "saga"; } - virtual GameDescriptor findGame(const char *gameid) const { - return Engines::findGameID(gameid, _gameids, obsoleteGameIDsTable); + virtual GameDescriptor findGame(const char *gameId) const { + return Engines::findGameID(gameId, _gameIds, obsoleteGameIDsTable); } virtual const char *getName() const { diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index cb42ac0aaa..ad940aaf8b 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -1862,8 +1862,10 @@ void Interface::drawStatusBar() { int stringWidth; int color; // The default colors in the Spanish version of IHNM are shifted by one - // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)" - int offset = (_vm->getLanguage() == Common::ES_ESP) ? 1 : 0; + // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This + // also applies to the German and French versions (bug #7064 - "IHNM: + // text mistake in german version"). + int offset = (_vm->getLanguage() == Common::ES_ESP || _vm->getLanguage() == Common::DE_DEU || _vm->getLanguage() == Common::FR_FRA) ? 1 : 0; // Disable the status text in IHNM when the chapter is 8 if (_vm->getGameId() == GID_IHNM && _vm->_scene->currentChapterNumber() == 8) diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 532b59d3c7..77a21e7f93 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -578,9 +578,11 @@ ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) { } #ifdef ENABLE_IHNM } else if (getGameId() == GID_IHNM) { - // The default colors in the Spanish version of IHNM are shifted by one - // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)" - int offset = (getLanguage() == Common::ES_ESP) ? 1 : 0; + // The default colors in the Spanish, version of IHNM are shifted by one + // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)". This + // also applies to the German and French versions (bug #7064 - "IHNM: + // text mistake in german version"). + int offset = (getLanguage() == Common::ES_ESP || getLanguage() == Common::DE_DEU || getLanguage() == Common::FR_FRA) ? 1 : 0; switch (knownColor) { case(kKnownColorTransparent): diff --git a/engines/sci/configure.engine b/engines/sci/configure.engine index d1c45a4654..f8a519002e 100644 --- a/engines/sci/configure.engine +++ b/engines/sci/configure.engine @@ -1,4 +1,4 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine sci "SCI" yes "sci32" "SCI 0-1.1 games" -add_engine sci32 "SCI32 games" no +add_engine sci32 "SCI32 games" no "" "" "highres" diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index cbc6dfaf74..a092e0676d 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -141,6 +141,8 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), registerCmd("vpl", WRAP_METHOD(Console, cmdVisiblePlaneList)); // alias registerCmd("plane_items", WRAP_METHOD(Console, cmdPlaneItemList)); registerCmd("pi", WRAP_METHOD(Console, cmdPlaneItemList)); // alias + registerCmd("visible_plane_items", WRAP_METHOD(Console, cmdVisiblePlaneItemList)); + registerCmd("vpi", WRAP_METHOD(Console, cmdVisiblePlaneItemList)); // alias registerCmd("saved_bits", WRAP_METHOD(Console, cmdSavedBits)); registerCmd("show_saved_bits", WRAP_METHOD(Console, cmdShowSavedBits)); // Segments @@ -192,6 +194,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger(), registerCmd("send", WRAP_METHOD(Console, cmdSend)); registerCmd("go", WRAP_METHOD(Console, cmdGo)); registerCmd("logkernel", WRAP_METHOD(Console, cmdLogKernel)); + registerCmd("vocab994", WRAP_METHOD(Console, cmdMapVocab994)); // Breakpoints registerCmd("bp_list", WRAP_METHOD(Console, cmdBreakpointList)); registerCmd("bplist", WRAP_METHOD(Console, cmdBreakpointList)); // alias @@ -385,6 +388,7 @@ bool Console::cmdHelp(int argc, const char **argv) { debugPrintf(" plane_list / pl - Shows a list of all the planes in the draw list (SCI2+)\n"); debugPrintf(" visible_plane_list / vpl - Shows a list of all the planes in the visible draw list (SCI2+)\n"); debugPrintf(" plane_items / pi - Shows a list of all items for a plane (SCI2+)\n"); + debugPrintf(" visible_plane_items / vpi - Shows a list of all items for a plane in the visible draw list (SCI2+)\n"); debugPrintf(" saved_bits - List saved bits on the hunk\n"); debugPrintf(" show_saved_bits - Display saved bits\n"); debugPrintf("\n"); @@ -1001,7 +1005,7 @@ bool Console::cmdHexgrep(int argc, const char **argv) { for (; resNumber <= resMax; resNumber++) { script = _engine->getResMan()->findResource(ResourceId(restype, resNumber), 0); if (script) { - unsigned int seeker = 0, seekerold = 0; + uint32 seeker = 0, seekerold = 0; uint32 comppos = 0; int output_script_name = 0; @@ -1507,7 +1511,7 @@ bool Console::cmdSaid(int argc, const char **argv) { } // TODO: Maybe turn this into a proper said spec compiler - unsigned int len = 0; + uint32 len = 0; for (p++; p < argc; p++) { if (strcmp(argv[p], ",") == 0) { spec[len++] = 0xf0; @@ -1544,7 +1548,7 @@ bool Console::cmdSaid(int argc, const char **argv) { spec[len++] = 0xfe; spec[len++] = 0xf6; } else { - unsigned int s = strtol(argv[p], 0, 16); + uint32 s = strtol(argv[p], 0, 16); if (s >= 0xf0 && s <= 0xff) { spec[len++] = s; } else { @@ -1816,6 +1820,34 @@ bool Console::cmdPlaneItemList(int argc, const char **argv) { return true; } +bool Console::cmdVisiblePlaneItemList(int argc, const char **argv) { + if (argc != 2) { + debugPrintf("Shows the list of items for a plane\n"); + debugPrintf("Usage: %s <plane address>\n", argv[0]); + return true; + } + + reg_t planeObject = NULL_REG; + + if (parse_reg_t(_engine->_gamestate, argv[1], &planeObject, false)) { + debugPrintf("Invalid address passed.\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + +#ifdef ENABLE_SCI32 + if (_engine->_gfxFrameout) { + debugPrintf("Visible plane item list:\n"); + _engine->_gfxFrameout->printVisiblePlaneItemList(this, planeObject); + } else { + debugPrintf("This SCI version does not have a list of plane items\n"); + } +#else + debugPrintf("SCI32 isn't included in this compiled executable\n"); +#endif + return true; +} + bool Console::cmdSavedBits(int argc, const char **argv) { SegManager *segman = _engine->_gamestate->_segMan; SegmentId id = segman->findSegmentByType(SEG_TYPE_HUNK); @@ -3923,6 +3955,55 @@ bool Console::cmdSfx01Track(int argc, const char **argv) { return true; } +bool Console::cmdMapVocab994(int argc, const char **argv) { + EngineState *s = _engine->_gamestate; // for the several defines in this function + reg_t reg; + + if (argc != 4) { + debugPrintf("Attempts to map a range of vocab.994 entries to a given class\n"); + debugPrintf("Usage: %s <class addr> <first> <last>\n", argv[0]); + return true; + } + + if (parse_reg_t(_engine->_gamestate, argv[1], ®, false)) { + debugPrintf("Invalid address passed.\n"); + debugPrintf("Check the \"addresses\" command on how to use addresses\n"); + return true; + } + + Resource *resource = _engine->_resMan->findResource(ResourceId(kResourceTypeVocab, 994), 0); + const Object *obj = s->_segMan->getObject(reg); + uint16 *data = (uint16 *) resource->data; + uint32 first = atoi(argv[2]); + uint32 last = atoi(argv[3]); + Common::Array<bool> markers; + + markers.resize(_engine->getKernel()->getSelectorNamesSize()); + if (!obj->isClass() && getSciVersion() != SCI_VERSION_3) + obj = s->_segMan->getObject(obj->getSuperClassSelector()); + + first = MIN(first, (uint32) (resource->size / 2 - 2)); + last = MIN(last, (uint32) (resource->size / 2 - 2)); + + for (uint32 i = first; i <= last; ++i) { + uint16 ofs = data[i]; + + if (obj && ofs < obj->getVarCount()) { + uint16 varSelector = obj->getVarSelector(ofs); + debugPrintf("%d: property at index %04x of %s is %s %s\n", i, ofs, + s->_segMan->derefString(obj->getNameSelector()), + _engine->getKernel()->getSelectorName(varSelector).c_str(), + markers[varSelector] ? "(repeat!)" : ""); + markers[varSelector] = true; + } + else { + debugPrintf("%d: property at index %04x doesn't match up with %s\n", i, ofs, + s->_segMan->derefString(obj->getNameSelector())); + } + } + + return true; +} bool Console::cmdQuit(int argc, const char **argv) { if (argc != 2) { } @@ -4361,7 +4442,8 @@ int Console::printObject(reg_t pos) { debugPrintf(" "); if (var_container && i < var_container->getVarCount()) { uint16 varSelector = var_container->getVarSelector(i); - debugPrintf("[%03x] %s = ", varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str()); + // Times two commented out for now for easy parsing of vocab.994 + debugPrintf("(%04x) [%03x] %s = ", i /* *2 */, varSelector, _engine->getKernel()->getSelectorName(varSelector).c_str()); } else debugPrintf("p#%x = ", i); diff --git a/engines/sci/console.h b/engines/sci/console.h index 7c4de02182..cf85def950 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -98,6 +98,7 @@ private: bool cmdPlaneList(int argc, const char **argv); bool cmdVisiblePlaneList(int argc, const char **argv); bool cmdPlaneItemList(int argc, const char **argv); + bool cmdVisiblePlaneItemList(int argc, const char **argv); bool cmdSavedBits(int argc, const char **argv); bool cmdShowSavedBits(int argc, const char **argv); // Segments @@ -138,6 +139,7 @@ private: bool cmdSend(int argc, const char **argv); bool cmdGo(int argc, const char **argv); bool cmdLogKernel(int argc, const char **argv); + bool cmdMapVocab994(int argc, const char **argv); // Breakpoints bool cmdBreakpointList(int argc, const char **argv); bool cmdBreakpointDelete(int argc, const char **argv); diff --git a/engines/sci/decompressor.cpp b/engines/sci/decompressor.cpp index e65ff148de..ca2298e67e 100644 --- a/engines/sci/decompressor.cpp +++ b/engines/sci/decompressor.cpp @@ -257,7 +257,7 @@ int DecompressorLZW::unpackLZW1(Common::ReadStream *src, byte *dest, uint32 nPac init(src, dest, nPacked, nUnpacked); byte *stak = (byte *)malloc(0x1014); - unsigned int tokensSize = 0x1004 * sizeof(Tokenlist); + uint32 tokensSize = 0x1004 * sizeof(Tokenlist); Tokenlist *tokens = (Tokenlist *)malloc(tokensSize); if (!stak || !tokens) { free(stak); diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index 985e12a7f6..c920ef10e2 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -98,8 +98,11 @@ static const PlainGameDescriptor s_sciGameTitles[] = { {"lsl6", "Leisure Suit Larry 6: Shape Up or Slip Out!"}, {"pepper", "Pepper's Adventure in Time"}, {"slater", "Slater & Charlie Go Camping"}, + {"gk1demo", "Gabriel Knight: Sins of the Fathers"}, + {"qfg4demo", "Quest for Glory IV: Shadows of Darkness"}, + {"pq4demo", "Police Quest IV: Open Season"}, // === SCI2 games ========================================================= - {"gk1", "Gabriel Knight: Sins of the Fathers"}, // demo is SCI11, full version SCI32 + {"gk1", "Gabriel Knight: Sins of the Fathers"}, {"pq4", "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1 {"qfg4", "Quest for Glory IV: Shadows of Darkness"}, // floppy is SCI2, CD SCI2.1 // === SCI2.1 games ======================================================== @@ -146,6 +149,7 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "fairytales", GID_FAIRYTALES }, { "freddypharkas", GID_FREDDYPHARKAS }, { "funseeker", GID_FUNSEEKER }, + { "gk1demo", GID_GK1DEMO }, { "gk1", GID_GK1 }, { "gk2", GID_GK2 }, { "hoyle1", GID_HOYLE1 }, @@ -183,12 +187,14 @@ static const GameIdStrToEnum s_gameIdStrToEnum[] = { { "pq2", GID_PQ2 }, { "pq3", GID_PQ3 }, { "pq4", GID_PQ4 }, + { "pq4demo", GID_PQ4DEMO }, { "pqswat", GID_PQSWAT }, { "qfg1", GID_QFG1 }, { "qfg1vga", GID_QFG1VGA }, { "qfg2", GID_QFG2 }, { "qfg3", GID_QFG3 }, { "qfg4", GID_QFG4 }, + { "qfg4demo", GID_QFG4DEMO }, { "rama", GID_RAMA }, { "sci-fanmade", GID_FANMADE }, // FIXME: Do we really need/want this? { "shivers", GID_SHIVERS }, @@ -356,7 +362,7 @@ Common::String convertSierraGameId(Common::String sierraId, uint32 *gameFlags, R // qfg4 demo has less than 50 scripts if (resources.size() < 50) - return "qfg4"; + return "qfg4demo"; // Otherwise it's qfg3 return "qfg3"; @@ -473,7 +479,7 @@ static char s_fallbackGameIdBuf[256]; class SciMetaEngine : public AdvancedMetaEngine { public: SciMetaEngine() : AdvancedMetaEngine(Sci::SciGameDescriptions, sizeof(ADGameDescription), s_sciGameTitles, optionsList) { - _singleid = "sci"; + _singleId = "sci"; } virtual const char *getName() const { @@ -526,8 +532,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, s_fallbackDesc.language = Common::EN_ANY; s_fallbackDesc.flags = ADGF_NO_FLAGS; s_fallbackDesc.platform = Common::kPlatformDOS; // default to PC platform - s_fallbackDesc.gameid = "sci"; - s_fallbackDesc.guioptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); + s_fallbackDesc.gameId = "sci"; + s_fallbackDesc.guiOptions = GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); if (allFiles.contains("resource.map") || allFiles.contains("Data1") || allFiles.contains("resmap.001") || allFiles.contains("resmap.001")) { @@ -610,7 +616,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, Common::String gameId = convertSierraGameId(sierraGameId, &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; + s_fallbackDesc.gameId = s_fallbackGameIdBuf; // Try to determine the game language // Load up text 0 and start looking for "#" characters @@ -653,7 +659,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, const bool isCD = (s_fallbackDesc.flags & ADGF_CD); if (!isCD) - s_fallbackDesc.guioptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); + s_fallbackDesc.guiOptions = GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI); if (gameId.hasSuffix("sci")) { s_fallbackDesc.extra = "SCI"; @@ -686,7 +692,7 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, bool SciMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { const GameIdStrToEnum *g = s_gameIdStrToEnum; for (; g->gameidStr; ++g) { - if (0 == strcmp(desc->gameid, g->gameidStr)) { + if (0 == strcmp(desc->gameId, g->gameidStr)) { *engine = new SciEngine(syst, desc, g->gameidEnum); return true; } diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index 2fd433240b..d84500cc60 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -684,21 +684,23 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Gabriel Knight - English DOS CD Demo + // Gabriel Knight - English DOS Demo // SCI interpreter version 1.001.092 - {"gk1", "CD Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like gk1demo-demo + {"gk1demo", "Demo", { {"resource.map", 0, "39645952ae0ed8072c7e838f31b75464", 2490}, {"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, - // Gabriel Knight - English DOS CD Demo (from DrMcCoy) + // Gabriel Knight - English DOS Demo (from DrMcCoy) // SCI interpreter version 1.001.092 - {"gk1", "CD Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like gk1demo-demo + {"gk1demo", "Demo", { {"resource.map", 0, "8cad2a256f41463030cbb7ea1bfb2857", 2490}, {"resource.000", 0, "eb3ed7477ca4110813fe1fcf35928561", 1718450}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO3(GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 // Gabriel Knight - English DOS Floppy @@ -941,6 +943,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English DOS (supplied by m_kiewitz) + // SCI interpreter version 0.000.668, Ver 1.000.014, 2x5.25" + {"hoyle2", "", { + {"resource.map", 0, "8cef06c93d17d96f44aacd5902d84b30", 2100}, + {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 98289}, + {"resource.002", 0, "8f2dd70abe01112eca464cda818b5eb6", 197326}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English DOS (supplied by misterhands in bug report #6598) // Game v1.000.016, interpreter 0.000.668, INT #12.5.90 {"hoyle2", "", { @@ -2967,11 +2978,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Police Quest 4 - English DOS Non-Interactive Demo (from FRG) // SCI interpreter version 1.001.096 - {"pq4", "Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like pq4demo-demo + {"pq4demo", "Demo", { {"resource.map", 0, "be56f87a1c4a13062a30a362df860c2f", 1472}, {"resource.000", 0, "527d5684016e6816157cd15d9071b11b", 1121310}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 // Police Quest 4 - English DOS CD (from the Police Quest Collection) @@ -3379,11 +3391,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { // Quest for Glory 4 - English DOS Non-Interactive Demo (from FRG) // SCI interpreter version 1.001.069 (just a guess) - {"qfg4", "Demo", { + // Note: we are not using ADGF_DEMO here, to avoid a game ID like qfg4demo-demo + {"qfg4demo", "Demo", { {"resource.map", 0, "1ba7c7ae1efb315326d45cb931569b1b", 922}, {"resource.000", 0, "41ba03f0b188b029132daa3ece0d3e14", 623154}, AD_LISTEND}, - Common::EN_ANY, Common::kPlatformDOS, ADGF_DEMO, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + Common::EN_ANY, Common::kPlatformDOS, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, #ifdef ENABLE_SCI32 // Quest for Glory 4 1.1 Floppy - English DOS (supplied by markcool in bug report #2723852) diff --git a/engines/sci/engine/kernel.cpp b/engines/sci/engine/kernel.cpp index 0df4701334..796dea2382 100644 --- a/engines/sci/engine/kernel.cpp +++ b/engines/sci/engine/kernel.cpp @@ -853,7 +853,7 @@ void Kernel::loadKernelNames(GameFeatures *features) { _kernelNames[0x26] = "Portrait"; else if (g_sci->getPlatform() == Common::kPlatformMacintosh) _kernelNames[0x84] = "ShowMovie"; - } else if (g_sci->getGameId() == GID_QFG4 && g_sci->isDemo()) { + } else if (g_sci->getGameId() == GID_QFG4DEMO) { _kernelNames[0x7b] = "RemapColors"; // QFG4 Demo has this SCI2 function instead of StrSplit } diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index 92916ecc68..62566a74b2 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -412,7 +412,7 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv); reg_t kTextColors(EngineState *s, int argc, reg_t *argv); reg_t kTextFonts(EngineState *s, int argc, reg_t *argv); reg_t kShow(EngineState *s, int argc, reg_t *argv); -reg_t kRemapColors(EngineState *s, int argc, reg_t *argv); +reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv); reg_t kDummy(EngineState *s, int argc, reg_t *argv); reg_t kEmpty(EngineState *s, int argc, reg_t *argv); reg_t kStub(EngineState *s, int argc, reg_t *argv); @@ -452,23 +452,47 @@ reg_t kScrollWindowShow(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindowDestroy(EngineState *s, int argc, reg_t *argv); reg_t kMulDiv(EngineState *s, int argc, reg_t *argv); -reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv); -reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv); + +reg_t kRemapColors(EngineState *s, int argc, reg_t *argv); +reg_t kRemapOff(EngineState *s, int argc, reg_t *argv); +reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv); +reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv); +reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv); +reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv); +reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv); reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv); reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv); -reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCopyPixels(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapClone(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapGetInfo(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapScale(EngineState *s, int argc, reg_t *argv); +reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv); reg_t kAddPlane(EngineState *s, int argc, reg_t *argv); reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv); reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv); +reg_t kMovePlaneItems(EngineState *s, int argc, reg_t *argv); reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv); reg_t kSetPalStyleRange(EngineState *s, int argc, reg_t *argv); reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv); reg_t kFrameOut(EngineState *s, int argc, reg_t *argv); +reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv); +reg_t kCelWide32(EngineState *s, int argc, reg_t *argv); reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv); // kOnMe for SCI2, kIsOnMe for SCI2.1 reg_t kInPolygon(EngineState *s, int argc, reg_t *argv); @@ -499,6 +523,8 @@ reg_t kPalVaryMergeStart(EngineState *s, int argc, reg_t *argv); // SCI2.1 Kernel Functions reg_t kMorphOn(EngineState *s, int argc, reg_t *argv); reg_t kText(EngineState *s, int argc, reg_t *argv); +reg_t kTextSize32(EngineState *s, int argc, reg_t *argv); +reg_t kTextWidth(EngineState *s, int argc, reg_t *argv); reg_t kSave(EngineState *s, int argc, reg_t *argv); reg_t kAutoSave(EngineState *s, int argc, reg_t *argv); reg_t kList(EngineState *s, int argc, reg_t *argv); @@ -516,9 +542,9 @@ reg_t kGetSierraProfileInt(EngineState *s, int argc, reg_t *argv); reg_t kCelInfo(EngineState *s, int argc, reg_t *argv); reg_t kSetLanguage(EngineState *s, int argc, reg_t *argv); reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv); +reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv); reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv); reg_t kFont(EngineState *s, int argc, reg_t *argv); -reg_t kBitmap(EngineState *s, int argc, reg_t *argv); reg_t kAddLine(EngineState *s, int argc, reg_t *argv); reg_t kUpdateLine(EngineState *s, int argc, reg_t *argv); reg_t kDeleteLine(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index fbd0b13c88..3463d05e77 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -60,15 +60,19 @@ struct SciKernelMapSubEntry { #define SCI_SUBOPENTRY_TERMINATOR { SCI_VERSION_NONE, SCI_VERSION_NONE, 0, NULL, NULL, NULL, NULL } -#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE -#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 -#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE -#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 -#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE -#define SIG_SCI21EARLY_ONLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY -#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3 -#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE -#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3 +#define SIG_SCIALL SCI_VERSION_NONE, SCI_VERSION_NONE +#define SIG_SCI0 SCI_VERSION_NONE, SCI_VERSION_01 +#define SIG_SCI1 SCI_VERSION_1_EGA_ONLY, SCI_VERSION_1_LATE +#define SIG_SCI11 SCI_VERSION_1_1, SCI_VERSION_1_1 +#define SIG_SINCE_SCI11 SCI_VERSION_1_1, SCI_VERSION_NONE +#define SIG_SCI2 SCI_VERSION_2, SCI_VERSION_2 +#define SIG_SCI21EARLY SCI_VERSION_2_1_EARLY, SCI_VERSION_2_1_EARLY +#define SIG_UNTIL_SCI21EARLY SCI_VERSION_2, SCI_VERSION_2_1_EARLY +#define SIG_UNTIL_SCI21MID SCI_VERSION_2, SCI_VERSION_2_1_MIDDLE +#define SIG_SINCE_SCI21 SCI_VERSION_2_1_EARLY, SCI_VERSION_3 +#define SIG_SINCE_SCI21MID SCI_VERSION_2_1_MIDDLE, SCI_VERSION_3 +#define SIG_SINCE_SCI21LATE SCI_VERSION_2_1_LATE, SCI_VERSION_3 +#define SIG_SCI3 SCI_VERSION_3, SCI_VERSION_3 #define SIG_SCI16 SCI_VERSION_NONE, SCI_VERSION_1_1 #define SIG_SCI32 SCI_VERSION_2, SCI_VERSION_NONE @@ -211,7 +215,7 @@ static const SciKernelMapSubEntry kPalVary_subops[] = { { SIG_SCI16, 6, MAP_CALL(PalVaryPauseResume), "i", NULL }, #ifdef ENABLE_SCI32 { SIG_SCI32, 0, MAP_CALL(PalVarySetVary), "i(i)(i)(ii)", NULL }, - { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", NULL }, + { SIG_SCI32, 1, MAP_CALL(PalVarySetPercent), "(i)(i)", kPalVarySetPercent_workarounds }, { SIG_SCI32, 2, MAP_CALL(PalVaryGetPercent), "", NULL }, { SIG_SCI32, 3, MAP_CALL(PalVaryOff), "", NULL }, { SIG_SCI32, 4, MAP_CALL(PalVaryMergeTarget), "i", NULL }, @@ -284,6 +288,42 @@ static const SciKernelMapSubEntry kSave_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kFont_subops[] = { + { SIG_SINCE_SCI21MID, 0, MAP_CALL(SetFontHeight), "i", NULL }, + { SIG_SINCE_SCI21MID, 1, MAP_CALL(SetFontRes), "ii", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kText_subops[] = { + { SIG_SINCE_SCI21MID, 0, MAP_CALL(TextSize32), "r[r0]i(i)(i)", NULL }, + { SIG_SINCE_SCI21MID, 1, MAP_CALL(TextWidth), "ri", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kBitmap_subops[] = { + { SIG_SINCE_SCI21, 0, MAP_CALL(BitmapCreate), "iiii(i)(i)(i)", NULL }, + { SIG_SINCE_SCI21, 1, MAP_CALL(BitmapDestroy), "r", NULL }, + { SIG_SINCE_SCI21, 2, MAP_CALL(BitmapDrawLine), "riiiii(i)(i)", NULL }, + { SIG_SINCE_SCI21, 3, MAP_CALL(BitmapDrawView), "riii(i)(i)(0)(i)(i)", NULL }, + { SIG_SINCE_SCI21, 4, MAP_CALL(BitmapDrawText), "rriiiiiiiiiii", NULL }, + { SIG_SINCE_SCI21, 5, MAP_CALL(BitmapDrawColor), "riiiii", NULL }, + { SIG_SINCE_SCI21, 6, MAP_CALL(BitmapDrawBitmap), "rr(i)(i)(i)", NULL }, + { SIG_SINCE_SCI21, 7, MAP_CALL(BitmapInvert), "riiiiii", NULL }, + { SIG_SINCE_SCI21MID, 8, MAP_CALL(BitmapSetDisplace), "rii", NULL }, + { SIG_SINCE_SCI21MID, 9, MAP_CALL(BitmapCreateFromView), "iii(i)(i)(i)([r0])", NULL }, + { SIG_SINCE_SCI21MID, 10, MAP_CALL(BitmapCopyPixels), "rr", NULL }, + { SIG_SINCE_SCI21MID, 11, MAP_CALL(BitmapClone), "r", NULL }, + { SIG_SINCE_SCI21LATE, 12, MAP_CALL(BitmapGetInfo), "r(i)(i)", NULL }, + { SIG_SINCE_SCI21LATE, 13, MAP_CALL(BitmapScale), "r...ii", NULL }, + { SIG_SCI3, 14, MAP_CALL(BitmapCreateFromUnknown), "......", NULL }, + { SIG_SCI3, 15, MAP_EMPTY(Bitmap), "(.*)", NULL }, + { SIG_SCI3, 16, MAP_EMPTY(Bitmap), "(.*)", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kList_subops[] = { { SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL }, { SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", NULL }, @@ -312,6 +352,17 @@ static const SciKernelMapSubEntry kList_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kRemapColors_subops[] = { + { SIG_SCI32, 0, MAP_CALL(RemapOff), "(i)", NULL }, + { SIG_SCI32, 1, MAP_CALL(RemapByRange), "iiii(i)", NULL }, + { SIG_SCI32, 2, MAP_CALL(RemapByPercent), "ii(i)", NULL }, + { SIG_SCI32, 3, MAP_CALL(RemapToGray), "ii(i)", NULL }, + { SIG_SCI32, 4, MAP_CALL(RemapToPercentGray), "iii(i)", NULL }, + { SIG_SCI32, 5, MAP_CALL(RemapSetNoMatchRange), "ii", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kString_subops[] = { { SIG_SCI32, 0, MAP_CALL(StringNew), "i(i)", NULL }, { SIG_SCI32, 1, MAP_CALL(StringSize), "[or]", NULL }, @@ -406,12 +457,16 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL }, { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, + { MAP_CALL(CantBeHere), SIG_SCI16, SIGFOR_ALL, "o(l)", NULL, NULL }, #ifdef ENABLE_SCI32 - { "CantBeHere", kCantBeHere32, SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL }, + { MAP_CALL(CantBeHere), SIG_SCI32, SIGFOR_ALL, "ol", NULL, NULL }, +#endif + { MAP_CALL(CelHigh), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelHigh_workarounds }, + { MAP_CALL(CelWide), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelWide_workarounds }, +#ifdef ENABLE_SCI32 + { "CelHigh", kCelHigh32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL }, + { "CelWide", kCelWide32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL }, #endif - { MAP_CALL(CantBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, - { MAP_CALL(CelHigh), SIG_EVERYWHERE, "ii(i)", NULL, kCelHigh_workarounds }, - { MAP_CALL(CelWide), SIG_EVERYWHERE, "ii(i)", NULL, kCelWide_workarounds }, { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL }, { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL }, { MAP_CALL(CheckFreeSpace), SIG_EVERYWHERE, "r", NULL, NULL }, @@ -510,9 +565,9 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(Random), SIG_EVERYWHERE, "i(i)(i)", NULL, NULL }, { MAP_CALL(ReadNumber), SIG_EVERYWHERE, "r", NULL, kReadNumber_workarounds }, - { MAP_CALL(RemapColors), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, + { "RemapColors", kRemapColors16, SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, NULL }, #ifdef ENABLE_SCI32 - { "RemapColors", kRemapColors32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", NULL, NULL }, + { MAP_CALL(RemapColors), SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)(i)(i)", kRemapColors_subops, NULL }, #endif { MAP_CALL(ResCheck), SIG_EVERYWHERE, "ii(iiii)", NULL, NULL }, { MAP_CALL(RespondsTo), SIG_EVERYWHERE, ".i", NULL, NULL }, @@ -528,7 +583,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL }, { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL }, { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, - { MAP_CALL(SetNowSeen), SIG_EVERYWHERE, "o(i)", NULL, NULL }, + { MAP_CALL(SetNowSeen), SIG_SCI16, SIGFOR_ALL, "o(i)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { MAP_CALL(SetNowSeen), SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL }, +#endif { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds }, { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL }, @@ -546,10 +604,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(StrEnd), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(StrLen), SIG_EVERYWHERE, "[r0]", NULL, kStrLen_workarounds }, { MAP_CALL(StrSplit), SIG_EVERYWHERE, "rr[r0]", NULL, NULL }, - { MAP_CALL(TextColors), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(TextFonts), SIG_EVERYWHERE, "(i*)", NULL, NULL }, - { MAP_CALL(TextSize), SIG_SCIALL, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL }, - { MAP_CALL(TextSize), SIG_EVERYWHERE, "r[r0]i(i)(r0)", NULL, NULL }, + { MAP_CALL(TextColors), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL }, + { MAP_CALL(TextFonts), SIG_SCI16, SIGFOR_ALL, "(i*)", NULL, NULL }, + { MAP_CALL(TextSize), SIG_SCI16, SIGFOR_MAC, "r[r0]i(i)(r0)(i)", NULL, NULL }, + { MAP_CALL(TextSize), SIG_SCI16, SIGFOR_ALL, "r[r0]i(i)(r0)", NULL, NULL }, { MAP_CALL(TimesCos), SIG_EVERYWHERE, "ii", NULL, NULL }, { "CosMult", kTimesCos, SIG_EVERYWHERE, "ii", NULL, NULL }, { MAP_CALL(TimesCot), SIG_EVERYWHERE, "ii", NULL, NULL }, @@ -581,13 +639,17 @@ static SciKernelMapEntry s_kernelMap[] = { #ifdef ENABLE_SCI32 // SCI2 Kernel Functions // TODO: whoever knows his way through those calls, fix the signatures. + { "TextSize", kTextSize32, SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "r[r0]i(i)", NULL, NULL }, + { MAP_DUMMY(TextColors), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL }, + { MAP_DUMMY(TextFonts), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL }, + { MAP_CALL(AddPlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(AddScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(Array), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(CreateTextBitmap), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, { MAP_CALL(DeletePlane), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(DeleteScreenItem), SIG_EVERYWHERE, "o", NULL, NULL }, - { MAP_CALL(DisposeTextBitmap), SIG_EVERYWHERE, "r", NULL, NULL }, + { "DisposeTextBitmap", kBitmapDestroy, SIG_SCI2, SIGFOR_ALL, "r", NULL, NULL }, { MAP_CALL(FrameOut), SIG_EVERYWHERE, "(i)", NULL, NULL }, { MAP_CALL(GetHighPlanePri), SIG_EVERYWHERE, "", NULL, NULL }, { MAP_CALL(InPolygon), SIG_EVERYWHERE, "iio", NULL, NULL }, @@ -649,9 +711,9 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_DUMMY(MarkMemory), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(GetHighItemPri), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_DUMMY(ShowStylePercent), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_DUMMY(InvertRect), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_DUMMY(InvertRect), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "(.*)", NULL, NULL }, { MAP_DUMMY(InputText), SIG_EVERYWHERE, "(.*)", NULL, NULL }, - { MAP_DUMMY(TextWidth), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(TextWidth), SIG_UNTIL_SCI21EARLY, SIGFOR_ALL, "ri", NULL, NULL }, { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // SCI2.1 Kernel Functions @@ -662,18 +724,18 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL }, - { MAP_CALL(Text), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL }, { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii", NULL, NULL }, { MAP_CALL(GetWindowsOption), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(WinHelp), SIG_EVERYWHERE, "(.*)", NULL, NULL }, { MAP_CALL(GetConfig), SIG_EVERYWHERE, "ro", NULL, NULL }, { MAP_CALL(GetSierraProfileInt), SIG_EVERYWHERE, "rri", NULL, NULL }, - { MAP_CALL(CelInfo), SIG_EVERYWHERE, "iiiiii", NULL, NULL }, - { MAP_CALL(SetLanguage), SIG_EVERYWHERE, "r", NULL, NULL }, + { MAP_CALL(CelInfo), SIG_SINCE_SCI21MID, SIGFOR_ALL, "iiiiii", NULL, NULL }, + { MAP_CALL(SetLanguage), SIG_SINCE_SCI21MID, SIGFOR_ALL, "r", NULL, NULL }, { MAP_CALL(ScrollWindow), SIG_EVERYWHERE, "i(.*)", kScrollWindow_subops, NULL }, - { MAP_CALL(SetFontRes), SIG_SCI21EARLY_ONLY, SIGFOR_ALL, "ii", NULL, NULL }, - { MAP_CALL(Font), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, - { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(SetFontRes), SIG_SCI21EARLY, SIGFOR_ALL, "ii", NULL, NULL }, + { MAP_CALL(Font), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kFont_subops, NULL }, + { MAP_CALL(Bitmap), SIG_EVERYWHERE, "(.*)", kBitmap_subops, NULL }, { MAP_CALL(AddLine), SIG_EVERYWHERE, "oiiiiiiiii", NULL, NULL }, { MAP_CALL(UpdateLine), SIG_EVERYWHERE, "[r0]oiiiiiiiii", NULL, NULL }, { MAP_CALL(DeleteLine), SIG_EVERYWHERE, "[r0]o", NULL, NULL }, @@ -725,9 +787,9 @@ static SciKernelMapEntry s_kernelMap[] = { // <lskovlun> The idea, if I understand correctly, is that the engine generates events // of a special HotRect type continuously when the mouse is on that rectangle - // MovePlaneItems - used by SQ6 to scroll through the inventory via the up/down buttons - // SetPalStyleRange - 2 integer parameters, start and end. All styles from start-end - // (inclusive) are set to 0 + // Used by SQ6 to scroll through the inventory via the up/down buttons + { MAP_CALL(MovePlaneItems), SIG_SINCE_SCI21, SIGFOR_ALL, "oii(i)", NULL, NULL }, + { MAP_CALL(SetPalStyleRange), SIG_EVERYWHERE, "ii", NULL, NULL }, { MAP_CALL(MorphOn), SIG_EVERYWHERE, "", NULL, NULL }, @@ -1030,7 +1092,6 @@ static const char *const sci2_default_knames[] = { /*0x89*/ "TextWidth", // for debugging(?), only in SCI2, not used in any SCI2 game /*0x8a*/ "PointSize", // for debugging(?), only in SCI2, not used in any SCI2 game - // GK2 Demo (and similar) only kernel functions /*0x8b*/ "AddLine", /*0x8c*/ "DeleteLine", /*0x8d*/ "UpdateLine", diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index bb595e9960..534d9ce713 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -83,11 +83,12 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { } // For a real event we use its associated mouse position - mousePos = curEvent.mousePos; #ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1_EARLY) - g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); + if (getSciVersion() >= SCI_VERSION_2) + mousePos = curEvent.mousePosSci; + else #endif + mousePos = curEvent.mousePos; // Limit the mouse cursor position, if necessary g_sci->_gfxCursor->refreshPosition(); @@ -101,7 +102,25 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { // question. Check GfxCursor::setPosition(), for a more detailed // explanation and a list of cursor position workarounds. if (s->_cursorWorkaroundRect.contains(mousePos.x, mousePos.y)) { - s->_cursorWorkaroundActive = false; + // For OpenPandora and possibly other platforms, that support analog-stick control + touch screen + // control at the same time: in case the cursor is currently at the coordinate set by the scripts, + // we will count down instead of immediately disabling the workaround. + // On OpenPandora the cursor position is set, but it's overwritten shortly afterwards by the + // touch screen. In this case we would sometimes disable the workaround, simply because the touch + // screen hasn't yet overwritten the position and thus the workaround would not work anymore. + // On OpenPandora it would sometimes work and sometimes not without this. + if (s->_cursorWorkaroundPoint == mousePos) { + // Cursor is still at the same spot as set by the scripts + if (s->_cursorWorkaroundPosCount > 0) { + s->_cursorWorkaroundPosCount--; + } else { + // Was for quite a bit of time at that spot, so disable workaround now + s->_cursorWorkaroundActive = false; + } + } else { + // Cursor has moved, but is within the rect -> disable workaround immediately + s->_cursorWorkaroundActive = false; + } } else { mousePos.x = s->_cursorWorkaroundPoint.x; mousePos.y = s->_cursorWorkaroundPoint.y; diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index 91d241fb79..73236b98ed 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -45,6 +45,7 @@ #include "sci/graphics/paint16.h" #include "sci/graphics/picture.h" #include "sci/graphics/ports.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/view.h" @@ -359,12 +360,7 @@ reg_t kTextSize(EngineState *s, int argc, reg_t *argv) { uint16 languageSplitter = 0; Common::String splitText = g_sci->strSplitLanguage(text.c_str(), &languageSplitter, sep); -#ifdef ENABLE_SCI32 - if (g_sci->_gfxText32) - g_sci->_gfxText32->kernelTextSize(splitText.c_str(), font_nr, maxwidth, &textWidth, &textHeight); - else -#endif - g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight); + g_sci->_gfxText16->kernelTextSize(splitText.c_str(), languageSplitter, font_nr, maxwidth, &textWidth, &textHeight); // One of the game texts in LB2 German contains loads of spaces in // its end. We trim the text here, otherwise the graphics code will @@ -445,8 +441,15 @@ reg_t kCantBeHere(EngineState *s, int argc, reg_t *argv) { reg_t curObject = argv[0]; reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; - reg_t canBeHere = g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); - return canBeHere; +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + return g_sci->_gfxCompare->kernelCantBeHere32(curObject, listReference); + } else { +#endif + return g_sci->_gfxCompare->kernelCanBeHere(curObject, listReference); +#ifdef ENABLE_SCI32 + } +#endif } reg_t kIsItSkip(EngineState *s, int argc, reg_t *argv) { @@ -576,9 +579,17 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { } reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxCompare->kernelSetNowSeen(argv[0]); - - return s->r_acc; +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]); + return NULL_REG; + } else { +#endif + g_sci->_gfxCompare->kernelSetNowSeen(argv[0]); + return s->r_acc; +#ifdef ENABLE_SCI32 + } +#endif } reg_t kPalette(EngineState *s, int argc, reg_t *argv) { @@ -1247,22 +1258,22 @@ reg_t kShow(EngineState *s, int argc, reg_t *argv) { } // Early variant of the SCI32 kRemapColors kernel function, used in the demo of QFG4 -reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { +reg_t kRemapColors16(EngineState *s, int argc, reg_t *argv) { uint16 operation = argv[0].toUint16(); switch (operation) { case 0: { // remap by percent uint16 percent = argv[1].toUint16(); - g_sci->_gfxPalette16->resetRemapping(); - g_sci->_gfxPalette16->setRemappingPercent(254, percent); + g_sci->_gfxRemap16->resetRemapping(); + g_sci->_gfxRemap16->setRemappingPercent(254, percent); } break; case 1: { // remap by range uint16 from = argv[1].toUint16(); uint16 to = argv[2].toUint16(); uint16 base = argv[3].toUint16(); - g_sci->_gfxPalette16->resetRemapping(); - g_sci->_gfxPalette16->setRemappingRange(254, from, to, base); + g_sci->_gfxRemap16->resetRemapping(); + g_sci->_gfxRemap16->setRemappingRange(254, from, to, base); } break; case 2: // turn remapping off (unused) diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index 4d48ae4e99..30cae5d088 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -45,15 +45,17 @@ #include "sci/graphics/paint16.h" #include "sci/graphics/picture.h" #include "sci/graphics/ports.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/view.h" #ifdef ENABLE_SCI32 -#include "sci/graphics/palette32.h" +#include "sci/graphics/celobj32.h" #include "sci/graphics/controls32.h" #include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class -#include "sci/graphics/text32.h" #include "sci/graphics/frameout.h" +#include "sci/graphics/palette32.h" +#include "sci/graphics/text32.h" #endif namespace Sci { @@ -62,59 +64,59 @@ namespace Sci { extern void showScummVMDialog(const Common::String &message); reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) { - // Returns 0 if the screen width or height is less than 640 or 400, - // respectively. - if (g_system->getWidth() < 640 || g_system->getHeight() < 400) + const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer(); + if (buffer.screenWidth < 640 || buffer.screenHeight < 400) return make_reg(0, 0); return make_reg(0, 1); } -// SCI32 variant, can't work like sci16 variants -reg_t kCantBeHere32(EngineState *s, int argc, reg_t *argv) { - // TODO -// reg_t curObject = argv[0]; -// reg_t listReference = (argc > 1) ? argv[1] : NULL_REG; - - return NULL_REG; -} - reg_t kAddScreenItem(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kAddScreenItem %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kAddScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelAddScreenItem(argv[0]); return NULL_REG; } reg_t kUpdateScreenItem(EngineState *s, int argc, reg_t *argv) { - debugC(7, kDebugLevelGraphics, "kUpdateScreenItem %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(7, kDebugLevelGraphics, "kUpdateScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelUpdateScreenItem(argv[0]); return NULL_REG; } reg_t kDeleteScreenItem(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kDeleteScreenItem %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kDeleteScreenItem %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelDeleteScreenItem(argv[0]); return NULL_REG; } reg_t kAddPlane(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kAddPlane %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kAddPlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelAddPlane(argv[0]); return s->r_acc; } reg_t kUpdatePlane(EngineState *s, int argc, reg_t *argv) { - debugC(7, kDebugLevelGraphics, "kUpdatePlane %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(7, kDebugLevelGraphics, "kUpdatePlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelUpdatePlane(argv[0]); return s->r_acc; } reg_t kDeletePlane(EngineState *s, int argc, reg_t *argv) { - debugC(6, kDebugLevelGraphics, "kDeletePlane %x:%x (%s)", argv[0].getSegment(), argv[0].getOffset(), g_sci->getEngineState()->_segMan->getObjectName(argv[0])); + debugC(6, kDebugLevelGraphics, "kDeletePlane %x:%x (%s)", PRINT_REG(argv[0]), s->_segMan->getObjectName(argv[0])); g_sci->_gfxFrameout->kernelDeletePlane(argv[0]); return s->r_acc; } +reg_t kMovePlaneItems(EngineState *s, int argc, reg_t *argv) { + const reg_t plane = argv[0]; + const int16 deltaX = argv[1].toSint16(); + const int16 deltaY = argv[2].toSint16(); + const bool scrollPics = argc > 3 ? argv[3].toUint16() : false; + + g_sci->_gfxFrameout->kernelMovePlaneItems(plane, deltaX, deltaY, scrollPics); + return NULL_REG; +} + reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv) { reg_t planeObj = argv[0]; GuiResourceId pictureId = argv[1].toUint16(); @@ -132,7 +134,7 @@ reg_t kGetHighPlanePri(EngineState *s, int argc, reg_t *argv) { reg_t kFrameOut(EngineState *s, int argc, reg_t *argv) { bool showBits = argc > 0 ? argv[0].toUint16() : true; - g_sci->_gfxFrameout->kernelFrameout(showBits); + g_sci->_gfxFrameout->kernelFrameOut(showBits); s->speedThrottler(16); s->_throttleTrigger = true; return NULL_REG; @@ -149,14 +151,13 @@ reg_t kObjectIntersect(EngineState *s, int argc, reg_t *argv) { return make_reg(0, objRect1.intersects(objRect2)); } -// Tests if the coordinate is on the passed object reg_t kIsOnMe(EngineState *s, int argc, reg_t *argv) { - uint16 x = argv[0].toUint16(); - uint16 y = argv[1].toUint16(); - reg_t targetObject = argv[2]; - uint16 checkPixels = argv[3].getOffset(); + int16 x = argv[0].toSint16(); + int16 y = argv[1].toSint16(); + reg_t object = argv[2]; + bool checkPixel = argv[3].toSint16(); - return make_reg(0, g_sci->_gfxFrameout->kernelIsOnMe(x, y, checkPixels, targetObject)); + return g_sci->_gfxFrameout->kernelIsOnMe(object, Common::Point(x, y), checkPixel); } reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { @@ -196,22 +197,44 @@ reg_t kCreateTextBitmap(EngineState *s, int argc, reg_t *argv) { if (subop == 0) { TextAlign alignment = (TextAlign)readSelectorValue(segMan, object, SELECTOR(mode)); - reg_t out; - return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, 1, &out); + return g_sci->_gfxText32->createFontBitmap(width, height, rect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, true); } else { CelInfo32 celInfo; celInfo.type = kCelTypeView; celInfo.resourceId = readSelectorValue(segMan, object, SELECTOR(view)); celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop)); celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel)); - reg_t out; - return g_sci->_gfxText32->createTitledFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed, &out); + return g_sci->_gfxText32->createFontBitmap(celInfo, rect, text, foreColor, backColor, fontId, skipColor, borderColor, dimmed); } } -reg_t kDisposeTextBitmap(EngineState *s, int argc, reg_t *argv) { - g_sci->_gfxText32->disposeTextBitmap(argv[0]); - return s->r_acc; +reg_t kText(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kTextSize32(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText32->setFont(argv[2].toUint16()); + + reg_t *rect = s->_segMan->derefRegPtr(argv[0], 4); + + Common::String text = s->_segMan->getString(argv[1]); + int16 maxWidth = argc > 3 ? argv[3].toSint16() : 0; + bool doScaling = argc > 4 ? argv[4].toSint16() : true; + + Common::Rect textRect = g_sci->_gfxText32->getTextSize(text, maxWidth, doScaling); + rect[0] = make_reg(0, textRect.left); + rect[1] = make_reg(0, textRect.top); + rect[2] = make_reg(0, textRect.right - 1); + rect[3] = make_reg(0, textRect.bottom - 1); + return NULL_REG; +} + +reg_t kTextWidth(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxText32->setFont(argv[1].toUint16()); + Common::String text = s->_segMan->getString(argv[0]); + return make_reg(0, g_sci->_gfxText32->getStringWidth(text)); } reg_t kWinHelp(EngineState *s, int argc, reg_t *argv) { @@ -287,28 +310,47 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } +reg_t kCelHigh32(EngineState *s, int argc, reg_t *argv) { + GuiResourceId resourceId = argv[0].toUint16(); + int16 loopNo = argv[1].toSint16(); + int16 celNo = argv[2].toSint16(); + CelObjView celObj(resourceId, loopNo, celNo); + return make_reg(0, mulru(celObj._height, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight, celObj._scaledHeight))); +} + +reg_t kCelWide32(EngineState *s, int argc, reg_t *argv) { + GuiResourceId resourceId = argv[0].toUint16(); + int16 loopNo = argv[1].toSint16(); + int16 celNo = argv[2].toSint16(); + CelObjView celObj(resourceId, loopNo, celNo); + return make_reg(0, mulru(celObj._width, Ratio(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth, celObj._scaledWidth))); +} + reg_t kCelInfo(EngineState *s, int argc, reg_t *argv) { // Used by Shivers 1, room 23601 to determine what blocks on the red door puzzle board // are occupied by pieces already - switch (argv[0].toUint16()) { // subops 0 - 4 - // 0 - return the view - // 1 - return the loop - // 2, 3 - nop - case 4: { - GuiResourceId viewId = argv[1].toSint16(); - int16 loopNo = argv[2].toSint16(); - int16 celNo = argv[3].toSint16(); - int16 x = argv[4].toUint16(); - int16 y = argv[5].toUint16(); - byte color = g_sci->_gfxCache->kernelViewGetColorAtCoordinate(viewId, loopNo, celNo, x, y); - return make_reg(0, color); - } - default: { - kStub(s, argc, argv); - return s->r_acc; - } + CelObjView view(argv[1].toUint16(), argv[2].toSint16(), argv[3].toSint16()); + + int16 result = 0; + + switch (argv[0].toUint16()) { + case 0: + result = view._displace.x; + break; + case 1: + result = view._displace.y; + break; + case 2: + case 3: + // null operation + break; + case 4: + result = view.readPixel(argv[4].toSint16(), argv[5].toSint16(), view._mirrorX); + break; } + + return make_reg(0, result); } reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { @@ -454,18 +496,18 @@ reg_t kScrollWindow(EngineState *s, int argc, reg_t *argv) { #endif reg_t kFont(EngineState *s, int argc, reg_t *argv) { - // TODO: Handle font settings for SCI2.1 - - switch (argv[0].toUint16()) { - case 1: - g_sci->_gfxText32->_scaledWidth = argv[1].toUint16(); - g_sci->_gfxText32->_scaledHeight = argv[2].toUint16(); - return NULL_REG; - default: - error("kFont: unknown subop %d", argv[0].toUint16()); - } + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} - return s->r_acc; +reg_t kSetFontHeight(EngineState *s, int argc, reg_t *argv) { + // TODO: Setting font may have just been for side effect + // of setting the fontHeight on the font manager, in + // which case we could just get the font directly ourselves. + g_sci->_gfxText32->setFont(argv[0].toUint16()); + g_sci->_gfxText32->_scaledHeight = (g_sci->_gfxText32->_font->getHeight() * g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight + g_sci->_gfxText32->_scaledHeight - 1) / g_sci->_gfxText32->_scaledHeight; + return NULL_REG; } reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) { @@ -474,206 +516,207 @@ reg_t kSetFontRes(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } -// TODO: Eventually, all of the kBitmap operations should be put -// in a separate class +reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} -// NOTE: This size is correct only for SCI2.1mid; the size for -// SCI2/2.1early is 36 -#define BITMAP_HEADER_SIZE 46 +reg_t kBitmapCreate(EngineState *s, int argc, reg_t *argv) { + int16 width = argv[0].toSint16(); + int16 height = argv[1].toSint16(); + int16 skipColor = argv[2].toSint16(); + int16 backColor = argv[3].toSint16(); + int16 scaledWidth = argc > 4 ? argv[4].toSint16() : g_sci->_gfxText32->_scaledWidth; + int16 scaledHeight = argc > 5 ? argv[5].toSint16() : g_sci->_gfxText32->_scaledHeight; + bool useRemap = argc > 6 ? argv[6].toSint16() : false; -reg_t kBitmap(EngineState *s, int argc, reg_t *argv) { - // Used for bitmap operations in SCI2.1 and SCI3. - // This is the SCI2.1 version, the functionality seems to have changed in SCI3. + BitmapResource bitmap(s->_segMan, width, height, skipColor, 0, 0, scaledWidth, scaledHeight, 0, useRemap); + memset(bitmap.getPixels(), backColor, width * height); + return bitmap.getObject(); +} - switch (argv[0].toUint16()) { - case 0: // init bitmap surface - { - // 6 params, called e.g. from TextView::init() in Torin's Passage, - // script 64890 and TransView::init() in script 64884 - uint16 width = argv[1].toUint16(); - uint16 height = argv[2].toUint16(); - //uint16 skip = argv[3].toUint16(); - uint16 back = argv[4].toUint16(); // usually equals skip - //uint16 width2 = (argc >= 6) ? argv[5].toUint16() : 0; - //uint16 height2 = (argc >= 7) ? argv[6].toUint16() : 0; - //uint16 transparentFlag = (argc >= 8) ? argv[7].toUint16() : 0; - - // TODO: skip, width2, height2, transparentFlag - // (used for transparent bitmaps) - int entrySize = width * height + BITMAP_HEADER_SIZE; - reg_t memoryId = s->_segMan->allocateHunkEntry("Bitmap()", entrySize); - byte *memoryPtr = s->_segMan->getHunkPointer(memoryId); - memset(memoryPtr, 0, BITMAP_HEADER_SIZE); // zero out the bitmap header - memset(memoryPtr + BITMAP_HEADER_SIZE, back, width * height); - // Save totalWidth, totalHeight - // TODO: Save the whole bitmap header, like SSCI does - WRITE_SCI11ENDIAN_UINT16(memoryPtr, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 2, height); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 4, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 6, 0); - memoryPtr[8] = 0; - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 10, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 20, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(memoryPtr + 28, 46); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 36, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 38, width); - return memoryId; - } - break; - case 1: // dispose text bitmap surface - return kDisposeTextBitmap(s, argc - 1, argv + 1); - case 2: // dispose bitmap surface, with extra param - // 2 params, called e.g. from MenuItem::dispose in Torin's Passage, - // script 64893 - warning("kBitmap(2), unk1 %d, bitmap ptr %04x:%04x", argv[1].toUint16(), PRINT_REG(argv[2])); - break; - case 3: // tiled surface - { - // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage, - // script 64869 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - // The tiled view seems to always have 2 loops. - // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. - uint16 viewNum = argv[2].toUint16(); // vTiles selector - uint16 loop = argv[3].toUint16(); - uint16 cel = argv[4].toUint16(); - uint16 x = argv[5].toUint16(); - uint16 y = argv[6].toUint16(); - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - GfxView *view = g_sci->_gfxCache->getView(viewNum); - uint16 tileWidth = view->getWidth(loop, cel); - uint16 tileHeight = view->getHeight(loop, cel); - const byte *tileBitmap = view->getBitmap(loop, cel); - uint16 width = MIN<uint16>(totalWidth - x, tileWidth); - uint16 height = MIN<uint16>(totalHeight - y, tileHeight); - - for (uint16 curY = 0; curY < height; curY++) { - for (uint16 curX = 0; curX < width; curX++) { - bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX]; - } - } +reg_t kBitmapDestroy(EngineState *s, int argc, reg_t *argv) { + s->_segMan->freeHunkEntry(argv[0]); + return NULL_REG; +} - } - break; - case 4: // add text to bitmap - { - // 13 params, called e.g. from TextButton::createBitmap() in Torin's Passage, - // script 64894 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - Common::String text = s->_segMan->getString(argv[2]); - uint16 textX = argv[3].toUint16(); - uint16 textY = argv[4].toUint16(); - //reg_t unk5 = argv[5]; - //reg_t unk6 = argv[6]; - //reg_t unk7 = argv[7]; // skip? - //reg_t unk8 = argv[8]; // back? - //reg_t unk9 = argv[9]; - uint16 fontId = argv[10].toUint16(); - //uint16 mode = argv[11].toUint16(); - uint16 dimmed = argv[12].toUint16(); - //warning("kBitmap(4): bitmap ptr %04x:%04x, font %d, mode %d, dimmed %d - text: \"%s\"", - // PRINT_REG(bitmapPtr), font, mode, dimmed, text.c_str()); - uint16 foreColor = 255; // TODO - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - GfxFont *font = g_sci->_gfxCache->getFont(fontId); - - int16 charCount = 0; - uint16 curX = textX, curY = textY; - const char *txt = text.c_str(); - - while (*txt) { - charCount = g_sci->_gfxText32->GetLongest(txt, totalWidth, font); - if (charCount == 0) - break; - - for (int i = 0; i < charCount; i++) { - unsigned char curChar = txt[i]; - font->drawToBuffer(curChar, curY, curX, foreColor, dimmed, bitmap, totalWidth, totalHeight); - curX += font->getCharWidth(curChar); - } - - curX = textX; - curY += font->getHeight(); - txt += charCount; - while (*txt == ' ') - txt++; // skip over breaking spaces - } +reg_t kBitmapDrawLine(EngineState *s, int argc, reg_t *argv) { + // bitmapMemId, (x1, y1, x2, y2) OR (x2, y2, x1, y1), line color, unknown int, unknown int + return kStubNull(s, argc + 1, argv - 1); +} - } - break; - case 5: // fill with color - { - // 6 params, called e.g. from TextView::init() and TextView::draw() - // in Torin's Passage, script 64890 - reg_t hunkId = argv[1]; // obtained from kBitmap(0) - uint16 x = argv[2].toUint16(); - uint16 y = argv[3].toUint16(); - uint16 fillWidth = argv[4].toUint16(); // width - 1 - uint16 fillHeight = argv[5].toUint16(); // height - 1 - uint16 back = argv[6].toUint16(); - - byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); - // Get totalWidth, totalHeight - uint16 totalWidth = READ_LE_UINT16(memoryPtr); - uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); - uint16 width = MIN<uint16>(totalWidth - x, fillWidth); - uint16 height = MIN<uint16>(totalHeight - y, fillHeight); - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - - for (uint16 curY = 0; curY < height; curY++) { - for (uint16 curX = 0; curX < width; curX++) { - bitmap[(curY + y) * totalWidth + (curX + x)] = back; - } - } +reg_t kBitmapDrawView(EngineState *s, int argc, reg_t *argv) { + // viewId, loopNo, celNo, displace x, displace y, unused, view x, view y + + // called e.g. from TiledBitmap::resize() in Torin's Passage, script 64869 + // The tiled view seems to always have 2 loops. + // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. + return kStubNull(s, argc + 1, argv - 1); + +#if 0 + // tiled surface + // 6 params, called e.g. from TiledBitmap::resize() in Torin's Passage, + // script 64869 + reg_t hunkId = argv[1]; // obtained from kBitmap(0) + // The tiled view seems to always have 2 loops. + // These loops need to have 1 cel in loop 0 and 8 cels in loop 1. + uint16 viewNum = argv[2].toUint16(); // vTiles selector + uint16 loop = argv[3].toUint16(); + uint16 cel = argv[4].toUint16(); + uint16 x = argv[5].toUint16(); + uint16 y = argv[6].toUint16(); + + byte *memoryPtr = s->_segMan->getHunkPointer(hunkId); + // Get totalWidth, totalHeight + uint16 totalWidth = READ_LE_UINT16(memoryPtr); + uint16 totalHeight = READ_LE_UINT16(memoryPtr + 2); + byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; + + GfxView *view = g_sci->_gfxCache->getView(viewNum); + uint16 tileWidth = view->getWidth(loop, cel); + uint16 tileHeight = view->getHeight(loop, cel); + const byte *tileBitmap = view->getBitmap(loop, cel); + uint16 width = MIN<uint16>(totalWidth - x, tileWidth); + uint16 height = MIN<uint16>(totalHeight - y, tileHeight); + + for (uint16 curY = 0; curY < height; curY++) { + for (uint16 curX = 0; curX < width; curX++) { + bitmap[(curY + y) * totalWidth + (curX + x)] = tileBitmap[curY * tileWidth + curX]; } - break; - default: - kStub(s, argc, argv); - break; } +#endif +} - return s->r_acc; +reg_t kBitmapDrawText(EngineState *s, int argc, reg_t *argv) { + // called e.g. from TextButton::createBitmap() in Torin's Passage, script 64894 + + // bitmap, text, textLeft, textTop, textRight, textBottom, foreColor, backColor, skipColor, fontNo, alignment, borderColor, dimmed + BitmapResource bitmap(argv[0]); + Common::String text = s->_segMan->getString(argv[1]); + Common::Rect textRect( + argv[2].toSint16(), + argv[3].toSint16(), + argv[4].toSint16() + 1, + argv[5].toSint16() + 1 + ); + int16 foreColor = argv[6].toSint16(); + int16 backColor = argv[7].toSint16(); + int16 skipColor = argv[8].toSint16(); + GuiResourceId fontId = (GuiResourceId)argv[9].toUint16(); + TextAlign alignment = (TextAlign)argv[10].toSint16(); + int16 borderColor = argv[11].toSint16(); + bool dimmed = argv[12].toUint16(); + + // NOTE: Technically the engine checks these things: + // textRect.bottom > 0 + // textRect.right > 0 + // textRect.left < bitmap.width + // textRect.top < bitmap.height + // Then clips. But this seems stupid. + textRect.clip(Common::Rect(bitmap.getWidth(), bitmap.getHeight())); + + reg_t textBitmapObject = g_sci->_gfxText32->createFontBitmap(textRect.width(), textRect.height(), Common::Rect(textRect.width(), textRect.height()), text, foreColor, backColor, skipColor, fontId, alignment, borderColor, dimmed, false); + Buffer bitmapBuffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels()); + CelObjMem textCel(textBitmapObject); + textCel.draw(bitmapBuffer, textRect, Common::Point(textRect.left, textRect.top), false); + s->_segMan->freeHunkEntry(textBitmapObject); + + return NULL_REG; } -// Used for edit boxes in save/load dialogs. It's a rewritten version of kEditControl, -// but it handles events on its own, using an internal loop, instead of using SCI -// scripts for event management like kEditControl does. Called by script 64914, -// DEdit::hilite(). -reg_t kEditText(EngineState *s, int argc, reg_t *argv) { - reg_t controlObject = argv[0]; +reg_t kBitmapDrawColor(EngineState *s, int argc, reg_t *argv) { + // bitmap, left, top, right, bottom, color - if (!controlObject.isNull()) { - g_sci->_gfxControls32->kernelTexteditChange(controlObject); - } + // called e.g. from TextView::init() and TextView::draw() in Torin's Passage, script 64890 + + BitmapResource bitmap(argv[0]); + Common::Rect fillRect( + argv[1].toSint16(), + argv[2].toSint16(), + argv[3].toSint16() + 1, + argv[4].toSint16() + 1 + ); + + Buffer buffer(bitmap.getWidth(), bitmap.getHeight(), bitmap.getPixels()); + buffer.fillRect(fillRect, argv[5].toSint16()); + return NULL_REG; +} + +reg_t kBitmapDrawBitmap(EngineState *s, int argc, reg_t *argv) { + // target bitmap, source bitmap, x, y, unknown boolean + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapInvert(EngineState *s, int argc, reg_t *argv) { + // bitmap, left, top, right, bottom, foreColor, backColor + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapSetDisplace(EngineState *s, int argc, reg_t *argv) { + BitmapResource bitmap(argv[0]); + Common::Point newPos(argv[1].toUint16(), argv[2].toUint16()); + bitmap.setDisplace(newPos); return s->r_acc; } +reg_t kBitmapCreateFromView(EngineState *s, int argc, reg_t *argv) { + // viewId, loopNo, celNo, skipColor, backColor, useRemap, source overlay bitmap + + return kStub(s, argc + 1, argv - 1); +} + +reg_t kBitmapCopyPixels(EngineState *s, int argc, reg_t *argv) { + // target bitmap, source bitmap + + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapClone(EngineState *s, int argc, reg_t *argv) { + // bitmap + + return kStub(s, argc + 1, argv - 1); +} + +reg_t kBitmapGetInfo(EngineState *s, int argc, reg_t *argv) { + // bitmap + + // argc 1 = get width + // argc 2 = pixel at row 0 col n + // argc 3 = pixel at row n col n + return kStub(s, argc + 1, argv - 1); +} + +reg_t kBitmapScale(EngineState *s, int argc, reg_t *argv) { + // TODO: SCI3 + return kStubNull(s, argc + 1, argv - 1); +} + +reg_t kBitmapCreateFromUnknown(EngineState *s, int argc, reg_t *argv) { + // TODO: SCI3 + return kStub(s, argc + 1, argv - 1); +} + +reg_t kEditText(EngineState *s, int argc, reg_t *argv) { + return g_sci->_gfxControls32->kernelEditText(argv[0]); +} + reg_t kAddLine(EngineState *s, int argc, reg_t *argv) { return kStubNull(s, argc, argv); // return 0:0 for now, so that follow up calls won't create signature mismatches #if 0 reg_t plane = argv[0]; Common::Point startPoint(argv[1].toUint16(), argv[2].toUint16()); Common::Point endPoint(argv[3].toUint16(), argv[4].toUint16()); - // argv[5] is unknown (a number, usually 200) + byte priority = (byte)argv[5].toUint16(); byte color = (byte)argv[6].toUint16(); - byte priority = (byte)argv[7].toUint16(); - byte control = (byte)argv[8].toUint16(); - // argv[9] is unknown (usually a small number, 1 or 2). Thickness, perhaps? -// return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, control); + byte style = (byte)argv[7].toUint16(); // 0: solid, 1: dashed, 2: pattern + byte pattern = (byte)argv[8].toUint16(); + byte thickness = (byte)argv[9].toUint16(); +// return g_sci->_gfxFrameout->addPlaneLine(plane, startPoint, endPoint, color, priority, 0); return s->r_acc; #endif } @@ -764,6 +807,10 @@ reg_t kPalVarySetVary(EngineState *s, int argc, reg_t *argv) { reg_t kPalVarySetPercent(EngineState *s, int argc, reg_t *argv) { int time = argc > 0 ? argv[0].toSint16() * 60 : 0; int16 percent = argc > 1 ? argv[1].toSint16() : 0; + + // TODO: GK1 adds a third optional parameter here, at the end of chapter 1 + // (during the sunset/sunrise sequence, the parameter is 1) + g_sci->_gfxPalette32->setVaryPercent(percent, time, -1, -1); return NULL_REG; } @@ -871,76 +918,57 @@ reg_t kPalCycle(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } -reg_t kRemapColors32(EngineState *s, int argc, reg_t *argv) { - uint16 operation = argv[0].toUint16(); +reg_t kRemapColors(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} - switch (operation) { - case 0: { // turn remapping off - // WORKAROUND: Game scripts in QFG4 erroneously turn remapping off in room - // 140 (the character point allocation screen) and never turn it back on, - // even if it's clearly used in that screen. - if (g_sci->getGameId() == GID_QFG4 && s->currentRoomNumber() == 140) - return s->r_acc; +reg_t kRemapOff(EngineState *s, int argc, reg_t *argv) { + byte color = (argc >= 1) ? argv[0].toUint16() : 0; + g_sci->_gfxRemap32->remapOff(color); + return s->r_acc; +} - int16 base = (argc >= 2) ? argv[1].toSint16() : 0; - if (base > 0) - warning("kRemapColors(0) called with base %d", base); - g_sci->_gfxPalette32->resetRemapping(); - } - break; - case 1: { // remap by range - uint16 color = argv[1].toUint16(); - uint16 from = argv[2].toUint16(); - uint16 to = argv[3].toUint16(); - uint16 base = argv[4].toUint16(); - uint16 unk5 = (argc >= 6) ? argv[5].toUint16() : 0; - if (unk5 > 0) - warning("kRemapColors(1) called with 6 parameters, unknown parameter is %d", unk5); - g_sci->_gfxPalette32->setRemappingRange(color, from, to, base); - } - break; - case 2: { // remap by percent - uint16 color = argv[1].toUint16(); - uint16 percent = argv[2].toUint16(); // 0 - 100 - if (argc >= 4) - warning("RemapByPercent called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); - g_sci->_gfxPalette32->setRemappingPercent(color, percent); - } - break; - case 3: { // remap to gray - // Example call: QFG4 room 490 (Baba Yaga's hut) - params are color 253, 75% and 0. - // In this room, it's used for the cloud before Baba Yaga appears. - int16 color = argv[1].toSint16(); - int16 percent = argv[2].toSint16(); // 0 - 100 - if (argc >= 4) - warning("RemapToGray called with 4 parameters, unknown parameter is %d", argv[3].toUint16()); - g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); - } - break; - case 4: { // remap to percent gray - // Example call: QFG4 rooms 530/535 (swamp) - params are 253, 100%, 200 - int16 color = argv[1].toSint16(); - int16 percent = argv[2].toSint16(); // 0 - 100 - // argv[3] is unknown (a number, e.g. 200) - start color, perhaps? - if (argc >= 5) - warning("RemapToGrayPercent called with 5 parameters, unknown parameter is %d", argv[4].toUint16()); - g_sci->_gfxPalette32->setRemappingPercentGray(color, percent); - } - break; - case 5: { // don't map to range - //int16 mapping = argv[1].toSint16(); - uint16 intensity = argv[2].toUint16(); - // HACK for PQ4 - if (g_sci->getGameId() == GID_PQ4) - g_sci->_gfxPalette32->kernelSetIntensity(0, 255, intensity, true); +reg_t kRemapByRange(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte from = argv[1].toUint16(); + byte to = argv[2].toUint16(); + byte base = argv[3].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingRange(color, from, to, base); + return s->r_acc; +} - kStub(s, argc, argv); - } - break; - default: - break; - } +reg_t kRemapByPercent(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte percent = argv[1].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingPercent(color, percent); + return s->r_acc; +} + +reg_t kRemapToGray(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte gray = argv[1].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingToGray(color, gray); + return s->r_acc; +} + +reg_t kRemapToPercentGray(EngineState *s, int argc, reg_t *argv) { + byte color = argv[0].toUint16(); + byte gray = argv[1].toUint16(); + byte percent = argv[2].toUint16(); + // The last parameter, depth, is unused + g_sci->_gfxRemap32->setRemappingToPercentGray(color, gray, percent); + return s->r_acc; +} +reg_t kRemapSetNoMatchRange(EngineState *s, int argc, reg_t *argv) { + byte from = argv[0].toUint16(); + byte count = argv[1].toUint16(); + g_sci->_gfxRemap32->setNoMatchRange(from, count); return s->r_acc; } diff --git a/engines/sci/engine/kpathing.cpp b/engines/sci/engine/kpathing.cpp index 5b2245e84d..7ac744f584 100644 --- a/engines/sci/engine/kpathing.cpp +++ b/engines/sci/engine/kpathing.cpp @@ -1943,14 +1943,14 @@ static int liesBefore(const Vertex *v, const Common::Point &p1, const Common::Po // indexp1/vertexp1 on the polygon being merged. // It ends with the point intersection2, being the analogous intersection. struct Patch { - unsigned int indexw1; - unsigned int indexp1; + uint32 indexw1; + uint32 indexp1; const Vertex *vertexw1; const Vertex *vertexp1; Common::Point intersection1; - unsigned int indexw2; - unsigned int indexp2; + uint32 indexw2; + uint32 indexp2; const Vertex *vertexw2; const Vertex *vertexp2; Common::Point intersection2; @@ -1960,7 +1960,7 @@ struct Patch { // Check if the given vertex on the work polygon is bypassed by this patch. -static bool isVertexCovered(const Patch &p, unsigned int wi) { +static bool isVertexCovered(const Patch &p, uint32 wi) { // / v (outside) // ---w1--1----p----w2--2---- @@ -2402,7 +2402,7 @@ reg_t kMergePoly(EngineState *s, int argc, reg_t *argv) { // Copy work.vertices into arrayRef Vertex *vertex; - unsigned int n = 0; + uint32 n = 0; CLIST_FOREACH(vertex, &work.vertices) { if (vertex == work.vertices._head || vertex->v != vertex->_prev->v) writePoint(arrayRef, n++, vertex->v); diff --git a/engines/sci/engine/kstring.cpp b/engines/sci/engine/kstring.cpp index 310e38dbd1..f598cf7457 100644 --- a/engines/sci/engine/kstring.cpp +++ b/engines/sci/engine/kstring.cpp @@ -661,19 +661,6 @@ reg_t kStrSplit(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 -reg_t kText(EngineState *s, int argc, reg_t *argv) { - switch (argv[0].toUint16()) { - case 0: - return kTextSize(s, argc - 1, argv + 1); - default: - // TODO: Other subops here too, perhaps kTextColors and kTextFonts - warning("kText(%d)", argv[0].toUint16()); - break; - } - - return s->r_acc; -} - // TODO: there is an unused second argument, happens at least in LSL6 right during the intro reg_t kStringNew(EngineState *s, int argc, reg_t *argv) { reg_t stringHandle; diff --git a/engines/sci/engine/object.cpp b/engines/sci/engine/object.cpp index 0626c084c1..0566d6955f 100644 --- a/engines/sci/engine/object.cpp +++ b/engines/sci/engine/object.cpp @@ -255,6 +255,8 @@ void Object::initSelectorsSci3(const byte *buf) { if (g_sci->getKernel()->getSelectorNamesSize() % 32) ++groups; + _mustSetViewVisible.resize(groups); + methods = properties = 0; // Selectors are divided into groups of 32, of which the first @@ -270,7 +272,9 @@ void Object::initSelectorsSci3(const byte *buf) { // This object actually has selectors belonging to this group int typeMask = READ_SCI11ENDIAN_UINT32(seeker); - for (int bit = 2; bit < 32; ++bit) { + _mustSetViewVisible[groupNr] = (typeMask & 1); + + for (int bit = 2; bit < 32; ++bit) { int value = READ_SCI11ENDIAN_UINT16(seeker + bit * 2); if (typeMask & (1 << bit)) { // Property ++properties; @@ -281,7 +285,8 @@ void Object::initSelectorsSci3(const byte *buf) { } } - } + } else + _mustSetViewVisible[groupNr] = false; } _variables.resize(properties); diff --git a/engines/sci/engine/object.h b/engines/sci/engine/object.h index cc9f5ebb52..a7be170f4f 100644 --- a/engines/sci/engine/object.h +++ b/engines/sci/engine/object.h @@ -262,6 +262,8 @@ public: bool initBaseObject(SegManager *segMan, reg_t addr, bool doInitSuperClass = true); void syncBaseObject(const byte *ptr) { _baseObj = ptr; } + bool mustSetViewVisibleSci3(int selector) const { return _mustSetViewVisible[selector/32]; } + private: void initSelectorsSci3(const byte *buf); @@ -278,6 +280,7 @@ private: reg_t _superClassPosSci3; /**< reg_t pointing to superclass for SCI3 */ reg_t _speciesSelectorSci3; /**< reg_t containing species "selector" for SCI3 */ reg_t _infoSelectorSci3; /**< reg_t containing info "selector" for SCI3 */ + Common::Array<bool> _mustSetViewVisible; /** cached bit of info to make lookup fast, SCI3 only */ }; diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 18cee3321f..fcb65157d8 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -1012,6 +1012,26 @@ void gamestate_afterRestoreFixUp(EngineState *s, int savegameId) { g_sci->_gfxMenu->kernelSetAttribute(1025 >> 8, 1025 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Statistics g_sci->_gfxMenu->kernelSetAttribute(1026 >> 8, 1026 & 0xFF, SCI_MENU_ATTRIBUTE_ENABLED, TRUE_REG); // Status -> Goals break; + case GID_KQ6: + if (g_sci->isCD()) { + // WORKAROUND: + // For the CD version of King's Quest 6, set global depending on current hires/lowres state + // The game sets a global at the start depending on it and some things check that global + // instead of checking platform like for example the game action menu. + // This never happened in the original interpreter, because the original DOS interpreter + // was only capable of lowres graphics and the original Windows 3.11 interpreter was only capable + // of hires graphics. Saved games were not compatible between those two. + // Which means saving during lowres mode, then going into hires mode and restoring that saved game, + // will result in some graphics being incorrect (lowres). + // That's why we are setting the global after restoring a saved game depending on hires/lowres state. + // The CD demo of KQ6 does the same and uses the exact same global. + if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) { + s->variables[VAR_GLOBAL][0xA9].setOffset(1); + } else { + s->variables[VAR_GLOBAL][0xA9].setOffset(0); + } + } + break; case GID_PQ2: // HACK: Same as above - enable the save game menu option when loading in PQ2 (bug #6875). // It gets disabled in the game's death screen. @@ -1081,8 +1101,13 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { // We MUST NOT delete all planes/screen items. At least Space Quest 6 has a few in memory like for example // the options plane, which are not re-added and are in memory all the time right from the start of the // game. Sierra SCI32 did not clear planes, only scripts cleared the ones inside planes::elements. - if (getSciVersion() >= SCI_VERSION_2) - g_sci->_gfxFrameout->syncWithScripts(false); + if (getSciVersion() >= SCI_VERSION_2) { + if (!s->_delayedRestoreFromLauncher) { + // Only do it, when we are restoring regulary and not from launcher + // As it could result in option planes etc. on the screen (happens in gk1) + g_sci->_gfxFrameout->syncWithScripts(false); + } + } #endif s->reset(true); @@ -1131,6 +1156,8 @@ void gamestate_restore(EngineState *s, Common::SeekableReadStream *fh) { // signal restored game to game scripts s->gameIsRestarting = GAMEISRESTARTING_RESTORE; + + s->_delayedRestoreFromLauncher = false; } bool get_savegame_metadata(Common::SeekableReadStream *stream, SavegameMetadata *meta) { diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index ca6a439692..8039c5f282 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -408,6 +408,59 @@ static const SciScriptPatcherEntry fanmadeSignatures[] = { }; // =========================================================================== + +// WORKAROUND +// Freddy Pharkas intro screen +// Sierra used inner loops for the scaling of the 2 title views. +// Those inner loops don't call kGameIsRestarting, which is why +// we do not update the screen and we also do not throttle. +// +// This patch fixes this and makes it work. +// Applies to at least: English PC-CD +// Responsible method: sTownScript::changeState(1), sTownScript::changeState(3) (script 110) +static const uint16 freddypharkasSignatureIntroScaling[] = { + 0x38, SIG_ADDTOOFFSET(+2), // pushi (setLoop) (009b for PC CD) + 0x78, // push1 + PATCH_ADDTOOFFSET(1), // push0 for first code, push1 for second code + 0x38, SIG_ADDTOOFFSET(+2), // pushi (setStep) (0143 for PC CD) + 0x7a, // push2 + 0x39, 0x05, // pushi 05 + 0x3c, // dup + 0x72, SIG_ADDTOOFFSET(+2), // lofsa (view) + SIG_MAGICDWORD, + 0x4a, 0x1e, // send 1e + 0x35, 0x0a, // ldi 0a + 0xa3, 0x02, // sal local[2] + // start of inner loop + 0x8b, 0x02, // lsl local[2] + SIG_ADDTOOFFSET(+43), // skip almost all of inner loop + 0xa3, 0x02, // sal local[2] + 0x33, 0xcf, // jmp [inner loop start] + SIG_END +}; + +static const uint16 freddypharkasPatchIntroScaling[] = { + // remove setLoop(), objects in heap are already prepared, saves 5 bytes + 0x38, + PATCH_GETORIGINALBYTE(+6), + PATCH_GETORIGINALBYTE(+7), // pushi (setStep) + 0x7a, // push2 + 0x39, 0x05, // pushi 05 + 0x3c, // dup + 0x72, + PATCH_GETORIGINALBYTE(+13), + PATCH_GETORIGINALBYTE(+14), // lofsa (view) + 0x4a, 0x18, // send 18 - adjusted + 0x35, 0x0a, // ldi 0a + 0xa3, 0x02, // sal local[2] + // start of new inner loop + 0x39, 0x00, // pushi 00 + 0x43, 0x2c, 0x00, // callk GameIsRestarting <-- add this so that our speed throttler is triggered + SIG_ADDTOOFFSET(+47), // skip almost all of inner loop + 0x33, 0xca, // jmp [inner loop start] + PATCH_END +}; + // script 0 of freddy pharkas/CD PointsSound::check waits for a signal and if // no signal received will call kDoSound(0xD) which is a dummy in sierra sci // and ScummVM and will use acc (which is not set by the dummy) to trigger @@ -526,6 +579,7 @@ static const uint16 freddypharkasPatchMacInventory[] = { static const SciScriptPatcherEntry freddypharkasSignatures[] = { { true, 0, "CD: score early disposal", 1, freddypharkasSignatureScoreDisposal, freddypharkasPatchScoreDisposal }, { true, 15, "Mac: broken inventory", 1, freddypharkasSignatureMacInventory, freddypharkasPatchMacInventory }, + { true, 110, "intro scaling workaround", 2, freddypharkasSignatureIntroScaling, freddypharkasPatchIntroScaling }, { true, 235, "CD: canister pickup hang", 3, freddypharkasSignatureCanisterHang, freddypharkasPatchCanisterHang }, { true, 320, "ladder event issue", 2, freddypharkasSignatureLadderEvent, freddypharkasPatchLadderEvent }, SCI_SIGNATUREENTRY_TERMINATOR @@ -759,6 +813,32 @@ static const uint16 kq5PatchWitchCageInit[] = { PATCH_END }; +// The multilingual releases of KQ5 hang right at the end during the magic battle with Mordack. +// It seems additional code was added to wait for signals, but the signals are never set and thus +// the game hangs. We disable that code, so that the battle works again. +// This also happened in the original interpreter. +// We must not change similar code, that happens before. + +// Applies to at least: French PC floppy, German PC floppy, Spanish PC floppy +// Responsible method: stingScript::changeState, dragonScript::changeState, snakeScript::changeState +static const uint16 kq5SignatureMultilingualEndingGlitch[] = { + SIG_MAGICDWORD, + 0x89, 0x57, // lsg global[57h] + 0x35, 0x00, // ldi 0 + 0x1a, // eq? + 0x18, // not + 0x30, SIG_UINT16(0x0011), // bnt [skip signal check] + SIG_ADDTOOFFSET(+8), // skip globalSound::prevSignal get code + 0x36, // push + 0x35, 0x0a, // ldi 0Ah + SIG_END +}; + +static const uint16 kq5PatchMultilingualEndingGlitch[] = { + PATCH_ADDTOOFFSET(+6), + 0x32, // change BNT into JMP + PATCH_END +}; // In the final battle, the DOS version uses signals in the music to handle // timing, while in the Windows version another method is used and the GM @@ -789,9 +869,10 @@ static const uint16 kq5PatchWinGMSignals[] = { // script, description, signature patch static const SciScriptPatcherEntry kq5Signatures[] = { - { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, - { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, - { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, + { true, 0, "CD: harpy volume change", 1, kq5SignatureCdHarpyVolume, kq5PatchCdHarpyVolume }, + { true, 200, "CD: witch cage init", 1, kq5SignatureWitchCageInit, kq5PatchWitchCageInit }, + { true, 124, "Multilingual: Ending glitching out", 3, kq5SignatureMultilingualEndingGlitch, kq5PatchMultilingualEndingGlitch }, + { false, 124, "Win: GM Music signal checks", 4, kq5SignatureWinGMSignals, kq5PatchWinGMSignals }, SCI_SIGNATUREENTRY_TERMINATOR }; diff --git a/engines/sci/engine/scriptdebug.cpp b/engines/sci/engine/scriptdebug.cpp index f0157a6569..7d70f30d55 100644 --- a/engines/sci/engine/scriptdebug.cpp +++ b/engines/sci/engine/scriptdebug.cpp @@ -499,7 +499,7 @@ void Kernel::dumpScriptClass(char *data, int seeker, int objsize) { void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { int objectctr[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - unsigned int _seeker = 0; + uint32 _seeker = 0; Resource *script = _resMan->findResource(ResourceId(kResourceTypeScript, scriptNumber), 0); if (!script) { @@ -510,7 +510,7 @@ void Kernel::dissectScript(int scriptNumber, Vocabulary *vocab) { while (_seeker < script->size) { int objType = (int16)READ_SCI11ENDIAN_UINT16(script->data + _seeker); int objsize; - unsigned int seeker = _seeker + 4; + uint32 seeker = _seeker + 4; if (!objType) { debugN("End of script object (#0) encountered.\n"); diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index de7f60ac16..2699bc2e5b 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -203,7 +203,7 @@ struct List { struct Hunk { void *mem; - unsigned int size; + uint32 size; const char *type; }; diff --git a/engines/sci/engine/selector.cpp b/engines/sci/engine/selector.cpp index 66f3793918..ac621f58ae 100644 --- a/engines/sci/engine/selector.cpp +++ b/engines/sci/engine/selector.cpp @@ -180,6 +180,7 @@ void Kernel::mapSelectors() { FIND_SELECTOR(back); FIND_SELECTOR(skip); FIND_SELECTOR(borderColor); + FIND_SELECTOR(width); FIND_SELECTOR(fixPriority); FIND_SELECTOR(mirrored); FIND_SELECTOR(visible); @@ -192,7 +193,12 @@ void Kernel::mapSelectors() { FIND_SELECTOR(textLeft); FIND_SELECTOR(textBottom); FIND_SELECTOR(textRight); + FIND_SELECTOR(title); + FIND_SELECTOR(titleFont); + FIND_SELECTOR(titleFore); + FIND_SELECTOR(titleBack); FIND_SELECTOR(magnifier); + FIND_SELECTOR(frameOut); FIND_SELECTOR(casts); #endif } @@ -206,6 +212,16 @@ reg_t readSelector(SegManager *segMan, reg_t object, Selector selectorId) { return *address.getPointer(segMan); } +#ifdef ENABLE_SCI32 +void updateInfoFlagViewVisible(Object *obj, int index) { + // TODO: Make this correct for all SCI versions + // Selectors 26 through 44 are selectors for View script objects in SQ6 + if (index >= 26 && index <= 44 && getSciVersion() >= SCI_VERSION_2) { + obj->setInfoSelectorFlag(kInfoFlagViewVisible); + } +} +#endif + void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t value) { ObjVarRef address; @@ -221,11 +237,7 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t else { *address.getPointer(segMan) = value; #ifdef ENABLE_SCI32 - // TODO: Make this correct for all SCI versions - // Selectors 26 through 44 are selectors for View script objects - if (getSciVersion() >= SCI_VERSION_2 && selectorId >= 26 && selectorId <= 44) { - segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewVisible); - } + updateInfoFlagViewVisible(segMan->getObject(object), selectorId); #endif } } diff --git a/engines/sci/engine/selector.h b/engines/sci/engine/selector.h index 12074ed7d2..f2d06d1cf4 100644 --- a/engines/sci/engine/selector.h +++ b/engines/sci/engine/selector.h @@ -147,6 +147,7 @@ struct SelectorCache { Selector skip; Selector dimmed; Selector borderColor; + Selector width; Selector fixPriority; Selector mirrored; @@ -155,9 +156,10 @@ struct SelectorCache { Selector useInsetRect; Selector inTop, inLeft, inBottom, inRight; Selector textTop, textLeft, textBottom, textRight; + Selector title, titleFont, titleFore, titleBack; Selector magnifier; - + Selector frameOut; Selector casts; // needed for sync'ing screen items/planes with scripts, when our save/restore code is patched in (see GfxFrameout::syncWithScripts) #endif }; @@ -197,6 +199,16 @@ void writeSelector(SegManager *segMan, reg_t object, Selector selectorId, reg_t void invokeSelector(EngineState *s, reg_t object, int selectorId, int k_argc, StackPtr k_argp, int argc = 0, const reg_t *argv = 0); +#ifdef ENABLE_SCI32 +/** + * SCI32 set kInfoFlagViewVisible in the -info- selector if a certain + * range of properties was written to. + * This function checks if index is in the right range, and sets the flag + * on obj.-info- if it is. + */ +void updateInfoFlagViewVisible(Object *obj, int index); +#endif + } // End of namespace Sci #endif // SCI_ENGINE_KERNEL_H diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index d53e6b48c8..fda78317b5 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -95,6 +95,7 @@ void EngineState::reset(bool isRestoring) { // reset delayed restore game functionality _delayedRestoreGame = false; _delayedRestoreGameId = 0; + _delayedRestoreFromLauncher = false; executionStackBase = 0; _executionStackPosChanged = false; diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index 0f04e32fe5..cf9a753f5c 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -138,10 +138,12 @@ public: // see detection.cpp / SciEngine::loadGameState() bool _delayedRestoreGame; // boolean, that triggers delayed restore (triggered by ScummVM menu) int _delayedRestoreGameId; // the saved game id, that it supposed to get restored (triggered by ScummVM menu) + bool _delayedRestoreFromLauncher; // is set, when the the delayed restore game was triggered from launcher uint _chosenQfGImportItem; // Remembers the item selected in QfG import rooms bool _cursorWorkaroundActive; // Refer to GfxCursor::setPosition() + int16 _cursorWorkaroundPosCount; // When the cursor is reported to be at the previously set coordinate, we won't disable the workaround unless it happened for this many times Common::Point _cursorWorkaroundPoint; Common::Rect _cursorWorkaroundRect; diff --git a/engines/sci/engine/vm.cpp b/engines/sci/engine/vm.cpp index 3729fc5236..64e6c045db 100644 --- a/engines/sci/engine/vm.cpp +++ b/engines/sci/engine/vm.cpp @@ -258,6 +258,10 @@ static void _exec_varselectors(EngineState *s) { if (xs.argc) { // write? *var = xs.variables_argp[1]; +#ifdef ENABLE_SCI32 + updateInfoFlagViewVisible(s->_segMan->getObject(xs.addr.varp.obj), xs.addr.varp.varindex); +#endif + } else // No, read s->r_acc = *var; } @@ -1095,6 +1099,9 @@ void run_vm(EngineState *s) { case op_aTop: // 0x32 (50) // Accumulator To Property validate_property(s, obj, opparams[0]) = s->r_acc; +#ifdef ENABLE_SCI32 + updateInfoFlagViewVisible(obj, opparams[0]>>1); +#endif break; case op_pTos: // 0x33 (51) @@ -1105,6 +1112,9 @@ void run_vm(EngineState *s) { case op_sTop: // 0x34 (52) // Stack To Property validate_property(s, obj, opparams[0]) = POP32(); +#ifdef ENABLE_SCI32 + updateInfoFlagViewVisible(obj, opparams[0]>>1); +#endif break; case op_ipToa: // 0x35 (53) @@ -1119,7 +1129,9 @@ void run_vm(EngineState *s) { opProperty += 1; else opProperty -= 1; - +#ifdef ENABLE_SCI32 + updateInfoFlagViewVisible(obj, opparams[0]>>1); +#endif if (opcode == op_ipToa || opcode == op_dpToa) s->r_acc = opProperty; else diff --git a/engines/sci/engine/vm_types.cpp b/engines/sci/engine/vm_types.cpp index 53a5a5c507..d74e2b194c 100644 --- a/engines/sci/engine/vm_types.cpp +++ b/engines/sci/engine/vm_types.cpp @@ -230,6 +230,10 @@ int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { return toUint16() - right.toUint16(); else return toSint16() - right.toSint16(); +#ifdef ENABLE_SCI32 + } else if (getSciVersion() >= SCI_VERSION_2) { + return sci32Comparison(right); +#endif } else if (pointerComparisonWithInteger(right)) { return 1; } else if (right.pointerComparisonWithInteger(*this)) { @@ -238,6 +242,26 @@ int reg_t::cmp(const reg_t right, bool treatAsUnsigned) const { return lookForWorkaround(right, "comparison").toSint16(); } +#ifdef ENABLE_SCI32 +int reg_t::sci32Comparison(const reg_t right) const { + // In SCI32, MemIDs are normally indexes into the memory manager's handle + // list, but the engine reserves indexes at and above 20000 for objects + // that were created inside the engine (as opposed to inside the VM). The + // engine compares these as a tiebreaker for graphics objects that are at + // the same priority, and it is necessary to at least minimally handle + // this situation. + // This is obviously a bogus comparision, but then, this entire thing is + // bogus. For the moment, it just needs to be deterministic. + if (isNumber() && !right.isNumber()) { + return 1; + } else if (right.isNumber() && !isNumber()) { + return -1; + } + + return getOffset() - right.getOffset(); +} +#endif + bool reg_t::pointerComparisonWithInteger(const reg_t right) const { // This function handles the case where a script tries to compare a pointer // to a number. Normally, we would not want to allow that. However, SCI0 - diff --git a/engines/sci/engine/vm_types.h b/engines/sci/engine/vm_types.h index a646478a8e..e60f52e85c 100644 --- a/engines/sci/engine/vm_types.h +++ b/engines/sci/engine/vm_types.h @@ -160,6 +160,10 @@ private: int cmp(const reg_t right, bool treatAsUnsigned) const; reg_t lookForWorkaround(const reg_t right, const char *operation) const; bool pointerComparisonWithInteger(const reg_t right) const; + +#ifdef ENABLE_SCI32 + int sci32Comparison(const reg_t right) const; +#endif }; static inline reg_t make_reg(SegmentId segment, uint16 offset) { diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index aab32032f7..a4e19dc8b9 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -630,7 +630,7 @@ const SciWorkaroundEntry kGraphUpdateBox_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kIsObject_workarounds[] = { - { GID_GK1, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 + { GID_GK1DEMO, 50, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // GK1 demo, when asking Grace for messages it gets called with an invalid parameter (type "error") - bug #4950 { GID_ISLANDBRAIN, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when going to the game options, choosing "Info" and selecting anything from the list, gets called with an invalid parameter (type "error") - bug #4989 { GID_QFG3, -1, 999, 0, "List", "eachElementDo", NULL, 0, { WORKAROUND_FAKE, 0 } }, // when asking for something, gets called with type error parameter SCI_WORKAROUNDENTRY_TERMINATOR @@ -656,6 +656,12 @@ const SciWorkaroundEntry kNewWindow_workarounds[] = { }; // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround +const SciWorkaroundEntry kPalVarySetPercent_workarounds[] = { + { GID_GK1, 370, 370, 0, "graceComeOut", "changeState", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // there's an extra parameter in GK1, when changing chapters. This extra parameter seems to be a bug or just unimplemented functionality, as there's no visible change from the original in the chapter change room + SCI_WORKAROUNDENTRY_TERMINATOR +}; + +// gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kReadNumber_workarounds[] = { { GID_CNICK_LAURABOW,100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 { GID_HOYLE3, 100, 101, 0, "dominoes.opt", "doit", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // When dominoes.opt is present, the game scripts call kReadNumber with an extra integer parameter - bug #6425 @@ -664,7 +670,7 @@ const SciWorkaroundEntry kReadNumber_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = { - { GID_QFG4, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 + { GID_QFG4DEMO, 100, 100, 0, "doMovie", "changeState", NULL, 0, { WORKAROUND_IGNORE, 0 } }, // after the Sierra logo, no flags are passed, thus the call is meaningless - bug #4947 SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/engine/workarounds.h b/engines/sci/engine/workarounds.h index 46059a175c..8f519a8c9c 100644 --- a/engines/sci/engine/workarounds.h +++ b/engines/sci/engine/workarounds.h @@ -89,6 +89,7 @@ extern const SciWorkaroundEntry kIsObject_workarounds[]; extern const SciWorkaroundEntry kMemory_workarounds[]; extern const SciWorkaroundEntry kMoveCursor_workarounds[]; extern const SciWorkaroundEntry kNewWindow_workarounds[]; +extern const SciWorkaroundEntry kPalVarySetPercent_workarounds[]; extern const SciWorkaroundEntry kReadNumber_workarounds[]; extern const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[]; extern const SciWorkaroundEntry kSetCursor_workarounds[]; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 9a87c6de0b..34f1618514 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -29,6 +29,9 @@ #include "sci/console.h" #include "sci/engine/state.h" #include "sci/engine/kernel.h" +#ifdef ENABLE_SCI32 +#include "sci/graphics/frameout.h" +#endif #include "sci/graphics/screen.h" namespace Sci { @@ -133,8 +136,13 @@ static int altify(int ch) { } SciEvent EventManager::getScummVMEvent() { - SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; - SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; +#ifdef ENABLE_SCI32 + SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() }; + SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() }; +#else + SciEvent input = { SCI_EVENT_NONE, 0, 0, Common::Point() }; + SciEvent noEvent = { SCI_EVENT_NONE, 0, 0, Common::Point() }; +#endif Common::EventManager *em = g_system->getEventManager(); Common::Event ev; @@ -155,7 +163,20 @@ SciEvent EventManager::getScummVMEvent() { // via pollEvent. // We also adjust the position based on the scaling of the screen. Common::Point mousePos = em->getMousePos(); - g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x); + +#if ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer(); + + Common::Point mousePosSci = mousePos; + mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight)); + noEvent.mousePosSci = input.mousePosSci = mousePosSci; + } else { +#endif + g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x); +#if ENABLE_SCI32 + } +#endif noEvent.mousePos = input.mousePos = mousePos; @@ -302,6 +323,11 @@ SciEvent EventManager::getScummVMEvent() { input.character = altify(input.character); if (getSciVersion() <= SCI_VERSION_1_MIDDLE && (scummVMKeyFlags & Common::KBD_CTRL) && input.character > 0 && input.character < 27) input.character += 96; // 0x01 -> 'a' +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2 && (scummVMKeyFlags & Common::KBD_CTRL) && input.character == 'c') { + input.character = SCI_KEY_ETX; + } +#endif // If no actual key was pressed (e.g. if only a modifier key was pressed), // ignore the event @@ -328,8 +354,12 @@ void EventManager::updateScreen() { } } -SciEvent EventManager::getSciEvent(unsigned int mask) { - SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(0, 0) }; +SciEvent EventManager::getSciEvent(uint32 mask) { +#ifdef ENABLE_SCI32 + SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point(), Common::Point() }; +#else + SciEvent event = { SCI_EVENT_NONE, 0, 0, Common::Point() }; +#endif EventManager::updateScreen(); diff --git a/engines/sci/event.h b/engines/sci/event.h index 76c884aba4..15a94b3e73 100644 --- a/engines/sci/event.h +++ b/engines/sci/event.h @@ -39,11 +39,18 @@ struct SciEvent { uint16 character; /** - * The mouse position at the time the event was created. - * - * These are display coordinates! + * The mouse position at the time the event was created, + * in display coordinates. */ Common::Point mousePos; + +#ifdef ENABLE_SCI32 + /** + * The mouse position at the time the event was created, + * in script coordinates. + */ + Common::Point mousePosSci; +#endif }; /*Values for type*/ @@ -59,6 +66,9 @@ struct SciEvent { #define SCI_EVENT_ANY 0x7fff /* Keycodes of special keys: */ +#ifdef ENABLE_SCI32 +#define SCI_KEY_ETX 3 +#endif #define SCI_KEY_ESC 27 #define SCI_KEY_BACKSPACE 8 #define SCI_KEY_ENTER 13 @@ -121,7 +131,7 @@ public: ~EventManager(); void updateScreen(); - SciEvent getSciEvent(unsigned int mask); + SciEvent getSciEvent(uint32 mask); private: SciEvent getScummVMEvent(); diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp index 59af8334eb..fb1f557ad6 100644 --- a/engines/sci/graphics/cache.cpp +++ b/engines/sci/graphics/cache.cpp @@ -102,8 +102,4 @@ int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) { return getView(viewId)->getCelCount(loopNo); } -byte GfxCache::kernelViewGetColorAtCoordinate(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y) { - return getView(viewId)->getColorAtCoordinate(loopNo, celNo, x, y); -} - } // End of namespace Sci diff --git a/engines/sci/graphics/cache.h b/engines/sci/graphics/cache.h index 33fa4fe399..61952718a9 100644 --- a/engines/sci/graphics/cache.h +++ b/engines/sci/graphics/cache.h @@ -49,8 +49,6 @@ public: int16 kernelViewGetLoopCount(GuiResourceId viewId); int16 kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo); - byte kernelViewGetColorAtCoordinate(GuiResourceId viewId, int16 loopNo, int16 celNo, int16 x, int16 y); - private: void purgeFontCache(); void purgeViewCache(); diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index f8bce26a2c..693bc5f196 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -27,6 +27,8 @@ #include "sci/graphics/frameout.h" #include "sci/graphics/palette32.h" #include "sci/graphics/picture.h" +#include "sci/graphics/remap.h" +#include "sci/graphics/text32.h" #include "sci/graphics/view.h" namespace Sci { @@ -84,10 +86,9 @@ const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio #pragma mark CelObj void CelObj::init() { + CelObj::deinit(); _nextCacheId = 1; - delete _scaler; _scaler = new CelScaler(); - delete _cache; _cache = new CelCache; _cache->resize(100); } @@ -95,6 +96,11 @@ void CelObj::init() { void CelObj::deinit() { delete _scaler; _scaler = nullptr; + if (_cache != nullptr) { + for (CelCache::iterator it = _cache->begin(); it != _cache->end(); ++it) { + delete it->celObj; + } + } delete _cache; _cache = nullptr; } @@ -102,27 +108,44 @@ void CelObj::deinit() { #pragma mark - #pragma mark CelObj - Scalers -template <bool FLIP, typename READER> +template<bool FLIP, typename READER> struct SCALER_NoScale { +#ifndef NDEBUG + const byte *_rowEdge; +#endif const byte *_row; READER _reader; const int16 _lastIndex; + const int16 _sourceX; + const int16 _sourceY; - SCALER_NoScale(const CelObj &celObj, const int16 maxWidth) : - _reader(celObj, maxWidth), - _lastIndex(maxWidth - 1) {} + SCALER_NoScale(const CelObj &celObj, const int16 maxWidth, const Common::Point &scaledPosition) : + _reader(celObj, FLIP ? celObj._width : maxWidth), + _lastIndex(celObj._width - 1), + _sourceX(scaledPosition.x), + _sourceY(scaledPosition.y) {} - inline void setSource(const int16 x, const int16 y) { - _row = _reader.getRow(y); + inline void setTarget(const int16 x, const int16 y) { + _row = _reader.getRow(y - _sourceY); if (FLIP) { - _row += _lastIndex - x; +#ifndef NDEBUG + _rowEdge = _row - 1; +#endif + _row += _lastIndex - (x - _sourceX); + assert(_row > _rowEdge); } else { - _row += x; +#ifndef NDEBUG + _rowEdge = _row + _lastIndex + 1; +#endif + _row += x - _sourceX; + assert(_row < _rowEdge); } } inline byte read() { + assert(_row != _rowEdge); + if (FLIP) { return *_row--; } else { @@ -133,55 +156,96 @@ struct SCALER_NoScale { template<bool FLIP, typename READER> struct SCALER_Scale { +#ifndef NDEBUG + int16 _maxX; +#endif const byte *_row; READER _reader; - const CelScalerTable *_table; int16 _x; - const uint16 _lastIndex; + static int16 _valuesX[1024]; + static int16 _valuesY[1024]; - SCALER_Scale(const CelObj &celObj, const int16 maxWidth, const Ratio scaleX, const Ratio scaleY) : + SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) : +#ifndef NDEBUG + _maxX(targetRect.right - 1), +#endif // The maximum width of the scaled object may not be as // wide as the source data it requires if downscaling, // so just always make the reader decompress an entire // line of source data when scaling - _reader(celObj, celObj._width), - _table(CelObj::_scaler->getScalerTable(scaleX, scaleY)), - _lastIndex(maxWidth - 1) {} - - inline void setSource(const int16 x, const int16 y) { - _row = _reader.getRow(_table->valuesY[y]); + _reader(celObj, celObj._width) { + // In order for scaling ratios to apply equally across objects that + // start at different positions on the screen, the pixels that are + // read from the source bitmap must all use the same pattern of + // division. In other words, cels must follow the same scaling pattern + // as if they were drawn starting at an even multiple of the scaling + // ratio, even if they were not. + // + // To get the correct source pixel when reading out through the scaler, + // the engine creates a lookup table for each axis that translates + // directly from target positions to the indexes of source pixels using + // the global cadence for the given scaling ratio. + + const CelScalerTable *table = CelObj::_scaler->getScalerTable(scaleX, scaleY); + + const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); if (FLIP) { - _x = _lastIndex - x; + int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); + } } else { - _x = x; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x] - unscaledX; + } } + + const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y] - unscaledY; + } + } + + inline void setTarget(const int16 x, const int16 y) { + _row = _reader.getRow(_valuesY[y]); + _x = x; + assert(_x >= 0 && _x <= _maxX); } inline byte read() { - if (FLIP) { - return _row[_table->valuesX[_x--]]; - } else { - return _row[_table->valuesX[_x++]]; - } + assert(_x >= 0 && _x <= _maxX); + return _row[_valuesX[_x++]]; } }; +template<bool FLIP, typename READER> +int16 SCALER_Scale<FLIP, READER>::_valuesX[1024]; +template<bool FLIP, typename READER> +int16 SCALER_Scale<FLIP, READER>::_valuesY[1024]; + #pragma mark - #pragma mark CelObj - Resource readers struct READER_Uncompressed { private: +#ifndef NDEBUG + const int16 _sourceHeight; +#endif byte *_pixels; const int16 _sourceWidth; public: READER_Uncompressed(const CelObj &celObj, const int16) : +#ifndef NDEBUG + _sourceHeight(celObj._height), +#endif _sourceWidth(celObj._width) { byte *resource = celObj.getResPointer(); _pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24); } inline const byte *getRow(const int16 y) const { + assert(y >= 0 && y < _sourceHeight); return _pixels + y * _sourceWidth; } }; @@ -205,7 +269,7 @@ public: _sourceHeight(celObj._height), _transparentColor(celObj._transparentColor), _maxWidth(maxWidth) { - assert(_maxWidth <= celObj._width); + assert(maxWidth <= celObj._width); byte *celHeader = _resource + celObj._celHeaderOffset; _dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24); @@ -214,6 +278,7 @@ public: } inline const byte *getRow(const int16 y) { + assert(y >= 0 && y < _sourceHeight); if (y != _y) { // compressed data segment for row byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4); @@ -222,7 +287,7 @@ public: byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4); uint8 length; - for (int i = 0; i < _maxWidth; i += length) { + for (int16 i = 0; i < _maxWidth; i += length) { byte controlByte = *row++; length = controlByte; @@ -269,153 +334,101 @@ struct MAPPER_NoMDNoSkip { } }; +struct MAPPER_Map { + inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { + if (pixel != skipColor) { + if (pixel < g_sci->_gfxRemap32->getStartColor()) { + *target = pixel; + } else { + if (g_sci->_gfxRemap32->remapEnabled(pixel)) + *target = g_sci->_gfxRemap32->remapColor(pixel, *target); + } + } + } +}; + void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const { - const Buffer &priorityMap = g_sci->_gfxFrameout->getPriorityMap(); const Common::Point &scaledPosition = screenItem._scaledPosition; const Ratio &scaleX = screenItem._ratioX; const Ratio &scaleY = screenItem._ratioY; if (_remap) { - if (g_sci->_gfxFrameout->_hasRemappedScreenItem) { - const uint8 priority = MAX((int16)0, MIN((int16)255, screenItem._priority)); - - // NOTE: In the original engine code, there was a second branch for - // _remap here that would then call the following functions if _remap was false: - // - // drawHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawUncompHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawUncompNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // scaleDraw(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8) - // scaleDrawUncomp(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8) - // - // However, obviously, _remap cannot be false here. This dead code branch existed in - // at least SCI2/GK1 and SCI2.1/SQ6. - + // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, + // but since we are already in a `_remap` branch, there is no reason to check it + // again + if (g_sci->_gfxRemap32->getRemapCount()) { if (scaleX.isOne() && scaleY.isOne()) { if (_compressionType == kCelCompressionNone) { if (_drawMirrored) { - drawUncompHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipMap(target, targetRect, scaledPosition); } else { - drawUncompNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipMap(target, targetRect, scaledPosition); } } else { if (_drawMirrored) { - drawHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawHzFlipMap(target, targetRect, scaledPosition); } else { - drawNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawNoFlipMap(target, targetRect, scaledPosition); } } } else { if (_compressionType == kCelCompressionNone) { - scaleDrawUncompMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition); } else { - scaleDrawMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition); } } } else { - // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, - // but since we are already in a `_remap` branch, there is no reason to check it - // again - if (/* TODO: g_Remap_numActiveRemaps */ false) { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_drawMirrored) { - drawUncompHzFlipMap(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipMap(target, targetRect, scaledPosition); - } + if (scaleX.isOne() && scaleY.isOne()) { + if (_compressionType == kCelCompressionNone) { + if (_drawMirrored) { + drawUncompHzFlip(target, targetRect, scaledPosition); } else { - if (_drawMirrored) { - drawHzFlipMap(target, targetRect, scaledPosition); - } else { - drawNoFlipMap(target, targetRect, scaledPosition); - } + drawUncompNoFlip(target, targetRect, scaledPosition); } } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition); + if (_drawMirrored) { + drawHzFlip(target, targetRect, scaledPosition); } else { - scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition); + drawNoFlip(target, targetRect, scaledPosition); } } } else { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_drawMirrored) { - drawUncompHzFlip(target, targetRect, scaledPosition); - } else { - drawUncompNoFlip(target, targetRect, scaledPosition); - } - } else { - if (_drawMirrored) { - drawHzFlip(target, targetRect, scaledPosition); - } else { - drawNoFlip(target, targetRect, scaledPosition); - } - } + if (_compressionType == kCelCompressionNone) { + scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition); } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition); - } else { - scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition); - } + scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition); } } } } else { - if (g_sci->_gfxFrameout->_hasRemappedScreenItem) { - const uint8 priority = MAX((int16)0, MIN((int16)255, screenItem._priority)); - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { + if (scaleX.isOne() && scaleY.isOne()) { + if (_compressionType == kCelCompressionNone) { + if (_transparent) { if (_drawMirrored) { - drawUncompHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipNoMD(target, targetRect, scaledPosition); } else { - drawUncompNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipNoMD(target, targetRect, scaledPosition); } } else { if (_drawMirrored) { - drawHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition); } else { - drawNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition); } } } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + if (_drawMirrored) { + drawHzFlipNoMD(target, targetRect, scaledPosition); } else { - scaleDrawNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + drawNoFlipNoMD(target, targetRect, scaledPosition); } } } else { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_transparent) { - if (_drawMirrored) { - drawUncompHzFlipNoMD(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipNoMD(target, targetRect, scaledPosition); - } - } else { - if (_drawMirrored) { - drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition); - } - } - } else { - if (_drawMirrored) { - drawHzFlipNoMD(target, targetRect, scaledPosition); - } else { - drawNoFlipNoMD(target, targetRect, scaledPosition); - } - } + if (_compressionType == kCelCompressionNone) { + scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition); } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition); - } else { - scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition); - } + scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition); } } } @@ -557,7 +570,7 @@ void CelObj::putCopyInCache(const int cacheIndex) const { #pragma mark - #pragma mark CelObj - Drawing -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> struct RENDERER { MAPPER &_mapper; SCALER &_scaler; @@ -569,18 +582,15 @@ struct RENDERER { _skipColor(skipColor) {} inline void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - const int16 sourceX = targetRect.left - scaledPosition.x; - const int16 sourceY = targetRect.top - scaledPosition.y; - byte *targetPixel = (byte *)target.getPixels() + target.screenWidth * targetRect.top + targetRect.left; const int16 skipStride = target.screenWidth - targetRect.width(); const int16 targetWidth = targetRect.width(); const int16 targetHeight = targetRect.height(); - for (int y = 0; y < targetHeight; ++y) { - _scaler.setSource(sourceX, sourceY + y); + for (int16 y = 0; y < targetHeight; ++y) { + _scaler.setTarget(targetRect.left, targetRect.top + y); - for (int x = 0; x < targetWidth; ++x) { + for (int16 x = 0; x < targetWidth; ++x) { _mapper.draw(targetPixel++, _scaler.read(), _skipColor); } @@ -589,20 +599,20 @@ struct RENDERER { } }; -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { MAPPER mapper; - SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width()); + SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition); RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor); renderer.draw(target, targetRect, scaledPosition); } -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const { MAPPER mapper; - SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaleX, scaleY); + SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY); RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor); renderer.draw(target, targetRect, scaledPosition); } @@ -615,49 +625,60 @@ void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Co debug("drawHzFlip"); dummyFill(target, targetRect); } + void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("drawNoFlip"); dummyFill(target, targetRect); } + void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("drawUncompNoFlip"); dummyFill(target, targetRect); } + void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("drawUncompHzFlip"); dummyFill(target, targetRect); } + void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("scaleDraw"); dummyFill(target, targetRect); } + void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { debug("scaleDrawUncomp"); dummyFill(target, targetRect); } + void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawHzFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawNoFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompNoFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompHzFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawMap"); - dummyFill(target, targetRect); + if (_drawMirrored) + render<MAPPER_Map, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + else + render<MAPPER_Map, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); } + void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawUncompMap"); - dummyFill(target, targetRect); + if (_drawMirrored) + render<MAPPER_Map, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + else + render<MAPPER_Map, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); } void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { @@ -675,43 +696,29 @@ void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect void CelObj::drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMDNoSkip, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMD, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMDNoSkip, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - if (_drawMirrored) { + if (_drawMirrored) render<MAPPER_NoMD, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } else { + else render<MAPPER_NoMD, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } } void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - if (_drawMirrored) { + if (_drawMirrored) render<MAPPER_NoMD, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } else { + else render<MAPPER_NoMD, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } } -// TODO: These functions may all be vestigial. -void CelObj::drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} - #pragma mark - #pragma mark CelObjView CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) { @@ -827,8 +834,8 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int bool CelObjView::analyzeUncompressedForRemap() const { byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { - uint8 pixel = pixels[i]; - if (/* TODO: pixel >= Remap::minRemapColor && pixel <= Remap::maxRemapColor */ false && pixel != _transparentColor) { + byte pixel = pixels[i]; + if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) { return true; } } @@ -836,7 +843,16 @@ bool CelObjView::analyzeUncompressedForRemap() const { } bool CelObjView::analyzeForRemap() const { - // TODO: Implement decompression and analysis + READER_Compressed reader(*this, _width); + for (int y = 0; y < _height; y++) { + const byte *curRow = reader.getRow(y); + for (int x = 0; x < _width; x++) { + byte pixel = curRow[x]; + if (pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && pixel != _transparentColor) { + return true; + } + } + } return false; } @@ -968,28 +984,23 @@ byte *CelObjPic::getResPointer() const { #pragma mark - #pragma mark CelObjMem -CelObjMem::CelObjMem(const reg_t bitmap) { +CelObjMem::CelObjMem(const reg_t bitmapObject) { _info.type = kCelTypeMem; - _info.bitmap = bitmap; + _info.bitmap = bitmapObject; _mirrorX = false; _compressionType = kCelCompressionNone; _celHeaderOffset = 0; _transparent = true; - byte *bitmapData = g_sci->getEngineState()->_segMan->getHunkPointer(bitmap); - if (bitmapData == nullptr || READ_SCI11ENDIAN_UINT32(bitmapData + 28) != 46) { - error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); - } - - _width = READ_SCI11ENDIAN_UINT16(bitmapData); - _height = READ_SCI11ENDIAN_UINT16(bitmapData + 2); - _displace.x = READ_SCI11ENDIAN_UINT16(bitmapData + 4); - _displace.y = READ_SCI11ENDIAN_UINT16(bitmapData + 6); - _transparentColor = bitmapData[8]; - _scaledWidth = READ_SCI11ENDIAN_UINT16(bitmapData + 36); - _scaledHeight = READ_SCI11ENDIAN_UINT16(bitmapData + 38); - _hunkPaletteOffset = READ_SCI11ENDIAN_UINT16(bitmapData + 20); - _remap = (READ_SCI11ENDIAN_UINT16(bitmapData + 10) & 2) ? true : false; + BitmapResource bitmap(bitmapObject); + _width = bitmap.getWidth(); + _height = bitmap.getHeight(); + _displace = bitmap.getDisplace(); + _transparentColor = bitmap.getSkipColor(); + _scaledWidth = bitmap.getScaledWidth(); + _scaledHeight = bitmap.getScaledHeight(); + _hunkPaletteOffset = bitmap.getHunkPaletteOffset(); + _remap = bitmap.getRemap(); } CelObjMem *CelObjMem::duplicate() const { @@ -1036,4 +1047,4 @@ CelObjColor *CelObjColor::duplicate() const { byte *CelObjColor::getResPointer() const { error("Unsupported method"); } -} +} // End of namespace Sci diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 1422b76a57..600ae82d32 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -180,13 +180,16 @@ public: CelScaler() : _scaleTables(), _activeIndex(0) { - CelScalerTable &table = _scaleTables[_activeIndex]; + CelScalerTable &table = _scaleTables[0]; table.scaleX = Ratio(); table.scaleY = Ratio(); for (int i = 0; i < ARRAYSIZE(table.valuesX); ++i) { table.valuesX[i] = i; table.valuesY[i] = i; } + for (int i = 1; i < ARRAYSIZE(_scaleTables); ++i) { + _scaleTables[i] = _scaleTables[0]; + } } /** @@ -379,10 +382,10 @@ public: #pragma mark - #pragma mark CelObj - Drawing private: - template <typename MAPPER, typename SCALER> + template<typename MAPPER, typename SCALER> void render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - template <typename MAPPER, typename SCALER> + template<typename MAPPER, typename SCALER> void render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const; void drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; @@ -391,18 +394,15 @@ private: void drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; + void drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - void drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; + // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32 + void drawHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; @@ -411,12 +411,7 @@ private: void drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; void scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const; - void drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; - void scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const; + // NOTE: The original includes versions of the above functions with priority parameters, which were not actually used in SCI32 #pragma mark - #pragma mark CelObj - Caching @@ -577,6 +572,6 @@ public: virtual CelObjColor *duplicate() const override; virtual byte *getResPointer() const override; }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 716a366b7c..729eeeaf81 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -67,7 +67,7 @@ uint16 GfxCompare::isOnControl(uint16 screenMask, const Common::Rect &rect) { return result; } -reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list) { +reg_t GfxCompare::canBeHereCheckRectList(const reg_t checkObject, const Common::Rect &checkRect, const List *list, const uint16 signalFlags) const { reg_t curAddress = list->first; Node *curNode = _segMan->lookupNode(curAddress); reg_t curObject; @@ -78,7 +78,7 @@ reg_t GfxCompare::canBeHereCheckRectList(reg_t checkObject, const Common::Rect & curObject = curNode->value; if (curObject != checkObject) { signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); - if (!(signal & (kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate))) { + if (!(signal & signalFlags)) { curRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); curRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); curRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); @@ -112,11 +112,6 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { GfxView *view = NULL; Common::Rect celRect(0, 0); GuiResourceId viewId = (GuiResourceId)readSelectorValue(_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)); @@ -126,26 +121,8 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { z = (int16)readSelectorValue(_segMan, objectReference, SELECTOR(z)); view = _cache->getView(viewId); - -#ifdef ENABLE_SCI32 - if (view->isSci2Hires()) - view->adjustToUpscaledCoordinates(y, x); - else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) - _coordAdjuster->fromScriptToDisplay(y, x); -#endif - view->getCelRect(loopNo, celNo, x, y, z, celRect); -#ifdef ENABLE_SCI32 - if (view->isSci2Hires()) { - view->adjustBackUpscaledCoordinates(celRect.top, celRect.left); - view->adjustBackUpscaledCoordinates(celRect.bottom, celRect.right); - } else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) { - _coordAdjuster->fromDisplayToScript(celRect.top, celRect.left); - _coordAdjuster->fromDisplayToScript(celRect.bottom, celRect.right); - } -#endif - if (lookupSelector(_segMan, objectReference, SELECTOR(nsTop), NULL, NULL) == kSelectorVariable) { setNSRect(objectReference, celRect); } @@ -153,32 +130,57 @@ void GfxCompare::kernelSetNowSeen(reg_t objectReference) { reg_t GfxCompare::kernelCanBeHere(reg_t curObject, reg_t listReference) { Common::Rect checkRect; - Common::Rect adjustedRect; - uint16 signal, controlMask; uint16 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)); + uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); if (!checkRect.isValidRect()) { // can occur in Iceman and Mother Goose - 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); return NULL_REG; // this means "can be here" } - adjustedRect = _coordAdjuster->onControl(checkRect); - - signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); - controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); + Common::Rect adjustedRect = _coordAdjuster->onControl(checkRect); + uint16 controlMask = readSelectorValue(_segMan, curObject, SELECTOR(illegalBits)); result = isOnControl(GFX_SCREEN_MASK_CONTROL, adjustedRect) & controlMask; if ((!result) && (signal & (kSignalIgnoreActor | kSignalRemoveView)) == 0) { List *list = _segMan->lookupList(listReference); if (!list) error("kCanBeHere called with non-list as parameter"); - return canBeHereCheckRectList(curObject, checkRect, list); + return canBeHereCheckRectList(curObject, checkRect, list, kSignalIgnoreActor | kSignalRemoveView | kSignalNoUpdate); } + + return make_reg(0, result); +} + +reg_t GfxCompare::kernelCantBeHere32(const reg_t curObject, const reg_t listReference) const { + // Most of SCI32 graphics code converts rects from the VM to exclusive + // rects before operating on them, but this call leverages SCI16 engine + // code that operates on inclusive rects, so the rect's bottom-right + // point is not modified like in other SCI32 kernel calls + Common::Rect checkRect( + readSelectorValue(_segMan, curObject, SELECTOR(brLeft)), + readSelectorValue(_segMan, curObject, SELECTOR(brTop)), + readSelectorValue(_segMan, curObject, SELECTOR(brRight)), + readSelectorValue(_segMan, curObject, SELECTOR(brBottom)) + ); + + uint16 result = 0; + uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); + const uint16 signalFlags = kSignalIgnoreActor | kSignalHidden; + + if ((signal & signalFlags) == 0) { + List *list = _segMan->lookupList(listReference); + if (!list) { + error("kCantBeHere called with non-list as parameter"); + } + result = !canBeHereCheckRectList(curObject, checkRect, list, signalFlags).isNull(); + } + return make_reg(0, result); } @@ -201,15 +203,9 @@ void GfxCompare::kernelBaseSetter(reg_t object) { GuiResourceId viewId = readSelectorValue(_segMan, object, SELECTOR(view)); int16 loopNo = readSelectorValue(_segMan, object, SELECTOR(loop)); int16 celNo = readSelectorValue(_segMan, object, SELECTOR(cel)); - - // HACK: Ignore invalid views for now (perhaps unimplemented text views?) - if (viewId == 0xFFFF) // invalid view - return; - uint16 scaleSignal = 0; - if (getSciVersion() >= SCI_VERSION_1_1) { + if (getSciVersion() >= SCI_VERSION_1_1) scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal)); - } Common::Rect celRect; diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h index 88b44aeeb1..c7005980d0 100644 --- a/engines/sci/graphics/compare.h +++ b/engines/sci/graphics/compare.h @@ -40,6 +40,7 @@ public: uint16 kernelOnControl(byte screenMask, const Common::Rect &rect); void kernelSetNowSeen(reg_t objectReference); reg_t kernelCanBeHere(reg_t curObject, reg_t listReference); + reg_t kernelCantBeHere32(const reg_t curObject, const reg_t listReference) const; bool kernelIsItSkip(GuiResourceId viewId, int16 loopNo, int16 celNo, Common::Point position); void kernelBaseSetter(reg_t object); Common::Rect getNSRect(reg_t object); @@ -58,7 +59,7 @@ private: * *different* from checkObject, has a brRect which is contained inside * checkRect. */ - reg_t canBeHereCheckRectList(reg_t checkObject, const Common::Rect &checkRect, List *list); + reg_t canBeHereCheckRectList(const reg_t checkObject, const Common::Rect &checkRect, const List *list, const uint16 signalFlags) const; }; } // End of namespace Sci diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index 04a70d35f3..a877d8c276 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -23,9 +23,11 @@ #include "common/system.h" #include "sci/sci.h" +#include "sci/console.h" #include "sci/event.h" #include "sci/engine/kernel.h" #include "sci/engine/seg_manager.h" +#include "sci/engine/state.h" #include "sci/graphics/cache.h" #include "sci/graphics/compare.h" #include "sci/graphics/controls32.h" @@ -34,171 +36,321 @@ #include "sci/graphics/text32.h" namespace Sci { +GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) : + _segMan(segMan), + _gfxCache(cache), + _gfxText32(text), + _overwriteMode(false), + _nextCursorFlashTick(0) {} -GfxControls32::GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text) - : _segMan(segMan), _cache(cache), _text(text) { -} +reg_t GfxControls32::kernelEditText(const reg_t controlObject) { + SegManager *segMan = _segMan; -GfxControls32::~GfxControls32() { -} + TextEditor editor; + reg_t textObject = readSelector(_segMan, controlObject, SELECTOR(text)); + editor.text = _segMan->getString(textObject); + editor.foreColor = readSelectorValue(_segMan, controlObject, SELECTOR(fore)); + editor.backColor = readSelectorValue(_segMan, controlObject, SELECTOR(back)); + editor.skipColor = readSelectorValue(_segMan, controlObject, SELECTOR(skip)); + editor.fontId = readSelectorValue(_segMan, controlObject, SELECTOR(font)); + editor.maxLength = readSelectorValue(_segMan, controlObject, SELECTOR(width)); + editor.bitmap = readSelector(_segMan, controlObject, SELECTOR(bitmap)); + editor.cursorCharPosition = 0; + editor.cursorIsDrawn = false; + editor.borderColor = readSelectorValue(_segMan, controlObject, SELECTOR(borderColor)); -void GfxControls32::kernelTexteditChange(reg_t controlObject) { - SciEvent curEvent; - uint16 maxChars = 40; //readSelectorValue(_segMan, controlObject, SELECTOR(max)); // TODO - reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); - GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font))); - Common::String text; - uint16 textSize; - bool textChanged = false; - bool textAddChar = false; - Common::Rect rect; + reg_t titleObject = readSelector(_segMan, controlObject, SELECTOR(title)); + + int16 titleHeight = 0; + GuiResourceId titleFontId = readSelectorValue(_segMan, controlObject, SELECTOR(titleFont)); + if (!titleObject.isNull()) { + GfxFont *titleFont = _gfxCache->getFont(titleFontId); + titleHeight += _gfxText32->scaleUpHeight(titleFont->getHeight()) + 1; + if (editor.borderColor != -1) { + titleHeight += 2; + } + } - if (textReference.isNull()) - error("kEditControl called on object that doesn't have a text reference"); - text = _segMan->getString(textReference); + int16 width = 0; + int16 height = titleHeight; - // TODO: Finish this - warning("kEditText ('%s')", text.c_str()); - return; + GfxFont *editorFont = _gfxCache->getFont(editor.fontId); + height += _gfxText32->scaleUpHeight(editorFont->getHeight()) + 1; + _gfxText32->setFont(editor.fontId); + int16 emSize = _gfxText32->getCharWidth('M', true); + width += editor.maxLength * emSize + 1; + if (editor.borderColor != -1) { + width += 4; + height += 2; + } - uint16 cursorPos = 0; - //uint16 oldCursorPos = cursorPos; - bool captureEvents = true; - EventManager* eventMan = g_sci->getEventManager(); + Common::Rect editorPlaneRect(width, height); + editorPlaneRect.translate(readSelectorValue(_segMan, controlObject, SELECTOR(x)), readSelectorValue(_segMan, controlObject, SELECTOR(y))); - while (captureEvents) { - curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); + reg_t planeObj = readSelector(_segMan, controlObject, SELECTOR(plane)); + Plane *sourcePlane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj); + if (sourcePlane == nullptr) { + error("Could not find plane %04x:%04x", PRINT_REG(planeObj)); + } + editorPlaneRect.translate(sourcePlane->_gameRect.left, sourcePlane->_gameRect.top); - if (curEvent.type == SCI_EVENT_NONE) { - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event + editor.textRect = Common::Rect(2, titleHeight + 2, width - 1, height - 1); + editor.width = width; + + if (editor.bitmap.isNull()) { + TextAlign alignment = (TextAlign)readSelectorValue(_segMan, controlObject, SELECTOR(mode)); + + if (titleObject.isNull()) { + bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed)); + editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true); } else { - textSize = text.size(); + Common::String title = _segMan->getString(titleObject); + int16 titleBackColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleBack)); + int16 titleForeColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleFore)); + editor.bitmap = _gfxText32->createTitledBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, title, titleForeColor, titleBackColor, titleFontId, true); + } + } + + drawCursor(editor); - switch (curEvent.type) { - case SCI_EVENT_MOUSE_PRESS: - // TODO: Implement mouse support for cursor change + Plane *plane = new Plane(editorPlaneRect, kPlanePicTransparent); + plane->changePic(); + g_sci->_gfxFrameout->addPlane(*plane); + + CelInfo32 celInfo; + celInfo.type = kCelTypeMem; + celInfo.bitmap = editor.bitmap; + + ScreenItem *screenItem = new ScreenItem(plane->_object, celInfo, Common::Point(), ScaleInfo()); + plane->_screenItemList.add(screenItem); + + // frameOut must be called after the screen item is + // created, and before it is updated at the end of the + // event loop, otherwise it has both created and updated + // flags set which crashes the engine (it runs updates + // before creations) + g_sci->_gfxFrameout->frameOut(true); + + EventManager *eventManager = g_sci->getEventManager(); + bool clearTextOnInput = true; + bool textChanged = false; + for (;;) { + // We peek here because the last event needs to be allowed to + // dispatch a second time to the normal event handling system. + // In the actual engine, the event is always consumed and then + // the last event just gets posted back to the event manager for + // reprocessing, but instead, we only remove the event from the + // queue *after* we have determined it is not a defocusing event + const SciEvent event = eventManager->getSciEvent(SCI_EVENT_ANY | SCI_EVENT_PEEK); + + bool focused = true; + // Original engine did not have a QUIT event but we have to handle it + if (event.type == SCI_EVENT_QUIT) { + focused = false; + break; + } else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) { + focused = false; + } else if (event.type == SCI_EVENT_KEYBOARD) { + switch (event.character) { + case SCI_KEY_ESC: + case SCI_KEY_UP: + case SCI_KEY_DOWN: + case SCI_KEY_TAB: + case SCI_KEY_SHIFT_TAB: + case SCI_KEY_ENTER: + focused = false; break; - case SCI_EVENT_KEYBOARD: - switch (curEvent.character) { - case SCI_KEY_BACKSPACE: - if (cursorPos > 0) { - cursorPos--; text.deleteChar(cursorPos); - textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_DELETE: - if (cursorPos < textSize) { - text.deleteChar(cursorPos); - textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_HOME: // HOME - cursorPos = 0; textChanged = true; - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_END: // END - cursorPos = textSize; textChanged = true; - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_LEFT: // LEFT - if (cursorPos > 0) { - cursorPos--; textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_RIGHT: // RIGHT - if (cursorPos + 1 <= textSize) { - cursorPos++; textChanged = true; - } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case 3: // returned in SCI1 late and newer when Control - C is pressed - if (curEvent.modifiers & SCI_KEYMOD_CTRL) { - // Control-C erases the whole line - cursorPos = 0; text.clear(); - textChanged = true; + } + } + + if (!focused) { + break; + } + + // Consume the event now that we know it is not one of the + // defocusing events above + eventManager->getSciEvent(SCI_EVENT_ANY); + + // NOTE: In the original engine, the font and bitmap were + // reset here on each iteration through the loop, but it + // doesn't seem like this should be necessary since + // control is not yielded back to the VM until input is + // received, which means there is nothing that could modify + // the GfxText32's state with a different font in the + // meantime + + bool shouldDeleteChar = false; + bool shouldRedrawText = false; + uint16 lastCursorPosition = editor.cursorCharPosition; + if (event.type == SCI_EVENT_KEYBOARD) { + switch (event.character) { + case SCI_KEY_LEFT: + clearTextOnInput = false; + if (editor.cursorCharPosition > 0) { + --editor.cursorCharPosition; + } + break; + + case SCI_KEY_RIGHT: + clearTextOnInput = false; + if (editor.cursorCharPosition < editor.text.size()) { + ++editor.cursorCharPosition; + } + break; + + case SCI_KEY_HOME: + clearTextOnInput = false; + editor.cursorCharPosition = 0; + break; + + case SCI_KEY_END: + clearTextOnInput = false; + editor.cursorCharPosition = editor.text.size(); + break; + + case SCI_KEY_INSERT: + clearTextOnInput = false; + // Redrawing also changes the cursor rect to + // reflect the new insertion mode + shouldRedrawText = true; + _overwriteMode = !_overwriteMode; + break; + + case SCI_KEY_DELETE: + clearTextOnInput = false; + if (editor.cursorCharPosition < editor.text.size()) { + shouldDeleteChar = true; + } + break; + + case SCI_KEY_BACKSPACE: + clearTextOnInput = false; + shouldDeleteChar = true; + if (editor.cursorCharPosition > 0) { + --editor.cursorCharPosition; + } + break; + + case SCI_KEY_ETX: + editor.text.clear(); + editor.cursorCharPosition = 0; + shouldRedrawText = true; + break; + + default: { + if (event.character >= 20 && event.character < 257) { + if (clearTextOnInput) { + clearTextOnInput = false; + editor.text.clear(); } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; - case SCI_KEY_UP: - case SCI_KEY_DOWN: - case SCI_KEY_ENTER: - case SCI_KEY_ESC: - case SCI_KEY_TAB: - case SCI_KEY_SHIFT_TAB: - captureEvents = false; - break; - default: - if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') { - // Control-C in earlier SCI games (SCI0 - SCI1 middle) - // Control-C erases the whole line - cursorPos = 0; text.clear(); - textChanged = true; - } else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) { - // insert pressed character - textAddChar = true; - textChanged = true; + + if ( + (_overwriteMode && editor.cursorCharPosition < editor.maxLength) || + (editor.text.size() < editor.maxLength && _gfxText32->getCharWidth(event.character, true) + _gfxText32->getStringWidth(editor.text) < editor.textRect.width()) + ) { + if (_overwriteMode && editor.cursorCharPosition < editor.text.size()) { + editor.text.setChar(event.character, editor.cursorCharPosition); + } else { + editor.text.insertChar(event.character, editor.cursorCharPosition); + } + + ++editor.cursorCharPosition; + shouldRedrawText = true; } - eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event - break; } - break; + } } } - if (textChanged) { - rect = g_sci->_gfxCompare->getNSRect(controlObject); + if (shouldDeleteChar) { + shouldRedrawText = true; + if (editor.cursorCharPosition < editor.text.size()) { + editor.text.deleteChar(editor.cursorCharPosition); + } + } - if (textAddChar) { - const char *textPtr = text.c_str(); + if (shouldRedrawText) { + eraseCursor(editor); + _gfxText32->erase(editor.textRect, true); + _gfxText32->drawTextBox(editor.text); + drawCursor(editor); + textChanged = true; + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } else if (editor.cursorCharPosition != lastCursorPosition) { + eraseCursor(editor); + drawCursor(editor); + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } else { + flashCursor(editor); + screenItem->_updated = g_sci->_gfxFrameout->getScreenCount(); + } - // We check if we are really able to add the new char - uint16 textWidth = 0; - while (*textPtr) - textWidth += font->getCharWidth((byte)*textPtr++); - textWidth += font->getCharWidth(curEvent.character); + g_sci->_gfxFrameout->frameOut(true); + g_sci->getSciDebugger()->onFrame(); + g_sci->getEngineState()->speedThrottler(16); + g_sci->getEngineState()->_throttleTrigger = true; + } - // Does it fit? - if (textWidth >= rect.width()) { - return; - } + g_sci->_gfxFrameout->deletePlane(*plane); + if (readSelectorValue(segMan, controlObject, SELECTOR(frameOut))) { + g_sci->_gfxFrameout->frameOut(true); + } - text.insertChar(curEvent.character, cursorPos++); + _segMan->freeHunkEntry(editor.bitmap); - // Note: the following checkAltInput call might make the text - // too wide to fit, but SSCI fails to check that too. - } + if (textChanged) { + editor.text.trim(); + SciString *string = _segMan->lookupString(textObject); + string->fromString(editor.text); + } + + return make_reg(0, textChanged); +} - reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap)); - Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject); - //texteditCursorErase(); // TODO: Cursor +void GfxControls32::drawCursor(TextEditor &editor) { + if (!editor.cursorIsDrawn) { + editor.cursorRect.left = editor.textRect.left + _gfxText32->getTextWidth(editor.text, 0, editor.cursorCharPosition); - // Write back string - _segMan->strcpy(textReference, text.c_str()); - // Modify the buffer and show it - _text->createTextBitmap(controlObject, 0, 0, hunkId); + const int16 scaledFontHeight = _gfxText32->scaleUpHeight(_gfxText32->_font->getHeight()); - _text->drawTextBitmap(0, 0, nsRect, controlObject); - //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor - g_system->updateScreen(); + // NOTE: The original code branched on borderColor here but + // the two branches appeared to be identical, differing only + // because the compiler decided to be differently clever + // when optimising multiplication in each branch + if (_overwriteMode) { + editor.cursorRect.top = editor.textRect.top; + editor.cursorRect.setHeight(scaledFontHeight); } else { - // TODO: Cursor - /* - if (g_system->getMillis() >= _texteditBlinkTime) { - _paint16->invertRect(_texteditCursorRect); - _paint16->bitsShow(_texteditCursorRect); - _texteditCursorVisible = !_texteditCursorVisible; - texteditSetBlinkTime(); - } - */ + editor.cursorRect.top = editor.textRect.top + scaledFontHeight - 1; + editor.cursorRect.setHeight(1); } - textAddChar = false; - textChanged = false; - g_sci->sleep(10); - } // while + const char currentChar = editor.cursorCharPosition < editor.text.size() ? editor.text[editor.cursorCharPosition] : ' '; + editor.cursorRect.setWidth(_gfxText32->getCharWidth(currentChar, true)); + + _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true); + + editor.cursorIsDrawn = true; + } + + _nextCursorFlashTick = g_sci->getTickCount() + 30; +} + +void GfxControls32::eraseCursor(TextEditor &editor) { + if (editor.cursorIsDrawn) { + _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true); + editor.cursorIsDrawn = false; + } + + _nextCursorFlashTick = g_sci->getTickCount() + 30; } +void GfxControls32::flashCursor(TextEditor &editor) { + if (g_sci->getTickCount() > _nextCursorFlashTick) { + _gfxText32->invertRect(editor.bitmap, editor.width, editor.cursorRect, editor.foreColor, editor.backColor, true); + + editor.cursorIsDrawn = !editor.cursorIsDrawn; + _nextCursorFlashTick = g_sci->getTickCount() + 30; + } +} } // End of namespace Sci diff --git a/engines/sci/graphics/controls32.h b/engines/sci/graphics/controls32.h index 5af7c20f16..1bb7679ddd 100644 --- a/engines/sci/graphics/controls32.h +++ b/engines/sci/graphics/controls32.h @@ -29,20 +29,95 @@ class GfxCache; class GfxScreen; class GfxText32; +struct TextEditor { + /** + * The bitmap where the editor is rendered. + */ + reg_t bitmap; + + /** + * The width of the editor, in bitmap pixels. + */ + int16 width; + + /** + * The text in the editor. + */ + Common::String text; + + /** + * The rect where text should be drawn into the editor, + * in bitmap pixels. + */ + Common::Rect textRect; + + /** + * The color of the border. -1 indicates no border. + */ + int16 borderColor; + + /** + * The text color. + */ + uint8 foreColor; + + /** + * The background color. + */ + uint8 backColor; + + /** + * The transparent color. + */ + uint8 skipColor; + + /** + * The font used to render the text in the editor. + */ + GuiResourceId fontId; + + /** + * The current position of the cursor within the editor. + */ + uint16 cursorCharPosition; + + /** + * Whether or not the cursor is currently drawn to the + * screen. + */ + bool cursorIsDrawn; + + /** + * The rectangle for drawing the input cursor, in bitmap + * pixels. + */ + Common::Rect cursorRect; + + /** + * The maximum allowed text length, in characters. + */ + uint16 maxLength; +}; + /** * Controls class, handles drawing of controls in SCI32 (SCI2, SCI2.1, SCI3) games */ class GfxControls32 { public: GfxControls32(SegManager *segMan, GfxCache *cache, GfxText32 *text); - ~GfxControls32(); - void kernelTexteditChange(reg_t controlObject); + reg_t kernelEditText(const reg_t controlObject); private: SegManager *_segMan; - GfxCache *_cache; - GfxText32 *_text; + GfxCache *_gfxCache; + GfxText32 *_gfxText32; + + bool _overwriteMode; + uint32 _nextCursorFlashTick; + void drawCursor(TextEditor &editor); + void eraseCursor(TextEditor &editor); + void flashCursor(TextEditor &editor); }; } // End of namespace Sci diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index e8496b96e5..f5dd473959 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -336,6 +336,9 @@ void GfxCursor::setPosition(Common::Point pos) { && ((workaround->newPositionX == pos.x) && (workaround->newPositionY == pos.y))) { EngineState *s = g_sci->getEngineState(); s->_cursorWorkaroundActive = true; + // At least on OpenPandora it seems that the cursor is actually set, but a bit afterwards + // touch screen controls will overwrite the position. More information see kGetEvent in kevent.cpp. + s->_cursorWorkaroundPosCount = 5; // should be enough for OpenPandora s->_cursorWorkaroundPoint = pos; s->_cursorWorkaroundRect = Common::Rect(workaround->rectLeft, workaround->rectTop, workaround->rectRight, workaround->rectBottom); return; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index ccce8ef046..fc9f5d8299 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -47,6 +47,7 @@ #include "sci/graphics/paint32.h" #include "sci/graphics/palette32.h" #include "sci/graphics/picture.h" +#include "sci/graphics/remap.h" #include "sci/graphics/text32.h" #include "sci/graphics/plane32.h" #include "sci/graphics/screen_item32.h" @@ -81,7 +82,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd _showStyles(nullptr), // TODO: Stop using _gfxScreen _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), - _priorityMap(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), _remapOccurred(false), _frameNowVisible(false), _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()), @@ -133,8 +133,9 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd } GfxFrameout::~GfxFrameout() { - CelObj::deinit(); clear(); + CelObj::deinit(); + free(_currentBuffer.getPixels()); } void GfxFrameout::run() { @@ -245,16 +246,11 @@ void GfxFrameout::syncWithScripts(bool addElements) { void GfxFrameout::kernelAddScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); -// TODO: Remove -// debug("Adding screen item %04x:%04x to plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); - _segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewInserted); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kAddScreenItem"); + error("kAddScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object)); } ScreenItem *screenItem = plane->_screenItemList.findByObject(object); @@ -272,21 +268,17 @@ void GfxFrameout::kernelUpdateScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kUpdateScreenItem"); + error("kUpdateScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object)); } ScreenItem *screenItem = plane->_screenItemList.findByObject(object); if (screenItem == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid screen item passed to kUpdateScreenItem"); + error("kUpdateScreenItem: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); } screenItem->update(object); } else { - warning("TODO: Magnifier view not implemented yet!"); + error("Magnifier view is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); } } @@ -296,18 +288,11 @@ void GfxFrameout::kernelDeleteScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - // TODO: Remove -// warning("Invalid plane selector %04x:%04x passed to kDeleteScreenItem (real engine ignores this)", PRINT_REG(object)); return; } -// TODO: Remove -// debug("Deleting screen item %04x:%04x from plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); - ScreenItem *screenItem = plane->_screenItemList.findByObject(object); if (screenItem == nullptr) { -// TODO: Remove -// warning("Invalid screen item %04x:%04x passed to kDeleteScreenItem (real engine ignores this)", PRINT_REG(object)); return; } @@ -338,8 +323,7 @@ void GfxFrameout::kernelAddPlane(const reg_t object) { void GfxFrameout::kernelUpdatePlane(const reg_t object) { Plane *plane = _planes.findByObject(object); if (plane == nullptr) { - warning("plane %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - error("Invalid plane selector passed to kUpdatePlane"); + error("kUpdatePlane: Plane %04x:%04x not found", PRINT_REG(object)); } plane->update(object); @@ -349,8 +333,7 @@ void GfxFrameout::kernelUpdatePlane(const reg_t object) { void GfxFrameout::kernelDeletePlane(const reg_t object) { Plane *plane = _planes.findByObject(object); if (plane == nullptr) { - warning("plane %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - error("Invalid plane selector passed to kDeletePlane"); + error("kDeletePlane: Plane %04x:%04x not found", PRINT_REG(object)); } if (plane->_created) { @@ -358,13 +341,53 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) { // just ends up doing this anyway so we skip the extra indirection _planes.erase(plane); } else { - // TODO: Remove -// debug("Deleting plane %04x:%04x", PRINT_REG(object)); plane->_created = 0; plane->_deleted = g_sci->_gfxFrameout->getScreenCount(); } } +void GfxFrameout::deletePlane(Plane &planeToFind) { + Plane *plane = _planes.findByObject(planeToFind._object); + if (plane == nullptr) { + error("deletePlane: Plane %04x:%04x not found", PRINT_REG(planeToFind._object)); + } + + if (plane->_created) { + _planes.erase(plane); + } else { + plane->_created = 0; + plane->_moved = 0; + plane->_deleted = getScreenCount(); + } +} + +void GfxFrameout::kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics) { + Plane *plane = _planes.findByObject(object); + if (plane == nullptr) { + error("kMovePlaneItems: Plane %04x:%04x not found", PRINT_REG(object)); + } + + plane->scrollScreenItems(deltaX, deltaY, scrollPics); + + for (ScreenItemList::iterator it = plane->_screenItemList.begin(); it != plane->_screenItemList.end(); ++it) { + ScreenItem &screenItem = **it; + + // If object is a number, the screen item from the + // engine, not a script, and should be ignored + if (screenItem._object.isNumber()) { + continue; + } + + if (deltaX != 0) { + writeSelectorValue(_segMan, screenItem._object, SELECTOR(x), readSelectorValue(_segMan, screenItem._object, SELECTOR(x)) + deltaX); + } + + if (deltaY != 0) { + writeSelectorValue(_segMan, screenItem._object, SELECTOR(y), readSelectorValue(_segMan, screenItem._object, SELECTOR(y)) + deltaY); + } + } +} + int16 GfxFrameout::kernelGetHighPlanePri() { return _planes.getTopSciPlanePriority(); } @@ -401,8 +424,7 @@ void GfxFrameout::updatePlane(Plane &plane) { void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 x, const int16 y, const bool mirrorX) { Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kAddPicAt"); + error("kAddPicAt: Plane %04x:%04x not found", PRINT_REG(planeObject)); } plane->addPic(pictureId, Common::Point(x, y), mirrorX); } @@ -424,9 +446,8 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) screenItemLists.resize(_planes.size()); eraseLists.resize(_planes.size()); - // _numActiveRemaps was a global in SCI engine - if (/* TODO Remap::_numActiveRemaps > 0 */ false && _remapOccurred) { - // remapMarkRedraw(); + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); } calcLists(screenItemLists, eraseLists, rect); @@ -750,22 +771,12 @@ void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) { } void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { - _hasRemappedScreenItem = false; - if (/* TODO: g_Remap_UnknownCounter2 */ false && !_priorityMap.isNull()) { - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { - if ((*it)->screenItem->getCelObj()._remap) { - _hasRemappedScreenItem = true; - break; - } - } - } - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { DrawItem &drawItem = **it; mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); ScreenItem &screenItem = *drawItem.screenItem; // TODO: Remove -// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), drawItem.rect.left, drawItem.rect.top, drawItem.rect.right, drawItem.rect.bottom); +// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect)); CelObj &celObj = *screenItem._celObj; celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX); } @@ -804,9 +815,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry Palette sourcePalette(*_palette->getNextPalette()); alterVmap(sourcePalette, sourcePalette, -1, styleRanges); - // TODO: unsure if this is what this variable actually - // represents, but it is the correct variable number - int16 lastRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); + int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight()); _showList.add(rect); @@ -822,11 +831,9 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry screenItemLists.resize(_planes.size()); eraseLists.resize(_planes.size()); - // TODO: Remap - // _numActiveRemaps was a global in SCI engine - // if (Remap::_numActiveRemaps > 0 && _remapOccurred) { - // _screen->remapMarkRedraw(); - // } + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } calcLists(screenItemLists, eraseLists, calcRect); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { @@ -849,7 +856,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry Palette nextPalette(*_palette->getNextPalette()); - if (lastRoom < 1000) { + if (prevRoom < 1000) { for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { if (styleRanges[i] == -1 || styleRanges[i] == 0) { sourcePalette.colors[i] = nextPalette.colors[i]; @@ -885,11 +892,9 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry // plane->updateRedrawAllCount(); } - // TODO: Remap - // _numActiveRemaps was a global in SCI engine - // if (Remap::_numActiveRemaps > 0 && _remapOccurred) { - // _screen->remapMarkRedraw(); - // } + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } calcLists(screenItemLists, eraseLists, calcRect); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { @@ -1010,8 +1015,6 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co // NOTE: This is currBuffer->ptr in SCI engine byte *pixels = (byte *)_currentBuffer.getPixels(); - // TODO: Guessing that display width/height is the correct - // equivalent to screen width/height in SCI engine for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) { byte currentValue = pixels[pixelIndex]; int8 styleRangeValue = styleRanges[currentValue]; @@ -1600,16 +1603,15 @@ void GfxFrameout::processShowStyles() { } if (doFrameOut) { - Common::Rect frameOutRect(0, 0); - frameOut(true, frameOutRect); - - // TODO: It seems like transitions without the âanimateâ - // flag are too fast in in SCI2â2.1early, but the throttle - // value is arbitrary. Someone on real hardware probably - // needs to test what the actual speed of transitions - // should be - //state->speedThrottler(30); - //state->_throttleTrigger = true; + frameOut(true); + + // TODO: Transitions without the âanimateâ flag are too + // fast, but the throttle value is arbitrary. Someone on + // real hardware probably needs to test what the actual + // speed of these transitions should be + EngineState *state = g_sci->getEngineState(); + state->speedThrottler(33); + state->_throttleTrigger = true; } } while(continueProcessing && doFrameOut); } @@ -1948,7 +1950,7 @@ bool GfxFrameout::processShowStyle12(ShowStyleEntry *const showStyle) { } #endif -void GfxFrameout::kernelFrameout(const bool shouldShowBits) { +void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { if (_showStyles != nullptr) { processShowStyles(); } else if (_palMorphIsOn) { @@ -1960,78 +1962,86 @@ void GfxFrameout::kernelFrameout(const bool shouldShowBits) { // doScroll(); // } - Common::Rect frameOutRect(0, 0); - frameOut(shouldShowBits, frameOutRect); + frameOut(shouldShowBits); } } -uint16 GfxFrameout::kernelIsOnMe(int16 x, int16 y, uint16 checkPixels, reg_t screenObject) { - reg_t planeObject = readSelector(_segMan, screenObject, SELECTOR(plane)); - Plane *screenItemPlane = _visiblePlanes.findByObject(planeObject); // Search for plane in visible planes - ScreenItem *screenItem = nullptr; +#pragma mark - +#pragma mark Mouse cursor - if (!screenItemPlane) { - // Specified plane not found - return 0; +reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &position, bool checkPixel) const { + reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); + Plane *plane = _visiblePlanes.findByObject(planeObject); + if (plane == nullptr) { + return make_reg(0, 0); } - screenItem = screenItemPlane->_screenItemList.findByObject(screenObject); - if (!screenItem) { - // Specified screen object not in item list - return 0; + ScreenItem *screenItem = plane->_screenItemList.findByObject(object); + if (screenItem == nullptr) { + return make_reg(0, 0); } - // Original SCI32 seems to have made a copy (?) of the screenitem? - // there is also a "or [ebp+arg_56], 1 - not sure what that did - return isOnMe(screenItemPlane, screenItem, x, y, checkPixels); + return make_reg(0, isOnMe(*screenItem, *plane, position, checkPixel)); } -uint16 GfxFrameout::isOnMe(Plane *screenItemPlane, ScreenItem *screenItem, int16 x, int16 y, uint16 checkPixels) { - // adjust coordinate according to resolution - int32 adjustedX = x * getCurrentBuffer().screenWidth / getCurrentBuffer().scriptWidth; - int32 adjustedY = y * getCurrentBuffer().screenHeight / getCurrentBuffer().scriptHeight; - - adjustedX += screenItemPlane->_planeRect.left; - adjustedY += screenItemPlane->_planeRect.top; +bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const { - //warning("kIsOnMe %s %d (%d, %d -> %d, %d) mouse %d, %d", _segMan->getObjectName(screenObject), checkPixels, screenItem->_screenRect.left, screenItem->_screenRect.top, screenItem->_screenRect.right, screenItem->_screenRect.bottom, adjustedX, adjustedY); + Common::Point scaledPosition(position); + mulru(scaledPosition, Ratio(_currentBuffer.screenWidth, _currentBuffer.scriptWidth), Ratio(_currentBuffer.screenHeight, _currentBuffer.scriptHeight)); + scaledPosition.x += plane._planeRect.left; + scaledPosition.y += plane._planeRect.top; - if (!screenItem->_screenRect.contains(adjustedX, adjustedY)) { - // Specified coordinates are not within screen item - return 0; + if (!screenItem._screenRect.contains(scaledPosition)) { + return false; } - //warning("HIT!"); - if (checkPixels) { - //warning("Check Pixels"); - CelObj &screenItemCelObject = screenItem->getCelObj(); + if (checkPixel) { + CelObj &celObj = screenItem.getCelObj(); - Common::Point celAdjustedPoint(adjustedX, adjustedY); - bool celMirrored = screenItem->_mirrorX ^ screenItemCelObject._mirrorX; + bool mirrorX = screenItem._mirrorX ^ celObj._mirrorX; - celAdjustedPoint.x -= screenItem->_scaledPosition.x; - celAdjustedPoint.y -= screenItem->_scaledPosition.y; + scaledPosition.x -= screenItem._scaledPosition.x; + scaledPosition.y -= screenItem._scaledPosition.y; - Ratio celAdjustXRatio(screenItemCelObject._scaledWidth, getCurrentBuffer().screenWidth); - Ratio celAdjustYRatio(screenItemCelObject._scaledHeight, getCurrentBuffer().screenHeight); - mulru(celAdjustedPoint, celAdjustXRatio, celAdjustYRatio); + mulru(scaledPosition, Ratio(celObj._scaledWidth, _currentBuffer.screenWidth), Ratio(celObj._scaledHeight, _currentBuffer.screenHeight)); - if ((screenItem->_scale.signal) && (screenItem->_scale.x) && (screenItem->_scale.y)) { - // Apply scaling - celAdjustedPoint.x = celAdjustedPoint.x * 128 / screenItem->_scale.x; - celAdjustedPoint.y = celAdjustedPoint.y * 128 / screenItem->_scale.y; + if (screenItem._scale.signal != kScaleSignalNone && screenItem._scale.x && screenItem._scale.y) { + scaledPosition.x = scaledPosition.x * 128 / screenItem._scale.x; + scaledPosition.y = scaledPosition.y * 128 / screenItem._scale.y; } - byte coordinateColor = screenItemCelObject.readPixel(celAdjustedPoint.x, celAdjustedPoint.y, celMirrored); - byte transparentColor = screenItemCelObject._transparentColor; + uint8 pixel = celObj.readPixel(scaledPosition.x, scaledPosition.y, mirrorX); + return pixel != celObj._transparentColor; + } - if (coordinateColor == transparentColor) { - // Coordinate is transparent - //warning("TRANSPARENT!"); - return 0; - } + return true; +} + +void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { + const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane)); + + Plane *plane = _planes.findByObject(planeObject); + if (plane == nullptr) { + error("kSetNowSeen: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(screenItemObject)); + } + + ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject); + if (screenItem == nullptr) { + error("kSetNowSeen: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItemObject), PRINT_REG(planeObject)); + } + + Common::Rect result = screenItem->getNowSeenRect(*plane); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsLeft), result.left); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1); +} + +void GfxFrameout::remapMarkRedraw() { + for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) { + Plane *p = *it; + p->remapMarkRedraw(); } - return 1; } #pragma mark - @@ -2052,6 +2062,15 @@ void GfxFrameout::printVisiblePlaneList(Console *con) const { printPlaneListInternal(con, _visiblePlanes); } +void GfxFrameout::printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const { + ScreenItemList::size_type i = 0; + for (ScreenItemList::const_iterator sit = screenItemList.begin(); sit != screenItemList.end(); sit++) { + ScreenItem *screenItem = *sit; + con->debugPrintf("%2d: ", i++); + screenItem->printDebugInfo(con); + } +} + void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) const { Plane *p = _planes.findByObject(planeObject); @@ -2060,12 +2079,18 @@ void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) cons return; } - ScreenItemList::size_type i = 0; - for (ScreenItemList::iterator sit = p->_screenItemList.begin(); sit != p->_screenItemList.end(); sit++) { - ScreenItem *screenItem = *sit; - con->debugPrintf("%2d: ", i++); - screenItem->printDebugInfo(con); + printPlaneItemListInternal(con, p->_screenItemList); +} + +void GfxFrameout::printVisiblePlaneItemList(Console *con, const reg_t planeObject) const { + Plane *p = _visiblePlanes.findByObject(planeObject); + + if (p == nullptr) { + con->debugPrintf("Plane does not exist"); + return; } + + printPlaneItemListInternal(con, p->_screenItemList); } } // End of namespace Sci diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 0da6866f7c..aef215356d 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -240,11 +240,13 @@ public: #pragma mark Screen items private: void deleteScreenItem(ScreenItem *screenItem, const reg_t plane); + void remapMarkRedraw(); public: void kernelAddScreenItem(const reg_t object); void kernelUpdateScreenItem(const reg_t object); void kernelDeleteScreenItem(const reg_t object); + void kernelSetNowSeen(const reg_t screenItemObject) const; #pragma mark - #pragma mark Planes @@ -259,6 +261,13 @@ private: PlaneList _planes; /** + * Updates an existing plane with properties from the + * given VM object. + */ + void updatePlane(Plane &plane); + +public: + /** * Creates and adds a new plane to the plane list, or * cancels deletion and updates an already-existing * plane if a plane matching the given plane VM object @@ -270,18 +279,23 @@ private: void addPlane(Plane &plane); /** - * Updates an existing plane with properties from the - * given VM object. + * Deletes a plane within the current plane list. + * + * @note This method is on Screen in SCI engine, but it + * is only ever called on `GraphicsMgr.screen`. */ - void updatePlane(Plane &plane); + void deletePlane(Plane &plane); -public: const PlaneList &getPlanes() const { return _planes; } + const PlaneList &getVisiblePlanes() const { + return _visiblePlanes; + } void kernelAddPlane(const reg_t object); void kernelUpdatePlane(const reg_t object); void kernelDeletePlane(const reg_t object); + void kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics); int16 kernelGetHighPlanePri(); #pragma mark - @@ -338,13 +352,6 @@ private: */ Buffer _currentBuffer; - // TODO: In SCI2.1/SQ6, priority map pixels are not allocated - // by default. In SCI2/GK1, pixels are allocated, but not used - // anywhere except within CelObj::Draw in seemingly the same - // way they are used in SCI2.1/SQ6: that is, never read, only - // written. - Buffer _priorityMap; - /** * TODO: Documentation */ @@ -423,13 +430,6 @@ private: void drawScreenItemList(const DrawList &screenItemList); /** - * Updates the internal screen buffer for the next - * frame. If `shouldShowBits` is true, also sends the - * buffer to hardware. - */ - void frameOut(const bool shouldShowBits, const Common::Rect &rect); - - /** * Adds a new rectangle to the list of regions to write * out to the hardware. The provided rect may be merged * into an existing rectangle to reduce the number of @@ -450,12 +450,6 @@ private: public: /** - * TODO: Document - * This is used by CelObj::Draw. - */ - bool _hasRemappedScreenItem; - - /** * Whether palMorphFrameOut should be used instead of * frameOut for rendering. Used by kMorphOn to * explicitly enable palMorphFrameOut for one frame. @@ -466,7 +460,14 @@ public: return _currentBuffer; } - void kernelFrameout(const bool showBits); + void kernelFrameOut(const bool showBits); + + /** + * Updates the internal screen buffer for the next + * frame. If `shouldShowBits` is true, also sends the + * buffer to hardware. + */ + void frameOut(const bool shouldShowBits, const Common::Rect &rect = Common::Rect()); /** * Modifies the raw pixel data for the next frame with @@ -474,11 +475,6 @@ public: */ void alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges); - // TODO: SCI2 engine never uses priority map? - inline Buffer &getPriorityMap() { - return _priorityMap; - } - // NOTE: This function is used within ScreenItem subsystem and assigned // to various booleanish fields that seem to represent the state of the // screen item (created, updated, deleted). In GK1/DOS, Phant1/m68k, @@ -491,8 +487,17 @@ public: return 1; }; - uint16 kernelIsOnMe(int16 x, int16 y, uint16 checkPixels, reg_t screenObject); - uint16 isOnMe(Plane *screenItemPlane, ScreenItem *screenItem, int16 x, int16 y, uint16 checkPixels); +#pragma mark - +#pragma mark Mouse cursor +private: + /** + * Determines whether or not the point given by + * `position` is inside of the given screen item. + */ + bool isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const; + +public: + reg_t kernelIsOnMe(const reg_t object, const Common::Point &position, const bool checkPixel) const; #pragma mark - #pragma mark Debugging @@ -501,6 +506,8 @@ public: void printVisiblePlaneList(Console *con) const; void printPlaneListInternal(Console *con, const PlaneList &planeList) const; void printPlaneItemList(Console *con, const reg_t planeObject) const; + void printVisiblePlaneItemList(Console *con, const reg_t planeObject) const; + void printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const; }; } // End of namespace Sci diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index fbad120374..19dddd74b8 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -139,16 +139,28 @@ inline void mul(Common::Rect &rect, const Common::Rational &ratioX, const Common } /** + * Multiplies a rectangle by two ratios with default + * rounding. Modifies the rect directly. Uses inclusive + * rectangle rounding. + */ +inline void mulinc(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) { + rect.left = (rect.left * ratioX).toInt(); + rect.top = (rect.top * ratioY).toInt(); + rect.right = ((rect.right - 1) * ratioX).toInt() + 1; + rect.bottom = ((rect.bottom - 1) * ratioY).toInt() + 1; +} + +/** * Multiplies a number by a rational number, rounding up to * the nearest whole number. */ -inline int mulru(const int value, const Common::Rational &ratio) { - int num = value * ratio.getNumerator(); +inline int mulru(const int value, const Common::Rational &ratio, const int extra = 0) { + int num = (value + extra) * ratio.getNumerator(); int result = num / ratio.getDenominator(); if (num > ratio.getDenominator() && num % ratio.getDenominator()) { ++result; } - return result; + return result - extra; } /** @@ -165,19 +177,12 @@ inline void mulru(Common::Point &point, const Common::Rational &ratioX, const Co * Multiplies a point by two rational numbers for X and Y, * rounding up to the nearest whole number. Modifies the * rect directly. - * - * @note In SCI engine, the bottom-right corner of rects - * received an additional one pixel during the - * multiplication in order to round up to include the - * bottom-right corner. Since ScummVM rects do not include - * the bottom-right corner, doing this ends up making rects - * a pixel too wide/tall depending upon the remainder. */ -inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY) { +inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Common::Rational &ratioY, const int extra) { rect.left = mulru(rect.left, ratioX); rect.top = mulru(rect.top, ratioY); - rect.right = mulru(rect.right, ratioX); - rect.bottom = mulru(rect.bottom, ratioY); + rect.right = mulru(rect.right - 1, ratioX, extra) + 1; + rect.bottom = mulru(rect.bottom - 1, ratioY, extra) + 1; } struct Buffer : public Graphics::Surface { diff --git a/engines/sci/graphics/lists32.h b/engines/sci/graphics/lists32.h index bb990e17ca..4f74c77325 100644 --- a/engines/sci/graphics/lists32.h +++ b/engines/sci/graphics/lists32.h @@ -36,7 +36,7 @@ namespace Sci { * calling `erase` or when destroying the * StablePointerArray. */ -template <class T, uint N> +template<class T, uint N> class StablePointerArray { uint _size; T *_items[N]; @@ -178,7 +178,7 @@ public: } }; -template <typename T> +template<typename T> class FindByObject { const reg_t &_object; public: @@ -188,5 +188,5 @@ public: } }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 6f6e0b672e..1514ad838f 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -32,6 +32,7 @@ #include "sci/graphics/cache.h" #include "sci/graphics/maciconbar.h" #include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/view.h" @@ -103,9 +104,6 @@ GfxPalette::GfxPalette(ResourceManager *resMan, GfxScreen *screen) default: error("GfxPalette: Unknown view type"); } - - _remapOn = false; - resetRemapping(); } GfxPalette::~GfxPalette() { @@ -336,79 +334,6 @@ void GfxPalette::set(Palette *newPalette, bool force, bool forceRealMerge) { } } -byte GfxPalette::remapColor(byte remappedColor, byte screenColor) { - assert(_remapOn); - if (_remappingType[remappedColor] == kRemappingByRange) - return _remappingByRange[screenColor]; - else if (_remappingType[remappedColor] == kRemappingByPercent) - return _remappingByPercent[screenColor]; - else - error("remapColor(): Color %d isn't remapped", remappedColor); - - return 0; // should never reach here -} - -void GfxPalette::resetRemapping() { - _remapOn = false; - _remappingPercentToSet = 0; - - for (int i = 0; i < 256; i++) { - _remappingType[i] = kRemappingNone; - _remappingByPercent[i] = i; - _remappingByRange[i] = i; - } -} - -void GfxPalette::setRemappingPercent(byte color, byte percent) { - _remapOn = true; - - // We need to defer the setup of the remapping table every time the screen - // palette is changed, so that kernelFindColor() can find the correct - // colors. Set it once here, in case the palette stays the same and update - // it on each palette change by copySysPaletteToScreen(). - _remappingPercentToSet = percent; - - for (int i = 0; i < 256; i++) { - byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; - byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; - byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; - _remappingByPercent[i] = kernelFindColor(r, g, b); - } - - _remappingType[color] = kRemappingByPercent; -} - -void GfxPalette::setRemappingPercentGray(byte color, byte percent) { - _remapOn = true; - - // We need to defer the setup of the remapping table every time the screen - // palette is changed, so that kernelFindColor() can find the correct - // colors. Set it once here, in case the palette stays the same and update - // it on each palette change by copySysPaletteToScreen(). - _remappingPercentToSet = percent; - - // Note: This is not what the original does, but the results are the same visually - for (int i = 0; i < 256; i++) { - byte rComponent = (byte)(_sysPalette.colors[i].r * _remappingPercentToSet * 0.30 / 100); - byte gComponent = (byte)(_sysPalette.colors[i].g * _remappingPercentToSet * 0.59 / 100); - byte bComponent = (byte)(_sysPalette.colors[i].b * _remappingPercentToSet * 0.11 / 100); - byte luminosity = rComponent + gComponent + bComponent; - _remappingByPercent[i] = kernelFindColor(luminosity, luminosity, luminosity); - } - - _remappingType[color] = kRemappingByPercent; -} - -void GfxPalette::setRemappingRange(byte color, byte from, byte to, byte base) { - _remapOn = true; - - for (int i = from; i <= to; i++) { - _remappingByRange[i] = i + base; - } - - _remappingType[color] = kRemappingByRange; -} - bool GfxPalette::insert(Palette *newPalette, Palette *destPalette) { bool paletteChanged = false; @@ -589,15 +514,8 @@ void GfxPalette::copySysPaletteToScreen() { } } - // Check if we need to reset remapping by percent with the new colors. - if (_remappingPercentToSet) { - for (int i = 0; i < 256; i++) { - byte r = _sysPalette.colors[i].r * _remappingPercentToSet / 100; - byte g = _sysPalette.colors[i].g * _remappingPercentToSet / 100; - byte b = _sysPalette.colors[i].b * _remappingPercentToSet / 100; - _remappingByPercent[i] = kernelFindColor(r, g, b); - } - } + if (g_sci->_gfxRemap16) + g_sci->_gfxRemap16->updateRemapping(); g_system->getPaletteManager()->setPalette(bpal, 0, 256); } diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 61a8d36cb9..7335dc59d0 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -35,12 +35,6 @@ class GfxScreen; #define SCI_PALETTE_MATCH_PERFECT 0x8000 #define SCI_PALETTE_MATCH_COLORMASK 0xFF -enum ColorRemappingType { - kRemappingNone = 0, - kRemappingByRange = 1, - kRemappingByPercent = 2 -}; - /** * Palette class, handles palette operations like changing intensity, setting up the palette, merging different palettes */ @@ -64,15 +58,6 @@ public: void getSys(Palette *pal); uint16 getTotalColorCount() const { return _totalScreenColors; } - void resetRemapping(); - void setRemappingPercent(byte color, byte percent); - void setRemappingPercentGray(byte color, byte percent); - void setRemappingRange(byte color, byte from, byte to, byte base); - bool isRemapped(byte color) const { - return _remapOn && (_remappingType[color] != kRemappingNone); - } - byte remapColor(byte remappedColor, byte screenColor); - void setOnScreen(); void copySysPaletteToScreen(); @@ -138,12 +123,6 @@ protected: int _palVarySignal; uint16 _totalScreenColors; - bool _remapOn; - ColorRemappingType _remappingType[256]; - byte _remappingByPercent[256]; - byte _remappingByRange[256]; - uint16 _remappingPercentToSet; - void loadMacIconBarPalette(); byte *_macClut; }; diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index 9204e4bf96..6844011675 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -28,6 +28,7 @@ #include "sci/event.h" #include "sci/resource.h" #include "sci/graphics/palette32.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" namespace Sci { @@ -55,7 +56,9 @@ GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen) _cyclers(), _cycleMap() { _varyPercent = _varyTargetPercent; - memset(_fadeTable, 100, sizeof(_fadeTable)); + for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) { + _fadeTable[i] = 100; + } // NOTE: In SCI engine, the palette manager constructor loads // the default palette, but in ScummVM this initialisation // is performed by SciEngine::run; see r49523 for details @@ -68,7 +71,7 @@ GfxPalette32::~GfxPalette32() { } inline void mergePaletteInternal(Palette *const to, const Palette *const from) { - for (int i = 0; i < ARRAYSIZE(to->colors); ++i) { + for (int i = 0, len = ARRAYSIZE(to->colors); i < len; ++i) { if (from->colors[i].used) { to->colors[i] = from->colors[i]; } @@ -221,9 +224,7 @@ int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const i bool GfxPalette32::updateForFrame() { applyAll(); _versionUpdated = false; - // TODO: Implement remapping - // return g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); - return false; + return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette); } void GfxPalette32::updateFFrame() { @@ -231,8 +232,7 @@ void GfxPalette32::updateFFrame() { _nextPalette.colors[i] = _sourcePalette.colors[i]; } _versionUpdated = false; - // TODO: Implement remapping - // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette); + g_sci->_gfxRemap32->remapAllTables(_nextPalette != _sysPalette); } void GfxPalette32::updateHardware() { @@ -695,14 +695,8 @@ void GfxPalette32::cycleAllOn() { } void GfxPalette32::cycleAllPause() { - // TODO: The SCI SQ6 cycleAllPause function does not seem to perform - // nullptr checking?? This would definitely cause null pointer - // dereference in SCI code. I have not seen anything actually call - // this function yet, so it is possible it is just unused and broken - // in SCI SQ6. This assert exists so that if this function is called, - // it is noticed and can be rechecked in the actual engine. - // Obviously this code *does* do nullptr checks instead of crashing. :) - assert(0); + // NOTE: The original engine did not check for null pointers in the + // palette cyclers pointer array. for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { PalCycler *cycler = _cyclers[i]; if (cycler != nullptr) { @@ -795,7 +789,7 @@ void GfxPalette32::applyCycles() { // the last palette entry is intentionally left unmodified, or if this is a bug // in the engine. It certainly seems confused because all other places that accept // color ranges typically receive values in the range of 0â255. -void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) { +void GfxPalette32::setFade(uint16 percent, uint8 fromColor, uint16 numColorsToFade) { if (fromColor > numColorsToFade) { return; } @@ -817,9 +811,10 @@ void GfxPalette32::applyFade() { Color &color = _nextPalette.colors[i]; - color.r = (int16)color.r * _fadeTable[i] / 100; - color.g = (int16)color.g * _fadeTable[i] / 100; - color.b = (int16)color.b * _fadeTable[i] / 100; + color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100); + color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100); + color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100); } } -} + +} // End of namespace Sci diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index 9da217bf31..a5450776dc 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -257,6 +257,7 @@ public: void cycleAllOff(); void applyAllCycles(); void applyCycles(); + const bool *getCyclemap() { return _cycleMap; } #pragma mark - #pragma mark Fading @@ -265,13 +266,19 @@ private: * The fade table records the expected intensity level of each pixel * in the palette that will be displayed on the next frame. */ - byte _fadeTable[256]; + uint16 _fadeTable[256]; public: - void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor); + /** + * Sets the intensity level for a range of palette + * entries. An intensity of zero indicates total + * darkness. Intensity may be set to over 100 percent. + */ + void setFade(const uint16 percent, const uint8 fromColor, const uint16 toColor); void fadeOff(); void applyFade(); }; -} + +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index d0de5b5917..9e75379b58 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -27,6 +27,7 @@ #include "sci/graphics/frameout.h" #include "sci/graphics/lists32.h" #include "sci/graphics/plane32.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/screen_item32.h" @@ -43,10 +44,10 @@ void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) { #pragma mark Plane uint16 Plane::_nextObjectId = 20000; -Plane::Plane(const Common::Rect &gameRect) : +Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) : _width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), _height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), -_pictureId(kPlanePicColored), +_pictureId(pictureId), _mirrored(false), _back(0), _priorityChanged(0), @@ -55,6 +56,7 @@ _redrawAllCount(g_sci->_gfxFrameout->getScreenCount()), _created(g_sci->_gfxFrameout->getScreenCount()), _updated(0), _deleted(0), +_moved(0), _gameRect(gameRect) { convertGameRectToPlaneRect(); _priority = MAX(10000, g_sci->_gfxFrameout->getPlanes().getTopPlanePriority() + 1); @@ -136,7 +138,7 @@ void Plane::convertGameRectToPlaneRect() { const Ratio ratioY = Ratio(screenHeight, scriptHeight); _planeRect = _gameRect; - mulru(_planeRect, ratioX, ratioY); + mulru(_planeRect, ratioX, ratioY, 1); } void Plane::printDebugInfo(Console *con) const { @@ -296,7 +298,7 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList ScreenItemList::size_type planeItemCount = _screenItemList.size(); ScreenItemList::size_type visiblePlaneItemCount = visiblePlane._screenItemList.size(); - for (PlaneList::size_type i = 0; i < planeItemCount; ++i) { + for (ScreenItemList::size_type i = 0; i < planeItemCount; ++i) { ScreenItem *vitem = nullptr; // NOTE: The original engine used an array without bounds checking // so could just get the visible screen item directly; we need to @@ -311,22 +313,23 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList if (i < _screenItemList.size() && item != nullptr) { if (item->_deleted) { // add item's rect to erase list - if (i < visiblePlane._screenItemList.size() && vitem != nullptr) { - if (!vitem->_screenRect.isEmpty()) { - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps? - mergeToRectList(vitem->_screenRect, eraseList); - } else { - eraseList.add(vitem->_screenRect); - } + if ( + i < visiblePlane._screenItemList.size() && + vitem != nullptr && + !vitem->_screenRect.isEmpty() + ) { + if (g_sci->_gfxRemap32->getRemapCount()) { + mergeToRectList(vitem->_screenRect, eraseList); + } else { + eraseList.add(vitem->_screenRect); } } } else if (item->_created) { // add item to draw list - item->getCelObj(); item->calcRects(*this); if(!item->_screenRect.isEmpty()) { - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps? + if (g_sci->_gfxRemap32->getRemapCount()) { drawList.add(item, item->_screenRect); mergeToRectList(item->_screenRect, eraseList); } else { @@ -335,9 +338,8 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList } } else if (item->_updated) { // add old rect to erase list, new item to draw list - item->getCelObj(); item->calcRects(*this); - if (/* TODO: g_Remap_numActiveRemaps */ false) { // active remaps + if (g_sci->_gfxRemap32->getRemapCount()) { // if item and vitem don't overlap, ... if (item->_screenRect.isEmpty() || i >= visiblePlaneItemCount || @@ -350,7 +352,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList drawList.add(item, item->_screenRect); mergeToRectList(item->_screenRect, eraseList); } - if (i < visiblePlaneItemCount && vitem != nullptr && !vitem->_screenRect.isEmpty()) { + if ( + i < visiblePlaneItemCount && + vitem != nullptr && + !vitem->_screenRect.isEmpty() + ) { mergeToRectList(vitem->_screenRect, eraseList); } } else { @@ -358,10 +364,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList // and item to draw list // TODO: This was changed from disasm, verify please! - Common::Rect extendedScreenItem = vitem->_screenRect; - extendedScreenItem.extend(item->_screenRect); + Common::Rect extendedScreenRect = vitem->_screenRect; + extendedScreenRect.extend(item->_screenRect); + drawList.add(item, item->_screenRect); - mergeToRectList(extendedScreenItem, eraseList); + mergeToRectList(extendedScreenRect, eraseList); } } else { // if no active remaps, just add item to draw list and old rect @@ -369,7 +376,11 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList if (!item->_screenRect.isEmpty()) { drawList.add(item, item->_screenRect); } - if (i < visiblePlaneItemCount && vitem != nullptr && !vitem->_screenRect.isEmpty()) { + if ( + i < visiblePlaneItemCount && + vitem != nullptr && + !vitem->_screenRect.isEmpty() + ) { eraseList.add(vitem->_screenRect); } } @@ -381,20 +392,24 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList breakEraseListByPlanes(eraseList, planeList); breakDrawListByPlanes(drawList, planeList); + // We store the current size of the drawlist, as we want to loop + // over the currently inserted entries later. + DrawList::size_type drawListSizePrimary = drawList.size(); + if (/* TODO: dword_C6288 */ false) { // "high resolution pictures"???? _screenItemList.sort(); bool encounteredPic = false; bool v81 = false; for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - Common::Rect *rect = eraseList[i]; + const Common::Rect *rect = eraseList[i]; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; if (j < _screenItemList.size() && item != nullptr) { if (rect->intersects(item->_screenRect)) { - Common::Rect intersection = rect->findIntersectingRect(item->_screenRect); + const Common::Rect intersection = rect->findIntersectingRect(item->_screenRect); if (!item->_deleted) { if (encounteredPic) { if (item->_celInfo.type == kCelTypePic) { @@ -427,31 +442,42 @@ void Plane::calcLists(Plane &visiblePlane, const PlaneList &planeList, DrawList for (RectList::size_type i = 0; i < eraseList.size(); ++i) { for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; - if (j < _screenItemList.size() && item != nullptr && !item->_updated && !item->_deleted && !item->_created && eraseList[i]->intersects(item->_screenRect)) { + if ( + item != nullptr && + !item->_created && !item->_updated && !item->_deleted && + eraseList[i]->intersects(item->_screenRect) + ) { drawList.add(item, eraseList[i]->findIntersectingRect(item->_screenRect)); } } } } - if (/* TODO: g_Remap_numActiveRemaps */ false) { // no remaps active? + + if (g_sci->_gfxRemap32->getRemapCount() == 0) { // no remaps active? // Add all items that overlap with items in the drawlist and have higher - // priority - for (DrawList::size_type i = 0; i < drawList.size(); ++i) { + // priority. + + // We only loop over "primary" items in the draw list, skipping + // those that were added because of the erase list in the previous loop, + // or those to be added in this loop. + for (DrawList::size_type i = 0; i < drawListSizePrimary; ++i) { DrawItem *dli = drawList[i]; - for (PlaneList::size_type j = 0; j < planeItemCount; ++j) { + for (ScreenItemList::size_type j = 0; j < planeItemCount; ++j) { ScreenItem *sli = _screenItemList[j]; - if (i < drawList.size() && dli) { - if (j < _screenItemList.size() && sli) { - if (!sli->_updated && !sli->_deleted && !sli->_created) { - ScreenItem *item = dli->screenItem; - if (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) { - if (dli->rect.intersects(sli->_screenRect)) { - drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); - } - } - } + if ( + i < drawList.size() && dli != nullptr && + j < _screenItemList.size() && sli != nullptr && + !sli->_created && !sli->_updated && !sli->_deleted + ) { + ScreenItem *item = dli->screenItem; + + if ( + (sli->_priority > item->_priority || (sli->_priority == item->_priority && sli->_object > item->_object)) && + dli->rect.intersects(sli->_screenRect) + ) { + drawList.add(sli, dli->rect.findIntersectingRect(sli->_screenRect)); } } } @@ -490,8 +516,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force if (item->_created) { item->_created--; if (visiblePlane != nullptr) { - ScreenItem *n = new ScreenItem(*item); - visiblePlane->_screenItemList.add(n); + visiblePlane->_screenItemList.add(new ScreenItem(*item)); } } @@ -510,7 +535,7 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectList &transparentEraseList) const { if (_type == kPlaneTypeTransparent) { for (RectList::size_type i = 0; i < transparentEraseList.size(); ++i) { - Common::Rect *r = transparentEraseList[i]; + const Common::Rect *r = transparentEraseList[i]; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; if (item != nullptr) { @@ -538,7 +563,7 @@ void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectLi } Common::Rect ptr[4]; - Common::Rect *r2 = transparentEraseList[i]; + const Common::Rect *r2 = transparentEraseList[i]; int count = splitRects(*r2, *r, ptr); for (int k = count - 1; k >= 0; --k) { transparentEraseList.add(ptr[k]); @@ -553,7 +578,7 @@ void Plane::filterDownEraseRects(DrawList &drawList, RectList &eraseList, RectLi void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &drawList) const { for (DrawList::size_type i = 0; i < drawList.size(); ++i) { - Common::Rect &r = drawList[i]->rect; + const Common::Rect &r = drawList[i]->rect; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; @@ -568,7 +593,7 @@ void Plane::filterUpDrawRects(DrawList &transparentDrawList, const DrawList &dra void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const { for (RectList::size_type i = 0; i < eraseList.size(); ++i) { - Common::Rect &r = *eraseList[i]; + const Common::Rect &r = *eraseList[i]; for (ScreenItemList::size_type j = 0; j < _screenItemList.size(); ++j) { ScreenItem *item = _screenItemList[j]; @@ -581,20 +606,19 @@ void Plane::filterUpEraseRects(DrawList &drawList, RectList &eraseList) const { } } -void Plane::mergeToDrawList(const DrawList::size_type index, const Common::Rect &rect, DrawList &drawList) const { +void Plane::mergeToDrawList(const ScreenItemList::size_type index, const Common::Rect &rect, DrawList &drawList) const { RectList rects; - Common::Rect r = _screenItemList[index]->_screenRect; + ScreenItem *item = _screenItemList[index]; + Common::Rect r = item->_screenRect; r.clip(rect); - rects.add(r); - ScreenItem *item = _screenItemList[index]; for (RectList::size_type i = 0; i < rects.size(); ++i) { r = *rects[i]; for (DrawList::size_type j = 0; j < drawList.size(); ++j) { - DrawItem *drawitem = drawList[j]; + const DrawItem *drawitem = drawList[j]; if (item->_object == drawitem->screenItem->_object) { if (drawitem->rect.contains(r)) { rects.erase_at(i); @@ -602,7 +626,7 @@ void Plane::mergeToDrawList(const DrawList::size_type index, const Common::Rect } Common::Rect outRects[4]; - int count = splitRects(r, drawitem->rect, outRects); + const int count = splitRects(r, drawitem->rect, outRects); if (count != -1) { for (int k = count - 1; k >= 0; --k) { rects.add(outRects[k]); @@ -632,14 +656,14 @@ void Plane::mergeToRectList(const Common::Rect &rect, RectList &rectList) const Common::Rect r = *temp[i]; for (RectList::size_type j = 0; j < rectList.size(); ++j) { - Common::Rect *innerRect = rectList[j]; + const Common::Rect *innerRect = rectList[j]; if (innerRect->contains(r)) { temp.erase_at(i); break; } Common::Rect out[4]; - int count = splitRects(r, *innerRect, out); + const int count = splitRects(r, *innerRect, out); if (count != -1) { for (int k = count - 1; k >= 0; --k) { temp.add(out[k]); @@ -665,7 +689,6 @@ void Plane::redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList if (*screenItemPtr != nullptr) { ScreenItem &screenItem = **screenItemPtr; if (!screenItem._deleted) { - screenItem.getCelObj(); screenItem.calcRects(*this); if (!screenItem._screenRect.isEmpty()) { drawList.add(&screenItem, screenItem._screenRect); @@ -775,8 +798,44 @@ void Plane::update(const reg_t object) { _back = readSelectorValue(segMan, object, SELECTOR(back)); } +void Plane::scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics) { + _redrawAllCount = g_sci->_gfxFrameout->getScreenCount(); + + for (ScreenItemList::iterator it = _screenItemList.begin(); it != _screenItemList.end(); ++it) { + if (*it != nullptr) { + ScreenItem &screenItem = **it; + if (!screenItem._deleted && (screenItem._celInfo.type != kCelTypePic || scrollPics)) { + screenItem._position.x += deltaX; + screenItem._position.y += deltaY; + } + } + } +} + +void Plane::remapMarkRedraw() { + for (ScreenItemList::const_iterator screenItemPtr = _screenItemList.begin(); screenItemPtr != _screenItemList.end(); ++screenItemPtr) { + if (*screenItemPtr != nullptr) { + ScreenItem &screenItem = **screenItemPtr; + if (screenItem.getCelObj()._remap && !screenItem._deleted && !screenItem._created) { + screenItem._updated = _screenItemList.size(); + } + } + } +} + #pragma mark - #pragma mark PlaneList +void PlaneList::add(Plane *plane) { + for (iterator it = begin(); it != end(); ++it) { + if ((*it)->_priority > plane->_priority) { + insert(it, plane); + return; + } + } + + push_back(plane); +} + void PlaneList::clear() { for (iterator it = begin(); it != end(); ++it) { delete *it; @@ -794,6 +853,11 @@ void PlaneList::erase(Plane *plane) { } } +PlaneList::iterator PlaneList::erase(iterator it) { + delete *it; + return PlaneListBase::erase(it); +} + int PlaneList::findIndexByObject(const reg_t object) const { for (size_type i = 0; i < size(); ++i) { if ((*this)[i] != nullptr && (*this)[i]->_object == object) { @@ -836,15 +900,8 @@ int16 PlaneList::getTopSciPlanePriority() const { return priority; } -void PlaneList::add(Plane *plane) { - for (iterator it = begin(); it != end(); ++it) { - if ((*it)->_priority < plane->_priority) { - insert(it, plane); - return; - } - } - - push_back(plane); +void PlaneList::remove_at(size_type index) { + delete PlaneListBase::remove_at(index); } -} +} // End of namespace Sci diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index be6f71464a..42629666ed 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -133,7 +133,7 @@ private: * synchronised to another plane (which calls * changePic). */ - bool _pictureChanged; // ? + bool _pictureChanged; // TODO: Are these ever actually used? int _field_34, _field_38; // probably a point or ratio @@ -241,30 +241,28 @@ public: */ static void init(); - Plane(const Common::Rect &gameRect); + // NOTE: This constructor signature originally did not accept a + // picture ID, but some calls to construct planes with this signature + // immediately set the picture ID and then called setType again, so + // it made more sense to just make the picture ID a parameter instead. + Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId = kPlanePicColored); + Plane(const reg_t object); + Plane(const Plane &other); + void operator=(const Plane &other); + inline bool operator<(const Plane &other) const { - // TODO: In SCI engine, _object is actually a uint16 and can either - // contain a MemID (a handle to MemoryMgr, similar to reg_t) or - // a serial (Plane::_nextObjectId). These numbers can be compared - // directly in the real engine and the lowest MemID wins, but in - // ScummVM reg_t pointers are not comparable so we have to use a - // different strategy when two planes generated by scripts conflict. - // For now we just don't check if the priority is below 0, since - // that priority is used to represent hidden planes and is guaranteed - // to generate conflicts with script-generated planes. If there are - // other future conflicts with script-generated planes then we need - // to come up with a solution that works, similar to - // reg_t::pointerComparisonWithInteger used by SCI16. - // - // For now, we check the object offsets, as this will likely work - // like in the original SCI engine, without comparing objects. - // However, this whole comparison is quite ugly, and if it still - // fails, we should try to change it to something equivalent, to avoid - // adding loads of workarounds just for this - return _priority < other._priority || (_priority == other._priority && _priority > -1 && _object.getOffset() < other._object.getOffset()); + if (_priority < other._priority) { + return true; + } + + if (_priority == other._priority) { + return _object < other._object; + } + + return false; } /** @@ -305,6 +303,13 @@ public: */ void update(const reg_t object); + /** + * Modifies the position of all non-pic screen items + * by the given delta. If `scrollPics` is true, pic + * items are also repositioned. + */ + void scrollScreenItems(const int16 deltaX, const int16 deltaY, const bool scrollPics); + #pragma mark - #pragma mark Plane - Pic private: @@ -318,12 +323,6 @@ private: inline void addPicInternal(const GuiResourceId pictureId, const Common::Point *position, const bool mirrorX); /** - * If the plane is a picture plane, re-adds all cels - * from its picture resource to the plane. - */ - void changePic(); - - /** * Marks all screen items to be deleted that are within * this plane and match the given picture ID. */ @@ -352,6 +351,13 @@ public: */ void addPic(const GuiResourceId pictureId, const Common::Point &position, const bool mirrorX); + /** + * If the plane is a picture plane, re-adds all cels + * from its picture resource to the plane. Otherwise, + * just clears the _pictureChanged flag. + */ + void changePic(); + #pragma mark - #pragma mark Plane - Rendering private: @@ -424,6 +430,8 @@ public: * and adds them to the given draw and erase lists. */ void redrawAll(Plane *visiblePlane, const PlaneList &planeList, DrawList &drawList, RectList &eraseList); + + void remapMarkRedraw(); }; #pragma mark - @@ -459,13 +467,14 @@ public: void add(Plane *plane); void clear(); - using PlaneListBase::erase; + iterator erase(iterator it); void erase(Plane *plane); inline void sort() { Common::sort(begin(), end(), sortHelper); } + void remove_at(size_type index); }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/remap.cpp b/engines/sci/graphics/remap.cpp new file mode 100644 index 0000000000..e331eaf971 --- /dev/null +++ b/engines/sci/graphics/remap.cpp @@ -0,0 +1,386 @@ +/* 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. + * + */ + +#include "sci/sci.h" +#include "sci/resource.h" +#include "sci/graphics/palette.h" +#include "sci/graphics/palette32.h" +#include "sci/graphics/remap.h" +#include "sci/graphics/screen.h" + +namespace Sci { + +#pragma mark - +#pragma mark SCI16 remapping (QFG4 demo) + +GfxRemap::GfxRemap(GfxPalette *palette) + : _palette(palette) { + _remapOn = false; + resetRemapping(); +} + +GfxRemap::~GfxRemap() { +} + +byte GfxRemap::remapColor(byte remappedColor, byte screenColor) { + assert(_remapOn); + if (_remappingType[remappedColor] == kRemappingByRange) + return _remappingByRange[screenColor]; + else if (_remappingType[remappedColor] == kRemappingByPercent) + return _remappingByPercent[screenColor]; + else + error("remapColor(): Color %d isn't remapped", remappedColor); + + return 0; // should never reach here +} + +void GfxRemap::resetRemapping() { + _remapOn = false; + _remappingPercentToSet = 0; + + for (int i = 0; i < 256; i++) { + _remappingType[i] = kRemappingNone; + _remappingByPercent[i] = i; + _remappingByRange[i] = i; + } +} + +void GfxRemap::setRemappingPercent(byte color, byte percent) { + _remapOn = true; + + // We need to defer the setup of the remapping table every time the screen + // palette is changed, so that kernelFindColor() can find the correct + // colors. Set it once here, in case the palette stays the same and update + // it on each palette change by copySysPaletteToScreen(). + _remappingPercentToSet = percent; + + for (int i = 0; i < 256; i++) { + byte r = _palette->_sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _palette->_sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _palette->_sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = _palette->kernelFindColor(r, g, b); + } + + _remappingType[color] = kRemappingByPercent; +} + +void GfxRemap::setRemappingRange(byte color, byte from, byte to, byte base) { + _remapOn = true; + + for (int i = from; i <= to; i++) { + _remappingByRange[i] = i + base; + } + + _remappingType[color] = kRemappingByRange; +} + +void GfxRemap::updateRemapping() { + // Check if we need to reset remapping by percent with the new colors. + if (_remappingPercentToSet) { + for (int i = 0; i < 256; i++) { + byte r = _palette->_sysPalette.colors[i].r * _remappingPercentToSet / 100; + byte g = _palette->_sysPalette.colors[i].g * _remappingPercentToSet / 100; + byte b = _palette->_sysPalette.colors[i].b * _remappingPercentToSet / 100; + _remappingByPercent[i] = _palette->kernelFindColor(r, g, b); + } + } +} + +#pragma mark - +#pragma mark SCI32 remapping + +#ifdef ENABLE_SCI32 + +GfxRemap32::GfxRemap32(GfxPalette32 *palette) : _palette(palette) { + for (int i = 0; i < REMAP_COLOR_COUNT; i++) + _remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone); + _noMapStart = _noMapCount = 0; + _update = false; + _remapCount = 0; + + // The remap range was 245 - 254 in SCI2, but was changed to 235 - 244 in SCI21 middle + _remapEndColor = (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) ? 244 : 254; +} + +void GfxRemap32::remapOff(byte color) { + if (!color) { + for (int i = 0; i < REMAP_COLOR_COUNT; i++) + _remaps[i] = RemapParams(0, 0, 0, 0, 100, kRemappingNone); + + _remapCount = 0; + } else { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + const byte index = _remapEndColor - color; + _remaps[index] = RemapParams(0, 0, 0, 0, 100, kRemappingNone); + _remapCount--; + } + + _update = true; +} + +void GfxRemap32::setRemappingRange(byte color, byte from, byte to, byte base) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(from, to, base, 0, 100, kRemappingByRange); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setRemappingPercent(byte color, byte percent) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, 0, percent, kRemappingByPercent); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setRemappingToGray(byte color, byte gray) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, 100, kRemappingToGray); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setRemappingToPercentGray(byte color, byte gray, byte percent) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + _remaps[_remapEndColor - color] = RemapParams(0, 0, 0, gray, percent, kRemappingToPercentGray); + initColorArrays(_remapEndColor - color); + _remapCount++; + _update = true; +} + +void GfxRemap32::setNoMatchRange(byte from, byte count) { + _noMapStart = from; + _noMapCount = count; +} + +bool GfxRemap32::remapEnabled(byte color) const { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + const byte index = _remapEndColor - color; + return (_remaps[index].type != kRemappingNone); +} + +byte GfxRemap32::remapColor(byte color, byte target) { + assert(_remapEndColor - color >= 0 && _remapEndColor - color < REMAP_COLOR_COUNT); + const byte index = _remapEndColor - color; + if (_remaps[index].type != kRemappingNone) + return _remaps[index].remap[target]; + else + return target; +} + +void GfxRemap32::initColorArrays(byte index) { + Palette *curPalette = &_palette->_sysPalette; + RemapParams *curRemap = &_remaps[index]; + + memcpy(curRemap->curColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color)); + memcpy(curRemap->targetColor, curPalette->colors, NON_REMAPPED_COLOR_COUNT * sizeof(Color)); +} + +bool GfxRemap32::updateRemap(byte index, bool palChanged) { + int result; + RemapParams *curRemap = &_remaps[index]; + const Palette *curPalette = &_palette->_sysPalette; + const Palette *nextPalette = _palette->getNextPalette(); + bool changed = false; + + if (!_update && !palChanged) + return false; + + Common::fill(_targetChanged, _targetChanged + NON_REMAPPED_COLOR_COUNT, false); + + switch (curRemap->type) { + case kRemappingNone: + return false; + case kRemappingByRange: + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) { + if (curRemap->from <= i && i <= curRemap->to) + result = i + curRemap->base; + else + result = i; + + if (curRemap->remap[i] != result) { + changed = true; + curRemap->remap[i] = result; + } + + curRemap->colorChanged[i] = true; + } + return changed; + case kRemappingByPercent: + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + // NOTE: This method uses nextPalette instead of curPalette + Color color = nextPalette->colors[i]; + + if (curRemap->curColor[i] != color) { + curRemap->colorChanged[i] = true; + curRemap->curColor[i] = color; + } + + if (curRemap->percent != curRemap->oldPercent || curRemap->colorChanged[i]) { + byte red = CLIP<byte>(color.r * curRemap->percent / 100, 0, 255); + byte green = CLIP<byte>(color.g * curRemap->percent / 100, 0, 255); + byte blue = CLIP<byte>(color.b * curRemap->percent / 100, 0, 255); + byte used = curRemap->targetColor[i].used; + + Color newColor = { used, red, green, blue }; + if (curRemap->targetColor[i] != newColor) { + _targetChanged[i] = true; + curRemap->targetColor[i] = newColor; + } + } + } + + changed = applyRemap(index); + Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false); + curRemap->oldPercent = curRemap->percent; + return changed; + case kRemappingToGray: + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + Color color = curPalette->colors[i]; + + if (curRemap->curColor[i] != color) { + curRemap->colorChanged[i] = true; + curRemap->curColor[i] = color; + } + + if (curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i]) { + byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8; + byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255); + byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255); + byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255); + byte used = curRemap->targetColor[i].used; + + Color newColor = { used, red, green, blue }; + if (curRemap->targetColor[i] != newColor) { + _targetChanged[i] = true; + curRemap->targetColor[i] = newColor; + } + } + } + + changed = applyRemap(index); + Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false); + curRemap->oldGray = curRemap->gray; + return changed; + case kRemappingToPercentGray: + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + Color color = curPalette->colors[i]; + + if (curRemap->curColor[i] != color) { + curRemap->colorChanged[i] = true; + curRemap->curColor[i] = color; + } + + if (curRemap->percent != curRemap->oldPercent || curRemap->gray != curRemap->oldGray || curRemap->colorChanged[i]) { + byte lumosity = ((color.r * 77) + (color.g * 151) + (color.b * 28)) >> 8; + lumosity = lumosity * curRemap->percent / 100; + byte red = CLIP<byte>(color.r - ((color.r - lumosity) * curRemap->gray / 100), 0, 255); + byte green = CLIP<byte>(color.g - ((color.g - lumosity) * curRemap->gray / 100), 0, 255); + byte blue = CLIP<byte>(color.b - ((color.b - lumosity) * curRemap->gray / 100), 0, 255); + byte used = curRemap->targetColor[i].used; + + Color newColor = { used, red, green, blue }; + if (curRemap->targetColor[i] != newColor) { + _targetChanged[i] = true; + curRemap->targetColor[i] = newColor; + } + } + } + + changed = applyRemap(index); + Common::fill(curRemap->colorChanged, curRemap->colorChanged + NON_REMAPPED_COLOR_COUNT, false); + curRemap->oldPercent = curRemap->percent; + curRemap->oldGray = curRemap->gray; + return changed; + default: + return false; + } +} + +static int colorDistance(Color a, Color b) { + int rDiff = (a.r - b.r) * (a.r - b.r); + int gDiff = (a.g - b.g) * (a.g - b.g); + int bDiff = (a.b - b.b) * (a.b - b.b); + return rDiff + gDiff + bDiff; +} + +bool GfxRemap32::applyRemap(byte index) { + RemapParams *curRemap = &_remaps[index]; + const bool *cycleMap = _palette->getCyclemap(); + bool unmappedColors[NON_REMAPPED_COLOR_COUNT]; + Color newColors[NON_REMAPPED_COLOR_COUNT]; + bool changed = false; + + Common::fill(unmappedColors, unmappedColors + NON_REMAPPED_COLOR_COUNT, false); + if (_noMapCount) + Common::fill(unmappedColors + _noMapStart, unmappedColors + _noMapStart + _noMapCount, true); + + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) { + if (cycleMap[i]) + unmappedColors[i] = true; + } + + int curColor = 0; + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + if (curRemap->colorChanged[i] && !unmappedColors[i]) + newColors[curColor++] = curRemap->curColor[i]; + } + + for (int i = 1; i < NON_REMAPPED_COLOR_COUNT; i++) { + Color targetColor = curRemap->targetColor[i]; + bool colorChanged = curRemap->colorChanged[curRemap->remap[i]]; + + if (!_targetChanged[i] && !colorChanged) + continue; + + if (_targetChanged[i] && colorChanged) + if (curRemap->distance[i] < 100 && colorDistance(targetColor, curRemap->curColor[curRemap->remap[i]]) <= curRemap->distance[i]) + continue; + + int diff = 0; + int16 result = _palette->matchColor(targetColor.r, targetColor.g, targetColor.b, curRemap->distance[i], diff, unmappedColors); + if (result != -1 && curRemap->remap[i] != result) { + changed = true; + curRemap->remap[i] = result; + curRemap->distance[i] = diff; + } + } + + return changed; +} + +bool GfxRemap32::remapAllTables(bool palChanged) { + bool changed = false; + + for (int i = 0; i < REMAP_COLOR_COUNT; i++) { + changed |= updateRemap(i, palChanged); + } + + _update = false; + return changed; +} + +#endif + +} // End of namespace Sci diff --git a/engines/sci/graphics/remap.h b/engines/sci/graphics/remap.h new file mode 100644 index 0000000000..d012568f7f --- /dev/null +++ b/engines/sci/graphics/remap.h @@ -0,0 +1,154 @@ +/* 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. + * + */ + +#ifndef SCI_GRAPHICS_REMAP_H +#define SCI_GRAPHICS_REMAP_H + +#include "common/array.h" +#include "sci/graphics/helpers.h" + +namespace Sci { + +class GfxScreen; + +enum ColorRemappingType { + kRemappingNone = 0, + kRemappingByRange = 1, + kRemappingByPercent = 2, + kRemappingToGray = 3, + kRemappingToPercentGray = 4 +}; + +#define REMAP_COLOR_COUNT 9 +#define NON_REMAPPED_COLOR_COUNT 236 + +/** + * Remap class, handles color remapping + */ +class GfxRemap { +public: + GfxRemap(GfxPalette *_palette); + ~GfxRemap(); + + void resetRemapping(); + void setRemappingPercent(byte color, byte percent); + void setRemappingRange(byte color, byte from, byte to, byte base); + bool isRemapped(byte color) const { + return _remapOn && (_remappingType[color] != kRemappingNone); + } + byte remapColor(byte remappedColor, byte screenColor); + void updateRemapping(); + +private: + GfxScreen *_screen; + GfxPalette *_palette; + + bool _remapOn; + ColorRemappingType _remappingType[256]; + byte _remappingByPercent[256]; + byte _remappingByRange[256]; + uint16 _remappingPercentToSet; +}; + +#ifdef ENABLE_SCI32 + +struct RemapParams { + byte from; + byte to; + byte base; + byte gray; + byte oldGray; + byte percent; + byte oldPercent; + ColorRemappingType type; + Color curColor[256]; + Color targetColor[256]; + byte distance[256]; + byte remap[256]; + bool colorChanged[256]; + + RemapParams() { + from = to = base = gray = oldGray = percent = oldPercent = 0; + type = kRemappingNone; + + // curColor and targetColor are initialized in GfxRemap32::initColorArrays + memset(curColor, 0, 256 * sizeof(Color)); + memset(targetColor, 0, 256 * sizeof(Color)); + memset(distance, 0, 256); + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) + remap[i] = i; + Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true); + } + + RemapParams(byte from_, byte to_, byte base_, byte gray_, byte percent_, ColorRemappingType type_) { + from = from_; + to = to_; + base = base_; + gray = oldGray = gray_; + percent = oldPercent = percent_; + type = type_; + + // curColor and targetColor are initialized in GfxRemap32::initColorArrays + memset(curColor, 0, 256 * sizeof(Color)); + memset(targetColor, 0, 256 * sizeof(Color)); + memset(distance, 0, 256); + for (int i = 0; i < NON_REMAPPED_COLOR_COUNT; i++) + remap[i] = i; + Common::fill(colorChanged, colorChanged + ARRAYSIZE(colorChanged), true); + } +}; + +class GfxRemap32 { +public: + GfxRemap32(GfxPalette32 *palette); + ~GfxRemap32() {} + + void remapOff(byte color); + void setRemappingRange(byte color, byte from, byte to, byte base); + void setRemappingPercent(byte color, byte percent); + void setRemappingToGray(byte color, byte gray); + void setRemappingToPercentGray(byte color, byte gray, byte percent); + void setNoMatchRange(byte from, byte count); + bool remapEnabled(byte color) const; + byte remapColor(byte color, byte target); + bool remapAllTables(bool palChanged); + int getRemapCount() const { return _remapCount; } + int getStartColor() const { return _remapEndColor - REMAP_COLOR_COUNT + 1; } + int getEndColor() const { return _remapEndColor; } +private: + GfxPalette32 *_palette; + RemapParams _remaps[REMAP_COLOR_COUNT]; + bool _update; + byte _noMapStart, _noMapCount; + bool _targetChanged[NON_REMAPPED_COLOR_COUNT]; + byte _remapEndColor; + int _remapCount; + + void initColorArrays(byte index); + bool applyRemap(byte index); + bool updateRemap(byte index, bool palChanged); +}; +#endif + +} // End of namespace Sci + +#endif diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index fe995a429a..c44d3e96f1 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -83,7 +83,7 @@ _mirrorX(false) { } } -ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo) : +ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo) : _plane(plane), _scale(scaleInfo), _useInsetRect(false), @@ -91,7 +91,7 @@ _z(0), _celInfo(celInfo), _celObj(nullptr), _fixPriority(false), -_position(rect.left, rect.top), +_position(position), _object(make_reg(0, _nextObjectId++)), _pictureId(-1), _created(g_sci->_gfxFrameout->getScreenCount()), @@ -126,6 +126,10 @@ void ScreenItem::operator=(const ScreenItem &other) { _scaledPosition = other._scaledPosition; } +ScreenItem::~ScreenItem() { + delete _celObj; +} + void ScreenItem::init() { _nextObjectId = 20000; } @@ -202,23 +206,6 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo writeSelectorValue(segMan, object, SELECTOR(priority), _position.y); } - // TODO: At this point (needs checking), GK1 seems to check for the "visible" - // selector of a plane object. If the object has such a selector, and it's set - // to 0, then the object should be hidden. - // - // This is needed for the inventory in GK1, and seemed to be used only for that - // game - the "visible" selector isn't present in any other SCI32 game. - // Possible disabled and unverified code that checks for this follows. This fixes - // the inventory in GK1. Verify against disassembly! -#if 0 - if (lookupSelector(segMan, object, SELECTOR(visible), NULL, NULL) != kSelectorNone) { - if (readSelectorValue(segMan, object, SELECTOR(visible)) == 0) { - _fixPriority = true; - _priority = -1; - } - } -#endif - _z = readSelectorValue(segMan, object, SELECTOR(z)); _position.y -= _z; @@ -232,14 +219,7 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo _useInsetRect = false; } - // TODO: SCI2.1/SQ6 engine clears this flag any time ScreenItem::Update(MemID) - // or ScreenItem::ScreenItem(MemID) are called, but doing this breaks - // view cycling because the flag isn't being set again later. There are over - // 100 places in the engine code where this flag is set, so it is probably - // a matter of figuring out what all of those calls are that re-set it. For - // now, since these are the *only* calls that clear this flag, we can just - // leave it set all the time. - // segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible); + segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible); } void ScreenItem::calcRects(const Plane &plane) { @@ -248,7 +228,9 @@ void ScreenItem::calcRects(const Plane &plane) { const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - Common::Rect celRect(_celObj->_width, _celObj->_height); + const CelObj &celObj = getCelObj(); + + Common::Rect celRect(celObj._width, celObj._height); if (_useInsetRect) { if (_insetRect.intersects(celRect)) { _insetRect.clip(celRect); @@ -259,28 +241,33 @@ void ScreenItem::calcRects(const Plane &plane) { _insetRect = celRect; } - Ratio newRatioX; - Ratio newRatioY; + Ratio scaleX, scaleY; if (_scale.signal & kScaleSignalDoScaling32) { if (_scale.signal & kScaleSignalUseVanishingPoint) { int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); - newRatioX = Ratio(num, 128); - newRatioY = Ratio(num, 128); + scaleX = Ratio(num, 128); + scaleY = Ratio(num, 128); } else { - newRatioX = Ratio(_scale.x, 128); - newRatioY = Ratio(_scale.y, 128); + scaleX = Ratio(_scale.x, 128); + scaleY = Ratio(_scale.y, 128); } } - if (newRatioX.getNumerator() && newRatioY.getNumerator()) { + if (scaleX.getNumerator() && scaleY.getNumerator()) { _screenItemRect = _insetRect; - if (_celObj->_scaledWidth != scriptWidth || _celObj->_scaledHeight != scriptHeight) { + const Ratio celToScreenX(screenWidth, celObj._scaledWidth); + const Ratio celToScreenY(screenHeight, celObj._scaledHeight); + + // Cel may use a coordinate system that is not the same size as the + // script coordinate system (usually this means high-resolution + // pictures with low-resolution scripts) + if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) { if (_useInsetRect) { - Ratio celScriptXRatio(_celObj->_scaledWidth, scriptWidth); - Ratio celScriptYRatio(_celObj->_scaledHeight, scriptHeight); - mulru(_screenItemRect, celScriptXRatio, celScriptYRatio); + const Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); + const Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); + mulru(_screenItemRect, scriptToCelX, scriptToCelY, 0); if (_screenItemRect.intersects(celRect)) { _screenItemRect.clip(celRect); @@ -289,53 +276,50 @@ void ScreenItem::calcRects(const Plane &plane) { } } - int displaceX = _celObj->_displace.x; - int displaceY = _celObj->_displace.y; + int displaceX = celObj._displace.x; + int displaceY = celObj._displace.y; - if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) { - displaceX = _celObj->_width - _celObj->_displace.x - 1; + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + displaceX = celObj._width - celObj._displace.x - 1; } - if (!newRatioX.isOne() || !newRatioY.isOne()) { - mulru(_screenItemRect, newRatioX, newRatioY); - displaceX = (displaceX * newRatioX).toInt(); - displaceY = (displaceY * newRatioY).toInt(); + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(_screenItemRect, scaleX, scaleY); + displaceX = (displaceX * scaleX).toInt(); + displaceY = (displaceY * scaleY).toInt(); } - Ratio celXRatio(screenWidth, _celObj->_scaledWidth); - Ratio celYRatio(screenHeight, _celObj->_scaledHeight); - - displaceX = (displaceX * celXRatio).toInt(); - displaceY = (displaceY * celYRatio).toInt(); + mulinc(_screenItemRect, celToScreenX, celToScreenY); + displaceX = (displaceX * celToScreenX).toInt(); + displaceY = (displaceY * celToScreenY).toInt(); - mulru(_screenItemRect, celXRatio, celYRatio); + const Ratio scriptToScreenX = Ratio(screenWidth, scriptWidth); + const Ratio scriptToScreenY = Ratio(screenHeight, scriptHeight); if (/* TODO: dword_C6288 */ false && _celInfo.type == kCelTypePic) { _scaledPosition.x = _position.x; _scaledPosition.y = _position.y; } else { - _scaledPosition.x = (_position.x * screenWidth / scriptWidth) - displaceX; - _scaledPosition.y = (_position.y * screenHeight / scriptHeight) - displaceY; + _scaledPosition.x = (_position.x * scriptToScreenX).toInt() - displaceX; + _scaledPosition.y = (_position.y * scriptToScreenY).toInt() - displaceY; } _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y); - if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) { + if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) { Common::Rect temp(_insetRect); - if (!newRatioX.isOne()) { - mulru(temp, newRatioX, Ratio()); + if (!scaleX.isOne()) { + mulinc(temp, scaleX, Ratio()); } - mulru(temp, celXRatio, Ratio()); + mulinc(temp, celToScreenX, Ratio()); CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); + temp.translate((celObjPic->_relativePosition.x * scriptToScreenX).toInt() - displaceX, 0); - temp.translate(celObjPic->_relativePosition.x * screenWidth / scriptWidth - displaceX, 0); - - // TODO: This is weird, and probably wrong calculation of widths - // due to BR-inclusion - int deltaX = plane._planeRect.right - plane._planeRect.left + 1 - temp.right - 1 - temp.left; + // TODO: This is weird. + int deltaX = plane._planeRect.width() - temp.right - 1 - temp.left; _scaledPosition.x += deltaX; _screenItemRect.translate(deltaX, 0); @@ -345,41 +329,40 @@ void ScreenItem::calcRects(const Plane &plane) { _scaledPosition.y += plane._planeRect.top; _screenItemRect.translate(plane._planeRect.left, plane._planeRect.top); - _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth); - _ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight); + _ratioX = scaleX * celToScreenX; + _ratioY = scaleY * celToScreenY; } else { - int displaceX = _celObj->_displace.x; - if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) { - displaceX = _celObj->_width - _celObj->_displace.x - 1; + int displaceX = celObj._displace.x; + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + displaceX = celObj._width - celObj._displace.x - 1; } - if (!newRatioX.isOne() || !newRatioY.isOne()) { - mulru(_screenItemRect, newRatioX, newRatioY); + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(_screenItemRect, scaleX, scaleY); // TODO: This was in the original code, baked into the - // multiplication though it is not immediately clear + // multiplication though it is not immediately clear // why this is the only one that reduces the BR corner _screenItemRect.right -= 1; _screenItemRect.bottom -= 1; } - _scaledPosition.x = _position.x - (displaceX * newRatioX).toInt(); - _scaledPosition.y = _position.y - (_celObj->_displace.y * newRatioY).toInt(); + _scaledPosition.x = _position.x - (displaceX * scaleX).toInt(); + _scaledPosition.y = _position.y - (celObj._displace.y * scaleY).toInt(); _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y); - if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) { + if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) { Common::Rect temp(_insetRect); - if (!newRatioX.isOne()) { - mulru(temp, newRatioX, Ratio()); + if (!scaleX.isOne()) { + mulinc(temp, scaleX, Ratio()); temp.right -= 1; } CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); - temp.translate(celObjPic->_relativePosition.x - (displaceX * newRatioX).toInt(), celObjPic->_relativePosition.y - (_celObj->_displace.y * newRatioY).toInt()); + temp.translate(celObjPic->_relativePosition.x - (displaceX * scaleX).toInt(), celObjPic->_relativePosition.y - (celObj._displace.y * scaleY).toInt()); - // TODO: This is weird, and probably wrong calculation of widths - // due to BR-inclusion - int deltaX = plane._gameRect.right - plane._gameRect.left + 1 - temp.right - 1 - temp.left; + // TODO: This is weird. + int deltaX = plane._gameRect.width() - temp.right - 1 - temp.left; _scaledPosition.x += deltaX; _screenItemRect.translate(deltaX, 0); @@ -389,15 +372,13 @@ void ScreenItem::calcRects(const Plane &plane) { _scaledPosition.y += plane._gameRect.top; _screenItemRect.translate(plane._gameRect.left, plane._gameRect.top); - if (screenWidth != _celObj->_scaledWidth || _celObj->_scaledHeight != screenHeight) { - Ratio celXRatio(screenWidth, _celObj->_scaledWidth); - Ratio celYRatio(screenHeight, _celObj->_scaledHeight); - mulru(_scaledPosition, celXRatio, celYRatio); - mulru(_screenItemRect, celXRatio, celYRatio); + if (celObj._scaledWidth != screenWidth || celObj._scaledHeight != screenHeight) { + mulru(_scaledPosition, celToScreenX, celToScreenY); + mulru(_screenItemRect, celToScreenX, celToScreenY, 1); } - _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth); - _ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight); + _ratioX = scaleX * celToScreenX; + _ratioY = scaleY * celToScreenY; } _screenRect = _screenItemRect; @@ -422,7 +403,7 @@ void ScreenItem::calcRects(const Plane &plane) { } } -CelObj &ScreenItem::getCelObj() { +CelObj &ScreenItem::getCelObj() const { if (_celObj == nullptr) { switch (_celInfo.type) { case kCelTypeView: @@ -444,7 +425,7 @@ CelObj &ScreenItem::getCelObj() { } void ScreenItem::printDebugInfo(Console *con) const { - con->debugPrintf("%x:%x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n", + con->debugPrintf("%04x:%04x (%s), prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n", _object.getSegment(), _object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(_object), _priority, @@ -518,6 +499,109 @@ void ScreenItem::update(const reg_t object) { _deleted = 0; } +// TODO: This code is quite similar to calcRects, so try to deduplicate +// if possible +Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { + CelObj &celObj = getCelObj(); + + Common::Rect celObjRect(celObj._width, celObj._height); + Common::Rect nsRect; + + if (_useInsetRect) { + // TODO: This is weird. Checking to see if the inset rect is + // fully inside the bounds of the celObjRect, and then + // clipping to the celObjRect, is pretty useless. + if (_insetRect.right > 0 && _insetRect.bottom > 0 && _insetRect.left < celObj._width && _insetRect.top < celObj._height) { + nsRect = _insetRect; + nsRect.clip(celObjRect); + } else { + nsRect = Common::Rect(); + } + } else { + nsRect = celObjRect; + } + + const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + Ratio scaleX, scaleY; + if (_scale.signal & kScaleSignalDoScaling32) { + if (_scale.signal & kScaleSignalUseVanishingPoint) { + int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); + scaleX = Ratio(num, 128); + scaleY = Ratio(num, 128); + } else { + scaleX = Ratio(_scale.x, 128); + scaleY = Ratio(_scale.y, 128); + } + } + + if (scaleX.getNumerator() == 0 || scaleY.getNumerator() == 0) { + return Common::Rect(); + } + + int16 displaceX = celObj._displace.x; + int16 displaceY = celObj._displace.y; + + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + displaceX = celObj._width - displaceX - 1; + } + + if (celObj._scaledWidth != scriptWidth || celObj._scaledHeight != scriptHeight) { + if (_useInsetRect) { + Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); + Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); + mulru(nsRect, scriptToCelX, scriptToCelY, 0); + + // TODO: This is weird. Checking to see if the inset rect is + // fully inside the bounds of the celObjRect, and then + // clipping to the celObjRect, is pretty useless. + if (nsRect.right > 0 && nsRect.bottom > 0 && nsRect.left < celObj._width && nsRect.top < celObj._height) { + nsRect.clip(celObjRect); + } else { + nsRect = Common::Rect(); + } + } + + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(nsRect, scaleX, scaleY); + // TODO: This was in the original code, baked into the + // multiplication though it is not immediately clear + // why this is the only one that reduces the BR corner + nsRect.right -= 1; + nsRect.bottom -= 1; + } + + Ratio celToScriptX(scriptWidth, celObj._scaledWidth); + Ratio celToScriptY(scriptHeight, celObj._scaledHeight); + + displaceX = (displaceX * scaleX * celToScriptX).toInt(); + displaceY = (displaceY * scaleY * celToScriptY).toInt(); + + mulinc(nsRect, celToScriptX, celToScriptY); + nsRect.translate(_position.x - displaceX, _position.y - displaceY); + } else { + if (!scaleX.isOne() || !scaleY.isOne()) { + mulinc(nsRect, scaleX, scaleY); + // TODO: This was in the original code, baked into the + // multiplication though it is not immediately clear + // why this is the only one that reduces the BR corner + nsRect.right -= 1; + nsRect.bottom -= 1; + } + + displaceX = (displaceX * scaleX).toInt(); + displaceY = (displaceY * scaleY).toInt(); + nsRect.translate(_position.x - displaceX, _position.y - displaceY); + + if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { + nsRect.translate(plane._gameRect.width() - nsRect.width(), 0); + } + } + + return nsRect; +} + #pragma mark - #pragma mark ScreenItemList ScreenItem *ScreenItemList::findByObject(const reg_t object) const { @@ -550,4 +634,4 @@ void ScreenItemList::unsort() { } } -} +} // End of namespace Sci diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index 0ca840a13a..977d80ebad 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -125,7 +125,7 @@ public: * item. This member is populated by calling * `getCelObj`. */ - CelObj *_celObj; + mutable CelObj *_celObj; /** * If set, the priority for this screen item is fixed @@ -213,8 +213,9 @@ public: ScreenItem(const reg_t screenItem); ScreenItem(const reg_t plane, const CelInfo32 &celInfo); ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect); - ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo); + ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Point &position, const ScaleInfo &scaleInfo); ScreenItem(const ScreenItem &other); + ~ScreenItem(); void operator=(const ScreenItem &); inline bool operator<(const ScreenItem &other) const { @@ -228,9 +229,7 @@ public: } if (_position.y + _z == other._position.y + other._z) { - return false; - // TODO: Failure in SQ6 room 220 -// return _object < other._object; + return _object < other._object; } } @@ -251,7 +250,7 @@ public: * screen item. If a cel object does not already exist, * one will be created and assigned. */ - CelObj &getCelObj(); + CelObj &getCelObj() const; void printDebugInfo(Console *con) const; @@ -260,6 +259,13 @@ public: * VM object. */ void update(const reg_t object); + + /** + * Gets the "now seen" rect for the screen item, which + * represents the current size and position of the + * screen item on the screen in script coordinates. + */ + Common::Rect getNowSeenRect(const Plane &plane) const; }; #pragma mark - @@ -277,6 +283,6 @@ public: void sort(); void unsort(); }; -} +} // End of namespace Sci #endif diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index e0fb56b74b..99ffc6e328 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -29,6 +29,7 @@ #include "sci/engine/selector.h" #include "sci/engine/state.h" #include "sci/graphics/cache.h" +#include "sci/graphics/celobj32.h" #include "sci/graphics/compare.h" #include "sci/graphics/font.h" #include "sci/graphics/frameout.h" @@ -37,49 +38,28 @@ namespace Sci { -#define BITMAP_HEADER_SIZE 46 +int16 GfxText32::_defaultFontId = 0; -GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen) : +GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : _segMan(segMan), _cache(fonts), - _screen(screen), _scaledWidth(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), _scaledHeight(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), - _bitmap(NULL_REG) {} - -void GfxText32::buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) const { - - WRITE_SCI11ENDIAN_UINT16(bitmap + 0, width); - WRITE_SCI11ENDIAN_UINT16(bitmap + 2, height); - WRITE_SCI11ENDIAN_UINT16(bitmap + 4, (uint16)displaceX); - WRITE_SCI11ENDIAN_UINT16(bitmap + 6, (uint16)displaceY); - bitmap[8] = skipColor; - bitmap[9] = 0; - WRITE_SCI11ENDIAN_UINT16(bitmap + 10, 0); - - if (useRemap) { - bitmap[10] |= 2; + // Not a typo, the original engine did not initialise height, only width + _width(0), + _text(""), + _field_20(0), + _field_2C(2), + _field_30(0), + _field_34(0), + _field_38(0), + _field_3C(0), + _bitmap(NULL_REG) { + _fontId = _defaultFontId; + _font = _cache->getFont(_defaultFontId); } - WRITE_SCI11ENDIAN_UINT32(bitmap + 12, width * height); - WRITE_SCI11ENDIAN_UINT32(bitmap + 16, 0); - - if (hunkPaletteOffset) { - WRITE_SCI11ENDIAN_UINT32(bitmap + 20, hunkPaletteOffset + BITMAP_HEADER_SIZE); - } else { - WRITE_SCI11ENDIAN_UINT32(bitmap + 20, 0); - } - - WRITE_SCI11ENDIAN_UINT32(bitmap + 24, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(bitmap + 28, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(bitmap + 32, 0); - WRITE_SCI11ENDIAN_UINT16(bitmap + 36, scaledWidth); - WRITE_SCI11ENDIAN_UINT16(bitmap + 38, scaledHeight); -} - -int16 GfxText32::_defaultFontId = 0; - -reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling, reg_t *outBitmapObject) { +reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling) { _field_22 = 0; _borderColor = borderColor; @@ -93,10 +73,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _alignment = alignment; _dimmed = dimmed; - if (fontId != _fontId) { - _fontId = fontId == -1 ? _defaultFontId : fontId; - _font = _cache->getFont(_fontId); - } + setFont(fontId); if (doScaling) { int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; @@ -107,7 +84,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _width = (_width * scaleX).toInt(); _height = (_height * scaleY).toInt(); - mul(_textRect, scaleX, scaleY); + mulinc(_textRect, scaleX, scaleY); } // _textRect represents where text is drawn inside the @@ -120,10 +97,8 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _textRect = Common::Rect(); } - _bitmap = _segMan->allocateHunkEntry("FontBitmap()", _width * _height + BITMAP_HEADER_SIZE); - - byte *bitmap = _segMan->getHunkPointer(_bitmap); - buildBitmapHeader(bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + BitmapResource bitmap(_segMan, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + _bitmap = bitmap.getObject(); erase(bitmapRect, false); @@ -132,466 +107,551 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect } drawTextBox(); + return _bitmap; +} + +reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed) { + _field_22 = 0; + _borderColor = borderColor; + _text = text; + _textRect = rect; + _foreColor = foreColor; + _dimmed = dimmed; + + setFont(fontId); + + int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + int borderSize = 1; + mulinc(_textRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight)); + + CelObjView view(celInfo.resourceId, celInfo.loopNo, celInfo.celNo); + _skipColor = view._transparentColor; + _width = view._width * _scaledWidth / view._scaledWidth; + _height = view._height * _scaledHeight / view._scaledHeight; + + Common::Rect bitmapRect(_width, _height); + if (_textRect.intersects(bitmapRect)) { + _textRect.clip(bitmapRect); + } else { + _textRect = Common::Rect(); + } + + BitmapResource bitmap(_segMan, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false); + _bitmap = bitmap.getObject(); + Buffer buffer(_width, _height, bitmap.getPixels()); + + // NOTE: The engine filled the bitmap pixels with 11 here, which is silly + // because then it just erased the bitmap using the skip color. So we don't + // fill the bitmap redundantly here. + + _backColor = _skipColor; + erase(bitmapRect, false); + _backColor = backColor; + + view.draw(buffer, bitmapRect, Common::Point(0, 0), false, Ratio(_scaledWidth, view._scaledWidth), Ratio(_scaledHeight, view._scaledHeight)); - debug("Drawing a bitmap %dx%d, scaled %dx%d, border %d, font %d", width, height, _width, _height, _borderColor, _fontId); + if (_backColor != skipColor && _foreColor != skipColor) { + erase(_textRect, false); + } + + if (text.size() > 0) { + if (_foreColor == skipColor) { + error("TODO: Implement transparent text"); + } else { + if (borderColor != -1) { + drawFrame(bitmapRect, borderSize, _borderColor, false); + } + + drawTextBox(); + } + } - *outBitmapObject = _bitmap; return _bitmap; } -reg_t GfxText32::createTitledFontBitmap(CelInfo32 &celInfo, Common::Rect &rect, Common::String &text, int16 foreColor, int16 backColor, int font, int16 skipColor, int16 borderColor, bool dimmed, void *unknown1) { - warning("TODO: createTitledFontBitmap"); - return NULL_REG; +reg_t GfxText32::createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling) { + warning("TODO: createTitledBitmap incomplete !"); + return createFontBitmap(width, height, textRect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, false, doScaling); +} + +void GfxText32::setFont(const GuiResourceId fontId) { + // NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font + // table is built on the FontMgr directly; instead, because we already have + // font resources, this code just grabs a font out of GfxCache. + if (fontId != _fontId) { + _fontId = fontId == -1 ? _defaultFontId : fontId; + _font = _cache->getFont(_fontId); + } } -void GfxText32::drawFrame(const Common::Rect &rect, const int size, const uint8 color, const bool doScaling) { +void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling) { Common::Rect targetRect = doScaling ? scaleRect(rect) : rect; byte *bitmap = _segMan->getHunkPointer(_bitmap); - byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); + byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28) + rect.top * _width + rect.left; // NOTE: Not fully disassembled, but this should be right - // TODO: Implement variable frame size - assert(size == 1); - Buffer buffer(_width, _height, pixels); - buffer.frameRect(targetRect, color); + int16 rectWidth = targetRect.width(); + int16 sidesHeight = targetRect.height() - size * 2; + int16 centerWidth = rectWidth - size * 2; + int16 stride = _width - rectWidth; + + for (int16 y = 0; y < size; ++y) { + memset(pixels, color, rectWidth); + pixels += _width; + } + for (int16 y = 0; y < sidesHeight; ++y) { + for (int16 x = 0; x < size; ++x) { + *pixels++ = color; + } + pixels += centerWidth; + for (int16 x = 0; x < size; ++x) { + *pixels++ = color; + } + pixels += stride; + } + for (int16 y = 0; y < size; ++y) { + memset(pixels, color, rectWidth); + pixels += _width; + } +} + +void GfxText32::drawChar(const char charIndex) { + byte *bitmap = _segMan->getHunkPointer(_bitmap); + byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); + + _font->drawToBuffer(charIndex, _drawPosition.y, _drawPosition.x, _foreColor, _dimmed, pixels, _width, _height); + _drawPosition.x += _font->getCharWidth(charIndex); +} + +uint16 GfxText32::getCharWidth(const char charIndex, const bool doScaling) const { + uint16 width = _font->getCharWidth(charIndex); + if (doScaling) { + width = scaleUpWidth(width); + } + return width; } -// TODO: This is not disassembled void GfxText32::drawTextBox() { - int16 charCount = 0; - uint16 curX = 0, curY = 0; - const char *txt = _text.c_str(); - int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0; - uint16 start = 0; - - // Calculate total text height - while (*txt) { - charCount = GetLongest(txt, _textRect.width(), _font); - if (charCount == 0) - break; - - Width(txt, 0, (int16)strlen(txt), _fontId, textWidth, textHeight, true); - - totalHeight += textHeight; - txt += charCount; - while (*txt == ' ') { - txt++; // skip over breaking spaces - } + if (_text.size() == 0) { + return; } - txt = _text.c_str(); - - byte *pixels = _segMan->getHunkPointer(_bitmap); - pixels = pixels + READ_SCI11ENDIAN_UINT32(pixels + 28) + _width * _textRect.top + _textRect.left; - - // Draw text in buffer - while (*txt) { - charCount = GetLongest(txt, _textRect.width(), _font); - if (charCount == 0) - break; - Width(txt, start, charCount, _fontId, textWidth, textHeight, true); - - switch (_alignment) { - case kTextAlignRight: - offsetX = _textRect.width() - textWidth; - break; - case kTextAlignCenter: - // Center text both horizontally and vertically - offsetX = (_textRect.width() - textWidth) / 2; - offsetY = (_textRect.height() - totalHeight) / 2; - break; - case kTextAlignLeft: - offsetX = 0; - break; - - default: - warning("Invalid alignment %d used in TextBox()", _alignment); - } + const char *text = _text.c_str(); + const char *sourceText = text; + int16 textRectWidth = _textRect.width(); + _drawPosition.y = _textRect.top; + uint charIndex = 0; + if (getLongest(&charIndex, textRectWidth) == 0) { + error("DrawTextBox GetLongest=0"); + } - byte curChar; - - for (int i = 0; i < charCount; i++) { - curChar = txt[i]; - - switch (curChar) { - case 0x0A: - case 0x0D: - case 0: - break; - case 0x7C: - warning("Code processing isn't implemented in SCI32"); - break; - default: - _font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, _foreColor, _dimmed, pixels, _width, _height); - curX += _font->getCharWidth(curChar); - break; - } - } + charIndex = 0; + uint nextCharIndex = 0; + while (*text != '\0') { + _drawPosition.x = _textRect.left; + + uint length = getLongest(&nextCharIndex, textRectWidth); + int16 textWidth = getTextWidth(charIndex, length); - curX = 0; - curY += _font->getHeight(); - txt += charCount; - while (*txt == ' ') { - txt++; // skip over breaking spaces + if (_alignment == kTextAlignCenter) { + _drawPosition.x += (textRectWidth - textWidth) / 2; + } else if (_alignment == kTextAlignRight) { + _drawPosition.x += textRectWidth - textWidth; } + + drawText(charIndex, length); + charIndex = nextCharIndex; + text = sourceText + charIndex; + _drawPosition.y += _font->getHeight(); } } -void GfxText32::erase(const Common::Rect &rect, const bool doScaling) { - Common::Rect targetRect = doScaling ? rect : scaleRect(rect); +void GfxText32::drawTextBox(const Common::String &text) { + _text = text; + drawTextBox(); +} - byte *bitmap = _segMan->getHunkPointer(_bitmap); - byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); +void GfxText32::drawText(const uint index, uint length) { + assert(index + length <= _text.size()); - // NOTE: There is an extra optimisation within the SCI code to - // do a single memset if the scaledRect is the same size as - // the bitmap, not implemented here. - Buffer buffer(_width, _height, pixels); - buffer.fillRect(targetRect, _backColor); -} + // NOTE: This draw loop implementation is somewhat different than the + // implementation in the actual engine, but should be accurate. Primarily + // the changes revolve around eliminating some extra temporaries and + // fixing the logic to match. + const char *text = _text.c_str() + index; + while (length-- > 0) { + char currentChar = *text++; -reg_t GfxText32::createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { - return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk); -} -reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { - reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text)); - // The object in the text selector of the item can be either a raw string - // or a Str object. In the latter case, we need to access the object's data - // selector to get the raw string. - if (_segMan->isHeapObject(stringObject)) - stringObject = readSelector(_segMan, stringObject, SELECTOR(data)); + if (currentChar == '|') { + const char controlChar = *text++; + --length; - Common::String text = _segMan->getString(stringObject); + if (length == 0) { + return; + } - return createTextBitmapInternal(text, textObject, maxWidth, maxHeight, prevHunk); -} + if (controlChar == 'a' || controlChar == 'c' || controlChar == 'f') { + uint16 value = 0; + + while (length > 0) { + const char valueChar = *text; + if (valueChar < '0' || valueChar > '9') { + break; + } + + ++text; + --length; + value = 10 * value + (valueChar - '0'); + } + + if (length == 0) { + return; + } + + if (controlChar == 'a') { + _alignment = (TextAlign)value; + } else if (controlChar == 'c') { + _foreColor = value; + } else if (controlChar == 'f') { + setFont(value); + } + } -reg_t GfxText32::createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t prevHunk) { - // HACK: The character offsets of the up and down arrow buttons are off by one - // in GK1, for some unknown reason. Fix them here. - if (text.size() == 1 && (text[0] == 29 || text[0] == 30)) { - text.setChar(text[0] + 1, 0); + while (length > 0 && *text != '|') { + ++text; + --length; + } + } else { + drawChar(currentChar); + } } - GuiResourceId fontId = readSelectorValue(_segMan, textObject, SELECTOR(font)); - GfxFont *font = _cache->getFont(fontId); - bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed)); - int16 alignment = readSelectorValue(_segMan, textObject, SELECTOR(mode)); - uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore)); - uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back)); - - Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(textObject); - uint16 width = nsRect.width() + 1; - uint16 height = nsRect.height() + 1; - - // Limit rectangle dimensions, if requested - if (maxWidth > 0) - width = maxWidth; - if (maxHeight > 0) - height = maxHeight; - - // Upscale the coordinates/width if the fonts are already upscaled - if (_screen->fontIsUpscaled()) { - width = width * _screen->getDisplayWidth() / _screen->getWidth(); - height = height * _screen->getDisplayHeight() / _screen->getHeight(); +} + +void GfxText32::invertRect(const reg_t bitmap, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) { + Common::Rect targetRect = rect; + if (doScaling) { + bitmapStride = bitmapStride * _scaledWidth / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + targetRect = scaleRect(rect); } - int entrySize = width * height + BITMAP_HEADER_SIZE; - reg_t memoryId = NULL_REG; - if (prevHunk.isNull()) { - memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize); + byte *bitmapData = _segMan->getHunkPointer(bitmap); - // Scroll text objects have no bitmap selector! - ObjVarRef varp; - if (lookupSelector(_segMan, textObject, SELECTOR(bitmap), &varp, NULL) == kSelectorVariable) - writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId); - } else { - memoryId = prevHunk; - } - byte *memoryPtr = _segMan->getHunkPointer(memoryId); - - if (prevHunk.isNull()) - memset(memoryPtr, 0, BITMAP_HEADER_SIZE); - - byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; - memset(bitmap, backColor, width * height); - - // Save totalWidth, totalHeight - WRITE_SCI11ENDIAN_UINT16(memoryPtr, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 2, height); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 4, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 6, 0); - memoryPtr[8] = 0; - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 10, 0); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 20, BITMAP_HEADER_SIZE); - WRITE_SCI11ENDIAN_UINT32(memoryPtr + 28, 46); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 36, width); - WRITE_SCI11ENDIAN_UINT16(memoryPtr + 38, height); - - int16 charCount = 0; - uint16 curX = 0, curY = 0; - const char *txt = text.c_str(); - int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0; - uint16 start = 0; - - // Calculate total text height - while (*txt) { - charCount = GetLongest(txt, width, font); - if (charCount == 0) - break; - - Width(txt, 0, (int16)strlen(txt), fontId, textWidth, textHeight, true); - - totalHeight += textHeight; - txt += charCount; - while (*txt == ' ') - txt++; // skip over breaking spaces + // NOTE: SCI code is super weird here; it seems to be trying to look at the + // entire size of the bitmap including the header, instead of just the pixel + // data size. We just look at the pixel size. This function generally is an + // odd duck since the stride dimension for a bitmap is built in to the bitmap + // header, so perhaps it was once an unheadered bitmap format and this + // function was never updated to match? Or maybe they exploit the + // configurable stride length somewhere else to do stair stepping inverts... + uint32 invertSize = targetRect.height() * bitmapStride + targetRect.width(); + uint32 bitmapSize = READ_SCI11ENDIAN_UINT32(bitmapData + 12); + + if (invertSize >= bitmapSize) { + error("InvertRect too big: %u >= %u", invertSize, bitmapSize); } - txt = text.c_str(); - - // Draw text in buffer - while (*txt) { - charCount = GetLongest(txt, width, font); - if (charCount == 0) - break; - Width(txt, start, charCount, fontId, textWidth, textHeight, true); - - switch (alignment) { - case kTextAlignRight: - offsetX = width - textWidth; - break; - case kTextAlignCenter: - // Center text both horizontally and vertically - offsetX = (width - textWidth) / 2; - offsetY = (height - totalHeight) / 2; - break; - case kTextAlignLeft: - offsetX = 0; - break; - - default: - warning("Invalid alignment %d used in TextBox()", alignment); - } + // NOTE: Actual engine just added the bitmap header size hardcoded here + byte *pixel = bitmapData + READ_SCI11ENDIAN_UINT32(bitmapData + 28) + bitmapStride * targetRect.top + targetRect.left; + + int16 stride = bitmapStride - targetRect.width(); + int16 targetHeight = targetRect.height(); + int16 targetWidth = targetRect.width(); - byte curChar; - - for (int i = 0; i < charCount; i++) { - curChar = txt[i]; - - switch (curChar) { - case 0x0A: - case 0x0D: - case 0: - break; - case 0x7C: - warning("Code processing isn't implemented in SCI32"); - break; - default: - font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height); - curX += font->getCharWidth(curChar); - break; + for (int16 y = 0; y < targetHeight; ++y) { + for (int16 x = 0; x < targetWidth; ++x) { + if (*pixel == foreColor) { + *pixel = backColor; + } else if (*pixel == backColor) { + *pixel = foreColor; } + + ++pixel; } - curX = 0; - curY += font->getHeight(); - txt += charCount; - while (*txt == ' ') - txt++; // skip over breaking spaces + pixel += stride; } - - return memoryId; } -void GfxText32::disposeTextBitmap(reg_t hunkId) { - _segMan->freeHunkEntry(hunkId); -} +uint GfxText32::getLongest(uint *charIndex, const int16 width) { + assert(width > 0); -void GfxText32::drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject) { - reg_t hunkId = readSelector(_segMan, textObject, SELECTOR(bitmap)); - drawTextBitmapInternal(x, y, planeRect, textObject, hunkId); -} + uint testLength = 0; + uint length = 0; -void GfxText32::drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y) { - /*reg_t plane = readSelector(_segMan, textObject, SELECTOR(plane)); - Common::Rect planeRect; - planeRect.top = readSelectorValue(_segMan, plane, SELECTOR(top)); - planeRect.left = readSelectorValue(_segMan, plane, SELECTOR(left)); - planeRect.bottom = readSelectorValue(_segMan, plane, SELECTOR(bottom)); - planeRect.right = readSelectorValue(_segMan, plane, SELECTOR(right)); + const uint initialCharIndex = *charIndex; - drawTextBitmapInternal(x, y, planeRect, textObject, hunkId);*/ + // The index of the next word after the last word break + uint lastWordBreakIndex = *charIndex; - // HACK: we pretty much ignore the plane rect and x, y... - drawTextBitmapInternal(0, 0, Common::Rect(20, 390, 600, 460), textObject, hunkId); -} + const char *text = _text.c_str() + *charIndex; -void GfxText32::drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId) { - int16 backColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(back)); - // Sanity check: Check if the hunk is set. If not, either the game scripts - // didn't set it, or an old saved game has been loaded, where it wasn't set. - if (hunkId.isNull()) - return; + char currentChar; + while ((currentChar = *text++) != '\0') { + // NOTE: In the original engine, the font, color, and alignment were + // reset here to their initial values - // Negative coordinates indicate that text shouldn't be displayed - if (x < 0 || y < 0) - return; + // The text to render contains a line break; stop at the line break + if (currentChar == '\r' || currentChar == '\n') { + // Skip the rest of the line break if it is a Windows-style + // \r\n or non-standard \n\r + // NOTE: In the original engine, the `text` pointer had not been + // advanced yet so the indexes used to access characters were + // one higher + if ( + (currentChar == '\r' && text[0] == '\n') || + (currentChar == '\n' && text[0] == '\r' && text[1] != '\n') + ) { + ++*charIndex; + } - byte *memoryPtr = _segMan->getHunkPointer(hunkId); + // We are at the end of a line but the last word in the line made + // it too wide to fit in the text area; return up to the previous + // word + if (length && getTextWidth(initialCharIndex, testLength) > width) { + *charIndex = lastWordBreakIndex; + return length; + } - if (!memoryPtr) { - // Happens when restoring in some SCI32 games (e.g. SQ6). - // Commented out to reduce console spam - //warning("Attempt to draw an invalid text bitmap"); - return; - } + // Skip the line break and return all text seen up to now + // NOTE: In original engine, the font, color, and alignment were + // reset, then getTextWidth was called to use its side-effects to + // set font, color, and alignment according to the text from + // `initialCharIndex` to `testLength` + ++*charIndex; + return testLength; + } else if (currentChar == ' ') { + // The last word in the line made it too wide to fit in the text area; + // return up to the previous word, then collapse the whitespace + // between that word and its next sibling word into the line break + if (getTextWidth(initialCharIndex, testLength) > width) { + *charIndex = lastWordBreakIndex; + const char *nextChar = _text.c_str() + lastWordBreakIndex; + while (*nextChar++ == ' ') { + ++*charIndex; + } + + // NOTE: In original engine, the font, color, and alignment were + // set here to the values that were seen at the last space character + return length; + } - byte *surface = memoryPtr + BITMAP_HEADER_SIZE; + // NOTE: In the original engine, the values of _fontId, _foreColor, + // and _alignment were stored for use in the return path mentioned + // just above here - int curByte = 0; - int16 skipColor = (int16)readSelectorValue(_segMan, textObject, SELECTOR(skip)); - uint16 textX = planeRect.left + x; - uint16 textY = planeRect.top + y; - // Get totalWidth, totalHeight - uint16 width = READ_LE_UINT16(memoryPtr); - uint16 height = READ_LE_UINT16(memoryPtr + 2); + // We found a word break that was within the text area, memorise it + // and continue processing. +1 on the character index because it has + // not been incremented yet so currently points to the word break + // and not the word after the break + length = testLength; + lastWordBreakIndex = *charIndex + 1; + } - // Upscale the coordinates/width if the fonts are already upscaled - if (_screen->fontIsUpscaled()) { - textX = textX * _screen->getDisplayWidth() / _screen->getWidth(); - textY = textY * _screen->getDisplayHeight() / _screen->getHeight(); - } + // In the middle of a line, keep processing + ++*charIndex; + ++testLength; - bool translucent = (skipColor == -1 && backColor == -1); + // NOTE: In the original engine, the font, color, and alignment were + // reset here to their initial values - for (int curY = 0; curY < height; curY++) { - for (int curX = 0; curX < width; curX++) { - byte pixel = surface[curByte++]; - if ((!translucent && pixel != skipColor && pixel != backColor) || - (translucent && pixel != 0xFF)) - _screen->putFontPixel(textY, curX + textX, curY, pixel); + // The text to render contained no word breaks yet but is already too + // wide for the text area; just split the word in half at the point + // where it overflows + if (length == 0 && getTextWidth(initialCharIndex, testLength) > width) { + *charIndex = --testLength + lastWordBreakIndex; + return testLength; } } -} -int16 GfxText32::GetLongest(const char *text, int16 maxWidth, GfxFont *font) { - uint16 curChar = 0; - int16 maxChars = 0, curCharCount = 0; - uint16 width = 0; - - while (width <= maxWidth) { - curChar = (*(const byte *)text++); - - switch (curChar) { - // We need to add 0xD, 0xA and 0xD 0xA to curCharCount and then exit - // which means, we split text like - // 'Mature, experienced software analyst available.' 0xD 0xA - // 'Bug installation a proven speciality. "No version too clean."' (normal game text, this is from lsl2) - // and 0xA '-------' 0xA (which is the official sierra subtitle separator) - // Sierra did it the same way. - case 0xD: - // Check, if 0xA is following, if so include it as well - if ((*(const unsigned char *)text) == 0xA) - curCharCount++; - // it's meant to pass through here - case 0xA: - curCharCount++; - // and it's also meant to pass through here - case 0: - return curCharCount; - case ' ': - maxChars = curCharCount; // return count up to (but not including) breaking space - break; - } - if (width + font->getCharWidth(curChar) > maxWidth) - break; - width += font->getCharWidth(curChar); - curCharCount++; + // The complete text to render was a single word, or was narrower than + // the text area, so return the entire line + if (length == 0 || getTextWidth(initialCharIndex, testLength) <= width) { + // NOTE: In original engine, the font, color, and alignment were + // reset, then getTextWidth was called to use its side-effects to + // set font, color, and alignment according to the text from + // `initialCharIndex` to `testLength` + return testLength; } - return maxChars; + // The last word in the line made it wider than the text area, so return + // up to the penultimate word + *charIndex = lastWordBreakIndex; + return length; } -void GfxText32::kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight) { - Common::Rect rect(0, 0, 0, 0); - Size(rect, text, font, maxWidth); - *textWidth = rect.width(); - *textHeight = rect.height(); -} - -void GfxText32::StringWidth(const char *str, GuiResourceId fontId, int16 &textWidth, int16 &textHeight) { - Width(str, 0, (int16)strlen(str), fontId, textWidth, textHeight, true); -} +int16 GfxText32::getTextWidth(const uint index, uint length) const { + int16 width = 0; + + const char *text = _text.c_str() + index; + + GfxFont *font = _font; + + char currentChar = *text++; + while (length > 0 && currentChar != '\0') { + // Control codes are in the format `|<code><value>|` + if (currentChar == '|') { + // NOTE: Original engine code changed the global state of the + // FontMgr here upon encountering any color, alignment, or + // font control code. + // To avoid requiring all callers to manually restore these + // values on every call, we ignore control codes other than + // font change (since alignment and color do not change the + // width of characters), and simply update the font pointer + // on stack instead of the member property font. + currentChar = *text++; + --length; + + if (length > 0 && currentChar == 'f') { + GuiResourceId fontId = 0; + do { + currentChar = *text++; + --length; + + fontId = fontId * 10 + currentChar - '0'; + } while (length > 0 && currentChar >= '0' && currentChar <= '9'); + + if (length > 0) { + font = _cache->getFont(fontId); + } + } -void GfxText32::Width(const char *text, int16 from, int16 len, GuiResourceId fontId, int16 &textWidth, int16 &textHeight, bool restoreFont) { - byte curChar; - textWidth = 0; textHeight = 0; - - GfxFont *font = _cache->getFont(fontId); - - if (font) { - text += from; - while (len--) { - curChar = (*(const byte *)text++); - switch (curChar) { - case 0x0A: - case 0x0D: - textHeight = MAX<int16> (textHeight, font->getHeight()); - break; - case 0x7C: - warning("Code processing isn't implemented in SCI32"); - break; - default: - textHeight = MAX<int16> (textHeight, font->getHeight()); - textWidth += font->getCharWidth(curChar); - break; + // Forward through any more unknown control character data + while (length > 0 && currentChar != '|') { + ++text; + --length; } + } else { + width += font->getCharWidth(currentChar); } + + currentChar = *text++; + --length; } + + return width; +} + +int16 GfxText32::getTextWidth(const Common::String &text, const uint index, const uint length) { + _text = text; + return scaleUpWidth(getTextWidth(index, length)); } -int16 GfxText32::Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth) { - int16 charCount; - int16 maxTextWidth = 0, textWidth; - int16 totalHeight = 0, textHeight; +Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, bool doScaling) { + // NOTE: Like most of the text rendering code, this function was pretty + // weird in the original engine. The initial result rectangle was actually + // a 1x1 rectangle (0, 0, 0, 0), which was then "fixed" after the main + // text size loop finished running by subtracting 1 from the right and + // bottom edges. Like other functions in SCI32, this has been converted + // to use exclusive rects with inclusive rounding. + + Common::Rect result; int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; maxWidth = maxWidth * _scaledWidth / scriptWidth; - rect.top = rect.left = 0; - GfxFont *font = _cache->getFont(fontId); + _text = text; - if (maxWidth < 0) { // force output as single line - StringWidth(text, fontId, textWidth, textHeight); - rect.bottom = textHeight; - rect.right = textWidth; - } else { - // rect.right=found widest line with RTextWidth and GetLongest - // rect.bottom=num. lines * GetPointSize - rect.right = (maxWidth ? maxWidth : 192); - const char *curPos = text; - while (*curPos) { - charCount = GetLongest(curPos, rect.right, font); - if (charCount == 0) - break; - Width(curPos, 0, charCount, fontId, textWidth, textHeight, false); - maxTextWidth = MAX(textWidth, maxTextWidth); - totalHeight += textHeight; - curPos += charCount; - while (*curPos == ' ') - curPos++; // skip over breaking spaces + if (maxWidth >= 0) { + if (maxWidth == 0) { + // TODO: This was hardcoded to 192, but guessing + // that it was originally 60% of the scriptWidth + // before the compiler took over. + // Verify this by looking at a game that uses a + // scriptWidth other than 320, like LSL7 + maxWidth = _scaledWidth * (scriptWidth * 0.6) / scriptWidth; + } + + result.right = maxWidth; + + int16 textWidth = 0; + if (_text.size() > 0) { + const char *rawText = _text.c_str(); + const char *sourceText = rawText; + uint charIndex = 0; + uint nextCharIndex = 0; + while (*rawText != '\0') { + uint length = getLongest(&nextCharIndex, result.width()); + textWidth = MAX(textWidth, getTextWidth(charIndex, length)); + charIndex = nextCharIndex; + rawText = sourceText + charIndex; + // TODO: Due to getLongest and getTextWidth not having side + // effects, it is possible that the currently loaded font's + // height is wrong for this line if it was changed inline + result.bottom += _font->getHeight(); + } + } + + if (textWidth < maxWidth) { + result.right = textWidth; } - rect.bottom = totalHeight; - rect.right = maxWidth ? maxWidth : MIN(rect.right, maxTextWidth); + } else { + result.right = getTextWidth(0, 10000); + // NOTE: In the original engine code, the bottom was not decremented + // by 1, which means that the rect was actually a pixel taller than + // the height of the font. This was not the case in the other branch, + // which decremented the bottom by 1 at the end of the loop. + result.bottom = _font->getHeight() + 1; } - rect.right = rect.right * scriptWidth / _scaledWidth; - rect.bottom = rect.bottom * scriptHeight / _scaledHeight; + if (doScaling) { + // NOTE: The original engine code also scaled top/left but these are + // always zero so there is no reason to do that. + result.right = ((result.right - 1) * scriptWidth + _scaledWidth - 1) / _scaledWidth + 1; + result.bottom = ((result.bottom - 1) * scriptHeight + _scaledHeight - 1) / _scaledHeight + 1; + } - return rect.right; + return result; } +void GfxText32::erase(const Common::Rect &rect, const bool doScaling) { + Common::Rect targetRect = doScaling ? scaleRect(rect) : rect; + + byte *bitmap = _segMan->getHunkPointer(_bitmap); + byte *pixels = bitmap + READ_SCI11ENDIAN_UINT32(bitmap + 28); + + // NOTE: There is an extra optimisation within the SCI code to + // do a single memset if the scaledRect is the same size as + // the bitmap, not implemented here. + Buffer buffer(_width, _height, pixels); + buffer.fillRect(targetRect, _backColor); +} + +int16 GfxText32::getStringWidth(const Common::String &text) { + return getTextWidth(text, 0, 10000); +} + +int16 GfxText32::getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling) { + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + Common::Rect scaledRect(textRect); + if (doScaling) { + mulinc(scaledRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight)); + } + + Common::String oldText = _text; + _text = text; + + uint charIndex = index; + int16 maxWidth = scaledRect.width(); + int16 lineCount = (scaledRect.height() - 2) / _font->getHeight(); + while (lineCount--) { + getLongest(&charIndex, maxWidth); + } + + _text = oldText; + return charIndex - index; +} + +int16 GfxText32::getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling) { + setFont(fontId); + return getTextCount(text, index, textRect, doScaling); +} + + } // End of namespace Sci diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 08568e4958..5768ea0c59 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -31,9 +31,203 @@ namespace Sci { enum TextAlign { kTextAlignLeft = 0, kTextAlignCenter = 1, - kTextAlignRight = -1 + kTextAlignRight = 2 }; +enum BitmapFlags { + kBitmapRemap = 2 +}; + +#define BITMAP_PROPERTY(size, property, offset)\ +inline uint##size get##property() const {\ + return READ_SCI11ENDIAN_UINT##size(_bitmap + (offset));\ +}\ +inline void set##property(uint##size value) {\ + WRITE_SCI11ENDIAN_UINT##size(_bitmap + (offset), (value));\ +} + +/** + * A convenience class for creating and modifying in-memory + * bitmaps. + */ +class BitmapResource { + byte *_bitmap; + reg_t _object; + + /** + * Gets the size of the bitmap header for the current + * engine version. + */ + static inline uint16 getBitmapHeaderSize() { + // TODO: These values are accurate for each engine, but there may be no reason + // to not simply just always use size 40, since SCI2.1mid does not seem to + // actually store any data above byte 40, and SCI2 did not allow bitmaps with + // scaling resolutions other than the default (320x200). Perhaps SCI3 used + // the extra bytes, or there is some reason why they tried to align the header + // size with other headers like pic headers? +// uint32 bitmapHeaderSize; +// if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { +// bitmapHeaderSize = 46; +// } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) { +// bitmapHeaderSize = 40; +// } else { +// bitmapHeaderSize = 36; +// } +// return bitmapHeaderSize; + return 46; + } + + /** + * Gets the byte size of a bitmap with the given width + * and height. + */ + static inline uint32 getBitmapSize(const uint16 width, const uint16 height) { + return width * height + getBitmapHeaderSize(); + } + +public: + /** + * Create a bitmap resource for an existing bitmap. + * Ownership of the bitmap is retained by the caller. + */ + inline BitmapResource(reg_t bitmap) : + _bitmap(g_sci->getEngineState()->_segMan->getHunkPointer(bitmap)), + _object(bitmap) { + if (_bitmap == nullptr || getUncompressedDataOffset() != getBitmapHeaderSize()) { + error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); + } + } + + /** + * Allocates and initialises a new bitmap in the given + * segment manager. + */ + inline BitmapResource(SegManager *segMan, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool remap) { + + _object = segMan->allocateHunkEntry("Bitmap()", getBitmapSize(width, height)); + _bitmap = segMan->getHunkPointer(_object); + + const uint16 bitmapHeaderSize = getBitmapHeaderSize(); + + setWidth(width); + setHeight(height); + setDisplace(Common::Point(displaceX, displaceY)); + setSkipColor(skipColor); + _bitmap[9] = 0; + WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, 0); + setRemap(remap); + setDataSize(width * height); + WRITE_SCI11ENDIAN_UINT32(_bitmap + 16, 0); + setHunkPaletteOffset(hunkPaletteOffset); + setDataOffset(bitmapHeaderSize); + setUncompressedDataOffset(bitmapHeaderSize); + setControlOffset(0); + setScaledWidth(scaledWidth); + setScaledHeight(scaledHeight); + } + + reg_t getObject() const { + return _object; + } + + BITMAP_PROPERTY(16, Width, 0); + BITMAP_PROPERTY(16, Height, 2); + + inline Common::Point getDisplace() const { + return Common::Point( + (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 4), + (int16)READ_SCI11ENDIAN_UINT16(_bitmap + 6) + ); + } + + inline void setDisplace(const Common::Point &displace) { + WRITE_SCI11ENDIAN_UINT16(_bitmap + 4, (uint16)displace.x); + WRITE_SCI11ENDIAN_UINT16(_bitmap + 6, (uint16)displace.y); + } + + inline uint8 getSkipColor() const { + return _bitmap[8]; + } + + inline void setSkipColor(const uint8 skipColor) { + _bitmap[8] = skipColor; + } + + inline bool getRemap() const { + return READ_SCI11ENDIAN_UINT16(_bitmap + 10) & kBitmapRemap; + } + + inline void setRemap(const bool remap) { + uint16 flags = READ_SCI11ENDIAN_UINT16(_bitmap + 10); + if (remap) { + flags |= kBitmapRemap; + } else { + flags &= ~kBitmapRemap; + } + WRITE_SCI11ENDIAN_UINT16(_bitmap + 10, flags); + } + + BITMAP_PROPERTY(32, DataSize, 12); + + inline uint32 getHunkPaletteOffset() const { + return READ_SCI11ENDIAN_UINT32(_bitmap + 20); + } + + void setHunkPaletteOffset(uint32 hunkPaletteOffset) { + if (hunkPaletteOffset) { + hunkPaletteOffset += getBitmapHeaderSize(); + } + + WRITE_SCI11ENDIAN_UINT32(_bitmap + 20, hunkPaletteOffset); + } + + BITMAP_PROPERTY(32, DataOffset, 24); + + // NOTE: This property is used as a "magic number" for + // validating that a block of memory is a valid bitmap, + // and so is always set to the size of the header. + BITMAP_PROPERTY(32, UncompressedDataOffset, 28); + + // NOTE: This property always seems to be zero + BITMAP_PROPERTY(32, ControlOffset, 32); + + inline uint16 getScaledWidth() const { + if (getDataOffset() >= 40) { + return READ_SCI11ENDIAN_UINT16(_bitmap + 36); + } + + // SCI2 bitmaps did not have scaling ability + return 320; + } + + inline void setScaledWidth(uint16 scaledWidth) { + if (getDataOffset() >= 40) { + WRITE_SCI11ENDIAN_UINT16(_bitmap + 36, scaledWidth); + } + } + + inline uint16 getScaledHeight() const { + if (getDataOffset() >= 40) { + return READ_SCI11ENDIAN_UINT16(_bitmap + 38); + } + + // SCI2 bitmaps did not have scaling ability + return 200; + } + + inline void setScaledHeight(uint16 scaledHeight) { + if (getDataOffset() >= 40) { + WRITE_SCI11ENDIAN_UINT16(_bitmap + 38, scaledHeight); + } + } + + inline byte *getPixels() { + return _bitmap + getUncompressedDataOffset(); + } +}; + +class GfxFont; + /** * This class handles text calculation and rendering for * SCI32 games. The text calculation system in SCI32 is @@ -43,6 +237,9 @@ enum TextAlign { */ class GfxText32 { private: + SegManager *_segMan; + GfxCache *_cache; + /** * The resource ID of the default font used by the game. * @@ -54,6 +251,8 @@ private: /** * The width and height of the currently active text * bitmap, in text-system coordinates. + * + * @note These are unsigned in the actual engine. */ int16 _width, _height; @@ -105,33 +304,43 @@ private: */ TextAlign _alignment; - /** - * The memory handle of the currently active bitmap. - */ - reg_t _bitmap; + int16 _field_20; /** * TODO: Document */ - int _field_22; + int16 _field_22; + + int _field_2C, _field_30, _field_34, _field_38; + + int16 _field_3C; /** - * The currently active font resource used to write text - * into the bitmap. - * - * @note SCI engine builds the font table directly - * inside of FontMgr; we use GfxFont instead. + * The position of the text draw cursor. */ - GfxFont *_font; + Common::Point _drawPosition; - // TODO: This is general for all CelObjMem and should be - // put in a single location, like maybe as a static - // method of CelObjMem?! - void buildBitmapHeader(byte *bitmap, const int16 width, const int16 height, const uint8 skipColor, const int16 displaceX, const int16 displaceY, const int16 scaledWidth, const int16 scaledHeight, const uint32 hunkPaletteOffset, const bool useRemap) const; + void drawFrame(const Common::Rect &rect, const int16 size, const uint8 color, const bool doScaling); - void drawFrame(const Common::Rect &rect, const int size, const uint8 color, const bool doScaling); - void drawTextBox(); - void erase(const Common::Rect &rect, const bool doScaling); + void drawChar(const char charIndex); + void drawText(const uint index, uint length); + + /** + * Gets the length of the longest run of text available + * within the currently loaded text, starting from the + * given `charIndex` and running for up to `maxWidth` + * pixels. Returns the number of characters that can be + * written, and mutates the value pointed to by + * `charIndex` to point to the index of the next + * character to render. + */ + uint getLongest(uint *charIndex, const int16 maxWidth); + + /** + * Gets the pixel width of a substring of the currently + * loaded text, without scaling. + */ + int16 getTextWidth(const uint index, uint length) const; inline Common::Rect scaleRect(const Common::Rect &rect) { Common::Rect scaledRect(rect); @@ -139,12 +348,17 @@ private: int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; Ratio scaleX(_scaledWidth, scriptWidth); Ratio scaleY(_scaledHeight, scriptHeight); - mul(scaledRect, scaleX, scaleY); + mulinc(scaledRect, scaleX, scaleY); return scaledRect; } public: - GfxText32(SegManager *segMan, GfxCache *fonts, GfxScreen *screen); + GfxText32(SegManager *segMan, GfxCache *fonts); + + /** + * The memory handle of the currently active bitmap. + */ + reg_t _bitmap; /** * The size of the x-dimension of the coordinate system @@ -158,32 +372,106 @@ public: */ int16 _scaledHeight; - reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling, reg_t *outBitmapObject); + /** + * The currently active font resource used to write text + * into the bitmap. + * + * @note SCI engine builds the font table directly + * inside of FontMgr; we use GfxFont instead. + */ + GfxFont *_font; - reg_t createTitledFontBitmap(CelInfo32 &celInfo, Common::Rect &rect, Common::String &text, int16 foreColor, int16 backColor, int font, int16 skipColor, int16 borderColor, bool dimmed, void *unknown1); + /** + * Creates a plain font bitmap with a flat color + * background. + */ + reg_t createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, TextAlign alignment, const int16 borderColor, bool dimmed, const bool doScaling); -#pragma mark - -#pragma mark Old stuff + /** + * Creates a font bitmap with a view background. + */ + reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed); - reg_t createTextBitmap(reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG); - reg_t createScrollTextBitmap(Common::String text, reg_t textObject, uint16 maxWidth = 0, uint16 maxHeight = 0, reg_t prevHunk = NULL_REG); - void drawTextBitmap(int16 x, int16 y, Common::Rect planeRect, reg_t textObject); - void drawScrollTextBitmap(reg_t textObject, reg_t hunkId, uint16 x, uint16 y); - void disposeTextBitmap(reg_t hunkId); - int16 GetLongest(const char *text, int16 maxWidth, GfxFont *font); + /** + * Creates a font bitmap with a title. + */ + reg_t createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling); - void kernelTextSize(const char *text, int16 font, int16 maxWidth, int16 *textWidth, int16 *textHeight); + inline int scaleUpWidth(int value) const { + const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; + } -private: - reg_t createTextBitmapInternal(Common::String &text, reg_t textObject, uint16 maxWidth, uint16 maxHeight, reg_t hunkId); - void drawTextBitmapInternal(int16 x, int16 y, Common::Rect planeRect, reg_t textObject, reg_t hunkId); - int16 Size(Common::Rect &rect, const char *text, GuiResourceId fontId, int16 maxWidth); - void Width(const char *text, int16 from, int16 len, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight, bool restoreFont); - void StringWidth(const char *str, GuiResourceId orgFontId, int16 &textWidth, int16 &textHeight); + inline int scaleUpHeight(int value) const { + const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + return (value * scriptHeight + _scaledHeight - 1) / _scaledHeight; + } - SegManager *_segMan; - GfxCache *_cache; - GfxScreen *_screen; + /** + * Draws the text to the bitmap. + */ + void drawTextBox(); + + /** + * Draws the given text to the bitmap. + * + * @note The original engine holds a reference to a + * shared string which lets the text be updated from + * outside of the font manager. Instead, we give this + * extra signature to send the text to draw. + * + * TODO: Use shared string instead? + */ + void drawTextBox(const Common::String &text); + + /** + * Erases the given rect by filling with the background + * color. + */ + void erase(const Common::Rect &rect, const bool doScaling); + + void invertRect(const reg_t bitmap, const int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling); + + /** + * Sets the font to be used for rendering and + * calculation of text dimensions. + */ + void setFont(const GuiResourceId fontId); + + /** + * Gets the width of a character. + */ + uint16 getCharWidth(const char charIndex, const bool doScaling) const; + + /** + * Retrieves the width and height of a block of text. + */ + Common::Rect getTextSize(const Common::String &text, const int16 maxWidth, bool doScaling); + + /** + * Gets the pixel width of a substring of the currently + * loaded text, with scaling. + */ + int16 getTextWidth(const Common::String &text, const uint index, const uint length); + + /** + * Retrieves the width of a line of text. + */ + int16 getStringWidth(const Common::String &text); + + /** + * Gets the number of characters of `text`, starting + * from `index`, that can be safely rendered into + * `textRect`. + */ + int16 getTextCount(const Common::String &text, const uint index, const Common::Rect &textRect, const bool doScaling); + + /** + * Gets the number of characters of `text`, starting + * from `index`, that can be safely rendered into + * `textRect` using the given font. + */ + int16 getTextCount(const Common::String &text, const uint index, const GuiResourceId fontId, const Common::Rect &textRect, const bool doScaling); }; } // End of namespace Sci diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 4116eda876..1939e66179 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -25,6 +25,7 @@ #include "sci/engine/state.h" #include "sci/graphics/screen.h" #include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" #include "sci/graphics/coordadjuster.h" #include "sci/graphics/view.h" @@ -842,12 +843,11 @@ void GfxView::draw(const Common::Rect &rect, const Common::Rect &clipRect, const const int y2 = clipRectTranslated.top + y; if (!upscaledHires) { if (priority >= _screen->getPriority(x2, y2)) { - if (!_palette->isRemapped(palette->mapping[color])) { - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); - } else { - byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); - _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); - } + byte outputColor = palette->mapping[color]; + // SCI16 remapping (QFG4 demo) + if (g_sci->_gfxRemap16 && g_sci->_gfxRemap16->isRemapped(outputColor)) + outputColor = g_sci->_gfxRemap16->remapColor(outputColor, _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, outputColor, priority, 0); } } else { // UpscaledHires means view is hires and is supposed to @@ -957,12 +957,11 @@ void GfxView::drawScaled(const Common::Rect &rect, const Common::Rect &clipRect, const int x2 = clipRectTranslated.left + x; const int y2 = clipRectTranslated.top + y; if (color != clearKey && priority >= _screen->getPriority(x2, y2)) { - if (!_palette->isRemapped(palette->mapping[color])) { - _screen->putPixel(x2, y2, drawMask, palette->mapping[color], priority, 0); - } else { - byte remappedColor = _palette->remapColor(palette->mapping[color], _screen->getVisual(x2, y2)); - _screen->putPixel(x2, y2, drawMask, remappedColor, priority, 0); - } + byte outputColor = palette->mapping[color]; + // SCI16 remapping (QFG4 demo) + if (g_sci->_gfxRemap16 && g_sci->_gfxRemap16->isRemapped(outputColor)) + outputColor = g_sci->_gfxRemap16->remapColor(outputColor, _screen->getVisual(x2, y2)); + _screen->putPixel(x2, y2, drawMask, outputColor, priority, 0); } } } @@ -976,13 +975,4 @@ void GfxView::adjustBackUpscaledCoordinates(int16 &y, int16 &x) { _screen->adjustBackUpscaledCoordinates(y, x, _sci2ScaleRes); } -byte GfxView::getColorAtCoordinate(int16 loopNo, int16 celNo, int16 x, int16 y) { - const CelInfo *celInfo = getCelInfo(loopNo, celNo); - const byte *bitmap = getBitmap(loopNo, celNo); - const int16 celWidth = celInfo->width; - - bitmap += (celWidth * y); - return bitmap[x]; -} - } // End of namespace Sci diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h index d8803db208..91590208c1 100644 --- a/engines/sci/graphics/view.h +++ b/engines/sci/graphics/view.h @@ -85,8 +85,6 @@ public: void adjustToUpscaledCoordinates(int16 &y, int16 &x); void adjustBackUpscaledCoordinates(int16 &y, int16 &x); - byte getColorAtCoordinate(int16 loopNo, int16 celNo, int16 x, int16 y); - private: void initData(GuiResourceId resourceId); void unpackCel(int16 loopNo, int16 celNo, byte *outPtr, uint32 pixelCount); diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 963b65742d..a02147e4d0 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -57,6 +57,7 @@ MODULE_OBJS := \ graphics/picture.o \ graphics/portrait.o \ graphics/ports.o \ + graphics/remap.o \ graphics/screen.o \ graphics/text16.o \ graphics/transitions.o \ diff --git a/engines/sci/parser/vocabulary.cpp b/engines/sci/parser/vocabulary.cpp index 828a57abeb..a09ba8f3ce 100644 --- a/engines/sci/parser/vocabulary.cpp +++ b/engines/sci/parser/vocabulary.cpp @@ -121,7 +121,7 @@ bool Vocabulary::loadParserWords() { } } - unsigned int seeker; + uint32 seeker; if (resourceType == kVocabularySCI1) seeker = 255 * 2; // vocab.900 starts with 255 16-bit pointers which we don't use else @@ -202,7 +202,7 @@ bool Vocabulary::loadSuffixes() { if (!resource) return false; // No vocabulary found - unsigned int seeker = 1; + uint32 seeker = 1; while ((seeker < resource->size - 1) && (resource->data[seeker + 1] != 0xff)) { suffix_t suffix; @@ -288,7 +288,7 @@ bool Vocabulary::loadAltInputs() { AltInput t; t._input = data; - unsigned int l = strlen(data); + uint32 l = strlen(data); t._inputLength = l; data += l + 1; @@ -325,15 +325,15 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) { return false; bool ret = false; - unsigned int loopCount = 0; + uint32 loopCount = 0; bool changed; do { changed = false; const char* t = text.c_str(); - unsigned int tlen = text.size(); + uint32 tlen = text.size(); - for (unsigned int p = 0; p < tlen && !changed; ++p) { + for (uint32 p = 0; p < tlen && !changed; ++p) { unsigned char s = t[p]; if (s >= _altInputs.size() || _altInputs[s].empty()) continue; @@ -351,7 +351,7 @@ bool Vocabulary::checkAltInput(Common::String& text, uint16& cursorPos) { cursorPos = p + strlen(i->_replacement); } - for (unsigned int j = 0; j < i->_inputLength; ++j) + for (uint32 j = 0; j < i->_inputLength; ++j) text.deleteChar(p); const char *r = i->_replacement; while (*r) diff --git a/engines/sci/parser/vocabulary.h b/engines/sci/parser/vocabulary.h index f4adee6e55..59558ce18a 100644 --- a/engines/sci/parser/vocabulary.h +++ b/engines/sci/parser/vocabulary.h @@ -156,7 +156,7 @@ typedef Common::Array<synonym_t> SynonymList; struct AltInput { const char *_input; const char *_replacement; - unsigned int _inputLength; + uint32 _inputLength; bool _prefix; }; diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 2a4cd95b91..6a5af1a6d6 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -307,7 +307,7 @@ bool Resource::loadPatch(Common::SeekableReadStream *file) { error("Can't allocate %d bytes needed for loading %s", res->size + res->_headerSize, res->_id.toString().c_str()); } - unsigned int really_read; + uint32 really_read; if (res->_headerSize > 0) { really_read = file->read(res->_header, res->_headerSize); if (really_read != res->_headerSize) @@ -565,12 +565,11 @@ Resource *ResourceManager::testResource(ResourceId id) { } int ResourceManager::addAppropriateSources() { - Common::ArchiveMemberList files; - if (Common::File::exists("resource.map")) { // SCI0-SCI2 file naming scheme ResourceSource *map = addExternalMap("resource.map"); + Common::ArchiveMemberList files; SearchMan.listMatchingMembers(files, "resource.0??"); for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { @@ -587,20 +586,20 @@ int ResourceManager::addAppropriateSources() { #endif } else if (Common::MacResManager::exists("Data1")) { // Mac SCI1.1+ file naming scheme - SearchMan.listMatchingMembers(files, "Data?*"); + Common::StringArray files; + Common::MacResManager::listFiles(files, "Data?"); - for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { - Common::String filename = (*x)->getName(); - addSource(new MacResourceForkResourceSource(filename, atoi(filename.c_str() + 4))); + for (Common::StringArray::const_iterator x = files.begin(); x != files.end(); ++x) { + addSource(new MacResourceForkResourceSource(*x, atoi(x->c_str() + 4))); } #ifdef ENABLE_SCI32 // There can also be a "Patches" resource fork with patches - if (Common::File::exists("Patches")) + if (Common::MacResManager::exists("Patches")) addSource(new MacResourceForkResourceSource("Patches", 100)); } else { // SCI2.1-SCI3 file naming scheme - Common::ArchiveMemberList mapFiles; + Common::ArchiveMemberList mapFiles, files; SearchMan.listMatchingMembers(mapFiles, "resmap.0??"); SearchMan.listMatchingMembers(files, "ressci.0??"); diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 6869e6379e..5717a09121 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -139,7 +139,7 @@ bool Resource::loadFromAudioVolumeSCI1(Common::SeekableReadStream *file) { error("Can't allocate %d bytes needed for loading %s", size, _id.toString().c_str()); } - unsigned int really_read = file->read(data, size); + uint32 really_read = file->read(data, size); if (really_read != size) warning("Read %d bytes from %s but expected %d", really_read, _id.toString().c_str(), size); @@ -688,6 +688,12 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers channel->data = resource->data + dataOffset; channel->size = READ_LE_UINT16(data + 4); + + if (dataOffset + channel->size > resource->size) { + warning("Invalid size inside sound resource %d: track %d, channel %d", resourceNr, trackNr, channelNr); + channel->size = resource->size - dataOffset; + } + channel->curPos = 0; channel->number = *channel->data; diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 6d36fabde9..e14d12b918 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -58,6 +58,7 @@ #include "sci/graphics/picture.h" #include "sci/graphics/ports.h" #include "sci/graphics/palette.h" +#include "sci/graphics/remap.h" #include "sci/graphics/screen.h" #include "sci/graphics/text16.h" #include "sci/graphics/transitions.h" @@ -163,6 +164,7 @@ SciEngine::~SciEngine() { delete _gfxText32; delete _robotDecoder; delete _gfxFrameout; + delete _gfxRemap32; #endif delete _gfxMenu; delete _gfxControls16; @@ -175,6 +177,7 @@ SciEngine::~SciEngine() { delete _gfxPorts; delete _gfxCache; delete _gfxPalette16; + delete _gfxRemap16; delete _gfxCursor; delete _gfxScreen; @@ -238,13 +241,7 @@ Common::Error SciEngine::run() { // Only DOS+Windows switch (_gameId) { case GID_KQ6: - if (isCD()) - _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); - break; case GID_GK1: - if ((isCD()) && (!isDemo())) - _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); - break; case GID_PQ4: if (isCD()) _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); @@ -316,6 +313,7 @@ Common::Error SciEngine::run() { if (directSaveSlotLoading >= 0) { _gamestate->_delayedRestoreGame = true; _gamestate->_delayedRestoreGameId = directSaveSlotLoading; + _gamestate->_delayedRestoreFromLauncher = true; // Jones only initializes its menus when restarting/restoring, thus set // the gameIsRestarting flag here before initializing. Fixes bug #6536. @@ -665,6 +663,7 @@ void SciEngine::initGraphics() { _gfxPaint = 0; _gfxPaint16 = 0; _gfxPalette16 = 0; + _gfxRemap16 = 0; _gfxPorts = 0; _gfxText16 = 0; _gfxTransitions = 0; @@ -675,6 +674,7 @@ void SciEngine::initGraphics() { _gfxFrameout = 0; _gfxPaint32 = 0; _gfxPalette32 = 0; + _gfxRemap32 = 0; #endif if (hasMacIconBar()) @@ -684,9 +684,12 @@ void SciEngine::initGraphics() { if (getSciVersion() >= SCI_VERSION_2) { _gfxPalette32 = new GfxPalette32(_resMan, _gfxScreen); _gfxPalette16 = _gfxPalette32; + _gfxRemap32 = new GfxRemap32(_gfxPalette32); } else { #endif _gfxPalette16 = new GfxPalette(_resMan, _gfxScreen); + if (getGameId() == GID_QFG4DEMO) + _gfxRemap16 = new GfxRemap(_gfxPalette16); #ifdef ENABLE_SCI32 } #endif @@ -704,7 +707,7 @@ void SciEngine::initGraphics() { _gfxPaint = _gfxPaint32; _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxCache, _gfxScreen, _gfxPalette32, _gfxPaint32); - _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache, _gfxScreen); + _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32); _gfxFrameout->run(); } else { @@ -833,7 +836,7 @@ Console *SciEngine::getSciDebugger() { } const char *SciEngine::getGameIdStr() const { - return _gameDescription->gameid; + return _gameDescription->gameId; } Common::Language SciEngine::getLanguage() const { diff --git a/engines/sci/sci.h b/engines/sci/sci.h index 3945e68e33..2474db81d9 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -71,6 +71,8 @@ class GfxPaint16; class GfxPaint32; class GfxPalette; class GfxPalette32; +class GfxRemap; +class GfxRemap32; class GfxPorts; class GfxScreen; class GfxText16; @@ -128,6 +130,7 @@ enum SciGameId { GID_FAIRYTALES, GID_FREDDYPHARKAS, GID_FUNSEEKER, + GID_GK1DEMO, // We have a separate ID for GK1 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1) GID_GK1, GID_GK2, GID_HOYLE1, @@ -165,12 +168,14 @@ enum SciGameId { GID_PQ2, GID_PQ3, GID_PQ4, + GID_PQ4DEMO, // We have a separate ID for PQ4 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1) GID_PQSWAT, GID_QFG1, GID_QFG1VGA, GID_QFG2, GID_QFG3, GID_QFG4, + GID_QFG4DEMO, // We have a separate ID for QFG4 demo, because it's actually a completely different game (SCI1.1 vs SCI2/SCI2.1) GID_RAMA, GID_SHIVERS, //GID_SHIVERS2, // Not SCI @@ -349,6 +354,8 @@ public: GfxMenu *_gfxMenu; // Menu for 16-bit gfx GfxPalette *_gfxPalette16; GfxPalette32 *_gfxPalette32; // Palette for 32-bit gfx + GfxRemap *_gfxRemap16; // Remapping for the QFG4 demo + GfxRemap32 *_gfxRemap32; // Remapping for 32-bit gfx GfxPaint *_gfxPaint; GfxPaint16 *_gfxPaint16; // Painting in 16-bit gfx GfxPaint32 *_gfxPaint32; // Painting in 32-bit gfx diff --git a/engines/sci/sound/drivers/amigamac.cpp b/engines/sci/sound/drivers/amigamac.cpp index 5ce49086ca..0f93b19e7c 100644 --- a/engines/sci/sound/drivers/amigamac.cpp +++ b/engines/sci/sound/drivers/amigamac.cpp @@ -497,7 +497,7 @@ MidiDriver_AmigaMac::InstrumentSample *MidiDriver_AmigaMac::readInstrumentSCI0(C } instrument->samples = (int8 *) malloc(size + 1); - if (file.read(instrument->samples, size) < (unsigned int)size) { + if (file.read(instrument->samples, size) < (uint32)size) { warning("Amiga/Mac driver: failed to read instrument samples"); free(instrument->samples); delete instrument; diff --git a/engines/sci/sound/soundcmd.cpp b/engines/sci/sound/soundcmd.cpp index ee5903fda2..e7b25eb1fc 100644 --- a/engines/sci/sound/soundcmd.cpp +++ b/engines/sci/sound/soundcmd.cpp @@ -44,7 +44,7 @@ SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segM // resource number, but it's totally unrelated to the menu music). // The GK1 demo (very late SCI1.1) does the same thing // TODO: Check the QFG4 demo - _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1 || ConfMan.getBool("prefer_digitalsfx")); + _useDigitalSFX = (getSciVersion() >= SCI_VERSION_2 || g_sci->getGameId() == GID_GK1DEMO || ConfMan.getBool("prefer_digitalsfx")); _music = new SciMusic(_soundVersion, _useDigitalSFX); _music->init(); diff --git a/engines/scumm/charset-fontdata.cpp b/engines/scumm/charset-fontdata.cpp index 23e89b1878..a1e92a9950 100644 --- a/engines/scumm/charset-fontdata.cpp +++ b/engines/scumm/charset-fontdata.cpp @@ -591,35 +591,40 @@ CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language) _fontHeight = 8; _curId = 0; - const byte *replacementData = NULL; + const byte *replacementMap = NULL, *replacementData = NULL; int replacementChars = 0; switch (language) { case Common::DE_DEU: if (_vm->_game.version == 0) { - replacementData = germanCharsetDataV0; + replacementMap = germanCharsetDataV0; replacementChars = sizeof(germanCharsetDataV0) / 2; } else { - replacementData = germanCharsetDataV2; + replacementMap = germanCharsetDataV2; replacementChars = sizeof(germanCharsetDataV2) / 2; } + replacementData = specialCharsetData; break; case Common::FR_FRA: - replacementData = frenchCharsetDataV2; + replacementMap = frenchCharsetDataV2; replacementChars = sizeof(frenchCharsetDataV2) / 2; + replacementData = specialCharsetData; break; case Common::IT_ITA: - replacementData = italianCharsetDataV2; + replacementMap = italianCharsetDataV2; replacementChars = sizeof(italianCharsetDataV2) / 2; + replacementData = specialCharsetData; break; case Common::ES_ESP: - replacementData = spanishCharsetDataV2; + replacementMap = spanishCharsetDataV2; replacementChars = sizeof(spanishCharsetDataV2) / 2; + replacementData = specialCharsetData; break; case Common::RU_RUS: if (((_vm->_game.id == GID_MANIAC) || (_vm->_game.id == GID_ZAK)) && (_vm->_game.version == 2)) { - replacementData = russCharsetDataV2; + replacementMap = russCharsetDataV2; replacementChars = sizeof(russCharsetDataV2) / 2; + replacementData = russianCharsetDataV2; } else { _fontPtr = russianCharsetDataV2; } @@ -629,20 +634,16 @@ CharsetRendererV2::CharsetRendererV2(ScummEngine *vm, Common::Language language) break; } - if (replacementData) { + if (replacementMap && replacementData) { _fontPtr = new byte[sizeof(englishCharsetDataV2)]; _deleteFontPtr = true; memcpy(const_cast<byte *>(_fontPtr), englishCharsetDataV2, sizeof(englishCharsetDataV2)); for (int i = 0; i < replacementChars; i++) { - int ch1 = replacementData[2 * i]; - int ch2 = replacementData[2 * i + 1]; + int ch1 = replacementMap[2 * i]; + int ch2 = replacementMap[2 * i + 1]; - if (((_vm->_game.id == GID_MANIAC) || (_vm->_game.id == GID_ZAK)) && (_vm->_game.version == 2)) { - memcpy(const_cast<byte *>(_fontPtr) + 8 * ch1, russianCharsetDataV2 + 8 * ch2, 8); - } else { - memcpy(const_cast<byte *>(_fontPtr) + 8 * ch1, specialCharsetData + 8 * ch2, 8); - } + memcpy(const_cast<byte *>(_fontPtr) + 8 * ch1, replacementData + 8 * ch2, 8); } } else _deleteFontPtr = false; diff --git a/engines/scumm/configure.engine b/engines/scumm/configure.engine index e1de788061..e8962a371e 100644 --- a/engines/scumm/configure.engine +++ b/engines/scumm/configure.engine @@ -2,4 +2,4 @@ # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] add_engine scumm "SCUMM" yes "scumm_7_8 he" "v0-v6 games" add_engine scumm_7_8 "v7 & v8 games" yes -add_engine he "HE71+ games" yes +add_engine he "HE71+ games" yes "" "" "highres" diff --git a/engines/scumm/players/player_ad.cpp b/engines/scumm/players/player_ad.cpp index 4d4be2c3c2..55bbeeef98 100644 --- a/engines/scumm/players/player_ad.cpp +++ b/engines/scumm/players/player_ad.cpp @@ -50,7 +50,7 @@ Player_AD::Player_AD(ScummEngine *scumm) writeReg(0x01, 0x20); _engineMusicTimer = 0; - _soundPlaying = -1; + _musicResource = -1; _curOffset = 0; @@ -104,8 +104,8 @@ void Player_AD::startSound(int sound) { stopMusic(); // Lock the new music resource - _soundPlaying = sound; - _vm->_res->lock(rtSound, _soundPlaying); + _musicResource = sound; + _vm->_res->lock(rtSound, _musicResource); // Start the new music resource _musicData = res; @@ -150,7 +150,7 @@ void Player_AD::startSound(int sound) { void Player_AD::stopSound(int sound) { Common::StackLock lock(_mutex); - if (sound == _soundPlaying) { + if (sound == _musicResource) { stopMusic(); } else { for (int i = 0; i < ARRAYSIZE(_sfx); ++i) { @@ -178,7 +178,17 @@ int Player_AD::getMusicTimer() { } int Player_AD::getSoundStatus(int sound) const { - return (sound == _soundPlaying); + if (sound == _musicResource) { + return true; + } + + for (int i = 0; i < ARRAYSIZE(_sfx); ++i) { + if (_sfx[i].resource == sound) { + return true; + } + } + + return false; } void Player_AD::saveLoadWithSerializer(Serializer *ser) { @@ -193,7 +203,7 @@ void Player_AD::saveLoadWithSerializer(Serializer *ser) { if (ser->getVersion() >= VER(96)) { int32 res[4] = { - _soundPlaying, _sfx[0].resource, _sfx[1].resource, _sfx[2].resource + _musicResource, _sfx[0].resource, _sfx[1].resource, _sfx[2].resource }; // The first thing we save is a list of sound resources being played @@ -461,13 +471,13 @@ void Player_AD::startMusic() { } void Player_AD::stopMusic() { - if (_soundPlaying == -1) { + if (_musicResource == -1) { return; } // Unlock the music resource if present - _vm->_res->unlock(rtSound, _soundPlaying); - _soundPlaying = -1; + _vm->_res->unlock(rtSound, _musicResource); + _musicResource = -1; // Stop the music playback _curOffset = 0; @@ -510,7 +520,7 @@ void Player_AD::updateMusic() { // important to note that we need to parse a command directly // at the new position, i.e. there is no time value we need to // parse. - if (_soundPlaying == -1) { + if (_musicResource == -1) { return; } else { continue; diff --git a/engines/scumm/players/player_ad.h b/engines/scumm/players/player_ad.h index 63fda3cc7c..9cd1a06261 100644 --- a/engines/scumm/players/player_ad.h +++ b/engines/scumm/players/player_ad.h @@ -68,7 +68,7 @@ private: OPL::OPL *_opl2; - int _soundPlaying; + int _musicResource; int32 _engineMusicTimer; struct SfxSlot; diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 24d676a1ff..89d2d3dc72 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -2611,8 +2611,12 @@ bool ScummEngine::startManiac() { Common::String path = dom.getVal("path"); if (path.hasPrefix(currentPath)) { - path.erase(0, currentPath.size() + 1); - if (path.equalsIgnoreCase("maniac")) { + path.erase(0, currentPath.size()); + // Do a case-insensitive non-path-mode match of the remainder. + // While strictly speaking it's too broad, this matchString + // ignores the presence or absence of trailing path separators + // in either currentPath or path. + if (path.matchString("*maniac*", true, false)) { maniacTarget = iter->_key; break; } diff --git a/engines/sherlock/tattoo/tattoo_darts.cpp b/engines/sherlock/tattoo/tattoo_darts.cpp index fe707a8c13..0a815af39e 100644 --- a/engines/sherlock/tattoo/tattoo_darts.cpp +++ b/engines/sherlock/tattoo/tattoo_darts.cpp @@ -178,7 +178,7 @@ void Darts::playDarts(GameType gameType) { scoredPoints = Common::String::format(FIXED(DartsScoredPoints), lastDart); } - screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, scoredPoints.c_str()); + screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", scoredPoints.c_str()); } else { Common::String hitText; @@ -213,7 +213,7 @@ void Darts::playDarts(GameType gameType) { break; } } - screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, hitText.c_str()); + screen.print(Common::Point(_dartInfo.left, _dartInfo.top + _spacing), 0, "%s", hitText.c_str()); } if (score != 0 && playerNum == 0 && !gameOver) @@ -417,12 +417,12 @@ void Darts::showStatus(int playerNum) { // "Round: x" Common::String dartsRoundStatus = Common::String::format(FIXED(DartsCurrentRound), _roundNum); - screen.print(Common::Point(STATUS_INFO_X, temp), 0, dartsRoundStatus.c_str()); + screen.print(Common::Point(STATUS_INFO_X, temp), 0, "%s", dartsRoundStatus.c_str()); if (_gameType == GAME_301) { // "Turn Total: x" Common::String dartsTotalPoints = Common::String::format(FIXED(DartsCurrentTotalPoints), _roundScore); - screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, dartsTotalPoints.c_str()); + screen.print(Common::Point(STATUS_INFO_X, STATUS_INFO_Y + 75), 0, "%s", dartsTotalPoints.c_str()); } else { // Show cricket scores for (int x = 0; x < 7; ++x) { @@ -935,7 +935,7 @@ int Darts::throwDart(int dartNum, int computer) { // "Dart # x" Common::String currentDart = Common::String::format(FIXED(DartsCurrentDart), dartNum); - screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, currentDart.c_str()); + screen.print(Common::Point(_dartInfo.left, _dartInfo.top), 0, "%s", currentDart.c_str()); drawDartsLeft(dartNum, computer); diff --git a/engines/sky/detection.cpp b/engines/sky/detection.cpp index d85299cc24..4b91f50a61 100644 --- a/engines/sky/detection.cpp +++ b/engines/sky/detection.cpp @@ -136,7 +136,7 @@ const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &ta } GameDescriptor SkyMetaEngine::findGame(const char *gameid) const { - if (0 == scumm_stricmp(gameid, skySetting.gameid)) + if (0 == scumm_stricmp(gameid, skySetting.gameId)) return skySetting; return GameDescriptor(); } @@ -175,7 +175,7 @@ GameList SkyMetaEngine::detectGames(const Common::FSList &fslist) const { // Match found, add to list of candidates, then abort inner loop. // The game detector uses US English by default. We want British // English to match the recorded voices better. - GameDescriptor dg(skySetting.gameid, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown); + GameDescriptor dg(skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown); const SkyVersion *sv = skyVersions; while (sv->dinnerTableEntries) { if (dinnerTableEntries == sv->dinnerTableEntries && diff --git a/engines/sword1/configure.engine b/engines/sword1/configure.engine index 0578d176a9..1d17903b69 100644 --- a/engines/sword1/configure.engine +++ b/engines/sword1/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine sword1 "Broken Sword" yes +add_engine sword1 "Broken Sword" yes "" "" "highres" diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp index 3eac95cdf2..0edf856125 100644 --- a/engines/sword1/detection.cpp +++ b/engines/sword1/detection.cpp @@ -128,17 +128,17 @@ GameList SwordMetaEngine::getSupportedGames() const { } GameDescriptor SwordMetaEngine::findGame(const char *gameid) const { - if (0 == scumm_stricmp(gameid, sword1FullSettings.gameid)) + if (0 == scumm_stricmp(gameid, sword1FullSettings.gameId)) return sword1FullSettings; - if (0 == scumm_stricmp(gameid, sword1DemoSettings.gameid)) + if (0 == scumm_stricmp(gameid, sword1DemoSettings.gameId)) return sword1DemoSettings; - if (0 == scumm_stricmp(gameid, sword1MacFullSettings.gameid)) + if (0 == scumm_stricmp(gameid, sword1MacFullSettings.gameId)) return sword1MacFullSettings; - if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameid)) + if (0 == scumm_stricmp(gameid, sword1MacDemoSettings.gameId)) return sword1MacDemoSettings; - if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameid)) + if (0 == scumm_stricmp(gameid, sword1PSXSettings.gameId)) return sword1PSXSettings; - if (0 == scumm_stricmp(gameid, sword1PSXDemoSettings.gameid)) + if (0 == scumm_stricmp(gameid, sword1PSXDemoSettings.gameId)) return sword1PSXDemoSettings; return GameDescriptor(); } diff --git a/engines/sword2/configure.engine b/engines/sword2/configure.engine index 7153605433..a794e7287c 100644 --- a/engines/sword2/configure.engine +++ b/engines/sword2/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine sword2 "Broken Sword II" yes +add_engine sword2 "Broken Sword II" yes "" "" "highres" diff --git a/engines/sword25/POTFILES b/engines/sword25/POTFILES new file mode 100644 index 0000000000..f4b0e6fc27 --- /dev/null +++ b/engines/sword25/POTFILES @@ -0,0 +1 @@ +engines/sword25/detection.cpp diff --git a/engines/sword25/configure.engine b/engines/sword25/configure.engine index 6a9428c758..f805483f54 100644 --- a/engines/sword25/configure.engine +++ b/engines/sword25/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine sword25 "Broken Sword 2.5" yes "" "" "png zlib 16bit" +add_engine sword25 "Broken Sword 2.5" yes "" "" "png zlib 16bit highres" diff --git a/engines/sword25/detection.cpp b/engines/sword25/detection.cpp index df68d11609..4ca565c972 100644 --- a/engines/sword25/detection.cpp +++ b/engines/sword25/detection.cpp @@ -21,6 +21,7 @@ */ #include "base/plugins.h" +#include "common/translation.h" #include "engines/advancedDetector.h" #include "sword25/sword25.h" @@ -41,10 +42,17 @@ static const char *directoryGlobs[] = { 0 }; +static const ExtraGuiOption sword25ExtraGuiOption = { + _s("Use English speech"), + _s("Use English speech instead of German for every language other than German"), + "english_speech", + false +}; + class Sword25MetaEngine : public AdvancedMetaEngine { public: Sword25MetaEngine() : AdvancedMetaEngine(Sword25::gameDescriptions, sizeof(ADGameDescription), sword25Game) { - _guioptions = GUIO1(GUIO_NOMIDI); + _guiOptions = GUIO1(GUIO_NOMIDI); _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } @@ -58,6 +66,7 @@ public: virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; virtual bool hasFeature(MetaEngineFeature f) const; + virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; virtual int getMaximumSaveSlot() const { return Sword25::PersistenceService::getSlotCount(); } virtual SaveStateList listSaves(const char *target) const; }; @@ -74,6 +83,12 @@ bool Sword25MetaEngine::hasFeature(MetaEngineFeature f) const { (f == kSupportsListSaves); } +const ExtraGuiOptions Sword25MetaEngine::getExtraGuiOptions(const Common::String &target) const { + ExtraGuiOptions options; + options.push_back(sword25ExtraGuiOption); + return options; +} + SaveStateList Sword25MetaEngine::listSaves(const char *target) const { Common::String pattern = target; pattern = pattern + ".???"; diff --git a/engines/sword25/detection_tables.h b/engines/sword25/detection_tables.h index b58f430fcf..927060bf18 100644 --- a/engines/sword25/detection_tables.h +++ b/engines/sword25/detection_tables.h @@ -29,7 +29,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588), Common::EN_ANY, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -38,7 +38,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_fr.b25c", "690caf157387e06d2c3d1ca53c43f428", 1006043), Common::FR_FRA, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -47,7 +47,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("data.b25c", "f8b6e03ada2d2f6cf27fbc11ad1572e9", 654310588), Common::DE_DEU, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -56,7 +56,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_hr.b25c", "e881054d1f8ec1e527422fc521c25405", 1273217), Common::HR_HRV, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -65,7 +65,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_it.b25c", "f3325666da0515cc2b42062e953c0889", 996197), Common::IT_ITA, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -74,7 +74,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_pl.b25c", "49dc1a20f95391a808e475c49be2bac0", 1281799), Common::PL_POL, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -83,7 +83,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_pt.b25c", "1df701432f9e13dcefe1adeb890b9c69", 993812), Common::PT_BRA, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -92,7 +92,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_ru.b25c", "deb33dd2f90a71ff60181918a8ce5063", 1235378), Common::RU_RUS, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, { @@ -101,7 +101,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_es.b25c", "384c19072d83725f351bb9ecb4d3f02b", 987965), Common::ES_ESP, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, // Hungarian "psylog" version. @@ -112,7 +112,7 @@ static const ADGameDescription gameDescriptions[] = { AD_ENTRY1s("lang_hu.b25c", "7de51a3b4926a192549e75b1a7d81667", 1864915), Common::HU_HUN, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, @@ -126,19 +126,22 @@ static const ADGameDescription gameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformUnknown, - GF_EXTRACTED | ADGF_TESTING, + GF_EXTRACTED | ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, // Distributed by ScummVM // Contains all language packs, English voice-overs and Hungarian version + // Mark it as Unknown Language since it contains multiple languages. If we + // mark it as English, then changing the language in-game causes the detection + // to fail the next time we try to start the engine. { "sword25", "Latest version", AD_ENTRY1s("data.b25c", "880a8a67faf4a4e7ab62cf114b771428", 827397764), - Common::EN_ANY, + Common::UNK_LANG, Common::kPlatformUnknown, - ADGF_TESTING, + ADGF_NO_FLAGS, GUIO1(GUIO_NOASPECT) }, diff --git a/engines/sword25/fmv/movieplayer.cpp b/engines/sword25/fmv/movieplayer.cpp index eb0f0390dc..62a897a332 100644 --- a/engines/sword25/fmv/movieplayer.cpp +++ b/engines/sword25/fmv/movieplayer.cpp @@ -58,6 +58,8 @@ MoviePlayer::~MoviePlayer() { } bool MoviePlayer::loadMovie(const Common::String &filename, uint z) { + if (isMovieLoaded()) + unloadMovie(); // Get the file and load it into the decoder Common::SeekableReadStream *in = Kernel::getInstance()->getPackage()->getStream(filename); _decoder.loadStream(in); diff --git a/engines/sword25/gfx/animationresource.cpp b/engines/sword25/gfx/animationresource.cpp index 431d466658..423a2b86b4 100644 --- a/engines/sword25/gfx/animationresource.cpp +++ b/engines/sword25/gfx/animationresource.cpp @@ -211,8 +211,9 @@ bool AnimationResource::precacheAllFrames() const { error("Could not precache \"%s\".", (*iter).fileName.c_str()); return false; } -#else - Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName); +#else + Resource *pResource = Kernel::getInstance()->getResourceManager()->requestResource((*iter).fileName); + pResource->release(); //unlock precached resource #endif } diff --git a/engines/sword25/gfx/fontresource.cpp b/engines/sword25/gfx/fontresource.cpp index c4d4c3c52e..1d7aedcb6e 100644 --- a/engines/sword25/gfx/fontresource.cpp +++ b/engines/sword25/gfx/fontresource.cpp @@ -103,8 +103,9 @@ bool FontResource::parserCallback_font(ParserNode *node) { if (!_pKernel->getResourceManager()->precacheResource(_bitmapFileName)) { error("Could not precache \"%s\".", _bitmapFileName.c_str()); } -#else - _pKernel->getResourceManager()->requestResource(_bitmapFileName); +#else + Resource *pResource = _pKernel->getResourceManager()->requestResource(_bitmapFileName); + pResource->release(); //unlock precached resource #endif return true; diff --git a/engines/sword25/gfx/text.cpp b/engines/sword25/gfx/text.cpp index d409c538c0..769c9b1162 100644 --- a/engines/sword25/gfx/text.cpp +++ b/engines/sword25/gfx/text.cpp @@ -77,7 +77,8 @@ bool Text::setFont(const Common::String &font) { return false; } #else - getResourceManager()->requestResource(font); + Resource *pResource = getResourceManager()->requestResource(font); + pResource->release(); //unlock precached resource _font = font; updateFormat(); forceRefresh(); diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp index 2db4f2da74..457dda6268 100644 --- a/engines/sword25/package/packagemanager.cpp +++ b/engines/sword25/package/packagemanager.cpp @@ -56,7 +56,8 @@ static Common::String normalizePath(const Common::String &path, const Common::St PackageManager::PackageManager(Kernel *pKernel) : Service(pKernel), _currentDirectory(PATH_SEPARATOR), - _rootFolder(ConfMan.get("path")) { + _rootFolder(ConfMan.get("path")), + _useEnglishSpeech(ConfMan.getBool("english_speech")) { if (!registerScriptBindings()) error("Script bindings could not be registered."); else @@ -71,14 +72,34 @@ PackageManager::~PackageManager() { } +Common::String PackageManager::ensureSpeechLang(const Common::String &fileName) { + if (!_useEnglishSpeech || fileName.size() < 9 || !fileName.hasPrefix("/speech/")) + return fileName; + + // Always keep German speech as a fallback in case the English speech pack is not present. + // However this means we cannot play with German text and English voice. + if (fileName.hasPrefix("/speech/de")) + return fileName; + + Common::String newFileName = "/speech/en"; + int fileIdx = 9; + while (fileIdx < fileName.size() && fileName[fileIdx] != '/') + ++fileIdx; + if (fileIdx < fileName.size()) + newFileName += fileName.c_str() + fileIdx; + + return newFileName; +} + /** * Scans through the archive list for a specified file */ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String &fileName) { + Common::String fileName2 = ensureSpeechLang(fileName); // Loop through checking each archive Common::List<ArchiveEntry *>::iterator i; for (i = _archiveList.begin(); i != _archiveList.end(); ++i) { - if (!fileName.hasPrefix((*i)->_mountPath)) { + if (!fileName2.hasPrefix((*i)->_mountPath)) { // The mount path is in different subtree. Skipping continue; } @@ -87,7 +108,7 @@ Common::ArchiveMemberPtr PackageManager::getArchiveMember(const Common::String & Common::Archive *archiveFolder = (*i)->archive; // Construct relative path - Common::String resPath(&fileName.c_str()[(*i)->_mountPath.size()]); + Common::String resPath(&fileName2.c_str()[(*i)->_mountPath.size()]); if (archiveFolder->hasFile(resPath)) { return archiveFolder->getMember(resPath); @@ -203,23 +224,29 @@ bool PackageManager::changeDirectory(const Common::String &directory) { } Common::String PackageManager::getAbsolutePath(const Common::String &fileName) { - return normalizePath(fileName, _currentDirectory); + return normalizePath(ensureSpeechLang(fileName), _currentDirectory); } bool PackageManager::fileExists(const Common::String &fileName) { // FIXME: The current Zip implementation doesn't support getting a folder entry, which is needed for detecting - // the English voick pack - if (fileName == "/speech/en") { + // the English voice pack + Common::String fileName2 = ensureSpeechLang(fileName); + if (fileName2 == "/speech/en") { // To get around this, change to detecting one of the files in the folder - return getArchiveMember(normalizePath(fileName + "/APO0001.ogg", _currentDirectory)); + bool exists = getArchiveMember(normalizePath(fileName2 + "/APO0001.ogg", _currentDirectory)); + if (!exists && _useEnglishSpeech) { + _useEnglishSpeech = false; + warning("English speech not found"); + } + return exists; } - Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName, _currentDirectory)); + Common::ArchiveMemberPtr fileNode = getArchiveMember(normalizePath(fileName2, _currentDirectory)); return fileNode; } int PackageManager::doSearch(Common::ArchiveMemberList &list, const Common::String &filter, const Common::String &path, uint typeFilter) { - Common::String normalizedFilter = normalizePath(filter, _currentDirectory); + Common::String normalizedFilter = normalizePath(ensureSpeechLang(filter), _currentDirectory); int num = 0; if (path.size() > 0) diff --git a/engines/sword25/package/packagemanager.h b/engines/sword25/package/packagemanager.h index a1806a4046..5475cb02fc 100644 --- a/engines/sword25/package/packagemanager.h +++ b/engines/sword25/package/packagemanager.h @@ -87,6 +87,9 @@ private: Common::String _currentDirectory; Common::FSNode _rootFolder; Common::List<ArchiveEntry *> _archiveList; + + bool _useEnglishSpeech; + Common::String ensureSpeechLang(const Common::String &fileName); Common::ArchiveMemberPtr getArchiveMember(const Common::String &fileName); diff --git a/engines/teenagent/detection.cpp b/engines/teenagent/detection.cpp index cefc1a89d3..caa7bdbec9 100644 --- a/engines/teenagent/detection.cpp +++ b/engines/teenagent/detection.cpp @@ -88,7 +88,7 @@ enum { class TeenAgentMetaEngine : public AdvancedMetaEngine { public: TeenAgentMetaEngine() : AdvancedMetaEngine(teenAgentGameDescriptions, sizeof(ADGameDescription), teenAgentGames) { - _singleid = "teenagent"; + _singleId = "teenagent"; } virtual const char *getName() const { diff --git a/engines/testbed/detection.cpp b/engines/testbed/detection.cpp index 348ade62b0..7aff7a1805 100644 --- a/engines/testbed/detection.cpp +++ b/engines/testbed/detection.cpp @@ -49,7 +49,7 @@ class TestbedMetaEngine : public AdvancedMetaEngine { public: TestbedMetaEngine() : AdvancedMetaEngine(testbedDescriptions, sizeof(ADGameDescription), testbed_setting) { _md5Bytes = 512; - _singleid = "testbed"; + _singleId = "testbed"; } virtual const char *getName() const { diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index 0c7d0b0665..2fde6e788a 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -85,7 +85,7 @@ static const PlainGameDescriptor tinselGames[] = { class TinselMetaEngine : public AdvancedMetaEngine { public: TinselMetaEngine() : AdvancedMetaEngine(Tinsel::gameDescriptions, sizeof(Tinsel::TinselGameDescription), tinselGames) { - _singleid = "tinsel"; + _singleId = "tinsel"; } virtual const char *getName() const { @@ -228,8 +228,8 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile // Check which files are included in some dw2 ADGameDescription *and* present // in fslist without a '1' suffix character. Compute MD5s and file sizes for these files. - for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) { - if (strcmp(g->desc.gameid, "dw2") != 0) + for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) { + if (strcmp(g->desc.gameId, "dw2") != 0) continue; for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { @@ -265,8 +265,8 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile int maxFilesMatched = 0; // MD5 based matching - for (g = &Tinsel::gameDescriptions[0]; g->desc.gameid != 0; ++g) { - if (strcmp(g->desc.gameid, "dw2") != 0) + for (g = &Tinsel::gameDescriptions[0]; g->desc.gameId != 0; ++g) { + if (strcmp(g->desc.gameId, "dw2") != 0) continue; bool fileMissing = false; diff --git a/engines/toltecs/configure.engine b/engines/toltecs/configure.engine index be5533efa2..8310a6d6ef 100644 --- a/engines/toltecs/configure.engine +++ b/engines/toltecs/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine toltecs "3 Skulls of the Toltecs" yes +add_engine toltecs "3 Skulls of the Toltecs" yes "" "" "highres" diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp index 145d9544f2..7c707895e6 100644 --- a/engines/toltecs/detection.cpp +++ b/engines/toltecs/detection.cpp @@ -206,7 +206,7 @@ static const ExtraGuiOption toltecsExtraGuiOption = { class ToltecsMetaEngine : public AdvancedMetaEngine { public: ToltecsMetaEngine() : AdvancedMetaEngine(Toltecs::gameDescriptions, sizeof(Toltecs::ToltecsGameDescription), toltecsGames) { - _singleid = "toltecs"; + _singleId = "toltecs"; } virtual const char *getName() const { diff --git a/engines/tony/configure.engine b/engines/tony/configure.engine index f85f45d158..2df4434982 100644 --- a/engines/tony/configure.engine +++ b/engines/tony/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit" +add_engine tony "Tony Tough and the Night of Roasted Moths" yes "" "" "16bit highres" diff --git a/engines/toon/configure.engine b/engines/toon/configure.engine index 00c98f7d8a..689bce1c02 100644 --- a/engines/toon/configure.engine +++ b/engines/toon/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine toon "Toonstruck" yes +add_engine toon "Toonstruck" yes "" "" "highres" diff --git a/engines/toon/detection.cpp b/engines/toon/detection.cpp index 2ca2bce2ee..5d2e0a9bca 100644 --- a/engines/toon/detection.cpp +++ b/engines/toon/detection.cpp @@ -127,7 +127,7 @@ static const char * const directoryGlobs[] = { class ToonMetaEngine : public AdvancedMetaEngine { public: ToonMetaEngine() : AdvancedMetaEngine(Toon::gameDescriptions, sizeof(ADGameDescription), toonGames) { - _singleid = "toon"; + _singleId = "toon"; _maxScanDepth = 3; _directoryGlobs = directoryGlobs; } diff --git a/engines/touche/configure.engine b/engines/touche/configure.engine index 777578e623..f35940ef47 100644 --- a/engines/touche/configure.engine +++ b/engines/touche/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes +add_engine touche "Touche: The Adventures of the Fifth Musketeer" yes "" "" "highres" diff --git a/engines/touche/detection.cpp b/engines/touche/detection.cpp index 1d0e136d69..dcb58ffae6 100644 --- a/engines/touche/detection.cpp +++ b/engines/touche/detection.cpp @@ -128,7 +128,7 @@ class ToucheMetaEngine : public AdvancedMetaEngine { public: ToucheMetaEngine() : AdvancedMetaEngine(Touche::gameDescriptions, sizeof(ADGameDescription), toucheGames) { _md5Bytes = 4096; - _singleid = "touche"; + _singleId = "touche"; _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp index 7784a3de1a..fe555f2fdb 100644 --- a/engines/tsage/detection.cpp +++ b/engines/tsage/detection.cpp @@ -40,7 +40,7 @@ struct tSageGameDescription { }; const char *TSageEngine::getGameId() const { - return _gameDescription->desc.gameid; + return _gameDescription->desc.gameId; } uint32 TSageEngine::getGameID() const { @@ -75,7 +75,7 @@ enum { class TSageMetaEngine : public AdvancedMetaEngine { public: TSageMetaEngine() : AdvancedMetaEngine(TsAGE::gameDescriptions, sizeof(TsAGE::tSageGameDescription), tSageGameTitles) { - _singleid = "tsage"; + _singleId = "tsage"; } virtual const char *getName() const { diff --git a/engines/tsage/detection_tables.h b/engines/tsage/detection_tables.h index 109ac353e6..f331ecdab5 100644 --- a/engines/tsage/detection_tables.h +++ b/engines/tsage/detection_tables.h @@ -165,7 +165,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("r2rw.rlb", "df6c25622387007788ca36d99362c1f0", 47586928), Common::EN_ANY, Common::kPlatformDOS, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO0() }, GType_Ringworld2, @@ -179,7 +179,7 @@ static const tSageGameDescription gameDescriptions[] = { AD_ENTRY1s("r2rw.rlb", "c8e1a82c67c3caf57368eadde13dc15f", 32384464), Common::EN_ANY, Common::kPlatformDOS, - ADGF_CD | ADGF_TESTING, + ADGF_CD, GUIO0() }, GType_Ringworld2, diff --git a/engines/tucker/detection.cpp b/engines/tucker/detection.cpp index a2878f4cc8..2447e15d6b 100644 --- a/engines/tucker/detection.cpp +++ b/engines/tucker/detection.cpp @@ -116,7 +116,7 @@ class TuckerMetaEngine : public AdvancedMetaEngine { public: TuckerMetaEngine() : AdvancedMetaEngine(tuckerGameDescriptions, sizeof(ADGameDescription), tuckerGames) { _md5Bytes = 512; - _singleid = "tucker"; + _singleId = "tucker"; } virtual const char *getName() const { diff --git a/engines/tucker/resource.cpp b/engines/tucker/resource.cpp index 9cba7b523d..d7b75e39c1 100644 --- a/engines/tucker/resource.cpp +++ b/engines/tucker/resource.cpp @@ -662,9 +662,11 @@ void TuckerEngine::loadData3() { void TuckerEngine::loadData4() { loadFile("data4.c", _loadTempBuf); DataTokenizer t(_loadTempBuf, _fileLoadSize); - t.findNextToken(kDataTokenDw); - _gameDebug = t.getNextInteger() != 0; - _displayGameHints = t.getNextInteger() != 0; + if ((_gameFlags & kGameFlagDemo) == 0) { + t.findNextToken(kDataTokenDw); + _gameDebug = t.getNextInteger() != 0; + _displayGameHints = t.getNextInteger() != 0; + } _locationObjectsCount = 0; if (t.findIndex(_locationNum)) { while (t.findNextToken(kDataTokenDw)) { diff --git a/engines/wage/debugger.cpp b/engines/wage/debugger.cpp new file mode 100644 index 0000000000..7d01b0b85e --- /dev/null +++ b/engines/wage/debugger.cpp @@ -0,0 +1,97 @@ +/* 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. + * + */ + +#include "common/file.h" +#include "wage/wage.h" +#include "wage/debugger.h" +#include "wage/entities.h" +#include "wage/script.h" +#include "wage/world.h" + +namespace Wage { + +Debugger::Debugger(WageEngine *engine) : GUI::Debugger(), _engine(engine) { + registerCmd("continue", WRAP_METHOD(Debugger, cmdExit)); + registerCmd("scenes", WRAP_METHOD(Debugger, Cmd_ListScenes)); + registerCmd("script", WRAP_METHOD(Debugger, Cmd_Script)); +} + +Debugger::~Debugger() { +} + +static int strToInt(const char *s) { + if (!*s) + // No string at all + return 0; + else if (toupper(s[strlen(s) - 1]) != 'H') + // Standard decimal string + return atoi(s); + + // Hexadecimal string + uint tmp = 0; + int read = sscanf(s, "%xh", &tmp); + + if (read < 1) + error("strToInt failed on string \"%s\"", s); + return (int)tmp; +} + +bool Debugger::Cmd_ListScenes(int argc, const char **argv) { + int currentScene; + + for (uint i = 1; i < _engine->_world->_orderedScenes.size(); i++) { // #0 is STORAGE@ + if (_engine->_world->_player->_currentScene == _engine->_world->_orderedScenes[i]) + currentScene = i; + + debugPrintf("%d: %s\n", i, _engine->_world->_orderedScenes[i]->_name.c_str()); + } + + debugPrintf("\nCurrent scene is #%d: %s\n", currentScene, _engine->_world->_orderedScenes[currentScene]->_name.c_str()); + + return true; +} + +bool Debugger::Cmd_Script(int argc, const char **argv) { + Script *script = _engine->_world->_player->_currentScene->_script; + + if (argc >= 2) { + int scriptNum = strToInt(argv[1]); + + if (scriptNum) + script = _engine->_world->_orderedScenes[scriptNum]->_script; + else + script = _engine->_world->_globalScript; + } + + if (script == NULL) { + debugPrintf("There is no script for current scene\n"); + return true; + } + + for (uint i = 0; i < script->_scriptText.size(); i++) { + debugPrintf("%d [%04x]: %s\n", i, script->_scriptText[i]->offset, script->_scriptText[i]->line.c_str()); + } + + return true; +} + +} // End of namespace Wage diff --git a/engines/wage/debugger.h b/engines/wage/debugger.h new file mode 100644 index 0000000000..90687760cb --- /dev/null +++ b/engines/wage/debugger.h @@ -0,0 +1,47 @@ +/* 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. + * + */ + +#ifndef WAGE_DEBUGGER_H +#define WAGE_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace Wage { + +class WageEngine; + +class Debugger : public GUI::Debugger { +protected: + WageEngine *_engine; + + bool Cmd_ListScenes(int argc, const char **argv); + bool Cmd_Script(int argc, const char **argv); + +public: + Debugger(WageEngine *engine); + virtual ~Debugger(); +}; + +} // End of namespace Wage + +#endif diff --git a/engines/wage/design.cpp b/engines/wage/design.cpp index 2a63436f5a..907a1ec435 100644 --- a/engines/wage/design.cpp +++ b/engines/wage/design.cpp @@ -56,9 +56,10 @@ struct PlotData { Patterns *patterns; uint fillType; int thickness; + Design *design; - PlotData(Graphics::Surface *s, Patterns *p, int f, int t) : - surface(s), patterns(p), fillType(f), thickness(t) {} + PlotData(Graphics::Surface *s, Patterns *p, int f, int t, Design *d) : + surface(s), patterns(p), fillType(f), thickness(t), design(d) {} }; void drawPixel(int x, int y, int color, void *data); @@ -71,6 +72,8 @@ Design::Design(Common::SeekableReadStream *data) { _surface = NULL; _bounds = NULL; + + _boundsCalculationMode = false; } Design::~Design() { @@ -81,20 +84,30 @@ Design::~Design() { } void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) { - Common::MemoryReadStream in(_data, _len); - Common::Rect r(0, 0, _bounds->width(), _bounds->height()); bool needRender = false; if (_surface == NULL) { + _boundsCalculationMode = true; + _bounds->debugPrint(4, "Internal bounds:"); + render(patterns); + _boundsCalculationMode = false; + if (_bounds->right == -10000) { + _bounds->left = _bounds->top = _bounds->right = _bounds->bottom = 0; + } + _bounds->debugPrint(4, "Calculated bounds:"); + _surface = new Graphics::Surface; _surface->create(_bounds->width(), _bounds->height(), Graphics::PixelFormat::createFormatCLUT8()); + + Common::Rect r(0, 0, _bounds->width(), _bounds->height()); _surface->fillRect(r, kColorGreen); needRender = true; } + _bounds->debugPrint(4, "Using bounds:"); #if 0 - PlotData pd(_surface, &patterns, 8, 1); + PlotData pd(_surface, &patterns, 8, 1, this); int x1 = 50, y1 = 50, x2 = 200, y2 = 200, borderThickness = 30; Common::Rect inn(x1-5, y1-5, x2+5, y2+5); drawRoundRect(inn, 6, kColorGray, false, drawPixelPlain, &pd); @@ -115,6 +128,28 @@ void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) return; #endif + if (needRender) + render(patterns); + + if (_bounds->width() && _bounds->height()) { + const int padding = 3; + for (int i = padding; i < _bounds->height() - 2 * padding; i++) { + const byte *src = (const byte *)_surface->getBasePtr(padding, i); + byte *dst = (byte *)surface->getBasePtr(x + padding, y+i); + for (int j = padding; j < _bounds->width() - 2 * padding; j++) { + if (*src != kColorGreen) + *dst = *src; + src++; + dst++; + } + } + } +} + +void Design::render(Patterns &patterns) { + Common::MemoryReadStream in(_data, _len); + bool needRender = true; + while (needRender) { byte fillType = in.readByte(); byte borderThickness = in.readByte(); @@ -152,18 +187,6 @@ void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) //g_system->updateScreen(); //g_system->delayMillis(500); } - - const int padding = 3; - for (int i = padding; i < _bounds->height() - 2 * padding; i++) { - const byte *src = (const byte *)_surface->getBasePtr(padding, i); - byte *dst = (byte *)surface->getBasePtr(x + padding, y+i); - for (int j = padding; j < _bounds->width() - 2 * padding; j++) { - if (*src != kColorGreen) - *dst = *src; - src++; - dst++; - } - } } bool Design::isPointOpaque(int x, int y) { @@ -175,18 +198,43 @@ bool Design::isPointOpaque(int x, int y) { return pixel != kColorGreen; } +void Design::adjustBounds(int16 x, int16 y) { + _bounds->right = MAX(x, _bounds->right); + _bounds->bottom = MAX(y, _bounds->bottom); +} + void drawPixel(int x, int y, int color, void *data) { PlotData *p = (PlotData *)data; if (p->fillType > p->patterns->size()) return; + if (p->design && p->design->isBoundsCalculation()) { + if (x < 0 || y < 0) + return; + if (p->thickness == 1) { + p->design->adjustBounds(x, y); + } else { + int x1 = x - p->thickness / 2; + int x2 = x1 + p->thickness; + int y1 = y - p->thickness / 2; + int y2 = y1 + p->thickness; + + for (y = y1; y < y2; y++) + for (x = x1; x < x2; x++) + p->design->adjustBounds(x, y); + } + + return; + } + byte *pat = p->patterns->operator[](p->fillType - 1); if (p->thickness == 1) { if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) { uint xu = (uint)x; // for letting compiler optimize it uint yu = (uint)y; + *((byte *)p->surface->getBasePtr(xu, yu)) = (pat[yu % 8] & (1 << (7 - xu % 8))) ? color : kColorWhite; @@ -212,6 +260,11 @@ void drawPixel(int x, int y, int color, void *data) { void drawPixelPlain(int x, int y, int color, void *data) { PlotData *p = (PlotData *)data; + if (p->design && p->design->isBoundsCalculation()) { + p->design->adjustBounds(x, y); + return; + } + if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) *((byte *)p->surface->getBasePtr(x, y)) = (byte)color; } @@ -229,7 +282,7 @@ void Design::drawRect(Graphics::Surface *surface, Common::ReadStream &in, SWAP(y1, y2); Common::Rect r(x1, y1, x2, y2); - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) Graphics::drawFilledRect(r, kColorBlack, drawPixel, &pd); @@ -259,16 +312,16 @@ void Design::drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, SWAP(y1, y2); Common::Rect r(x1, y1, x2, y2); - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) - Graphics::drawRoundRect(r, arc/2, kColorBlack, true, drawPixel, &pd); + Graphics::drawRoundRect(r, arc / 2, kColorBlack, true, drawPixel, &pd); pd.fillType = borderFillType; pd.thickness = borderThickness; if (borderThickness > 0 && borderFillType <= patterns.size()) - Graphics::drawRoundRect(r, arc/2, kColorBlack, false, drawPixel, &pd); + Graphics::drawRoundRect(r, arc / 2, kColorBlack, false, drawPixel, &pd); } void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, @@ -331,7 +384,7 @@ void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, ypoints[i] = ycoords[i]; } - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) { Graphics::drawPolygonScan(xpoints, ypoints, npoints, bbox, kColorBlack, drawPixel, &pd); @@ -354,7 +407,7 @@ void Design::drawOval(Graphics::Surface *surface, Common::ReadStream &in, int16 x1 = in.readSint16BE(); int16 y2 = in.readSint16BE(); int16 x2 = in.readSint16BE(); - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, this); if (fillType <= patterns.size()) Graphics::drawEllipse(x1, y1, x2-1, y2-1, kColorBlack, true, drawPixel, &pd); @@ -410,7 +463,9 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & color = b; for (int c = 0; c < 8; c++) { - if (x1 + x >= 0 && x1 + x < surface->w && y1 + y >= 0 && y1 + y < surface->h) + if (_boundsCalculationMode) { + adjustBounds(x1 + x, y1 + y); + } else if (x1 + x >= 0 && x1 + x < surface->w && y1 + y >= 0 && y1 + y < surface->h) *((byte *)tmp.getBasePtr(x, y)) = (color & (1 << (7 - c % 8))) ? kColorBlack : kColorWhite; x++; if (x == w) { @@ -424,6 +479,9 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & in.skip(numBytes); + if (_boundsCalculationMode) + return; + FloodFill ff(&tmp, kColorWhite, kColorGreen); for (int yy = 0; yy < h; yy++) { ff.addSeed(0, yy); @@ -435,7 +493,7 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & } ff.fill(); - for (y = 0; y < h; y++) { + for (y = 0; y < h && y1 + y < surface->h; y++) { byte *src = (byte *)tmp.getBasePtr(0, y); byte *dst = (byte *)surface->getBasePtr(x1, y1 + y); for (x = 0; x < w; x++) { @@ -454,7 +512,7 @@ void Design::drawRect(Graphics::Surface *surface, Common::Rect &rect, int thickn } void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, thickness); + PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawLine(x1, y1, x2, y1, kColorBlack, drawPixel, &pd); Graphics::drawLine(x2, y1, x2, y2, kColorBlack, drawPixel, &pd); @@ -464,26 +522,26 @@ void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2 void Design::drawFilledRect(Graphics::Surface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, nullptr); for (int y = rect.top; y <= rect.bottom; y++) Graphics::drawHLine(rect.left, rect.right, y, color, drawPixel, &pd); } void Design::drawFilledRoundRect(Graphics::Surface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, 1); + PlotData pd(surface, &patterns, fillType, 1, nullptr); Graphics::drawRoundRect(rect, arc, color, true, drawPixel, &pd); } void Design::drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, thickness); + PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawHLine(x1, x2, y, color, drawPixel, &pd); } void Design::drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType) { - PlotData pd(surface, &patterns, fillType, thickness); + PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawVLine(x, y1, y2, color, drawPixel, &pd); } diff --git a/engines/wage/design.h b/engines/wage/design.h index baa99730fe..e8f42f4e04 100644 --- a/engines/wage/design.h +++ b/engines/wage/design.h @@ -76,14 +76,18 @@ public: static void drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType); static void drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType); + bool isBoundsCalculation() { return _boundsCalculationMode; } + void adjustBounds(int16 x, int16 y); private: byte *_data; int _len; Common::Rect *_bounds; Graphics::Surface *_surface; + bool _boundsCalculationMode; private: + void render(Patterns &patterns); void drawRect(Graphics::Surface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType); void drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, diff --git a/engines/wage/detection.cpp b/engines/wage/detection.cpp index 91e20f3750..512d432e54 100644 --- a/engines/wage/detection.cpp +++ b/engines/wage/detection.cpp @@ -41,7 +41,10 @@ static const PlainGameDescriptor wageGames[] = { {"afm", "Another Fine Mess"}, {"amot", "A Mess O' Trouble"}, {"cantitoe", "Camp Cantitoe"}, + {"drakmythcastle", "Drakmyth Castle"}, + {"raysmaze", "Ray's Maze"}, {"scepters", "Enchanted Scepters"}, + {"twisted", "Twisted!"}, {"wage", "WAGE"}, {0, 0} }; @@ -51,8 +54,8 @@ static const PlainGameDescriptor wageGames[] = { class WageMetaEngine : public AdvancedMetaEngine { public: WageMetaEngine() : AdvancedMetaEngine(Wage::gameDescriptions, sizeof(ADGameDescription), wageGames) { - _singleid = "wage"; - _guioptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI); + _singleId = "wage"; + _guiOptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI); } virtual const char *getName() const { diff --git a/engines/wage/detection_tables.h b/engines/wage/detection_tables.h index 8723310294..bfb1c12e96 100644 --- a/engines/wage/detection_tables.h +++ b/engines/wage/detection_tables.h @@ -22,33 +22,93 @@ namespace Wage { -#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM) -#define ADGF_GENERIC (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_USEEXTRAASTITLE) +#define ADGF_DEFAULT (ADGF_DROPLANGUAGE|ADGF_DROPPLATFORM|ADGF_MACRESFORK) +#define ADGF_GENERIC (ADGF_DEFAULT|ADGF_USEEXTRAASTITLE|ADGF_AUTOGENTARGET) +#define ADGF_DEMO (ADGF_GENERIC|ADGF_DEMO) -#define FANGAME(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} +#define FANGAME(n,m,s) { "wage",n,AD_ENTRY1s(n,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} +#define FANGAMEN(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_GENERIC,GUIO0()} +#define FANGAMEND(n,f,m,s) { "wage",n,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEMO,GUIO0()} #define BIGGAME(t,v,f,m,s) { t,v,AD_ENTRY1s(f,m,s),Common::EN_ANY,Common::kPlatformMacintosh,ADGF_DEFAULT,GUIO0()} static const ADGameDescription gameDescriptions[] = { - FANGAME("3rd Floor", "3rd Floor", "a107d7a177970b2259e32681bd8b47c9", 285056), - BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "8e5aa915f3253efb2aab52435647b25e", 1456000), - BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "b3ef53afed282671b704e45df829350c", 1895552), - FANGAME("Bug Hunt", "Bug Hunt", "2ebd3515a87941063ad66c3cf93c5e78", 200064), + FANGAME("3rd Floor", "913812a1ac7a6b0e48dadd1afa1c7763", 281409), + BIGGAME("afm", "v1.8", "Another Fine Mess 1.8", "94a9c4f8b3dabd1846d76215a49bd221", 1420723), + BIGGAME("amot", "v1.8", "A Mess O' Trouble 1.8", "26207bdf0bb539464f136f0669af885f", 1843104), + FANGAME("Bug Hunt", "595117cbed33e8de1ab3714b33880205", 195699), + BIGGAME("cantitoe", "", "Camp Cantitoe", "913812a1ac7a6b0e48dadd1afa1c7763", 616985), // Problems with letter rendering - FANGAME("Canal District", "Canal District", "8856bc699a20fc5b7fc67accee12cac7", 658176), - BIGGAME("cantitoe", "", "Camp Cantitoe", "098aa5c11c58e1ef274a30a9e01b4755", 621440), - FANGAME("Deep Angst", "Deep Angst", "635f62bbc569e72b03cab9107927d03d", 335232), - FANGAME("Dungeon World II", "DungeonWorld2", "e10c5e3cc17879c298b1551f33571b15", 234880), + FANGAME("Canal District", "a56aa3cd4a6e070e15ce1d5815c7be0a", 641470), + FANGAME("Carbon Copy", "913812a1ac7a6b0e48dadd1afa1c7763", 519445), + // Invalid rect in scene "FINALE" + FANGAME("Castle of Ert", "327610eb2298a9427a566288312df040", 198955), + FANGAME("Deep Angst", "b130b3c811cd89024dd5fdd2b71f70b8", 329550), + FANGAME("Deep Ennui", "913812a1ac7a6b0e48dadd1afa1c7763", 86075), // Polygons with ignored byte 1 - FANGAME("Double Trouble", "Double Trouble", "5e9ee13d09ac54918ed111fa9727ac1c", 557184), - FANGAME("Eidisi I", "Eidisi I", "299d1de4baccf1c66118396519953652", 180480), - FANGAME("Escape from School!", "Escape from School!", "a854be48d4af20126d18a9cad93a969b", 51840), - FANGAME("Magic Rings", "Magic Rings", "6e0d1dd561d3dad8f9a7a20ed1f09b16", 112000), - FANGAME("Midnight Snack", "Midnight Snack", "346982a32fc701f53bb19771d72063d0", 69504), - FANGAME("Queen Quest", "Queen Quest", "730605d312efedb5e3ff108522fcac18", 59776), - BIGGAME("scepters", "", "Scepters", "b80bff315897776dda7689cdf829fab4", 360832), - FANGAME("Time Bomb", "Time Bomb", "2df84b636237686b624e736a698a16c4", 66432), - FANGAME("ZikTuria", "ZikTuria", "e793155bed1a70fa2074a3fcd696b751", 54784), - FANGAME("Zoony", "Zoony", "e6cc8a914a4215dafbcce6315dd12cf5", 160256), + FANGAME("Double Trouble", "1652e36857a04c01dc560234c4818619", 542371), + BIGGAME("drakmythcastle", "disk I", "Drakmyth Castle disk I of II", "94a9c4f8b3dabd1846d76215a49bd221", 793784), + BIGGAME("drakmythcastle", "disk II", "Drakmyth Castle II", "cc978cc9a5256724702463cb5aaaffa0", 1685659), + // Crash at start in GUI rendering + FANGAME("Dune Eternity", "94a9c4f8b3dabd1846d76215a49bd221", 290201), // Original file name is "***DUNE ETERNITY*** " + FANGAMEN("Dungeon World II", "DungeonWorld2", "0154ea11d3cbb536c13b4ae9e6902d48", 230199), + FANGAME("Eidisi I", "595117cbed33e8de1ab3714b33880205", 172552), + // Problems(?) with text on the first screen + FANGAMEN("Enchanted Pencils", "Enchanted Pencils 0.99 (PG)", "595117cbed33e8de1ab3714b33880205", 408913), + FANGAME("Escape from School!", "913812a1ac7a6b0e48dadd1afa1c7763", 50105), + FANGAME("Exploration Zeta!", "c477921aeee6ed0f8997ba44447eb2d0", 366599), + // Crash in console rendering on the first scene + FANGAME("Fantasy Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 762754), + FANGAME("Find the Heart", "595117cbed33e8de1ab3714b33880205", 106235), // From Joshua's Worlds 1.0 + // Problems with window overlay + FANGAMEN("Jumble", "LSJUMBLE", "e12ec4d76d48bdc86567c5e63750547e", 647339), // Original file name is "LSJUMBLEâ " + FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96711), + FANGAME("Karth of the Jungle", "595117cbed33e8de1ab3714b33880205", 96960), // Alternative version + FANGAME("Karth of the Jungle II", "c106835ab4436de054e03aec3ce904ce", 201053), + FANGAMEN("Little Pythagoras", "Little Pythagoras 1.1.1", "94a9c4f8b3dabd1846d76215a49bd221", 628821), + FANGAME("Lost Crystal", "8174c81ea1858d0079ae040dae2cefd3", 771072), + FANGAME("Magic Rings", "913812a1ac7a6b0e48dadd1afa1c7763", 109044), + // No way to click on the house + FANGAME("Messy House", "913812a1ac7a6b0e48dadd1afa1c7763", 177120), + FANGAME("Midnight Snack", "913812a1ac7a6b0e48dadd1afa1c7763", 67952), + FANGAME("Minitorian", "913812a1ac7a6b0e48dadd1afa1c7763", 586464), + // No way to pass through the first screen + FANGAME("Nightcrawler Ned", "94a9c4f8b3dabd1846d76215a49bd221", 366542), + FANGAME("Pavilion", "4d991d7d1534d48d90598d86ea6d5d97", 231687), + FANGAME("Puzzle Piece Search", "595117cbed33e8de1ab3714b33880205", 247693), // From Joshua's Worlds 1.0 + // Empty(?) first scene + FANGAME("Pyramid of No Return", "77a55a45f794b4d4a56703d3acce871e", 385145), + FANGAME("Queen Quest", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 57026), + FANGAME("Quest for T-Rex", "913812a1ac7a6b0e48dadd1afa1c7763", 592584), + // Crash in console rendering on the initial scene + FANGAME("Quest for the Dark Sword", "b35dd0c078da9f35fc25a455f56bb129", 572576), + FANGAME("Radical Castle 1.0", "677bfee4afeca2f7152eb8b76c85ca8d", 347278), + BIGGAME("raysmaze", "v1.5", "Ray's Maze1.5", "064b16d8c20724f8debbbdc3aafde538", 1408516), + BIGGAME("raysmaze", "v1.5/alt", "Ray's Maze1.5", "92cca777800c3d31a77b5ed7f6ee49ad", 1408516), + BIGGAME("scepters", "", "Scepters", "3311deef8bf82f0b4b1cfa15a3b3289d", 346595), + // ??? problems with dog bitmap? + FANGAMEN("Space Adventure", "SpaceAdventure", "f9f3f1c419f56955f7966355b34ea5c8", 155356), + FANGAMEN("Spear of Destiny", "SpearOfDestiny", "913812a1ac7a6b0e48dadd1afa1c7763", 333665), // Original file name "SpearOfDestinyâ " + FANGAME("Star Trek", "44aaef4806578700429de5aaf95c266e", 53320), + FANGAME("Strange Disappearance", "d81f2d03a1e863f04fb1e3a5495b720e", 772282), + // Code 0x03 in text + FANGAME("Swamp Witch", "913812a1ac7a6b0e48dadd1afa1c7763", 739781), // Original file name "Swamp Witchâ " + FANGAME("Sweetspace Now!", "e12ec4d76d48bdc86567c5e63750547e", 123813), // Comes with Jumble + FANGAME("Time Bomb", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 64564), + FANGAMEND("The Ashland Revolution", "The Ashland Revolution Demo", "913812a1ac7a6b0e48dadd1afa1c7763", 145023), // Original file name "The Ashland Revolution Demoâ " + FANGAMEN("The Hotel Caper", "The Hotel Caper V1.0", "595117cbed33e8de1ab3714b33880205", 231969), + // Invalid rect in scene "Access Tube 1" + FANGAMEN("The Phoenix v1.2", "The Phoenix", "4b0e1a1fbaaa4930accd0f9f0e1519c7", 431640), + FANGAME("The Sultan's Palace", "358799d446ee4fc12f793febd6c94b95", 456855), + // Admission for on 3rd screen is messed up + FANGAME("The Tower", "435f420b9dff895ae1ddf1338040c51d", 556539), + // Polygons with ignored byte 1 and 2 on second scene + FANGAME("The Village", "913812a1ac7a6b0e48dadd1afa1c7763", 314828), + // Doesn't go past first scene + BIGGAME("twisted", "", "Twisted! 1.6", "26207bdf0bb539464f136f0669af885f", 960954), + FANGAME("Wishing Well", "913812a1ac7a6b0e48dadd1afa1c7763", 103688), + FANGAME("Wizard's Warehouse", "913812a1ac7a6b0e48dadd1afa1c7763", 159748), + FANGAME("ZikTuria", "418e74ca71029a1e9db80d0eb30c0843", 52972), + FANGAME("Zoony", "539a64151426edc92da5eedadf39f23c", 154990), // original filename "Zoonyâ¢" AD_TABLE_END_MARKER }; diff --git a/engines/wage/entities.cpp b/engines/wage/entities.cpp index d9e4b60591..a2648c49fe 100644 --- a/engines/wage/entities.cpp +++ b/engines/wage/entities.cpp @@ -97,6 +97,8 @@ Scene::Scene() { } Scene::Scene(Common::String name, Common::SeekableReadStream *data) { + debug(9, "Creating scene: %s", name.c_str()); + _name = name; _classType = SCENE; _design = new Design(data); diff --git a/engines/wage/menu.cpp b/engines/wage/menu.cpp index 48f16421b5..12ef8c2219 100644 --- a/engines/wage/menu.cpp +++ b/engines/wage/menu.cpp @@ -206,10 +206,10 @@ void Menu::createCommandsMenu(MenuItem *menu) { char shortcut = 0; const char *shortPtr = strrchr(item.c_str(), '/'); if (shortPtr != NULL) { - if (strlen(shortPtr) == 2) { + if (strlen(shortPtr) >= 2) { shortcut = shortPtr[1]; - item.deleteLastChar(); - item.deleteLastChar(); + item.deleteChar(shortPtr - item.c_str()); + item.deleteChar(shortPtr - item.c_str()); } else { error("Unexpected shortcut: '%s', item '%s' in menu '%s'", shortPtr, item.c_str(), string.c_str()); } diff --git a/engines/wage/module.mk b/engines/wage/module.mk index 548e440c28..21316bbf83 100644 --- a/engines/wage/module.mk +++ b/engines/wage/module.mk @@ -2,6 +2,7 @@ MODULE := engines/wage MODULE_OBJS := \ combat.o \ + debugger.o \ design.o \ detection.o \ dialog.o \ diff --git a/engines/wage/script.cpp b/engines/wage/script.cpp index bd99fa1d86..294c08ed82 100644 --- a/engines/wage/script.cpp +++ b/engines/wage/script.cpp @@ -1124,7 +1124,7 @@ void Script::convertToText() { if (c < 0x80) { if (c < 0x20) - error("Unknown code 0x%02x at %d", c, _data->pos()); + error("convertToText: Unknown code 0x%02x at %d", c, _data->pos()); do { scr->line += c; diff --git a/engines/wage/script.h b/engines/wage/script.h index 325733add7..de9476228c 100644 --- a/engines/wage/script.h +++ b/engines/wage/script.h @@ -150,8 +150,10 @@ private: void assign(byte operandType, int uservar, uint16 value); - Common::Array<ScriptText *> _scriptText; void convertToText(); + +public: + Common::Array<ScriptText *> _scriptText; }; } // End of namespace Wage diff --git a/engines/wage/util.cpp b/engines/wage/util.cpp index 1b3dfc9452..f31a83ca04 100644 --- a/engines/wage/util.cpp +++ b/engines/wage/util.cpp @@ -81,6 +81,8 @@ Common::Rect *readRect(Common::SeekableReadStream *in) { y2 = in->readUint16BE() + 4; x2 = in->readUint16BE() + 4; + debug(9, "readRect: %d, %d, %d, %d", x1, y1, x2, y2); + return new Common::Rect(x1, y1, x2, y2); } diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp index b708cff134..e0299c8da2 100644 --- a/engines/wage/wage.cpp +++ b/engines/wage/wage.cpp @@ -102,12 +102,14 @@ WageEngine::~WageEngine() { } Common::Error WageEngine::run() { + debug("WageEngine::init"); + initGraphics(512, 342, true); // Create debugger console. It requires GFX to be initialized _console = new Console(this); - debug("WageEngine::init"); + _debugger = new Debugger(this); // Your main event loop should be (invoked from) here. _resManager = new Common::MacResManager(); @@ -130,6 +132,8 @@ Common::Error WageEngine::run() { _shouldQuit = false; while (!_shouldQuit) { + _debugger->onFrame(); + processEvents(); _gui->draw(); @@ -180,6 +184,11 @@ void WageEngine::processEvents() { break; default: + if (event.kbd.ascii == '~') { + _debugger->attach(); + break; + } + if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) { if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) { _gui->processMenuShortCut(event.kbd.flags, event.kbd.ascii); diff --git a/engines/wage/wage.h b/engines/wage/wage.h index 6905fdc530..8ca306aea3 100644 --- a/engines/wage/wage.h +++ b/engines/wage/wage.h @@ -50,12 +50,13 @@ #include "engines/engine.h" #include "common/debug.h" -#include "gui/debugger.h" #include "common/endian.h" #include "common/rect.h" #include "common/macresman.h" #include "common/random.h" +#include "wage/debugger.h" + struct ADGameDescription; namespace Wage { @@ -181,6 +182,8 @@ public: public: Common::RandomSource *_rnd; + Debugger *_debugger; + Gui *_gui; World *_world; @@ -212,6 +215,8 @@ public: void redrawScene(); void saveGame(); + virtual GUI::Debugger *getDebugger() { return _debugger; } + private: Console *_console; diff --git a/engines/wintermute/base/base_engine.h b/engines/wintermute/base/base_engine.h index 0f4a6b0775..cbf5d92d00 100644 --- a/engines/wintermute/base/base_engine.h +++ b/engines/wintermute/base/base_engine.h @@ -74,7 +74,7 @@ public: static const Timer *getTimer(); static const Timer *getLiveTimer(); static void LOG(bool res, const char *fmt, ...); - const char *getGameTargetName() const { return _targetName.c_str(); } + Common::String getGameTargetName() const { return _targetName; } Common::String getGameId() const { return _gameId; } Common::Language getLanguage() const { return _language; } WMETargetExecutable getTargetExecutable() const { diff --git a/engines/wintermute/base/base_persistence_manager.cpp b/engines/wintermute/base/base_persistence_manager.cpp index bb5e0c4091..39462f7a15 100644 --- a/engines/wintermute/base/base_persistence_manager.cpp +++ b/engines/wintermute/base/base_persistence_manager.cpp @@ -56,7 +56,7 @@ namespace Wintermute { #define SAVE_MAGIC_3 0x12564154 ////////////////////////////////////////////////////////////////////////// -BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool deleteSingleton) { +BasePersistenceManager::BasePersistenceManager(const Common::String &savePrefix, bool deleteSingleton) { _saving = false; _offset = 0; _saveStream = nullptr; @@ -91,7 +91,7 @@ BasePersistenceManager::BasePersistenceManager(const char *savePrefix, bool dele _thumbnailDataSize = 0; _thumbnailData = nullptr; - if (savePrefix) { + if (savePrefix != "") { _savePrefix = savePrefix; } else if (_gameRef) { _savePrefix = _gameRef->getGameTargetName(); @@ -215,8 +215,8 @@ bool BasePersistenceManager::getSaveExists(int slot) { } ////////////////////////////////////////////////////////////////////////// -bool BasePersistenceManager::initSave(const char *desc) { - if (!desc) { +bool BasePersistenceManager::initSave(const Common::String &desc) { + if (desc == "") { return STATUS_FAILED; } @@ -297,11 +297,11 @@ bool BasePersistenceManager::initSave(const char *desc) { uint32 dataOffset = _offset + sizeof(uint32) + // data offset - sizeof(uint32) + strlen(desc) + 1 + // description + sizeof(uint32) + strlen(desc.c_str()) + 1 + // description sizeof(uint32); // timestamp putDWORD(dataOffset); - putString(desc); + putString(desc.c_str()); g_system->getTimeAndDate(_savedTimestamp); putTimeDate(_savedTimestamp); diff --git a/engines/wintermute/base/base_persistence_manager.h b/engines/wintermute/base/base_persistence_manager.h index 373d1580de..760b45c907 100644 --- a/engines/wintermute/base/base_persistence_manager.h +++ b/engines/wintermute/base/base_persistence_manager.h @@ -63,7 +63,7 @@ public: uint32 getMaxUsedSlot(); bool getSaveExists(int slot); bool initLoad(const Common::String &filename); - bool initSave(const char *desc); + bool initSave(const Common::String &desc); bool getBytes(byte *buffer, uint32 size); bool putBytes(byte *buffer, uint32 size); uint32 _offset; @@ -86,7 +86,7 @@ public: bool transferCharPtr(const char *name, char **val); bool transferString(const char *name, Common::String *val); bool transferVector2(const char *name, Vector2 *val); - BasePersistenceManager(const char *savePrefix = nullptr, bool deleteSingleton = false); + BasePersistenceManager(const Common::String &savePrefix = "", bool deleteSingleton = false); virtual ~BasePersistenceManager(); bool checkVersion(byte verMajor, byte verMinor, byte verBuild); diff --git a/engines/wintermute/base/file/base_disk_file.cpp b/engines/wintermute/base/file/base_disk_file.cpp index 82a9e24dfb..d0c51616f4 100644 --- a/engines/wintermute/base/file/base_disk_file.cpp +++ b/engines/wintermute/base/file/base_disk_file.cpp @@ -113,13 +113,28 @@ Common::SeekableReadStream *openDiskFile(const Common::String &filename) { Common::String fixedFilename = filename; correctSlashes(fixedFilename); - // Absolute path: TODO: Add specific fallbacks here. + // HACK: There are a few games around which mistakenly refer to absolute paths in the scripts. + // The original interpreter on Windows usually simply ignores them when it can't find them. + // We try to turn the known ones into relative paths. if (fixedFilename.contains(':')) { - if (fixedFilename.hasPrefix("c:/windows/fonts/")) { // East Side Story refers to "c:\windows\fonts\framd.ttf" - fixedFilename = filename.c_str() + 14; - } else if (fixedFilename.hasPrefix("c:/carol6/svn/data/")) { // Carol Reed 6: Black Circle refers to "c:\carol6\svn\data\sprites\system\help.png" - fixedFilename = fixedFilename.c_str() + 19; - } else { + const char* const knownPrefixes[] = { // Known absolute paths + "c:/windows/fonts/", // East Side Story refers to "c:\windows\fonts\framd.ttf" + "c:/carol6/svn/data/", // Carol Reed 6: Black Circle refers to "c:\carol6\svn\data\sprites\system\help.png" + "f:/dokument/spel 5/demo/data/" // Carol Reed 5 (non-demo) refers to "f:\dokument\spel 5\demo\data\scenes\credits\op_cred_00\op_cred_00.jpg" + }; + + bool matched = false; + + for (uint i = 0; i < ARRAYSIZE(knownPrefixes); i++) { + if (fixedFilename.hasPrefix(knownPrefixes[i])) { + fixedFilename = fixedFilename.c_str() + strlen(knownPrefixes[i]); + matched = true; + } + } + + if (!matched) { + // fixedFilename is unchanged and thus still broken, none of the above workarounds worked. + // We can only bail out error("openDiskFile::Absolute path or invalid filename used in %s", filename.c_str()); } } diff --git a/engines/wintermute/configure.engine b/engines/wintermute/configure.engine index bdaf49de3f..55385776de 100644 --- a/engines/wintermute/configure.engine +++ b/engines/wintermute/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit" +add_engine wintermute "Wintermute" yes "" "" "jpeg png zlib vorbis 16bit highres" diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp index aca682ae99..f77eb5c64d 100644 --- a/engines/wintermute/detection.cpp +++ b/engines/wintermute/detection.cpp @@ -75,8 +75,8 @@ static const char *directoryGlobs[] = { class WintermuteMetaEngine : public AdvancedMetaEngine { public: WintermuteMetaEngine() : AdvancedMetaEngine(Wintermute::gameDescriptions, sizeof(WMEGameDescription), Wintermute::wintermuteGames, gameGuiOptions) { - _singleid = "wintermute"; - _guioptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_SHOW_FPS); + _singleId = "wintermute"; + _guiOptions = GUIO2(GUIO_NOMIDI, GAMEOPTION_SHOW_FPS); _maxScanDepth = 2; _directoryGlobs = directoryGlobs; } @@ -94,8 +94,8 @@ public: s_fallbackDesc.language = Common::UNK_LANG; s_fallbackDesc.flags = ADGF_UNSTABLE; s_fallbackDesc.platform = Common::kPlatformWindows; // default to Windows - s_fallbackDesc.gameid = "wintermute"; - s_fallbackDesc.guioptions = GUIO0(); + s_fallbackDesc.gameId = "wintermute"; + s_fallbackDesc.guiOptions = GUIO0(); if (allFiles.contains("data.dcp")) { Common::String name, caption; @@ -109,7 +109,7 @@ public: // Prefix to avoid collisions with actually known games name = "wmeunk-" + name; Common::strlcpy(s_fallbackGameIdBuf, name.c_str(), sizeof(s_fallbackGameIdBuf) - 1); - s_fallbackDesc.gameid = s_fallbackGameIdBuf; + s_fallbackDesc.gameId = s_fallbackGameIdBuf; if (caption != name) { caption += " (unknown version) "; char *offset = s_fallbackGameIdBuf + name.size() + 1; diff --git a/engines/wintermute/detection_tables.h b/engines/wintermute/detection_tables.h index 25a01766e4..ca30204462 100644 --- a/engines/wintermute/detection_tables.h +++ b/engines/wintermute/detection_tables.h @@ -181,10 +181,13 @@ static const WMEGameDescription gameDescriptions[] = { WME_ENTRY1s("data.dcp", "b3f8b09bb4b05ee3e9d14697525257f9", 59296246), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 4 - East Side Story WME_WINENTRY("carolreed4", "", - WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), + WME_ENTRY1s("data.dcp", "b26377797f060afc2d440d820100c1ce", 529320536), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), // Carol Reed 5 - The Colour of Murder WME_WINENTRY("carolreed5", "", WME_ENTRY1s("data.dcp", "3fcfca44209545d0e26774156427b494", 603660415), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), + // Carol Reed 5 - The Colour of Murder (1.0 Demo) + WME_WINENTRY("carolreed5", "Demo", + WME_ENTRY1s("data.dcp", "27b3efc018ade5ee8f4adf08b4e3c0dd", 92019500), Common::EN_ANY, ADGF_UNSTABLE | ADGF_DEMO, LATEST_VERSION), // Carol Reed 6 - Black Circle WME_WINENTRY("carolreed6", "", WME_ENTRY1s("data.dcp", "0e4c532beecf23d85012168753f41189", 456258147), Common::EN_ANY, ADGF_UNSTABLE, LATEST_VERSION), diff --git a/engines/wintermute/wintermute.cpp b/engines/wintermute/wintermute.cpp index e35bb60c3d..955f2dc1c2 100644 --- a/engines/wintermute/wintermute.cpp +++ b/engines/wintermute/wintermute.cpp @@ -133,7 +133,7 @@ Common::Error WintermuteEngine::run() { } int WintermuteEngine::init() { - BaseEngine::createInstance(_targetName, _gameDescription->adDesc.gameid, _gameDescription->adDesc.language, _gameDescription->targetExecutable); + BaseEngine::createInstance(_targetName, _gameDescription->adDesc.gameId, _gameDescription->adDesc.language, _gameDescription->targetExecutable); _game = new AdGame(_targetName); if (!_game) { return 1; diff --git a/engines/zvision/configure.engine b/engines/zvision/configure.engine index 226870c3fd..8681522a35 100644 --- a/engines/zvision/configure.engine +++ b/engines/zvision/configure.engine @@ -1,3 +1,3 @@ # This file is included from the main "configure" script # add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] -add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit" +add_engine zvision "Z-Vision" yes "" "" "freetype2 16bit highres" diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp index 8b47590861..cc967070d9 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -61,7 +61,7 @@ public: ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), ZVision::zVisionGames, ZVision::optionsList) { _maxScanDepth = 2; _directoryGlobs = ZVision::directoryGlobs; - _singleid = "zvision"; + _singleId = "zvision"; } virtual const char *getName() const { diff --git a/gui/credits.h b/gui/credits.h index cda523bb79..cb9a10fec4 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -347,6 +347,9 @@ static const char *credits[] = { "C1""Dreamcast", "C0""Marcus Comstedt", "", +"C1""GCW0", +"C0""Eugene Sandulenko", +"", "C1""GPH Devices (GP2X, GP2XWiz & Caanoo)", "C0""John Willis", "", diff --git a/gui/predictivedialog.cpp b/gui/predictivedialog.cpp index 6ff10267db..9557da1206 100644 --- a/gui/predictivedialog.cpp +++ b/gui/predictivedialog.cpp @@ -724,6 +724,10 @@ int PredictiveDialog::binarySearch(const char *const *const dictLine, const Comm } bool PredictiveDialog::matchWord() { + // If there is no dictionary, then there is no match. + if (_unitedDict.dictLineCount <= 0) + return false; + // If no text has been entered, then there is no match. if (_currentCode.empty()) return false; @@ -981,6 +985,7 @@ void PredictiveDialog::loadAllDictionary(Dict &dict) { Common::File *inFile = new Common::File(); if (!inFile->open(ConfMan.get(dict.nameDict))) { warning("Predictive Dialog: cannot read file: %s", dict.defaultFilename.c_str()); + delete inFile; return; } loadDictionary(inFile, dict); diff --git a/gui/predictivedialog.h b/gui/predictivedialog.h index 37c80a2a14..4c167c3efa 100644 --- a/gui/predictivedialog.h +++ b/gui/predictivedialog.h @@ -85,6 +85,7 @@ private: struct Dict { Dict() : dictLine(nullptr), dictText(nullptr), dictActLine(nullptr), dictLineCount(0), dictTextSize(0) {} + ~Dict() { free(dictText); } char **dictLine; char *dictText; char *dictActLine; // using only for united dict... diff --git a/gui/themes/translations.dat b/gui/themes/translations.dat Binary files differindex 87f3bf0ade..2378bc4e13 100644 --- a/gui/themes/translations.dat +++ b/gui/themes/translations.dat diff --git a/po/da_DK.po b/po/da_DK.po index ca09c0bc20..934297bd80 100644 --- a/po/da_DK.po +++ b/po/da_DK.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-02-20 21:22+0000\n" -"PO-Revision-Date: 2016-02-14 15:28+0100\n" +"PO-Revision-Date: 2016-02-25 21:08+0100\n" "Last-Translator: Steffen Nyeland <steffen@nyeland.dk>\n" "Language-Team: Steffen Nyeland <steffen@nyeland.dk>\n" "Language: Dansk\n" @@ -2685,18 +2685,16 @@ msgstr "" "\n" #: engines/parallaction/saveload.cpp:197 -#, fuzzy msgid "Load file" -msgstr "Indlæs spil:" +msgstr "Indlæs fil" #: engines/parallaction/saveload.cpp:204 msgid "Loading game..." msgstr "Indlæser spil..." #: engines/parallaction/saveload.cpp:212 -#, fuzzy msgid "Save file" -msgstr "Gemmer:" +msgstr "Gem fil" #: engines/parallaction/saveload.cpp:219 msgid "Saving game..." @@ -3544,58 +3542,60 @@ msgstr "" "at læse instrumenterne fra. Musik bliver deaktiveret." #: engines/sherlock/detection.cpp:71 -#, fuzzy msgid "Use original savegame dialog" -msgstr "Brug original gem/indlæs skærme" +msgstr "Brug original gem/indlæs dialog" #: engines/sherlock/detection.cpp:72 msgid "" "Files button in-game shows original savegame dialog rather than the ScummVM " "menu" msgstr "" +"Knappen Files i spillet viser original gem/indlæs dialog snarere end ScummVM " +"menuen" #: engines/sherlock/detection.cpp:81 msgid "Pixellated scene transitions" -msgstr "" +msgstr "Pixelleret scene overgange" #: engines/sherlock/detection.cpp:82 msgid "When changing scenes, a randomized pixel transition is done" -msgstr "" +msgstr "Ved sceneskift, bruges en tilfældig pixelleret overgang" #: engines/sherlock/detection.cpp:91 msgid "Don't show hotspots when moving mouse" -msgstr "" +msgstr "Vis ikke hotspots, når du flytter musen" #: engines/sherlock/detection.cpp:92 msgid "" "Only show hotspot names after you actually click on a hotspot or action " "button" msgstr "" +"Vis kun hotspot navne efter du rent faktisk klikker på et hotspot eller en " +"handlingsknap" #: engines/sherlock/detection.cpp:101 -#, fuzzy msgid "Show character portraits" -msgstr "Skift person" +msgstr "Vis person portrætter" #: engines/sherlock/detection.cpp:102 msgid "Show portraits for the characters when conversing" -msgstr "" +msgstr "Vis portrætter for personer når de taler" #: engines/sherlock/detection.cpp:111 msgid "Slide dialogs into view" -msgstr "" +msgstr "Skub dialoger til syne" #: engines/sherlock/detection.cpp:112 msgid "Slide UI dialogs into view, rather than simply showing them immediately" -msgstr "" +msgstr "Skub dialoger til syne, i stedet for blot at vise dem straks" #: engines/sherlock/detection.cpp:121 msgid "Transparent windows" -msgstr "" +msgstr "Gennemsigtige vinduer" #: engines/sherlock/detection.cpp:122 msgid "Show windows with a partially transparent background" -msgstr "" +msgstr "Vis vinduer med delvis gennemsigtig baggrund" #: engines/sky/compact.cpp:130 msgid "" diff --git a/po/nb_NO.po b/po/nb_NO.po index e0870f8437..8f105c6d55 100644 --- a/po/nb_NO.po +++ b/po/nb_NO.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-02-20 21:22+0000\n" -"PO-Revision-Date: 2014-07-11 00:02+0100\n" +"PO-Revision-Date: 2016-02-25 23:42+0100\n" "Last-Translator: Einar Johan Trøan Sømåen <einarjohants@gmail.com>\n" "Language-Team: somaen <einarjohants@gmail.com>\n" "Language: Norsk (bokmaal)\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Poedit-SourceCharset: iso-8859-1\n" -"X-Generator: Poedit 1.5.5\n" +"X-Generator: Poedit 1.8.7\n" #: gui/about.cpp:94 #, c-format @@ -75,7 +75,7 @@ msgstr "Velg" #: gui/editrecorddialog.cpp:58 msgid "Author:" -msgstr "" +msgstr "Forfatter:" #: gui/editrecorddialog.cpp:59 gui/launcher.cpp:204 msgid "Name:" @@ -83,24 +83,23 @@ msgstr "Navn:" #: gui/editrecorddialog.cpp:60 msgid "Notes:" -msgstr "" +msgstr "Notater:" #: gui/editrecorddialog.cpp:68 gui/predictivedialog.cpp:74 msgid "Ok" -msgstr "" +msgstr "Ok" #: gui/filebrowser-dialog.cpp:49 msgid "Choose file for loading" -msgstr "" +msgstr "Velg fil for lasting" #: gui/filebrowser-dialog.cpp:49 msgid "Enter filename for saving" -msgstr "" +msgstr "Skriv inn filnavn for lagring" #: gui/filebrowser-dialog.cpp:132 -#, fuzzy msgid "Do you really want to overwrite the file?" -msgstr "Vil du virkelig slette dette lagrede spillet?" +msgstr "Vil du virkelig overskrive denne filen?" #: gui/filebrowser-dialog.cpp:132 gui/fluidsynth-dialog.cpp:217 #: gui/launcher.cpp:795 gui/launcher.cpp:943 gui/launcher.cpp:1002 @@ -132,7 +131,7 @@ msgstr "Aktiv" #: gui/fluidsynth-dialog.cpp:72 msgid "Room:" -msgstr "" +msgstr "Rom:" #: gui/fluidsynth-dialog.cpp:79 msgid "Damp:" @@ -678,21 +677,19 @@ msgstr "" #: gui/onscreendialog.cpp:101 gui/onscreendialog.cpp:103 msgid "Stop" -msgstr "" +msgstr "Stopp" #: gui/onscreendialog.cpp:106 msgid "Edit record description" msgstr "" #: gui/onscreendialog.cpp:108 -#, fuzzy msgid "Switch to Game" -msgstr "Bytt" +msgstr "Bytt til Spill" #: gui/onscreendialog.cpp:110 -#, fuzzy msgid "Fast replay" -msgstr "Rask modus" +msgstr "Rask replay" #: gui/options.cpp:85 msgid "Never" @@ -906,6 +903,8 @@ msgid "" "Check if you want to enable patch mappings to emulate an MT-32 on a Roland " "GS device" msgstr "" +"Aktiver hvis du vil slå på patch mappinger for å emulere en MT-32 eller " +"Roland GS enhet" #: gui/options.cpp:898 msgid "Don't use Roland MT-32 music" @@ -1080,20 +1079,19 @@ msgstr "" #. I18N: You must leave "#" as is, only word 'next' is translatable #: gui/predictivedialog.cpp:86 msgid "# next" -msgstr "" +msgstr "# neste" #: gui/predictivedialog.cpp:87 msgid "add" msgstr "" #: gui/predictivedialog.cpp:92 gui/predictivedialog.cpp:164 -#, fuzzy msgid "Delete char" -msgstr "Slett" +msgstr "" #: gui/predictivedialog.cpp:97 gui/predictivedialog.cpp:168 msgid "<" -msgstr "" +msgstr "<" #. I18N: Pre means 'Predictive', leave '*' as is #: gui/predictivedialog.cpp:99 gui/predictivedialog.cpp:572 @@ -1103,12 +1101,12 @@ msgstr "" #. I18N: 'Num' means Numbers #: gui/predictivedialog.cpp:575 msgid "* Num" -msgstr "" +msgstr "* Tall" #. I18N: 'Abc' means Latin alphabet input #: gui/predictivedialog.cpp:578 msgid "* Abc" -msgstr "" +msgstr "* Abc" #: gui/recorderdialog.cpp:64 msgid "Recorder or Playback Gameplay" @@ -1124,33 +1122,30 @@ msgid "Record" msgstr "" #: gui/recorderdialog.cpp:72 -#, fuzzy msgid "Playback" -msgstr "Spill" +msgstr "" #: gui/recorderdialog.cpp:74 msgid "Edit" -msgstr "" +msgstr "Rediger" #: gui/recorderdialog.cpp:86 gui/recorderdialog.cpp:243 #: gui/recorderdialog.cpp:253 msgid "Author: " -msgstr "" +msgstr "Forfatter: " #: gui/recorderdialog.cpp:87 gui/recorderdialog.cpp:244 #: gui/recorderdialog.cpp:254 msgid "Notes: " -msgstr "" +msgstr "Notater: " #: gui/recorderdialog.cpp:155 -#, fuzzy msgid "Do you really want to delete this record?" -msgstr "Vil du virkelig slette dette lagrede spillet?" +msgstr "" #: gui/recorderdialog.cpp:174 -#, fuzzy msgid "Unknown Author" -msgstr "Ukjent feil" +msgstr "Ukjent Forfatter" #: gui/saveload-dialog.cpp:167 msgid "List view" @@ -1357,11 +1352,11 @@ msgstr "Hercules Oransje" #: common/rendermode.cpp:42 msgid "PC-9821 (256 Colors)" -msgstr "" +msgstr "PC-9821 (256 Farger)" #: common/rendermode.cpp:43 msgid "PC-9801 (16 Colors)" -msgstr "" +msgstr "PC-9801 (16 Farger)" #: common/rendermode.cpp:73 msgctxt "lowres" @@ -1563,7 +1558,7 @@ msgstr "DOSBox OPL emulator" #: audio/fmopl.cpp:67 msgid "ALSA Direct FM" -msgstr "" +msgstr "ALSA Direct FM" #: audio/mididrv.cpp:209 #, c-format @@ -1620,17 +1615,15 @@ msgstr "Apple II GS Emulator (IKKE IMPLEMENTERT)" #: audio/softsynth/cms.cpp:350 msgid "Creative Music System Emulator" -msgstr "" +msgstr "Creative Music System Emulator" #: audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp:33 -#, fuzzy msgid "FM-Towns Audio" -msgstr "FM Towns Emulator" +msgstr "FM Towns Lyd" #: audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp:58 -#, fuzzy msgid "PC-98 Audio" -msgstr "Lyd" +msgstr "PC-98 Lyd" #: audio/softsynth/mt32.cpp:200 msgid "Initializing MT-32 Emulator" @@ -1725,34 +1718,33 @@ msgstr "Vil du avslutte?" #. I18N: Trackpad mode toggle status. #: backends/events/webossdl/webossdl-events.cpp:308 -#, fuzzy msgid "Trackpad mode is now" -msgstr "Touchpad-modus deaktivert." +msgstr "Trackpadmodus er nå" #. I18N: Trackpad mode on or off. #. I18N: Auto-drag on or off. #: backends/events/webossdl/webossdl-events.cpp:311 #: backends/events/webossdl/webossdl-events.cpp:338 msgid "ON" -msgstr "" +msgstr "PÅ" #: backends/events/webossdl/webossdl-events.cpp:311 #: backends/events/webossdl/webossdl-events.cpp:338 msgid "OFF" -msgstr "" +msgstr "AV" #: backends/events/webossdl/webossdl-events.cpp:315 msgid "Swipe two fingers to the right to toggle." -msgstr "" +msgstr "Sveip to fingre til høyre for å slå av/på" #. I18N: Auto-drag toggle status. #: backends/events/webossdl/webossdl-events.cpp:335 msgid "Auto-drag mode is now" -msgstr "" +msgstr "Auto-dramodus er nå" #: backends/events/webossdl/webossdl-events.cpp:342 msgid "Swipe three fingers to the right to toggle." -msgstr "" +msgstr "Sveip tre fingre til høyre for å veksle" #: backends/graphics/opengl/opengl-graphics.cpp:119 msgid "OpenGL" @@ -2047,24 +2039,23 @@ msgstr "Styr Mus" #: backends/platform/tizen/fs.cpp:259 msgid "[ Data ]" -msgstr "" +msgstr "[ Data ]" #: backends/platform/tizen/fs.cpp:263 msgid "[ Resources ]" -msgstr "" +msgstr "[ Ressurser ]" #: backends/platform/tizen/fs.cpp:267 msgid "[ SDCard ]" -msgstr "" +msgstr "[ SDKort ]" #: backends/platform/tizen/fs.cpp:271 msgid "[ Media ]" -msgstr "" +msgstr "[ Media ]" #: backends/platform/tizen/fs.cpp:275 -#, fuzzy msgid "[ Shared ]" -msgstr "Delt Ressurs:" +msgstr "[ Delt ]" #: backends/platform/wii/options.cpp:51 msgid "Video" @@ -2331,25 +2322,26 @@ msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "Bruk de originale lagre/laste-skjermene, istedenfor ScummVM-variantene" #: engines/agi/detection.cpp:157 -#, fuzzy msgid "Use an alternative palette" -msgstr "Bruk en alternativ intro (Kun for CD-versjon)" +msgstr "Bruk en alternativ palett" #: engines/agi/detection.cpp:158 msgid "" "Use an alternative palette, common for all Amiga games. This was the old " "behavior" msgstr "" +"Bruk en alternativ palett, fells for alle Amigaspill. Dette var den gamle " +"oppførselen" #: engines/agi/detection.cpp:167 -#, fuzzy msgid "Mouse support" -msgstr "Hopp over" +msgstr "Musstøtte" #: engines/agi/detection.cpp:168 msgid "" "Enables mouse support. Allows to use mouse for movement and in game menus." msgstr "" +"Aktiver musstøtte. Tillater å bruke mus for bevegelse og i spillmenyer." #: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 #: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 @@ -2410,13 +2402,12 @@ msgid "Cutscene file '%s' not found!" msgstr "Fant ikke cutscenefil '%s'!" #: engines/cge/detection.cpp:105 engines/cge2/detection.cpp:101 -#, fuzzy msgid "Color Blind Mode" -msgstr "Klikkmodus" +msgstr "Fargeblindmodus" #: engines/cge/detection.cpp:106 engines/cge2/detection.cpp:102 msgid "Enable Color Blind Mode by default" -msgstr "" +msgstr "Aktiver fargeblindmodus som standard" #: engines/drascula/saveload.cpp:47 msgid "" @@ -2472,11 +2463,11 @@ msgstr "Klarte ikke å lagre spill." #: engines/hopkins/detection.cpp:76 engines/hopkins/detection.cpp:86 msgid "Gore Mode" -msgstr "" +msgstr "Gørrmodus" #: engines/hopkins/detection.cpp:77 engines/hopkins/detection.cpp:87 msgid "Enable Gore Mode when available" -msgstr "" +msgstr "Aktiver gørrmodus når tilgjengelig" #. I18N: Studio audience adds an applause and cheering sounds whenever #. Malcolm makes a joke. @@ -2490,9 +2481,8 @@ msgstr "Aktiver studiopublikum" #. I18N: This option allows the user to skip text and cutscenes. #: engines/kyra/detection.cpp:73 -#, fuzzy msgid "Skip support" -msgstr "Hopp over" +msgstr "Hopp over-støtte" #: engines/kyra/detection.cpp:74 msgid "Allow text and cutscenes to be skipped" @@ -2609,6 +2599,12 @@ msgid "" "Do you wish to use this save game file with ScummVM?\n" "\n" msgstr "" +"Det følgende originale lagrede spillet ble funnet i spillstien din:\n" +"\n" +"%s %s\n" +"\n" +"Vil du bruke dette lagrede spillet med ScummVM?\n" +"\n" #: engines/kyra/saveload_eob.cpp:590 #, c-format @@ -2616,6 +2612,8 @@ msgid "" "A save game file was found in the specified slot %d. Overwrite?\n" "\n" msgstr "" +"Et lagret spill ble funnet i den valgte posisjonen %d. Overskrive?\n" +"\n" #: engines/kyra/saveload_eob.cpp:623 #, c-format @@ -2627,6 +2625,10 @@ msgid "" "'import_savefile'.\n" "\n" msgstr "" +"%d originale lagrede spill har blitt importert vellykket til ScummVM.\n" +"Hvis du vil importere flere originale lagrede spill senere, må du åpne\n" +"ScummVM debugkonsollen og bruke kommandoen «import_savefile»\n" +"\n" #. I18N: Option for fast scene switching #: engines/mohawk/dialogs.cpp:92 engines/mohawk/dialogs.cpp:167 @@ -2680,18 +2682,16 @@ msgstr "" "\n" #: engines/parallaction/saveload.cpp:197 -#, fuzzy msgid "Load file" -msgstr "Åpne spill:" +msgstr "Last fil" #: engines/parallaction/saveload.cpp:204 msgid "Loading game..." msgstr "Laster spill..." #: engines/parallaction/saveload.cpp:212 -#, fuzzy msgid "Save file" -msgstr "Lagret spill:" +msgstr "Lagre fil" #: engines/parallaction/saveload.cpp:219 msgid "Saving game..." @@ -2776,21 +2776,19 @@ msgstr "Bruk en alternativ intro (Kun for CD-versjon)" #: engines/sci/detection.cpp:374 msgid "Skip EGA dithering pass (full color backgrounds)" -msgstr "" +msgstr "Hopp over EGA dithering (fullfarge bakgrunner)" #: engines/sci/detection.cpp:375 msgid "Skip dithering pass in EGA games, graphics are shown with full colors" msgstr "" #: engines/sci/detection.cpp:384 -#, fuzzy msgid "Enable high resolution graphics" -msgstr "Aktiver hit point-bar grafer" +msgstr "Aktiver høyoppløselig grafikk" #: engines/sci/detection.cpp:385 -#, fuzzy msgid "Enable high resolution graphics/content" -msgstr "Aktiver hit point-bar grafer" +msgstr "Aktiver høyoppløselig grafikk/innhold" #: engines/sci/detection.cpp:394 msgid "Prefer digital sound effects" @@ -2862,15 +2860,13 @@ msgstr "Spill pauset. Trykk på MELLOMROMstasten for å fortsette." #. "Moechten Sie wirklich neu starten? (J/N)J" #. Will react to J as 'Yes' #: engines/scumm/dialogs.cpp:183 -#, fuzzy msgid "Are you sure you want to restart? (Y/N)Y" -msgstr "Er du sikker på at du vil avslutte? (Y/N)" +msgstr "Er du sikker på at du vil starte på nytt? (J/N)J" #. I18N: you may specify 'Yes' symbol at the end of the line. See previous comment #: engines/scumm/dialogs.cpp:185 -#, fuzzy msgid "Are you sure you want to quit? (Y/N)Y" -msgstr "Er du sikker på at du vil avslutte? (Y/N)" +msgstr "Er du sikker på at du vil avslutte? (J/N)J" #: engines/scumm/dialogs.cpp:190 msgid "Play" @@ -3383,19 +3379,19 @@ msgstr "" #: engines/scumm/help.cpp:293 msgid "Toggle Keyboard/Mouse Fighting (*)" -msgstr "" +msgstr "Velg Tastatur/Mus-slåssing (*)" #: engines/scumm/help.cpp:295 msgid "* Keyboard Fighting is always on," -msgstr "" +msgstr "* Tastaturslåssing er alltid på," #: engines/scumm/help.cpp:296 msgid " so despite the in-game message this" -msgstr "" +msgstr " så til tross for beskjeden i spillet, vil" #: engines/scumm/help.cpp:297 msgid " actually toggles Mouse Fighting Off/On" -msgstr "" +msgstr " dette faktisk slå musslåssing av/på" #: engines/scumm/help.cpp:304 msgid "Fighting controls (numpad):" @@ -3491,23 +3487,20 @@ msgid "Fly to lower right" msgstr "Fly til nedre høyre" #: engines/scumm/input.cpp:580 -#, fuzzy msgid "Snap scroll on" -msgstr "Myk scrolling" +msgstr "" #: engines/scumm/input.cpp:582 msgid "Snap scroll off" msgstr "" #: engines/scumm/input.cpp:595 -#, fuzzy msgid "Music volume: " -msgstr "Musikkvolum:" +msgstr "Musikkvolum: " #: engines/scumm/input.cpp:612 -#, fuzzy msgid "Subtitle speed: " -msgstr "Teksthastighet:" +msgstr "Teksthastighet: " #: engines/scumm/scumm.cpp:1832 #, c-format @@ -3519,65 +3512,70 @@ msgstr "" "men %s mangler. Bruker AdLib istedet." #: engines/scumm/scumm.cpp:2644 -#, fuzzy msgid "" "Usually, Maniac Mansion would start now. But for that to work, the game " "files for Maniac Mansion have to be in the 'Maniac' directory inside the " "Tentacle game directory, and the game has to be added to ScummVM." msgstr "" -"Vanligvis, ville Maniac Mansion ha startet nå. Men ScummVM støtter ikke det " -"ennå. Så, for å spille Maniac Mansion, gå til 'Legg til spill' i ScummVM-" -"hovedmenyen og velg 'Maniac'-undermappa i Tentacle-mappa." +"Vanligvis, ville Maniac Mansion ha startet nå. Men for at det skal fungere " +"må Maniac Mansion-filene ligge i «Maniac» mappa inni Tentacle-spillmappa, og " +"spillet må være lagt til i ScummVM." #: engines/scumm/players/player_v3m.cpp:129 msgid "" "Could not find the 'Loom' Macintosh executable to read the\n" "instruments from. Music will be disabled." msgstr "" +"Kunne ikke finne Macintosh binærfila «Loom» for å lese instrumenter\n" +"fra den. Musikk vil bli deaktivert." #: engines/scumm/players/player_v5m.cpp:107 msgid "" "Could not find the 'Monkey Island' Macintosh executable to read the\n" "instruments from. Music will be disabled." msgstr "" +"Kunne ikke finne Macintosh-binærfila «Monkey Island» for å lese\n" +"instrumenter fra den. Musikk vil bli deaktivert." #: engines/sherlock/detection.cpp:71 -#, fuzzy msgid "Use original savegame dialog" -msgstr "Bruk originale lagre/laste-skjermer" +msgstr "Bruk original lagringsdialog" #: engines/sherlock/detection.cpp:72 msgid "" "Files button in-game shows original savegame dialog rather than the ScummVM " "menu" msgstr "" +"«Files»-knappen i spillet viser original lagre/laste-dialog istedenfor " +"ScummVM menyen" #: engines/sherlock/detection.cpp:81 msgid "Pixellated scene transitions" -msgstr "" +msgstr "Pikselerte sceneoverganger" #: engines/sherlock/detection.cpp:82 msgid "When changing scenes, a randomized pixel transition is done" -msgstr "" +msgstr "Under scenskifter vil en tilfeldig pikselovergang bli gjort" #: engines/sherlock/detection.cpp:91 msgid "Don't show hotspots when moving mouse" -msgstr "" +msgstr "Ikke vis hotspots når du beveger musa" #: engines/sherlock/detection.cpp:92 msgid "" "Only show hotspot names after you actually click on a hotspot or action " "button" msgstr "" +"Vis hotspots først etter at du faktisk har klikket på en hotspot eller " +"handlingsknapp" #: engines/sherlock/detection.cpp:101 -#, fuzzy msgid "Show character portraits" -msgstr "Bytt karakter" +msgstr "Vis karakterportretter" #: engines/sherlock/detection.cpp:102 msgid "Show portraits for the characters when conversing" -msgstr "" +msgstr "Vis portretter for karakterene når de snakker sammen" #: engines/sherlock/detection.cpp:111 msgid "Slide dialogs into view" @@ -3589,11 +3587,11 @@ msgstr "" #: engines/sherlock/detection.cpp:121 msgid "Transparent windows" -msgstr "" +msgstr "Gjennomsiktige vinduer" #: engines/sherlock/detection.cpp:122 msgid "Show windows with a partially transparent background" -msgstr "" +msgstr "Vis vinduer med en delvis gjennomsiktig bakgrunn" #: engines/sky/compact.cpp:130 msgid "" @@ -3705,52 +3703,52 @@ msgstr "" #: engines/wintermute/detection.cpp:58 msgid "Show FPS-counter" -msgstr "" +msgstr "Vis FPS-teller" #: engines/wintermute/detection.cpp:59 msgid "Show the current number of frames per second in the upper left corner" msgstr "" +"Vis det gjeldende antall bilder per sekund (FPS) i øvre venstre hjørne av " +"skjermen" #: engines/zvision/detection_tables.h:52 -#, fuzzy msgid "Use the original save/load screens instead of the ScummVM interface" -msgstr "Bruk de originale lagre/laste-skjermene, istedenfor ScummVM-variantene" +msgstr "" +"Bruk de originale lagre/laste-skjermene istedenfor ScummVM-grensesnittet" #: engines/zvision/detection_tables.h:61 msgid "Double FPS" -msgstr "" +msgstr "Dobbel FPS" #: engines/zvision/detection_tables.h:62 msgid "Increase framerate from 30 to 60 FPS" -msgstr "" +msgstr "Øk bilderate fra 30 til 60 FPS" #: engines/zvision/detection_tables.h:71 -#, fuzzy msgid "Enable Venus" -msgstr "Aktiver helium-modus" +msgstr "Aktiver Venus" #: engines/zvision/detection_tables.h:72 -#, fuzzy msgid "Enable the Venus help system" -msgstr "Aktiver helium-modus" +msgstr "Aktiver Venus hjelpesystemet" #: engines/zvision/detection_tables.h:81 msgid "Disable animation while turning" -msgstr "" +msgstr "Deaktiver animasjoner under snuing" #: engines/zvision/detection_tables.h:82 msgid "Disable animation while turning in panorama mode" -msgstr "" +msgstr "Deaktiver animasjoner under snuing i panoramamodus" #: engines/zvision/detection_tables.h:91 msgid "Use high resolution MPEG video" -msgstr "" +msgstr "Bruk høyoppløst MPEG-video" #: engines/zvision/detection_tables.h:92 -#, fuzzy msgid "Use MPEG video from the DVD version, instead of lower resolution AVI" msgstr "" -"Bruk det alternative settet med sølvmuspekere, istedenfor de normale gylne." +"Bruk MPEG-video fra DVD-versjonen istedenfor AVI-versjonen med lavere " +"oppløsning" #~ msgid "EGA undithering" #~ msgstr "EGA av-dithering" diff --git a/po/nn_NO.po b/po/nn_NO.po index d200992a14..62f7c07beb 100644 --- a/po/nn_NO.po +++ b/po/nn_NO.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: ScummVM 1.3.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-02-20 21:22+0000\n" -"PO-Revision-Date: 2014-07-11 00:04+0100\n" -"Last-Translator: Einar Johan Trøan Sømåen <einarjohants@gmail.com>\n" +"PO-Revision-Date: 2016-02-26 00:37+0100\n" +"Last-Translator: Einar Johan TrÞan SÞmÃ¥en <einarjohants@gmail.com>\n" "Language-Team: somaen <einarjohants@gmail.com>\n" "Language: Norsk (nynorsk)\n" "MIME-Version: 1.0\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-SourceCharset: iso-8859-1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.5.5\n" +"X-Generator: Poedit 1.8.7\n" #: gui/about.cpp:94 #, c-format @@ -75,7 +75,7 @@ msgstr "Vel" #: gui/editrecorddialog.cpp:58 msgid "Author:" -msgstr "" +msgstr "Forfattar:" #: gui/editrecorddialog.cpp:59 gui/launcher.cpp:204 msgid "Name:" @@ -83,24 +83,23 @@ msgstr "Namn:" #: gui/editrecorddialog.cpp:60 msgid "Notes:" -msgstr "" +msgstr "Notatar:" #: gui/editrecorddialog.cpp:68 gui/predictivedialog.cpp:74 msgid "Ok" -msgstr "" +msgstr "Ok" #: gui/filebrowser-dialog.cpp:49 msgid "Choose file for loading" -msgstr "" +msgstr "Vel fil for lasting" #: gui/filebrowser-dialog.cpp:49 msgid "Enter filename for saving" -msgstr "" +msgstr "Skriv inn filnamn for lagring" #: gui/filebrowser-dialog.cpp:132 -#, fuzzy msgid "Do you really want to overwrite the file?" -msgstr "Vil du verkeleg slette det lagra spelet?" +msgstr "Vil du verkeleg overskrive fila?" #: gui/filebrowser-dialog.cpp:132 gui/fluidsynth-dialog.cpp:217 #: gui/launcher.cpp:795 gui/launcher.cpp:943 gui/launcher.cpp:1002 @@ -132,7 +131,7 @@ msgstr "Aktiv" #: gui/fluidsynth-dialog.cpp:72 msgid "Room:" -msgstr "" +msgstr "Rom:" #: gui/fluidsynth-dialog.cpp:79 msgid "Damp:" @@ -226,10 +225,10 @@ msgid "OK" msgstr "OK" #: gui/fluidsynth-dialog.cpp:217 -#, fuzzy msgid "" "Do you really want to reset all FluidSynth settings to their default values?" -msgstr "Vil du verkeleg slette det lagra spelet?" +msgstr "" +"Vil du verkeleg sette alle FluidSynth-innstillingar til standardverdiar?" #: gui/gui-manager.cpp:119 backends/keymapper/remap-dialog.cpp:53 #: engines/scumm/help.cpp:126 engines/scumm/help.cpp:141 @@ -447,7 +446,7 @@ msgstr "Ekstrasti:" #: gui/launcher.cpp:330 gui/launcher.cpp:332 gui/launcher.cpp:333 msgid "Specifies path to additional data used by the game" -msgstr "" +msgstr "Veljer sti til tilleggsdata nytta av spelet" #: gui/launcher.cpp:332 gui/options.cpp:1150 msgctxt "lowres" @@ -461,7 +460,7 @@ msgstr "Lagringssti:" #: gui/launcher.cpp:339 gui/launcher.cpp:341 gui/launcher.cpp:342 #: gui/options.cpp:1132 gui/options.cpp:1134 gui/options.cpp:1135 msgid "Specifies where your saved games are put" -msgstr "" +msgstr "Veljer kor lagra spel vert lagra" #: gui/launcher.cpp:341 gui/options.cpp:1134 msgctxt "lowres" @@ -493,9 +492,8 @@ msgid "Select directory with game data" msgstr "Vel mappe med speldata" #: gui/launcher.cpp:547 -#, fuzzy msgid "Select additional game directory" -msgstr "Vel mappe med speldata" +msgstr "Vel mappe med tileggsdata for spelet" #: gui/launcher.cpp:559 gui/options.cpp:1377 msgid "Select directory for saved games" @@ -503,7 +501,7 @@ msgstr "Vel mappe for lagra spel" #: gui/launcher.cpp:586 msgid "This game ID is already taken. Please choose another one." -msgstr "" +msgstr "Denne spel-IDen er allerede teken. Vær vennleg og vel ein anna." #: gui/launcher.cpp:626 engines/dialogs.cpp:111 msgid "~Q~uit" @@ -567,7 +565,7 @@ msgstr "~F~jern spel" #: gui/launcher.cpp:642 gui/launcher.cpp:649 msgid "Remove game from the list. The game data files stay intact" -msgstr "" +msgstr "Fjern spel frå lista. Speldataene forblir intakte" #: gui/launcher.cpp:645 msgctxt "lowres" @@ -670,27 +668,26 @@ msgid "Scanned %d directories ..." msgstr "Søkt i %d mappar ..." #: gui/massadd.cpp:269 -#, fuzzy, c-format +#, c-format msgid "Discovered %d new games, ignored %d previously added games ..." -msgstr "Oppdaga %d nye spel ..." +msgstr "" +"Oppdaga %d nye spel, ignorerte %d spel som har vorte lagt til tidlegare..." #: gui/onscreendialog.cpp:101 gui/onscreendialog.cpp:103 msgid "Stop" -msgstr "" +msgstr "Stopp" #: gui/onscreendialog.cpp:106 msgid "Edit record description" msgstr "" #: gui/onscreendialog.cpp:108 -#, fuzzy msgid "Switch to Game" -msgstr "Bytt" +msgstr "Bytt til spel" #: gui/onscreendialog.cpp:110 -#, fuzzy msgid "Fast replay" -msgstr "Rask modus" +msgstr "" #: gui/options.cpp:85 msgid "Never" @@ -740,7 +737,7 @@ msgstr "Ingen" #: gui/options.cpp:389 msgid "Failed to apply some of the graphic options changes:" -msgstr "" +msgstr "Klarte ikkje å aktivere nokre av grafikkvalendringane:" #: gui/options.cpp:401 msgid "the video mode could not be changed." @@ -752,7 +749,7 @@ msgstr "Fullskjerminstillinga kunne ikkje endrast" #: gui/options.cpp:413 msgid "the aspect ratio setting could not be changed" -msgstr "" +msgstr "aspektrate-innstillinga kunne ikkje endrast" #: gui/options.cpp:732 msgid "Graphics mode:" @@ -796,16 +793,14 @@ msgid "Specifies output sound device or sound card emulator" msgstr "" #: gui/options.cpp:771 -#, fuzzy msgctxt "lowres" msgid "Preferred Dev.:" -msgstr "Føretrukken eining:" +msgstr "" #: gui/options.cpp:771 -#, fuzzy msgctxt "lowres" msgid "Music Device:" -msgstr "Ingen musikk" +msgstr "Musikkeining:" #: gui/options.cpp:798 msgid "AdLib emulator:" @@ -833,7 +828,7 @@ msgstr "GM Eining:" #: gui/options.cpp:820 msgid "Specifies default sound device for General MIDI output" -msgstr "" +msgstr "Veljer standard lydeining for General MIDI avspeling" #: gui/options.cpp:831 msgid "Don't use General MIDI music" @@ -878,7 +873,7 @@ msgstr "MT-32 Eining:" #: gui/options.cpp:879 msgid "Specifies default sound device for Roland MT-32/LAPC1/CM32l/CM64 output" -msgstr "" +msgstr "Veljer standard lydeining for Roland MT-32/LAPC1/CM32l/CM64 avspeling" #: gui/options.cpp:884 msgid "True Roland MT-32 (disable GM emulation)" @@ -889,6 +884,8 @@ msgid "" "Check if you want to use your real hardware Roland-compatible sound device " "connected to your computer" msgstr "" +"Vel om du vil nytte din Roland-kompatible lydeining som du har tilkopla " +"datamaskina di." #: gui/options.cpp:886 msgctxt "lowres" @@ -896,9 +893,8 @@ msgid "True Roland MT-32 (no GM emulation)" msgstr "Ekte Roland MT-32 (ingen GS-emulering)" #: gui/options.cpp:889 -#, fuzzy msgid "Roland GS Device (enable MT-32 mappings)" -msgstr "Ekte Roland MT-32 (deaktiver GM-emulering)" +msgstr "" #: gui/options.cpp:889 msgid "" @@ -976,7 +972,7 @@ msgstr "Lydeffektvolum:" #: gui/options.cpp:971 gui/options.cpp:973 gui/options.cpp:974 msgid "Special sound effects volume" -msgstr "" +msgstr "Spesiallydeffekt volum" #: gui/options.cpp:973 msgctxt "lowres" @@ -1079,20 +1075,19 @@ msgstr "" #. I18N: You must leave "#" as is, only word 'next' is translatable #: gui/predictivedialog.cpp:86 msgid "# next" -msgstr "" +msgstr "# neste" #: gui/predictivedialog.cpp:87 msgid "add" msgstr "" #: gui/predictivedialog.cpp:92 gui/predictivedialog.cpp:164 -#, fuzzy msgid "Delete char" -msgstr "Slett" +msgstr "" #: gui/predictivedialog.cpp:97 gui/predictivedialog.cpp:168 msgid "<" -msgstr "" +msgstr "<" #. I18N: Pre means 'Predictive', leave '*' as is #: gui/predictivedialog.cpp:99 gui/predictivedialog.cpp:572 @@ -1102,12 +1097,12 @@ msgstr "" #. I18N: 'Num' means Numbers #: gui/predictivedialog.cpp:575 msgid "* Num" -msgstr "" +msgstr "* Tal" #. I18N: 'Abc' means Latin alphabet input #: gui/predictivedialog.cpp:578 msgid "* Abc" -msgstr "" +msgstr "* Abc" #: gui/recorderdialog.cpp:64 msgid "Recorder or Playback Gameplay" @@ -1123,33 +1118,30 @@ msgid "Record" msgstr "" #: gui/recorderdialog.cpp:72 -#, fuzzy msgid "Playback" -msgstr "Spel" +msgstr "" #: gui/recorderdialog.cpp:74 msgid "Edit" -msgstr "" +msgstr "Rediger" #: gui/recorderdialog.cpp:86 gui/recorderdialog.cpp:243 #: gui/recorderdialog.cpp:253 msgid "Author: " -msgstr "" +msgstr "Forfattar: " #: gui/recorderdialog.cpp:87 gui/recorderdialog.cpp:244 #: gui/recorderdialog.cpp:254 msgid "Notes: " -msgstr "" +msgstr "Notatar: " #: gui/recorderdialog.cpp:155 -#, fuzzy msgid "Do you really want to delete this record?" -msgstr "Vil du verkeleg slette det lagra spelet?" +msgstr "" #: gui/recorderdialog.cpp:174 -#, fuzzy msgid "Unknown Author" -msgstr "Ukjend feil" +msgstr "Ukjend Forfattar" #: gui/saveload-dialog.cpp:167 msgid "List view" @@ -1339,7 +1331,7 @@ msgstr "Spelmotor-plugin støttar ikkje lagra tilstandar." #: common/error.cpp:71 msgid "User canceled" -msgstr "" +msgstr "Brukar avbraut" #: common/error.cpp:75 msgid "Unknown error" @@ -1356,11 +1348,11 @@ msgstr "Hercules Raudgul" #: common/rendermode.cpp:42 msgid "PC-9821 (256 Colors)" -msgstr "" +msgstr "PC-9821 (256 Fargar)" #: common/rendermode.cpp:43 msgid "PC-9801 (16 Colors)" -msgstr "" +msgstr "PC-9801 (16 Fargar)" #: common/rendermode.cpp:73 msgctxt "lowres" @@ -1454,6 +1446,9 @@ msgid "" "the README for basic information, and for instructions on how to obtain " "further assistance." msgstr "" +"Orsak, men denne spelmotoren støtter for augeblikket ikkje hjelp i spelet. " +"Vennlegst se i README-fila for grunnlegjande informasjon, og for " +"instruksjonar om korleis du kan få ytterlegare hjelp." #: engines/dialogs.cpp:234 engines/pegasus/pegasus.cpp:393 #, c-format @@ -1461,6 +1456,9 @@ msgid "" "Gamestate save failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" +"Speltilstandslagring feila (%s)!. Vennligst sjå i README-fila for " +"grunnlegjande informasjon, og for instruskjonar om korleis du kan få " +"ytterlegare hjelp." #: engines/dialogs.cpp:307 engines/mohawk/dialogs.cpp:109 #: engines/mohawk/dialogs.cpp:170 engines/tsage/dialogs.cpp:106 @@ -1478,20 +1476,19 @@ msgstr "~T~astar" #: engines/engine.cpp:339 msgid "Could not initialize color format." -msgstr "" +msgstr "Høgkvalitetslyd (treigare) (omstart)" #: engines/engine.cpp:347 msgid "Could not switch to video mode: '" msgstr "Kunne ikkje veksle til videomodus: '" #: engines/engine.cpp:356 -#, fuzzy msgid "Could not apply aspect ratio setting." -msgstr "Veksle aspekt-korrigering" +msgstr "Kunne ikkje slå på aspekt-korrigering" #: engines/engine.cpp:361 msgid "Could not apply fullscreen setting." -msgstr "" +msgstr "Kunne ikkje aktiver fullskjermsinnstilling." #: engines/engine.cpp:461 msgid "" @@ -1501,6 +1498,11 @@ msgid "" "the data files to your hard disk instead.\n" "See the README file for details." msgstr "" +"Det ser ut til at du speler dette spelet rett frå\n" +"CD. Dette er kjend for å skape problemar,\n" +"og det er derfor tilråda at du kopierar\n" +"datafilane til harddisken din istaden. \n" +"Sjå README-fila for detaljar." #: engines/engine.cpp:472 msgid "" @@ -1517,6 +1519,9 @@ msgid "" "Gamestate load failed (%s)! Please consult the README for basic information, " "and for instructions on how to obtain further assistance." msgstr "" +"Speltilstandslasting feila (%s)!. Vennligst sjå i README-fila for " +"grunnlegjande informasjon, og for instruskjonar om korleis du kan få " +"ytterlegare hjelp." #: engines/engine.cpp:543 msgid "" @@ -1546,7 +1551,7 @@ msgstr "DOSBox OPL emulator" #: audio/fmopl.cpp:67 msgid "ALSA Direct FM" -msgstr "" +msgstr "ALSA Direct FM" #: audio/mididrv.cpp:209 #, c-format @@ -1554,11 +1559,13 @@ msgid "" "The selected audio device '%s' was not found (e.g. might be turned off or " "disconnected)." msgstr "" +"Den valde lydeininga '%s' vart ikkje funne (t.d. kan den vere avslått eller " +"fråkopla)" #: audio/mididrv.cpp:209 audio/mididrv.cpp:221 audio/mididrv.cpp:257 #: audio/mididrv.cpp:272 msgid "Attempting to fall back to the next available device..." -msgstr "" +msgstr "Prøver å nytte den neste tilgjengelege eininga..." #: audio/mididrv.cpp:221 #, c-format @@ -1566,6 +1573,8 @@ msgid "" "The selected audio device '%s' cannot be used. See log file for more " "information." msgstr "" +"Den foretrukne lydeininga '%s' kan ikkje nyttast. Sjå loggfila for meir " +"informasjon." #: audio/mididrv.cpp:257 #, c-format @@ -1573,6 +1582,8 @@ msgid "" "The preferred audio device '%s' was not found (e.g. might be turned off or " "disconnected)." msgstr "" +"Den foretrukne lydeininga '%s' vart ikkje funne (t.d. kan den vere avslått " +"eller fråkopla)" #: audio/mididrv.cpp:272 #, c-format @@ -1580,6 +1591,8 @@ msgid "" "The preferred audio device '%s' cannot be used. See log file for more " "information." msgstr "" +"Den foretrukne lydeininga '%s' kan ikkje nyttast. Sjå loggfila for meir " +"informasjon." #: audio/mods/paula.cpp:196 msgid "Amiga Audio Emulator" @@ -1595,22 +1608,19 @@ msgstr "Apple II GS Emulator (IKKJE IMPLEMENTERT)" #: audio/softsynth/cms.cpp:350 msgid "Creative Music System Emulator" -msgstr "" +msgstr "Creative Music System Emulator" #: audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp:33 -#, fuzzy msgid "FM-Towns Audio" -msgstr "FM Towns Emulator" +msgstr "FM-Towns Lyd" #: audio/softsynth/fmtowns_pc98/towns_pc98_plugins.cpp:58 -#, fuzzy msgid "PC-98 Audio" -msgstr "Lyd" +msgstr "PC-98 Lyd" #: audio/softsynth/mt32.cpp:200 -#, fuzzy msgid "Initializing MT-32 Emulator" -msgstr "MT-32 Emulator" +msgstr "Initialiserar MT-32 Emulator" #: audio/softsynth/mt32.cpp:426 msgid "MT-32 Emulator" @@ -1652,16 +1662,14 @@ msgstr "Avslutt" #: backends/events/gph/gph-events.cpp:385 #: backends/events/gph/gph-events.cpp:428 #: backends/events/openpandora/op-events.cpp:168 -#, fuzzy msgid "Touchscreen 'Tap Mode' - Left Click" -msgstr "Tap for venstre-klikk, dobbelt-tap for høgre-klikk" +msgstr "Touchscreen 'Tap Mode' - Venstreklikk" #: backends/events/gph/gph-events.cpp:387 #: backends/events/gph/gph-events.cpp:430 #: backends/events/openpandora/op-events.cpp:170 -#, fuzzy msgid "Touchscreen 'Tap Mode' - Right Click" -msgstr "Tap for venstre-klikk, dobbelt-tap for høgre-klikk" +msgstr "Touchscreen 'Tap Mode' - Høgreklikk" #: backends/events/gph/gph-events.cpp:389 #: backends/events/gph/gph-events.cpp:432 @@ -1674,24 +1682,20 @@ msgid "Maximum Volume" msgstr "Maks Volum" #: backends/events/gph/gph-events.cpp:411 -#, fuzzy msgid "Increasing Volume" -msgstr "Volum" +msgstr "Auker Volum" #: backends/events/gph/gph-events.cpp:417 -#, fuzzy msgid "Minimal Volume" -msgstr "Volum" +msgstr "Minste Volum" #: backends/events/gph/gph-events.cpp:419 -#, fuzzy msgid "Decreasing Volume" -msgstr "Volum" +msgstr "Senkar Volum" #: backends/events/maemosdl/maemosdl-events.cpp:180 -#, fuzzy msgid "Clicking Enabled" -msgstr "~O~vergangar aktivert" +msgstr "Klikking aktivert" #: backends/events/maemosdl/maemosdl-events.cpp:180 msgid "Clicking Disabled" @@ -1707,21 +1711,20 @@ msgstr "Vil du avslutte?" #. I18N: Trackpad mode toggle status. #: backends/events/webossdl/webossdl-events.cpp:308 -#, fuzzy msgid "Trackpad mode is now" -msgstr "Deaktivert GFX" +msgstr "Trackpadmodus er no" #. I18N: Trackpad mode on or off. #. I18N: Auto-drag on or off. #: backends/events/webossdl/webossdl-events.cpp:311 #: backends/events/webossdl/webossdl-events.cpp:338 msgid "ON" -msgstr "" +msgstr "PÅ" #: backends/events/webossdl/webossdl-events.cpp:311 #: backends/events/webossdl/webossdl-events.cpp:338 msgid "OFF" -msgstr "" +msgstr "AV" #: backends/events/webossdl/webossdl-events.cpp:315 msgid "Swipe two fingers to the right to toggle." @@ -1734,7 +1737,7 @@ msgstr "" #: backends/events/webossdl/webossdl-events.cpp:342 msgid "Swipe three fingers to the right to toggle." -msgstr "" +msgstr "Sveip tre fingre til høgre for å veksle" #: backends/graphics/opengl/opengl-graphics.cpp:119 msgid "OpenGL" @@ -1756,14 +1759,12 @@ msgid "Normal (no scaling)" msgstr "Normal (ikkje skaler)" #: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2215 -#, fuzzy msgid "Enabled aspect ratio correction" -msgstr "Aspekt-korrigering" +msgstr "Aspekt-korrigering aktivert" #: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2221 -#, fuzzy msgid "Disabled aspect ratio correction" -msgstr "Aspekt-korrigering" +msgstr "Aspekt-korrigering ikkje aktivert" #: backends/graphics/surfacesdl/surfacesdl-graphics.cpp:2276 msgid "Active graphics filter:" @@ -1778,9 +1779,8 @@ msgid "Keymap:" msgstr "Tastkopling:" #: backends/keymapper/remap-dialog.cpp:67 -#, fuzzy msgid " (Effective)" -msgstr " (Aktivt)" +msgstr "" #: backends/keymapper/remap-dialog.cpp:107 msgid " (Active)" @@ -1894,16 +1894,14 @@ msgstr "" #: backends/platform/ios7/ios7_osys_events.cpp:322 #: backends/platform/ios7/ios7_osys_events.cpp:540 #: backends/platform/iphone/osys_events.cpp:313 -#, fuzzy msgid "Touchpad mode enabled." -msgstr "~O~vergangar aktivert" +msgstr "Touchpadmodus påslått." #: backends/platform/ios7/ios7_osys_events.cpp:324 #: backends/platform/ios7/ios7_osys_events.cpp:542 #: backends/platform/iphone/osys_events.cpp:315 -#, fuzzy msgid "Touchpad mode disabled." -msgstr "Deaktivert GFX" +msgstr "Touchpadmodus avslått." #: backends/platform/maemo/maemo.cpp:208 msgid "Click Mode" @@ -1978,16 +1976,15 @@ msgstr "Sone" #: backends/platform/wince/CEActionsPocket.cpp:54 #: backends/platform/wince/CEActionsSmartphone.cpp:48 msgid "Multi Function" -msgstr "" +msgstr "Multifunksjon" #: backends/platform/symbian/src/SymbianActions.cpp:48 msgid "Swap character" -msgstr "" +msgstr "Bytt karakter" #: backends/platform/symbian/src/SymbianActions.cpp:49 -#, fuzzy msgid "Skip text" -msgstr "Hopp over tekstlinje" +msgstr "Hopp over tekst" #: backends/platform/symbian/src/SymbianActions.cpp:51 msgid "Fast mode" @@ -2010,14 +2007,12 @@ msgid "Key mapper" msgstr "Tastkopler" #: backends/platform/tizen/form.cpp:263 -#, fuzzy msgid "Right Click Once" -msgstr "Høgreklikk" +msgstr "Høgreklikk ein gong" #: backends/platform/tizen/form.cpp:271 -#, fuzzy msgid "Move Only" -msgstr "Tale" +msgstr "" #: backends/platform/tizen/form.cpp:294 msgid "Escape Key" @@ -2032,30 +2027,28 @@ msgid "Show Keypad" msgstr "Syn taltastatur" #: backends/platform/tizen/form.cpp:309 -#, fuzzy msgid "Control Mouse" -msgstr "Musklikk" +msgstr "" #: backends/platform/tizen/fs.cpp:259 msgid "[ Data ]" -msgstr "" +msgstr "[ Data ]" #: backends/platform/tizen/fs.cpp:263 msgid "[ Resources ]" -msgstr "" +msgstr "[ Ressursar ]" #: backends/platform/tizen/fs.cpp:267 msgid "[ SDCard ]" -msgstr "" +msgstr "[ SDKort ]" #: backends/platform/tizen/fs.cpp:271 msgid "[ Media ]" -msgstr "" +msgstr "[ Media ]" #: backends/platform/tizen/fs.cpp:275 -#, fuzzy msgid "[ Shared ]" -msgstr ", delt ressurs ikkje montert" +msgstr "[ Delt ]" #: backends/platform/wii/options.cpp:51 msgid "Video" @@ -2082,9 +2075,8 @@ msgid "Input" msgstr "Input" #: backends/platform/wii/options.cpp:74 -#, fuzzy msgid "GC Pad sensitivity:" -msgstr "Sensitivitet" +msgstr "GC Pad Sensitivitet" #: backends/platform/wii/options.cpp:80 msgid "GC Pad acceleration:" @@ -2119,9 +2111,8 @@ msgid "Server:" msgstr "Teinar:" #: backends/platform/wii/options.cpp:110 -#, fuzzy msgid "Share:" -msgstr ", delt ressurs ikkje montert" +msgstr "Delt:" #: backends/platform/wii/options.cpp:114 msgid "Username:" @@ -2164,9 +2155,8 @@ msgid "Network up" msgstr "Nettverket er oppe" #: backends/platform/wii/options.cpp:166 -#, fuzzy msgid ", error while mounting the share" -msgstr "Feil under montering av DVD" +msgstr ", feil under montering av delt ressurs" #: backends/platform/wii/options.cpp:168 msgid ", share not mounted" @@ -2177,19 +2167,17 @@ msgid "Network down" msgstr "Nettverket er nede" #: backends/platform/wii/options.cpp:178 -#, fuzzy msgid "Initializing network" -msgstr "Init nettverk" +msgstr "Initialiserar nettverk" #: backends/platform/wii/options.cpp:182 -#, fuzzy msgid "Timeout while initializing network" -msgstr "Initialiserer nettverk" +msgstr "" #: backends/platform/wii/options.cpp:186 -#, fuzzy, c-format +#, c-format msgid "Network not initialized (%d)" -msgstr "Init nettverk" +msgstr "Nettverk ikkje initialisert (%d)" #: backends/platform/wince/CEActionsPocket.cpp:46 msgid "Hide Toolbar" @@ -2325,9 +2313,8 @@ msgid "Use the original save/load screens, instead of the ScummVM ones" msgstr "" #: engines/agi/detection.cpp:157 -#, fuzzy msgid "Use an alternative palette" -msgstr "Nytt diskettversjonens åpning (Kun CD-versjon)" +msgstr "Nytt ein alternativ palett" #: engines/agi/detection.cpp:158 msgid "" @@ -2336,9 +2323,8 @@ msgid "" msgstr "" #: engines/agi/detection.cpp:167 -#, fuzzy msgid "Mouse support" -msgstr "Hopp over" +msgstr "Musstøtte" #: engines/agi/detection.cpp:168 msgid "" @@ -2395,13 +2381,12 @@ msgid "Cutscene file '%s' not found!" msgstr "" #: engines/cge/detection.cpp:105 engines/cge2/detection.cpp:101 -#, fuzzy msgid "Color Blind Mode" -msgstr "Klikkmodus" +msgstr "Fargeblindmodus" #: engines/cge/detection.cpp:106 engines/cge2/detection.cpp:102 msgid "Enable Color Blind Mode by default" -msgstr "" +msgstr "Slå på fargeblindmodus som standard" #: engines/drascula/saveload.cpp:47 msgid "" @@ -2413,11 +2398,17 @@ msgid "" "Press OK to convert them now, otherwise you will be asked again the next " "time you start the game.\n" msgstr "" +"ScummVM oppdaga at du har gamle spellagringar for Drascula som vi kan " +"konvertere.\n" +"Det gamle spellagringsformatet er ikkje lenger støtta, så du vil ikkje vere " +"i stand til å laste dei om du ikkje konverterar dei\n" +"\n" +"Trykk OK for å konvertere dei no, ellers kjem du til å verte spurt neste " +"gong du startar spelet.\n" #: engines/dreamweb/detection.cpp:57 -#, fuzzy msgid "Use bright palette mode" -msgstr "Øvre høgre gjenstand" +msgstr "Nytt lys palett-modus" #: engines/dreamweb/detection.cpp:58 msgid "Display graphics using the game's bright palette" @@ -2438,26 +2429,24 @@ msgid "Failed to delete file." msgstr "Klarte ikkje slette fil." #: engines/groovie/detection.cpp:312 -#, fuzzy msgid "Fast movie speed" -msgstr "Rask modus" +msgstr "Rask filmfart" #: engines/groovie/detection.cpp:313 msgid "Play movies at an increased speed" msgstr "Spel filmar med auka hastighet" #: engines/groovie/script.cpp:408 -#, fuzzy msgid "Failed to save game" -msgstr "Lagra spel:" +msgstr "Klarte ikkje lagre spel" #: engines/hopkins/detection.cpp:76 engines/hopkins/detection.cpp:86 msgid "Gore Mode" -msgstr "" +msgstr "Gørrmodus" #: engines/hopkins/detection.cpp:77 engines/hopkins/detection.cpp:87 msgid "Enable Gore Mode when available" -msgstr "" +msgstr "Slå på gørrmodus når tilgjengeleg" #. I18N: Studio audience adds an applause and cheering sounds whenever #. Malcolm makes a joke. @@ -2471,46 +2460,42 @@ msgstr "Aktiver studiopublikum" #. I18N: This option allows the user to skip text and cutscenes. #: engines/kyra/detection.cpp:73 -#, fuzzy msgid "Skip support" -msgstr "Hopp over" +msgstr "Hopp over-støtte" #: engines/kyra/detection.cpp:74 msgid "Allow text and cutscenes to be skipped" -msgstr "" +msgstr "Tillat å hoppe over tekst og cutscenes" #. I18N: Helium mode makes people sound like they've inhaled Helium. #: engines/kyra/detection.cpp:84 -#, fuzzy msgid "Helium mode" -msgstr "Grafikkmodus:" +msgstr "Heliummodus" #: engines/kyra/detection.cpp:85 -#, fuzzy msgid "Enable helium mode" -msgstr "Grafikkmodus:" +msgstr "Slå på heliummodus" #. I18N: When enabled, this option makes scrolling smoother when #. changing from one screen to another. #: engines/kyra/detection.cpp:99 msgid "Smooth scrolling" -msgstr "" +msgstr "Mjuk rulling" #: engines/kyra/detection.cpp:100 msgid "Enable smooth scrolling when walking" -msgstr "" +msgstr "Slå på mjuk rulling under gåing" #. I18N: When enabled, this option changes the cursor when it floats to the #. edge of the screen to a directional arrow. The player can then click to #. walk towards that direction. #: engines/kyra/detection.cpp:112 -#, fuzzy msgid "Floating cursors" -msgstr "Vanleg peikar" +msgstr "Flytande peikarar" #: engines/kyra/detection.cpp:113 msgid "Enable floating cursors" -msgstr "" +msgstr "Slå på flytande peikarar" #. I18N: HP stands for Hit Points #: engines/kyra/detection.cpp:127 @@ -2522,61 +2507,52 @@ msgid "Enable hit point bar graphs" msgstr "" #: engines/kyra/lol.cpp:478 -#, fuzzy msgid "Attack 1" -msgstr "byttast 7, 4, og 1 med" +msgstr "Åtak 1" #: engines/kyra/lol.cpp:479 msgid "Attack 2" -msgstr "" +msgstr "Åtak 2" #: engines/kyra/lol.cpp:480 -#, fuzzy msgid "Attack 3" -msgstr "9, 6, og 3, henhaldsvis." +msgstr "Åtak 3" #: engines/kyra/lol.cpp:481 msgid "Move Forward" -msgstr "" +msgstr "Beveg Framover" #: engines/kyra/lol.cpp:482 -#, fuzzy msgid "Move Back" -msgstr "Bakoversteg" +msgstr "Beveg Bakover" #: engines/kyra/lol.cpp:483 -#, fuzzy msgid "Slide Left" -msgstr "Venstre" +msgstr "Skli til Venstre" #: engines/kyra/lol.cpp:484 -#, fuzzy msgid "Slide Right" -msgstr "Høgre" +msgstr "Skli til Høyre" #: engines/kyra/lol.cpp:485 engines/pegasus/pegasus.cpp:2509 -#, fuzzy msgid "Turn Left" -msgstr "Slå på" +msgstr "Snu til Venstre" #: engines/kyra/lol.cpp:486 engines/pegasus/pegasus.cpp:2510 -#, fuzzy msgid "Turn Right" -msgstr "Slå på" +msgstr "Snu til Høyre" #: engines/kyra/lol.cpp:487 -#, fuzzy msgid "Rest" -msgstr "Gjenopprett" +msgstr "Kvil" #: engines/kyra/lol.cpp:488 msgid "Options" msgstr "Val" #: engines/kyra/lol.cpp:489 -#, fuzzy msgid "Choose Spell" -msgstr "Vel" +msgstr "" #: engines/kyra/sound_midi.cpp:477 msgid "" @@ -2627,9 +2603,8 @@ msgstr "~O~vergangar aktivert" #. I18N: Drop book page #: engines/mohawk/dialogs.cpp:95 -#, fuzzy msgid "~D~rop Page" -msgstr "Søkt i %d mappar ..." +msgstr "" #: engines/mohawk/dialogs.cpp:99 msgid "~S~how Map" @@ -2669,18 +2644,16 @@ msgstr "" "\n" #: engines/parallaction/saveload.cpp:197 -#, fuzzy msgid "Load file" -msgstr "Åpne spel:" +msgstr "Last fil:" #: engines/parallaction/saveload.cpp:204 msgid "Loading game..." msgstr "Lastar spel..." #: engines/parallaction/saveload.cpp:212 -#, fuzzy msgid "Save file" -msgstr "Lagra spel:" +msgstr "Lagra fil:" #: engines/parallaction/saveload.cpp:219 msgid "Saving game..." @@ -2717,9 +2690,8 @@ msgid "Up/Zoom In/Move Forward/Open Doors" msgstr "" #: engines/pegasus/pegasus.cpp:2508 -#, fuzzy msgid "Down/Zoom Out" -msgstr "Zoom ned" +msgstr "Ned/Zoom Ut" #: engines/pegasus/pegasus.cpp:2511 msgid "Display/Hide Inventory Tray" @@ -2730,9 +2702,8 @@ msgid "Display/Hide Biochip Tray" msgstr "" #: engines/pegasus/pegasus.cpp:2513 -#, fuzzy msgid "Action/Select" -msgstr "Vel ei handling, og klikk 'Kople'" +msgstr "Handling/Vel" #: engines/pegasus/pegasus.cpp:2514 msgid "Toggle Center Data Display" @@ -2748,12 +2719,11 @@ msgstr "Skjul/Vis pausemeny" #: engines/queen/detection.cpp:56 msgid "Alternative intro" -msgstr "" +msgstr "Alternativ intro" #: engines/queen/detection.cpp:57 -#, fuzzy msgid "Use an alternative game intro (CD version only)" -msgstr "Nytt diskettversjonens åpning (Kun CD-versjon)" +msgstr "Nytt alternativ spillåpning (Kun CD-versjon)" #: engines/sci/detection.cpp:374 msgid "Skip EGA dithering pass (full color backgrounds)" @@ -2765,11 +2735,11 @@ msgstr "" #: engines/sci/detection.cpp:384 msgid "Enable high resolution graphics" -msgstr "" +msgstr "Nytt høgoppløyseleg grafikk" #: engines/sci/detection.cpp:385 msgid "Enable high resolution graphics/content" -msgstr "" +msgstr "Nytt høgoppløyseleg grafikk/innhald" #: engines/sci/detection.cpp:394 msgid "Prefer digital sound effects" @@ -2788,6 +2758,8 @@ msgid "" "Use an IBM Music Feature card or a Yamaha FB-01 FM synth module for MIDI " "output" msgstr "" +"Nytt eit IBM Music Feature-kort eller ein Yamaha FB-01 FM synth modul for " +"MIDI avspeling" #: engines/sci/detection.cpp:425 msgid "Use CD audio" @@ -2818,7 +2790,7 @@ msgstr "Nytt det alternative settet med sølvpeikarar, istaden for dei gylne" #: engines/scumm/dialogs.cpp:176 #, c-format msgid "Insert Disk %c and Press Button to Continue." -msgstr "" +msgstr "Sett inn disk %c og trykk på knappen for å fortsette." #: engines/scumm/dialogs.cpp:177 #, c-format @@ -2838,33 +2810,29 @@ msgstr "Spelet er pausa. Trykk MELLOMROM for å fortsette." #. "Moechten Sie wirklich neu starten? (J/N)J" #. Will react to J as 'Yes' #: engines/scumm/dialogs.cpp:183 -#, fuzzy msgid "Are you sure you want to restart? (Y/N)Y" -msgstr "Er du sikker på at du vil starte på nytt (Y/N)?" +msgstr "Er du sikker på at du vil starte på nytt (J/N)J" #. I18N: you may specify 'Yes' symbol at the end of the line. See previous comment #: engines/scumm/dialogs.cpp:185 -#, fuzzy msgid "Are you sure you want to quit? (Y/N)Y" -msgstr "Er du sikker på at du vil avslutte (Y/N)?" +msgstr "Er du sikker på at du vil avslutte (J/N)J" #: engines/scumm/dialogs.cpp:190 msgid "Play" msgstr "Spel" #: engines/scumm/dialogs.cpp:194 -#, fuzzy msgid "Insert save/load game disk" -msgstr "Vil du åpne eller lagre spelet?" +msgstr "Sett in lagre/laste speldisk" #: engines/scumm/dialogs.cpp:195 msgid "You must enter a name" msgstr "Du må skrive eit namn" #: engines/scumm/dialogs.cpp:196 -#, fuzzy msgid "The game was NOT saved (disk full?)" -msgstr "Full speltittel:" +msgstr "Spelet vart IKKJE lagra (full disk?)" #: engines/scumm/dialogs.cpp:197 msgid "The game was NOT loaded" @@ -2920,9 +2888,8 @@ msgid "Speech & Subs" msgstr "Tekst & Tale" #: engines/scumm/dialogs.cpp:658 -#, fuzzy msgid "Select a Proficiency Level." -msgstr "Gå til forrige mappenivå" +msgstr "" #: engines/scumm/dialogs.cpp:660 msgid "Refer to your Loom(TM) manual for help." @@ -2930,7 +2897,7 @@ msgstr "Sjå i Loom(TM)-manualen for hjelp." #: engines/scumm/dialogs.cpp:664 msgid "Practice" -msgstr "" +msgstr "Øving" #: engines/scumm/dialogs.cpp:665 msgid "Expert" @@ -3063,9 +3030,8 @@ msgid " since they may cause crashes" msgstr " dei kan forårsake kræsj og" #: engines/scumm/help.cpp:111 -#, fuzzy msgid " or incorrect game behavior." -msgstr "Spel" +msgstr " eller feilaktig speloppførsel." #: engines/scumm/help.cpp:115 msgid "Spinning drafts on the keyboard:" @@ -3471,23 +3437,20 @@ msgid "Fly to lower right" msgstr "Fly til nedre høgre" #: engines/scumm/input.cpp:580 -#, fuzzy msgid "Snap scroll on" -msgstr "Bla liste ned" +msgstr "" #: engines/scumm/input.cpp:582 msgid "Snap scroll off" msgstr "" #: engines/scumm/input.cpp:595 -#, fuzzy msgid "Music volume: " msgstr "Musikkvolum:" #: engines/scumm/input.cpp:612 -#, fuzzy msgid "Subtitle speed: " -msgstr "Undertekstfart:" +msgstr "Subtitle speed: " #: engines/scumm/scumm.cpp:1832 #, c-format @@ -3497,32 +3460,34 @@ msgid "" msgstr "" #: engines/scumm/scumm.cpp:2644 -#, fuzzy msgid "" "Usually, Maniac Mansion would start now. But for that to work, the game " "files for Maniac Mansion have to be in the 'Maniac' directory inside the " "Tentacle game directory, and the game has to be added to ScummVM." msgstr "" -"Opprinneleg, skulle Maniac Mansion ha starta no. Men ScummVM støttar ikkje " -"det enno. For å spele Maniac Mansion, gå til 'Legg til spel' i ScummVM-" -"menyen og vel 'Maniac'-undermappa i 'Tentacle'-mappa." +"Opprinneleg, skulle Maniac Mansion ha starta no. Men for at det skal virke " +"må du ha datafilane til Maniac Mansion i «Maniac»-mappa inni «Tentacle»-" +"spelmappa, og spelet må vere lagt til i ScummVM." #: engines/scumm/players/player_v3m.cpp:129 msgid "" "Could not find the 'Loom' Macintosh executable to read the\n" "instruments from. Music will be disabled." msgstr "" +"Kunne ikkje finne Macintosh-binærfila «Loom» for å lese\n" +"instrumenter frå den. Musikk vert deaktivert." #: engines/scumm/players/player_v5m.cpp:107 msgid "" "Could not find the 'Monkey Island' Macintosh executable to read the\n" "instruments from. Music will be disabled." msgstr "" +"Kunne ikkje finne Macintosh-binærfila «Monkey Island» for å lese\n" +"instrumentar frå den. Musikk vert deaktivert." #: engines/sherlock/detection.cpp:71 -#, fuzzy msgid "Use original savegame dialog" -msgstr "Nytt opprinnelege skjermar for lagring/lasting" +msgstr "Nytt opprinnelege skjerm for lagring/lasting" #: engines/sherlock/detection.cpp:72 msgid "" @@ -3532,7 +3497,7 @@ msgstr "" #: engines/sherlock/detection.cpp:81 msgid "Pixellated scene transitions" -msgstr "" +msgstr "Pikselerte sceneovergangar" #: engines/sherlock/detection.cpp:82 msgid "When changing scenes, a randomized pixel transition is done" @@ -3550,7 +3515,7 @@ msgstr "" #: engines/sherlock/detection.cpp:101 msgid "Show character portraits" -msgstr "" +msgstr "Syn karakterportrettar" #: engines/sherlock/detection.cpp:102 msgid "Show portraits for the characters when conversing" @@ -3566,7 +3531,7 @@ msgstr "" #: engines/sherlock/detection.cpp:121 msgid "Transparent windows" -msgstr "" +msgstr "Gjennomsiktige vindauge" #: engines/sherlock/detection.cpp:122 msgid "Show windows with a partially transparent background" @@ -3597,21 +3562,21 @@ msgstr "Nytt diskettversjonens åpning (Kun CD-versjon)" #: engines/sword1/animation.cpp:524 #, c-format msgid "PSX stream cutscene '%s' cannot be played in paletted mode" -msgstr "" +msgstr "PSX strømme cutscene '%s' kan ikkje avspelast i pallettmodus" #: engines/sword1/animation.cpp:545 engines/sword2/animation.cpp:445 msgid "DXA cutscenes found but ScummVM has been built without zlib" -msgstr "" +msgstr "DXA cutscenar funne, men ScummVM vart bygd utan zlib" #: engines/sword1/animation.cpp:561 engines/sword2/animation.cpp:461 msgid "" "MPEG-2 cutscenes found but ScummVM has been built without MPEG-2 support" -msgstr "" +msgstr "MPEG-2 cutscenar funne, men ScummVM er bygd utan MPEG-2 støtte" #: engines/sword1/animation.cpp:568 engines/sword2/animation.cpp:470 -#, fuzzy, c-format +#, c-format msgid "Cutscene '%s' not found" -msgstr "Hopp over cutscene" +msgstr "Cutscene '%s' ikkje funne" #: engines/sword1/control.cpp:863 msgid "" @@ -3623,6 +3588,13 @@ msgid "" "Press OK to convert them now, otherwise you will be asked again the next " "time you start the game.\n" msgstr "" +"ScummVM oppdaga at du har gamle lagra speltilstandar for Broken Sword 1 som " +"vi kan konvertere.\n" +"Det gamle formatet for lagra speltilstandar støttast ikkje lengre, så du vil " +"ikkje kunne laste dei utan å konvertere dei.\n" +"\n" +"Trykk OK for å konvertere dei no, ellers vil du bli spurt igjen neste gong " +"du starter spelet.\n" #: engines/sword1/control.cpp:1232 #, c-format @@ -3630,6 +3602,9 @@ msgid "" "Target new save game already exists!\n" "Would you like to keep the old save game (%s) or the new one (%s)?\n" msgstr "" +"Målet for den lagrede speltilstanden finst!\n" +"Vil du ta vare på den gamle tilstanden (%s) eller den nye (%s)?\n" +" \n" #: engines/sword1/control.cpp:1235 msgid "Keep the old one" @@ -3646,16 +3621,15 @@ msgstr "Dette er slutten på Broken Sword 1-demoen" #: engines/sword2/animation.cpp:425 msgid "" "PSX cutscenes found but ScummVM has been built without RGB color support" -msgstr "" +msgstr "PSX cutscenar vart funne, men ScummVM er bygd utan RGB fargestøtte" #: engines/sword2/sword2.cpp:79 -#, fuzzy msgid "Show object labels" -msgstr "Objekt" +msgstr "Syn objektmerkelappar" #: engines/sword2/sword2.cpp:80 msgid "Show labels for objects on mouse hover" -msgstr "" +msgstr "Syn merkelappar for objekt når musa er over dei" #: engines/teenagent/resources.cpp:95 msgid "" @@ -3667,54 +3641,57 @@ msgid "" "The teenagent.dat file is compressed and zlib hasn't been included in this " "executable. Please decompress it" msgstr "" +"Fila teenagent.dat er komprimert og zlib er ikkje inkludert i binærfila, " +"vennlegs dekomprimer den." #: engines/wintermute/detection.cpp:58 msgid "Show FPS-counter" -msgstr "" +msgstr "Syn FPS-teller" #: engines/wintermute/detection.cpp:59 msgid "Show the current number of frames per second in the upper left corner" msgstr "" +"Vis det gjeldande antall bilete per sekund i øvre venstre hjørne av skjermen" #: engines/zvision/detection_tables.h:52 -#, fuzzy msgid "Use the original save/load screens instead of the ScummVM interface" -msgstr "Nytt opprinnelege skjermar for lagring/lasting" +msgstr "" +"Nytt opprinnelege skjermar for lagring/lasting istadenfor ScummVM " +"grensesnittet" #: engines/zvision/detection_tables.h:61 msgid "Double FPS" -msgstr "" +msgstr "Dobbel FPS" #: engines/zvision/detection_tables.h:62 msgid "Increase framerate from 30 to 60 FPS" -msgstr "" +msgstr "Auk bilderate frå 30 til 60 FPS" #: engines/zvision/detection_tables.h:71 -#, fuzzy msgid "Enable Venus" -msgstr "Grafikkmodus:" +msgstr "Slå på Venus" #: engines/zvision/detection_tables.h:72 -#, fuzzy msgid "Enable the Venus help system" -msgstr "Grafikkmodus:" +msgstr "Slå på Venus-hjelpesystemet" #: engines/zvision/detection_tables.h:81 msgid "Disable animation while turning" -msgstr "" +msgstr "Slå av animasjonar under snuing" #: engines/zvision/detection_tables.h:82 msgid "Disable animation while turning in panorama mode" -msgstr "" +msgstr "Slå av animasjonar under snuing i panoramamodus" #: engines/zvision/detection_tables.h:91 msgid "Use high resolution MPEG video" -msgstr "" +msgstr "Nytt høgoppløyseleg MPEG-video" #: engines/zvision/detection_tables.h:92 -#, fuzzy msgid "Use MPEG video from the DVD version, instead of lower resolution AVI" -msgstr "Nytt det alternative settet med sølvpeikarar, istaden for dei gylne" +msgstr "" +"Nytt MPEG video frå DVD-versjonen, framfor AVI-versjonen med lågare " +"oppløysning" #~ msgctxt "lowres" #~ msgid "Mass Add..." diff --git a/po/sv_SE.po b/po/sv_SE.po index 44cc5cd0de..82357b4dc2 100644 --- a/po/sv_SE.po +++ b/po/sv_SE.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.5.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-02-20 21:22+0000\n" -"PO-Revision-Date: 2016-02-12 12:07+0100\n" +"PO-Revision-Date: 2016-02-25 23:06+0100\n" "Last-Translator: Hampus Flink <hampus.flink@gmail.com>\n" "Language-Team: \n" "Language: Svenska\n" @@ -1086,7 +1086,7 @@ msgstr "# nästa" #: gui/predictivedialog.cpp:87 msgid "add" -msgstr "lägg till" +msgstr "Lägg till" #: gui/predictivedialog.cpp:92 gui/predictivedialog.cpp:164 msgid "Delete char" @@ -2046,7 +2046,7 @@ msgstr "[ Data ]" #: backends/platform/tizen/fs.cpp:263 msgid "[ Resources ]" -msgstr "[ Resurser ]" +msgstr "[ Resurser ]" #: backends/platform/tizen/fs.cpp:267 msgid "[ SDCard ]" @@ -2054,7 +2054,7 @@ msgstr "[ SD-kort ]" #: backends/platform/tizen/fs.cpp:271 msgid "[ Media ]" -msgstr "[ Media ]" +msgstr "[ Media ]" #: backends/platform/tizen/fs.cpp:275 msgid "[ Shared ]" @@ -3547,7 +3547,6 @@ msgstr "" "för att läsa instrumenten. Musiken kommer att avaktiveras." #: engines/sherlock/detection.cpp:71 -#, fuzzy msgid "Use original savegame dialog" msgstr "Använd originalskärmar för spara/ladda" @@ -3556,49 +3555,54 @@ msgid "" "Files button in-game shows original savegame dialog rather than the ScummVM " "menu" msgstr "" +"Filknappen i spelet visar originalskärmen för spara/ladda istället för " +"ScummVM-menyn" #: engines/sherlock/detection.cpp:81 msgid "Pixellated scene transitions" -msgstr "" +msgstr "Pixliga scenövergångar" #: engines/sherlock/detection.cpp:82 msgid "When changing scenes, a randomized pixel transition is done" -msgstr "" +msgstr "En slumpmässig pixelövergång visas vid scenbyten" #: engines/sherlock/detection.cpp:91 msgid "Don't show hotspots when moving mouse" -msgstr "" +msgstr "Visa inte aktiveringspunkter vid musrörelse" #: engines/sherlock/detection.cpp:92 msgid "" "Only show hotspot names after you actually click on a hotspot or action " "button" msgstr "" +"Visar endast namn för aktiveringspunkter när du klickar på en " +"aktiveringspunkt eller handlingsknapp" #: engines/sherlock/detection.cpp:101 -#, fuzzy msgid "Show character portraits" -msgstr "Byt karaktär" +msgstr "Visa karaktärsporträtt" #: engines/sherlock/detection.cpp:102 msgid "Show portraits for the characters when conversing" -msgstr "" +msgstr "Visa porträtt för karaktärerna under dialoger" #: engines/sherlock/detection.cpp:111 msgid "Slide dialogs into view" -msgstr "" +msgstr "Låt dialogrutor glida in" #: engines/sherlock/detection.cpp:112 msgid "Slide UI dialogs into view, rather than simply showing them immediately" msgstr "" +"Låter gränssnittets dialogrutor glida in i bilden istället för att bara visa " +"dem direkt" #: engines/sherlock/detection.cpp:121 msgid "Transparent windows" -msgstr "" +msgstr "Genomskinliga fönster" #: engines/sherlock/detection.cpp:122 msgid "Show windows with a partially transparent background" -msgstr "" +msgstr "Visar fönster med en delvis genomskinlig bakgrund" #: engines/sky/compact.cpp:130 msgid "" @@ -3680,7 +3684,7 @@ msgstr "Behåll den nya" #: engines/sword1/logic.cpp:1633 msgid "This is the end of the Broken Sword 1 Demo" -msgstr "Här slutar Broken Sword 1 demon" +msgstr "Här slutar Broken Sword 1-demon" #: engines/sword2/animation.cpp:425 msgid "" @@ -3706,7 +3710,7 @@ msgid "" "executable. Please decompress it" msgstr "" "Teenagent.dat-filen är komprimerad och zlib har inte inkluderats i det här " -"programmet. Var god dekomprimera den" +"programmet. Var god dekomprimera den" #: engines/wintermute/detection.cpp:58 msgid "Show FPS-counter" @@ -223,6 +223,8 @@ ifneq ($(BACKEND), iphone) ifneq ($(BACKEND), ios7) # Static libaries, used for the scummvm-static and iphone targets OSX_STATIC_LIBS := `$(SDLCONFIG) --static-libs` +# With sdl2-config we don't always get the OpenGL framework +OSX_STATIC_LIBS := -framework OpenGL endif endif @@ -341,7 +343,7 @@ osxsnap: bundle mkdir ScummVM-snapshot/doc/se cp $(srcdir)/doc/se/LasMig ./ScummVM-snapshot/doc/se/LasMig cp $(srcdir)/doc/se/Snabbstart ./ScummVM-snapshot/doc/se/Snabbstart - /Developer/Tools/SetFile -t ttro -c ttxt ./ScummVM-snapshot/* + $(XCODETOOLSPATH)/SetFile -t ttro -c ttxt ./ScummVM-snapshot/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/cz/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/da/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/de/* @@ -350,11 +352,11 @@ osxsnap: bundle xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/it/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/no-nb/* xattr -w "com.apple.TextEncoding" "utf-8;134217984" ./ScummVM-snapshot/doc/se/* - /Developer/Tools/CpMac -r $(bundle_name) ./ScummVM-snapshot/ + $(XCODETOOLSPATH)/CpMac -r $(bundle_name) ./ScummVM-snapshot/ cp $(srcdir)/dists/macosx/DS_Store ./ScummVM-snapshot/.DS_Store cp $(srcdir)/dists/macosx/background.jpg ./ScummVM-snapshot/background.jpg - /Developer/Tools/SetFile -a V ./ScummVM-snapshot/.DS_Store - /Developer/Tools/SetFile -a V ./ScummVM-snapshot/background.jpg + $(XCODETOOLSPATH)/SetFile -a V ./ScummVM-snapshot/.DS_Store + $(XCODETOOLSPATH)/SetFile -a V ./ScummVM-snapshot/background.jpg hdiutil create -ov -format UDZO -imagekey zlib-level=9 -fs HFS+ \ -srcfolder ScummVM-snapshot \ -volname "ScummVM" \ |