diff options
142 files changed, 4453 insertions, 4551 deletions
@@ -261,6 +261,8 @@ ScummVM Team ZVision: Adrian Astley + Filippos Karapetis + Anton Yarcev Backend Teams ------------- diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index 69aa9d8303..f9a2bc9813 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -2,183 +2,92 @@ # These must be incremented for each market upload ANDROID_VERSIONCODE = 6 -ANDROID_PLUGIN_VERSIONCODE = 6 -JAVA_FILES = \ - ScummVM.java \ - ScummVMEvents.java \ - ScummVMEventsHoneycomb.java \ - ScummVMApplication.java \ - ScummVMActivity.java \ - EditableSurfaceView.java \ - MouseHelper.java \ - Unpacker.java +ANDROID_TARGET_VERSION = 14 -JAVA_FILES_PLUGIN = \ - PluginProvider.java - -JAVA_FILES_GEN = \ - Manifest.java \ - R.java +NDK_BUILD = $(ANDROID_NDK)/ndk-build +SDK_ANDROID = $(ANDROID_SDK)/tools/android PATH_DIST = $(srcdir)/dists/android PATH_RESOURCES = $(PATH_DIST)/res PORT_DISTFILES = $(PATH_DIST)/README.Android +DIST_JAVA_SRC_DIR = $(srcdir)/backends/platform/android/org RESOURCES = \ - $(PATH_RESOURCES)/values/strings.xml \ - $(PATH_RESOURCES)/values/margins.xml \ - $(PATH_RESOURCES)/values-television/margins.xml \ - $(PATH_RESOURCES)/layout/main.xml \ - $(PATH_RESOURCES)/layout/splash.xml \ - $(PATH_RESOURCES)/drawable/gradient.xml \ - $(PATH_RESOURCES)/drawable/scummvm.png \ - $(PATH_RESOURCES)/drawable/scummvm_big.png \ - $(PATH_RESOURCES)/drawable-xhdpi/ouya_icon.png - -PLUGIN_RESOURCES = \ - $(PATH_RESOURCES)/values/strings.xml \ - $(PATH_RESOURCES)/drawable/scummvm.png - -# FIXME: find/mark plugin entry points and add all this back again: -#LDFLAGS += -Wl,--gc-sections -#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden - -AAPT = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/aapt -ADB = $(ANDROID_SDK)/platform-tools/adb -DX = $(ANDROID_SDK)/$(ANDROID_BTOOLS)/dx -APKBUILDER = java -Xmx128M -classpath $(ANDROID_SDK)/tools/lib/sdklib.jar com.android.sdklib.build.ApkBuilderMain -JAVAC ?= javac -JAVACFLAGS = -source 1.5 -target 1.5 - -ANDROID_JAR = $(ANDROID_SDK)/platforms/android-14/android.jar + $(PATH_BUILD_RES)/values/strings.xml \ + $(PATH_BUILD_RES)/values-television/margins.xml \ + $(PATH_BUILD_RES)/layout/main.xml \ + $(PATH_BUILD_RES)/layout/splash.xml \ + $(PATH_BUILD_RES)/drawable/gradient.xml \ + $(PATH_BUILD_RES)/drawable/scummvm.png \ + $(PATH_BUILD_RES)/drawable/scummvm_big.png \ + $(PATH_BUILD_RES)/drawable-xhdpi/ouya_icon.png + +DIST_ANDROID_MK = $(PATH_DIST)/jni/Android.mk +DIST_BUILD_XML = $(PATH_DIST)/custom_rules.xml PATH_BUILD = ./build.tmp PATH_BUILD_ASSETS = $(PATH_BUILD)/assets -PATH_BUILD_CLASSES_MAIN_TOP = $(PATH_BUILD)/classes.main -PATH_BUILD_CLASSES_PLUGIN_TOP = $(PATH_BUILD)/classes.plugin - -PATH_STAGE_PREFIX = build.stage -PATH_STAGE_MAIN = $(PATH_STAGE_PREFIX).main - -PATH_REL = org/scummvm/scummvm -PATH_SRC_TOP = $(srcdir)/backends/platform/android -PATH_SRC = $(PATH_SRC_TOP)/$(PATH_REL) - -PATH_GEN_TOP = $(PATH_BUILD)/java -PATH_GEN = $(PATH_GEN_TOP)/$(PATH_REL) -PATH_CLASSES_MAIN = $(PATH_BUILD_CLASSES_MAIN_TOP)/$(PATH_REL) -PATH_CLASSES_PLUGIN = $(PATH_BUILD_CLASSES_PLUGIN_TOP)/$(PATH_REL) +PATH_BUILD_RES = $(PATH_BUILD)/res +PATH_BUILD_LIBSCUMMVM = $(PATH_BUILD)/mylib/armeabi/libscummvm.so FILE_MANIFEST_SRC = $(srcdir)/dists/android/AndroidManifest.xml FILE_MANIFEST = $(PATH_BUILD)/AndroidManifest.xml -FILE_DEX = $(PATH_BUILD)/classes.dex -FILE_DEX_PLUGIN = $(PATH_BUILD)/plugins/classes.dex -FILE_RESOURCES = resources.ap_ -FILE_RESOURCES_MAIN = $(PATH_BUILD)/$(FILE_RESOURCES) - -SRC_GEN = $(addprefix $(PATH_GEN)/, $(JAVA_FILES_GEN)) - -CLASSES_MAIN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES:%.java=%.class)) -CLASSES_GEN = $(addprefix $(PATH_CLASSES_MAIN)/, $(JAVA_FILES_GEN:%.java=%.class)) -CLASSES_PLUGIN = $(addprefix $(PATH_CLASSES_PLUGIN)/, $(JAVA_FILES_PLUGIN:%.java=%.class)) -APK_MAIN = scummvm.apk +APK_MAIN = ScummVM-debug.apk +APK_MAIN_RELEASE = ScummVM-release-unsigned.apk APK_PLUGINS = $(patsubst plugins/lib%.so, scummvm-engine-%.apk, $(PLUGINS)) -$(FILE_MANIFEST): $(FILE_MANIFEST_SRC) +$(FILE_MANIFEST): $(FILE_MANIFEST_SRC) | $(PATH_BUILD) @$(MKDIR) -p $(@D) sed "s/@ANDROID_VERSIONCODE@/$(ANDROID_VERSIONCODE)/" < $< > $@ -$(SRC_GEN): $(FILE_MANIFEST) $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR) - @$(MKDIR) -p $(PATH_GEN_TOP) - $(AAPT) package -m -J $(PATH_GEN_TOP) -M $< -S $(PATH_RESOURCES) -I $(ANDROID_JAR) - -$(PATH_CLASSES_MAIN)/%.class: $(PATH_GEN)/%.java $(SRC_GEN) +$(PATH_BUILD)/res/%: $(PATH_DIST)/res/% | $(PATH_BUILD) @$(MKDIR) -p $(@D) - $(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $< + $(CP) $< $@ -$(PATH_CLASSES_MAIN)/%.class: $(PATH_SRC)/%.java $(SRC_GEN) +$(PATH_BUILD)/libs/%: $(PATH_DIST)/libs/% | $(PATH_BUILD) @$(MKDIR) -p $(@D) - $(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP):$(PATH_GEN_TOP) -d $(PATH_BUILD_CLASSES_MAIN_TOP) -bootclasspath $(ANDROID_JAR) $< + $(CP) $< $@ -$(PATH_CLASSES_PLUGIN)/%.class: $(PATH_SRC)/%.java - @$(MKDIR) -p $(@D) - $(JAVAC) $(JAVACFLAGS) -cp $(PATH_SRC_TOP) -d $(PATH_BUILD_CLASSES_PLUGIN_TOP) -bootclasspath $(ANDROID_JAR) $< +$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_SHADERS) $(DIST_BUILD_XML) | $(PATH_BUILD) + $(INSTALL) -d $(PATH_BUILD_ASSETS) + $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(PATH_BUILD_ASSETS)/ + $(INSTALL) -d $(PATH_BUILD)/jni + $(INSTALL) -c -m 644 $(DIST_ANDROID_MK) $(PATH_BUILD)/jni + $(INSTALL) -c -m 644 $(DIST_BUILD_XML) $(PATH_BUILD) -$(FILE_DEX): $(CLASSES_MAIN) $(CLASSES_GEN) - $(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_MAIN_TOP) +$(PATH_BUILD): $(DIST_ANDROID_MK) + $(MKDIR) -p $(PATH_BUILD) $(PATH_BUILD)/res + $(MKDIR) -p $(PATH_BUILD)/libs -$(FILE_DEX_PLUGIN): $(CLASSES_PLUGIN) - @$(MKDIR) -p $(@D) - $(DX) --dex --output=$@ $(PATH_BUILD_CLASSES_PLUGIN_TOP) +$(PATH_BUILD_LIBSCUMMVM): libscummvm.so | $(PATH_BUILD) + $(INSTALL) -c -m 644 libscummvm.so $(PATH_BUILD) + $(STRIP) $(PATH_BUILD)/libscummvm.so + cd $(PATH_BUILD); $(NDK_BUILD) -$(PATH_BUILD)/%/AndroidManifest.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml - @$(MKDIR) -p $(@D) - $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-manifest.xml $(ANDROID_PLUGIN_VERSIONCODE) $@ +$(PATH_BUILD_RES): $(RESOURCES) | $(PATH_BUILD) -$(PATH_STAGE_PREFIX).%/res/values/strings.xml: $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $(PATH_DIST)/plugin-manifest.xml - @$(MKDIR) -p $(@D) - $(PATH_DIST)/mkplugin.sh $(srcdir)/configure $* $(PATH_DIST)/plugin-strings.xml $(ANDROID_PLUGIN_VERSIONCODE) $@ +setupapk: $(FILE_MANIFEST) $(PATH_BUILD_RES) $(PATH_BUILD_ASSETS) $(PATH_BUILD_LIBSCUMMVM) | $(PATH_BUILD) + $(SDK_ANDROID) update project -p $(PATH_BUILD) -t android-$(ANDROID_TARGET_VERSION) -n ScummVM -$(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png: $(PATH_RESOURCES)/drawable/scummvm.png - @$(MKDIR) -p $(@D) - $(CP) $< $@ +$(APK_MAIN): setupapk | $(PATH_BUILD) + (cd $(PATH_BUILD); ant debug -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))") + $(CP) $(PATH_BUILD)/bin/ScummVM-debug.apk $@ -$(FILE_RESOURCES_MAIN): $(FILE_MANIFEST) $(RESOURCES) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) - $(INSTALL) -d $(PATH_BUILD_ASSETS) - $(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(PATH_BUILD_ASSETS)/ - work_dir=`pwd`; \ - for i in $(PATH_BUILD_ASSETS)/*.zip; do \ - echo "recompress $$i"; \ - cd "$$work_dir"; \ - $(RM) -rf $(PATH_BUILD_ASSETS)/tmp; \ - $(MKDIR) $(PATH_BUILD_ASSETS)/tmp; \ - unzip -q $$i -d $(PATH_BUILD_ASSETS)/tmp; \ - cd $(PATH_BUILD_ASSETS)/tmp; \ - zip -r ../`basename $$i` *; \ - done - @$(RM) -rf $(PATH_BUILD_ASSETS)/tmp - $(AAPT) package -f -0 zip -M $< -S $(PATH_RESOURCES) -A $(PATH_BUILD_ASSETS) -I $(ANDROID_JAR) -F $@ - -$(PATH_BUILD)/%/$(FILE_RESOURCES): $(PATH_BUILD)/%/AndroidManifest.xml $(PATH_STAGE_PREFIX).%/res/values/strings.xml $(PATH_STAGE_PREFIX).%/res/drawable/scummvm.png plugins/lib%.so $(ANDROID_JAR) - $(AAPT) package -f -M $< -S $(PATH_STAGE_PREFIX).$*/res -I $(ANDROID_JAR) -F $@ - -# Package installer won't delete old libscummvm.so on upgrade so -# replace it with a zero size file -$(APK_MAIN): $(EXECUTABLE) $(FILE_RESOURCES_MAIN) $(FILE_DEX) - $(INSTALL) -d $(PATH_STAGE_MAIN)/common/lib/armeabi - touch $(PATH_STAGE_MAIN)/common/lib/armeabi/libscummvm.so - $(INSTALL) -d $(PATH_STAGE_MAIN)/common/mylib/armeabi - $(INSTALL) -c -m 644 libscummvm.so $(PATH_STAGE_MAIN)/common/mylib/armeabi/ - $(STRIP) $(PATH_STAGE_MAIN)/common/mylib/armeabi/libscummvm.so - $(APKBUILDER) $@ -z $(FILE_RESOURCES_MAIN) -f $(FILE_DEX) -rf $(PATH_STAGE_MAIN)/common || { $(RM) $@; exit 1; } - -scummvm-engine-%.apk: plugins/lib%.so $(PATH_BUILD)/%/$(FILE_RESOURCES) $(FILE_DEX_PLUGIN) - $(INSTALL) -d $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/ - $(INSTALL) -c -m 644 plugins/lib$*.so $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/ - $(STRIP) $(PATH_STAGE_PREFIX).$*/apk/mylib/armeabi/lib$*.so - $(APKBUILDER) $@ -z $(PATH_BUILD)/$*/$(FILE_RESOURCES) -f $(FILE_DEX_PLUGIN) -rf $(PATH_STAGE_PREFIX).$*/apk || { $(RM) $@; exit 1; } - -all: $(APK_MAIN) $(APK_PLUGINS) +$(APK_MAIN_RELEASE): setupapk | $(PATH_BUILD) + (cd $(PATH_BUILD); ant release -Dsource.dir="$(realpath $(DIST_JAVA_SRC_DIR))") + $(CP) $(PATH_BUILD)/bin/ScummVM-release-unsigned.apk $@ + +all: $(APK_MAIN) clean: androidclean androidclean: - @$(RM) -rf $(PATH_BUILD) $(PATH_STAGE_PREFIX).* *.apk release - -# remove debugging signature -release/%.apk: %.apk - @$(MKDIR) -p $(@D) - @$(RM) $@ - $(CP) $< $@.tmp - zip -d $@.tmp META-INF/\* - jarsigner $(JARSIGNER_FLAGS) $@.tmp release - zipalign 4 $@.tmp $@ - $(RM) $@.tmp + @$(RM) -rf $(PATH_BUILD) *.apk release -androidrelease: $(addprefix release/, $(APK_MAIN) $(APK_PLUGINS)) +androidrelease: $(APK_MAIN_RELEASE) androidtestmain: $(APK_MAIN) $(ADB) install -r $(APK_MAIN) @@ -198,4 +107,4 @@ androiddistdebug: all sed 's/$$/\r/' < $$i > debug/`basename $$i`.txt; \ done -.PHONY: androidrelease androidtest +.PHONY: androidrelease androidtest $(PATH_BUILD_SRC) diff --git a/base/main.cpp b/base/main.cpp index b5de7d94d2..0f5ebc7845 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -523,22 +523,42 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { } #endif + // At this point, we usually return to the launcher. However, the + // game may have requested that one or more other games be "chained" + // to the current one, with optional save slots to start the games + // at. At the time of writing, this is used for the Maniac Mansion + // easter egg in Day of the Tentacle. + + Common::String chainedGame; + int saveSlot = -1; + + ChainedGamesMan.pop(chainedGame, saveSlot); + // Discard any command line options. It's unlikely that the user // wanted to apply them to *all* games ever launched. ConfMan.getDomain(Common::ConfigManager::kTransientDomain)->clear(); - // Clear the active config domain - ConfMan.setActiveDomain(""); + if (!chainedGame.empty()) { + if (saveSlot != -1) { + ConfMan.setInt("save_slot", saveSlot, Common::ConfigManager::kTransientDomain); + } + // Start the chained game + ConfMan.setActiveDomain(chainedGame); + } else { + // Clear the active config domain + ConfMan.setActiveDomain(""); + } PluginManager::instance().loadAllPlugins(); // only for cached manager - } else { GUI::displayErrorDialog(_("Could not find any engine capable of running the selected game")); } // reset the graphics to default setupGraphics(system); - launcherDialog(); + if (0 == ConfMan.getActiveDomain()) { + launcherDialog(); + } } PluginManager::instance().unloadAllPlugins(); PluginManager::destroy(); diff --git a/common/endian.h b/common/endian.h index 6d6563f802..a18e6f088b 100644 --- a/common/endian.h +++ b/common/endian.h @@ -49,6 +49,17 @@ # error No endianness defined #endif + +#define SWAP_CONSTANT_64(a) \ + ((uint64)((((a) >> 56) & 0x000000FF) | \ + (((a) >> 40) & 0x0000FF00) | \ + (((a) >> 24) & 0x00FF0000) | \ + (((a) >> 8) & 0xFF000000) | \ + (((a) & 0xFF000000) << 8) | \ + (((a) & 0x00FF0000) << 24) | \ + (((a) & 0x0000FF00) << 40) | \ + (((a) & 0x000000FF) << 56) )) + #define SWAP_CONSTANT_32(a) \ ((uint32)((((a) >> 24) & 0x00FF) | \ (((a) >> 8) & 0xFF00) | \ @@ -60,6 +71,62 @@ (((a) << 8) & 0xFF00) )) /** + * Swap the bytes in a 64 bit word in order to convert LE encoded data to BE + * and vice versa. + */ + +// machine/compiler-specific variants come first, fallback last + +// Test for GCC and if the target has the MIPS rel.2 instructions (we know the psp does) +// +// TODO: Fix this #if statement. It isn't changed from 32 bit. Is there a 64 bit swap instruction? +#if defined(__GNUC__) && (defined(__psp__) || defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2)) + + FORCEINLINE uint32 SWAP_BYTES_32(const uint32 a) { + if (__builtin_constant_p(a)) { + return SWAP_CONSTANT_32(a); + } else { + uint32 result; +# if defined(__psp__) + // use special allegrex instruction + __asm__ ("wsbw %0,%1" : "=r" (result) : "r" (a)); +# else + __asm__ ("wsbh %0,%1\n" + "rotr %0,%0,16" : "=r" (result) : "r" (a)); +# endif + return result; + } + } + +// Test for GCC >= 4.3.0 as this version added the bswap builtin +#elif GCC_ATLEAST(4, 3) + + FORCEINLINE uint64 SWAP_BYTES_64(uint64 a) { + return __builtin_bswap64(a); + } + +#elif defined(_MSC_VER) + + FORCEINLINE uint64 SWAP_BYTES_64(uint64 a) { + return _byteswap_uint64(a); + } + +// generic fallback +#else + + inline uint64 SWAP_BYTES_64(uint64 a) { + uint32 low = (uint32)a, high = (uint32)(a >> 32); + uint16 lowLow = (uint16)low, lowHigh = (uint16)(low >> 16), + highLow = (uint16)high, highHigh = (uint16)(high >> 16); + + return ((uint64)(((uint32)(uint16)((lowLow >> 8) | (lowLow << 8)) << 16) | + (uint16)((lowHigh >> 8) | (lowHigh << 8))) << 32) | + (((uint32)(uint16)((highLow >> 8) | (highLow << 8)) << 16) | + (uint16)((highHigh >> 8) | (highHigh << 8))) + } +#endif + +/** * Swap the bytes in a 32 bit word in order to convert LE encoded data to BE * and vice versa. */ @@ -173,6 +240,11 @@ return ((const Unaligned32 *)ptr)->val; } + FORCEINLINE uint64 READ_UINT64(const void *ptr) { + struct Unaligned64 { uint64 val; } __attribute__ ((__packed__, __may_alias__)); + return ((const Unaligned64 *)ptr)->val; + } + FORCEINLINE void WRITE_UINT16(void *ptr, uint16 value) { struct Unaligned16 { uint16 val; } __attribute__ ((__packed__, __may_alias__)); ((Unaligned16 *)ptr)->val = value; @@ -183,6 +255,11 @@ ((Unaligned32 *)ptr)->val = value; } + FORCEINLINE void WRITE_UINT64(void *ptr, uint64 value) { + struct Unaligned64 { uint64 val; } __attribute__((__packed__, __may_alias__)); + ((Unaligned64 *)ptr)->val = value; + } + #elif !defined(SCUMM_NEED_ALIGNMENT) FORCEINLINE uint16 READ_UINT16(const void *ptr) { @@ -193,6 +270,10 @@ return *(const uint32 *)(ptr); } + FORCEINLINE uint64 READ_UINT64(const void *ptr) { + return *(const uint64 *)(ptr); + } + FORCEINLINE void WRITE_UINT16(void *ptr, uint16 value) { *(uint16 *)(ptr) = value; } @@ -201,6 +282,10 @@ *(uint32 *)(ptr) = value; } + FORCEINLINE void WRITE_UINT64(void *ptr, uint64 value) { + *(uint64 *)(ptr) = value; + } + // use software fallback by loading each byte explicitely #else @@ -215,6 +300,10 @@ const uint8 *b = (const uint8 *)ptr; return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); } + inline uint64 READ_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[7] << 56) | (b[6] << 48) | (b[5] << 40) | (b[4] << 32) | (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); + } inline void WRITE_UINT16(void *ptr, uint16 value) { uint8 *b = (uint8 *)ptr; b[0] = (uint8)(value >> 0); @@ -227,6 +316,17 @@ b[2] = (uint8)(value >> 16); b[3] = (uint8)(value >> 24); } + inline void WRITE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + b[2] = (uint8)(value >> 16); + b[3] = (uint8)(value >> 24); + b[4] = (uint8)(value >> 32); + b[5] = (uint8)(value >> 40); + b[6] = (uint8)(value >> 48); + b[7] = (uint8)(value >> 56); + } # elif defined(SCUMM_BIG_ENDIAN) @@ -238,6 +338,10 @@ const uint8 *b = (const uint8 *)ptr; return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]); } + inline uint64 READ_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 56) | (b[1] << 48) | (b[2] << 40) | (b[3] << 32) | (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]); + } inline void WRITE_UINT16(void *ptr, uint16 value) { uint8 *b = (uint8 *)ptr; b[0] = (uint8)(value >> 8); @@ -250,6 +354,17 @@ b[2] = (uint8)(value >> 8); b[3] = (uint8)(value >> 0); } + inline void WRITE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 56); + b[1] = (uint8)(value >> 48); + b[2] = (uint8)(value >> 40); + b[3] = (uint8)(value >> 32); + b[4] = (uint8)(value >> 24); + b[5] = (uint8)(value >> 16); + b[6] = (uint8)(value >> 8); + b[7] = (uint8)(value >> 0); + } # endif @@ -261,25 +376,33 @@ #define READ_LE_UINT16(a) READ_UINT16(a) #define READ_LE_UINT32(a) READ_UINT32(a) + #define READ_LE_UINT64(a) READ_UINT64(a) #define WRITE_LE_UINT16(a, v) WRITE_UINT16(a, v) #define WRITE_LE_UINT32(a, v) WRITE_UINT32(a, v) + #define WRITE_LE_UINT64(a, v) WRITE_UINT64(a, v) + #define FROM_LE_64(a) ((uint64)(a)) #define FROM_LE_32(a) ((uint32)(a)) #define FROM_LE_16(a) ((uint16)(a)) + #define FROM_BE_64(a) SWAP_BYTES_64(a) #define FROM_BE_32(a) SWAP_BYTES_32(a) #define FROM_BE_16(a) SWAP_BYTES_16(a) + #define TO_LE_64(a) ((uint64)(a)) #define TO_LE_32(a) ((uint32)(a)) #define TO_LE_16(a) ((uint16)(a)) + #define TO_BE_64(a) SWAP_BYTES_64(a) #define TO_BE_32(a) SWAP_BYTES_32(a) #define TO_BE_16(a) SWAP_BYTES_16(a) + #define CONSTANT_LE_64(a) ((uint64)(a)) #define CONSTANT_LE_32(a) ((uint32)(a)) #define CONSTANT_LE_16(a) ((uint16)(a)) + #define CONSTANT_BE_64(a) SWAP_CONSTANT_64(a) #define CONSTANT_BE_32(a) SWAP_CONSTANT_32(a) #define CONSTANT_BE_16(a) SWAP_CONSTANT_16(a) @@ -294,6 +417,10 @@ const uint8 *b = (const uint8 *)ptr; return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]); } + inline uint64 READ_BE_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[0] << 56) | b[1] << 48) | b[2] << 40) | b[3] << 32) | b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]); + } inline void WRITE_BE_UINT16(void *ptr, uint16 value) { uint8 *b = (uint8 *)ptr; b[0] = (uint8)(value >> 8); @@ -306,6 +433,17 @@ b[2] = (uint8)(value >> 8); b[3] = (uint8)(value >> 0); } + inline void WRITE_BE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 56); + b[1] = (uint8)(value >> 48); + b[2] = (uint8)(value >> 40); + b[3] = (uint8)(value >> 32); + b[4] = (uint8)(value >> 24); + b[5] = (uint8)(value >> 16); + b[6] = (uint8)(value >> 8); + b[7] = (uint8)(value >> 0); + } # else inline uint16 READ_BE_UINT16(const void *ptr) { @@ -314,12 +452,18 @@ inline uint32 READ_BE_UINT32(const void *ptr) { return SWAP_BYTES_32(READ_UINT32(ptr)); } + inline uint32 READ_BE_UINT64(const void *ptr) { + return SWAP_BYTES_64(READ_UINT64(ptr)); + } inline void WRITE_BE_UINT16(void *ptr, uint16 value) { WRITE_UINT16(ptr, SWAP_BYTES_16(value)); } inline void WRITE_BE_UINT32(void *ptr, uint32 value) { WRITE_UINT32(ptr, SWAP_BYTES_32(value)); } + inline void WRITE_BE_UINT64(void *ptr, uint64 value) { + WRITE_UINT64(ptr, SWAP_BYTES_64(value)); + } # endif // if defined(SCUMM_NEED_ALIGNMENT) @@ -327,25 +471,33 @@ #define READ_BE_UINT16(a) READ_UINT16(a) #define READ_BE_UINT32(a) READ_UINT32(a) + #define READ_BE_UINT64(a) READ_UINT64(a) #define WRITE_BE_UINT16(a, v) WRITE_UINT16(a, v) #define WRITE_BE_UINT32(a, v) WRITE_UINT32(a, v) + #define WRITE_BE_UINT64(a, v) WRITE_UINT64(a, v) + #define FROM_LE_64(a) SWAP_BYTES_64(a) #define FROM_LE_32(a) SWAP_BYTES_32(a) #define FROM_LE_16(a) SWAP_BYTES_16(a) + #define FROM_BE_64(a) ((uint64)(a)) #define FROM_BE_32(a) ((uint32)(a)) #define FROM_BE_16(a) ((uint16)(a)) + #define TO_LE_64(a) SWAP_BYTES_64(a) #define TO_LE_32(a) SWAP_BYTES_32(a) #define TO_LE_16(a) SWAP_BYTES_16(a) + #define TO_BE_64(a) ((uint64)(a)) #define TO_BE_32(a) ((uint32)(a)) #define TO_BE_16(a) ((uint16)(a)) + #define CONSTANT_LE_64(a) SWAP_CONSTANT_64(a) #define CONSTANT_LE_32(a) SWAP_CONSTANT_32(a) #define CONSTANT_LE_16(a) SWAP_CONSTANT_16(a) + #define CONSTANT_BE_64(a) ((uint64)(a)) #define CONSTANT_BE_32(a) ((uint32)(a)) #define CONSTANT_BE_16(a) ((uint16)(a)) @@ -360,6 +512,10 @@ const uint8 *b = (const uint8 *)ptr; return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); } + inline uint64 READ_LE_UINT64(const void *ptr) { + const uint8 *b = (const uint8 *)ptr; + return (b[7] << 56) | (b[6] << 48) | (b[5] << 40) | (b[4] << 32) | (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]); + } inline void WRITE_LE_UINT16(void *ptr, uint16 value) { uint8 *b = (uint8 *)ptr; b[0] = (uint8)(value >> 0); @@ -372,6 +528,17 @@ b[2] = (uint8)(value >> 16); b[3] = (uint8)(value >> 24); } + inline void WRITE_LE_UINT64(void *ptr, uint64 value) { + uint8 *b = (uint8 *)ptr; + b[0] = (uint8)(value >> 0); + b[1] = (uint8)(value >> 8); + b[2] = (uint8)(value >> 16); + b[3] = (uint8)(value >> 24); + b[4] = (uint8)(value >> 32); + b[5] = (uint8)(value >> 40); + b[6] = (uint8)(value >> 48); + b[7] = (uint8)(value >> 56); + } # else inline uint16 READ_LE_UINT16(const void *ptr) { @@ -380,12 +547,18 @@ inline uint32 READ_LE_UINT32(const void *ptr) { return SWAP_BYTES_32(READ_UINT32(ptr)); } + inline uint64 READ_LE_UINT64(const void *ptr) { + return SWAP_BYTES_64(READ_UINT64(ptr)); + } inline void WRITE_LE_UINT16(void *ptr, uint16 value) { WRITE_UINT16(ptr, SWAP_BYTES_16(value)); } inline void WRITE_LE_UINT32(void *ptr, uint32 value) { WRITE_UINT32(ptr, SWAP_BYTES_32(value)); } + inline void WRITE_LE_UINT64(void *ptr, uint64 value) { + WRITE_UINT64(ptr, SWAP_BYTES_64(value)); + } # endif // if defined(SCUMM_NEED_ALIGNMENT) diff --git a/common/stream.h b/common/stream.h index 2702068cf3..251995001c 100644 --- a/common/stream.h +++ b/common/stream.h @@ -125,6 +125,11 @@ public: write(&value, 4); } + void writeUint64LE(uint64 value) { + value = TO_LE_64(value); + write(&value, 8); + } + void writeUint16BE(uint16 value) { value = TO_BE_16(value); write(&value, 2); @@ -135,6 +140,11 @@ public: write(&value, 4); } + void writeUint64BE(uint64 value) { + value = TO_BE_64(value); + write(&value, 8); + } + FORCEINLINE void writeSint16LE(int16 value) { writeUint16LE((uint16)value); } @@ -143,6 +153,10 @@ public: writeUint32LE((uint32)value); } + FORCEINLINE void writeSint64LE(int64 value) { + writeUint64LE((uint64)value); + } + FORCEINLINE void writeSint16BE(int16 value) { writeUint16BE((uint16)value); } @@ -151,6 +165,10 @@ public: writeUint32BE((uint32)value); } + FORCEINLINE void writeSint64BE(int64 value) { + writeUint64BE((uint64)value); + } + /** * Write the given string to the stream. * This writes str.size() characters, but no terminating zero byte. @@ -242,6 +260,19 @@ public: } /** + * Read an unsigned 64-bit word stored in little endian (LSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + uint64 readUint64LE() { + uint64 val; + read(&val, 8); + return FROM_LE_64(val); + } + + /** * Read an unsigned 16-bit word stored in big endian (MSB first) order * from the stream and return it. * Performs no error checking. The return value is undefined @@ -268,6 +299,19 @@ public: } /** + * Read an unsigned 64-bit word stored in big endian (MSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + uint64 readUint64BE() { + uint64 val; + read(&val, 8); + return FROM_BE_64(val); + } + + /** * Read a signed 16-bit word stored in little endian (LSB first) order * from the stream and return it. * Performs no error checking. The return value is undefined @@ -290,6 +334,17 @@ public: } /** + * Read a signed 64-bit word stored in little endian (LSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + FORCEINLINE int64 readSint64LE() { + return (int64)readUint64LE(); + } + + /** * Read a signed 16-bit word stored in big endian (MSB first) order * from the stream and return it. * Performs no error checking. The return value is undefined @@ -312,6 +367,17 @@ public: } /** + * Read a signed 64-bit word stored in big endian (MSB first) order + * from the stream and return it. + * Performs no error checking. The return value is undefined + * if a read error occurred (for which client code can check by + * calling err() and eos() ). + */ + FORCEINLINE int64 readSint64BE() { + return (int64)readUint64BE(); + } + + /** * Read the specified amount of data into a malloc'ed buffer * which then is wrapped into a MemoryReadStream. * The returned stream might contain less data than requested, diff --git a/devtools/credits.pl b/devtools/credits.pl index e919c0b8ba..7dc954a6a7 100755 --- a/devtools/credits.pl +++ b/devtools/credits.pl @@ -791,6 +791,8 @@ begin_credits("Credits"); begin_section("ZVision"); add_person("Adrian Astley", "RichieSams", ""); + add_person("Filippos Karapetis", "[md5]", ""); + add_person("Anton Yarcev", "Zidane", ""); end_section(); end_section(); diff --git a/dists/android/custom_rules.xml b/dists/android/custom_rules.xml new file mode 100644 index 0000000000..5ed81b7273 --- /dev/null +++ b/dists/android/custom_rules.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="custom_rules"> + <!-- Override the built-in aapt task, don't compress assets. --> + <target name="-package-resources" depends="-crunch"> + <!-- only package resources if *not* a library project --> + <do-only-if-not-library elseText="Library project: do not package resources..." > + <aapt executable="${aapt}" + command="package" + versioncode="${version.code}" + versionname="${version.name}" + debug="${build.is.packaging.debug}" + manifest="${out.manifest.abs.file}" + assets="${asset.absolute.dir}" + androidjar="${project.target.android.jar}" + apkfolder="${out.absolute.dir}" + nocrunch="${build.packaging.nocrunch}" + resourcefilename="${resource.package.file.name}" + resourcefilter="${aapt.resource.filter}" + libraryResFolderPathRefid="project.library.res.folder.path" + libraryPackagesRefid="project.library.packages" + libraryRFileRefid="project.library.bin.r.file.path" + previousBuildType="${build.last.target}" + buildType="${build.target}" + ignoreAssets="${aapt.ignore.assets}"> + <res path="${out.res.absolute.dir}" /> + <res path="${resource.absolute.dir}" /> + <nocompress /> <!-- forces no compression on any files in assets or res/raw --> + </aapt> + </do-only-if-not-library> + </target> +</project> diff --git a/dists/android/jni/Android.mk b/dists/android/jni/Android.mk new file mode 100644 index 0000000000..d5b398412f --- /dev/null +++ b/dists/android/jni/Android.mk @@ -0,0 +1,9 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +APP_ABI := armeabi +LOCAL_MODULE := scummvm +LOCAL_SRC_FILES := ../libscummvm.so + +include $(PREBUILT_SHARED_LIBRARY) diff --git a/dists/android/project.properties b/dists/android/project.properties new file mode 100644 index 0000000000..730e911f2f --- /dev/null +++ b/dists/android/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-14 diff --git a/dists/android/res/layout/main.xml b/dists/android/res/layout/main.xml index 31aa345cc7..8b0d515d62 100644 --- a/dists/android/res/layout/main.xml +++ b/dists/android/res/layout/main.xml @@ -9,8 +9,4 @@ android:keepScreenOn="true" android:focusable="true" android:focusableInTouchMode="true" - android:layout_marginTop="@dimen/verticalMargin" - android:layout_marginLeft="@dimen/horizontalMargin" - android:layout_marginBottom="@dimen/verticalMargin" - android:layout_marginRight="@dimen/horizontalMargin" /> diff --git a/engines/access/access.cpp b/engines/access/access.cpp index 67255ff5ea..7f59ae7ad6 100644 --- a/engines/access/access.cpp +++ b/engines/access/access.cpp @@ -222,12 +222,13 @@ void AccessEngine::speakText(ASurface *s, const Common::String &msg) { _events->clearEvents(); while (!shouldQuit()) { _sound->freeSounds(); - Resource *sound = _sound->loadSound(_narateFile + 99, _sndSubFile); - _sound->_soundTable.push_back(SoundEntry(sound, 1)); + _sound->loadSoundTable(0, _narateFile + 99, _sndSubFile); _sound->playSound(0); - _scripts->cmdFreeSound(); - _events->pollEvents(); + while(_sound->isSFXPlaying() && !shouldQuit()) + _events->pollEvents(); + + _scripts->cmdFreeSound(); if (_events->isKeyMousePressed()) { _sndSubFile += soundsLeft; @@ -256,9 +257,11 @@ void AccessEngine::speakText(ASurface *s, const Common::String &msg) { Resource *res = _sound->loadSound(_narateFile + 99, _sndSubFile); _sound->_soundTable.push_back(SoundEntry(res, 1)); _sound->playSound(0); - _scripts->cmdFreeSound(); - _events->pollEvents(); + while(_sound->isSFXPlaying() && !shouldQuit()) + _events->pollEvents(); + + _scripts->cmdFreeSound(); if (_events->_leftButton) { _events->debounceLeft(); diff --git a/engines/access/amazon/amazon_game.cpp b/engines/access/amazon/amazon_game.cpp index d6b759b308..4c9df7b8ff 100644 --- a/engines/access/amazon/amazon_game.cpp +++ b/engines/access/amazon/amazon_game.cpp @@ -528,8 +528,8 @@ void AmazonEngine::startChapter(int chapter) { _sound->freeSounds(); if (isCD()) { - _sound->_soundTable.push_back(SoundEntry(_sound->loadSound(115, 0), 1)); - _sound->_soundTable.push_back(SoundEntry(_sound->loadSound(115, 1), 1)); + _sound->loadSoundTable(0, 115, 0); + _sound->loadSoundTable(1, 115, 1); _sound->playSound(0); _sound->playSound(1); diff --git a/engines/access/amazon/amazon_logic.cpp b/engines/access/amazon/amazon_logic.cpp index 4a313e8880..6dffb85e5e 100644 --- a/engines/access/amazon/amazon_logic.cpp +++ b/engines/access/amazon/amazon_logic.cpp @@ -317,8 +317,8 @@ void Opening::doTitle() { _vm->_events->hideCursor(); if (!_vm->isDemo()) { - _vm->_sound->queueSound(0, 98, 30); - _vm->_sound->queueSound(1, 98, 8); + _vm->_sound->loadSoundTable(0, 98, 30); + _vm->_sound->loadSoundTable(1, 98, 8); _vm->_files->_setPaletteFlag = false; _vm->_files->loadScreen(0, 3); @@ -341,15 +341,12 @@ void Opening::doTitle() { _vm->_objectsTable[0] = new SpriteResource(_vm, spriteData); delete spriteData; - _vm->_sound->playSound(1); - _vm->_files->_setPaletteFlag = false; _vm->_files->loadScreen(0, 4); _vm->_sound->playSound(1); _vm->_buffer2.copyFrom(*_vm->_screen); _vm->_buffer1.copyFrom(*_vm->_screen); - _vm->_sound->playSound(1); const int COUNTDOWN[6] = { 2, 0x80, 1, 0x7d, 0, 0x87 }; for (_pCount = 0; _pCount < 3 && !_vm->shouldQuit(); ++_pCount) { @@ -493,12 +490,12 @@ void Opening::doTent() { _vm->_screen->setDisplayScan(); _vm->_screen->forceFadeOut(); _vm->_events->hideCursor(); - _vm->_sound->_soundTable.push_back(SoundEntry(_vm->_sound->loadSound(98, 39), 1)); - _vm->_sound->_soundTable.push_back(SoundEntry(_vm->_sound->loadSound(98, 14), 1)); - _vm->_sound->_soundTable.push_back(SoundEntry(_vm->_sound->loadSound(98, 15), 1)); - _vm->_sound->_soundTable.push_back(SoundEntry(_vm->_sound->loadSound(98, 16), 1)); - _vm->_sound->_soundTable.push_back(SoundEntry(_vm->_sound->loadSound(98, 31), 2)); - _vm->_sound->_soundTable.push_back(SoundEntry(_vm->_sound->loadSound(98, 52), 2)); + _vm->_sound->loadSoundTable(0, 98, 39); + _vm->_sound->loadSoundTable(1, 98, 14); + _vm->_sound->loadSoundTable(2, 98, 15); + _vm->_sound->loadSoundTable(3, 98, 16); + _vm->_sound->loadSoundTable(4, 98, 31, 2); + _vm->_sound->loadSoundTable(5, 98, 52, 2); _vm->_sound->playSound(0); _vm->_files->_setPaletteFlag = false; @@ -508,40 +505,47 @@ void Opening::doTent() { _vm->_screen->forceFadeIn(); _vm->_video->setVideo(_vm->_screen, Common::Point(126, 73), FileIdent(2, 1), 10); + int previousFrame = -1; while (!_vm->shouldQuit() && !_vm->_video->_videoEnd) { _vm->_video->playVideo(); - if ((_vm->_video->_videoFrame == 32) || (_vm->_video->_videoFrame == 34)) - _vm->_sound->playSound(4); - else if (_vm->_video->_videoFrame == 36) { - if (step != 2) { - _vm->_sound->playSound(2); - step = 2; - } - } else if (_vm->_video->_videoFrame == 18) { - if (step != 1) { - _vm->_midi->newMusic(73, 1); - _vm->_midi->newMusic(11, 0); - step = 1; - _vm->_sound->playSound(1); + if (previousFrame != _vm->_video->_videoFrame) { + previousFrame = _vm->_video->_videoFrame; + + if ((_vm->_video->_videoFrame == 32) || (_vm->_video->_videoFrame == 34)) + _vm->_sound->playSound(4); + else if (_vm->_video->_videoFrame == 36) { + if (step != 2) { + _vm->_sound->playSound(2); + step = 2; + } + } else if (_vm->_video->_videoFrame == 18) { + if (step != 1) { + _vm->_midi->newMusic(73, 1); + _vm->_midi->newMusic(11, 0); + step = 1; + _vm->_sound->playSound(1); + } } } - _vm->_events->pollEventsAndWait(); } _vm->_sound->playSound(5); _vm->_video->setVideo(_vm->_screen, Common::Point(43, 11), FileIdent(2, 2), 10); + previousFrame = -1; while (!_vm->shouldQuit() && !_vm->_video->_videoEnd) { _vm->_video->playVideo(); - if (_vm->_video->_videoFrame == 26) { - _vm->_sound->playSound(5); - } else if (_vm->_video->_videoFrame == 15) { - if (step !=3) { - _vm->_sound->playSound(3); - step = 3; + if (previousFrame != _vm->_video->_videoFrame) { + previousFrame = _vm->_video->_videoFrame; + if (_vm->_video->_videoFrame == 26) { + _vm->_sound->playSound(5); + } else if (_vm->_video->_videoFrame == 15) { + if (step !=3) { + _vm->_sound->playSound(3); + step = 3; + } } } - _vm->_events->pollEventsAndWait(); } diff --git a/engines/access/amazon/amazon_scripts.cpp b/engines/access/amazon/amazon_scripts.cpp index 0b2ddbc854..92acb3686d 100644 --- a/engines/access/amazon/amazon_scripts.cpp +++ b/engines/access/amazon/amazon_scripts.cpp @@ -95,7 +95,7 @@ void AmazonScripts::mWhile1() { _vm->_oldRects.clear(); _sequence = 2200; - _vm->_sound->queueSound(0, 14, 15); + _vm->_sound->loadSoundTable(0, 14, 15); do { cLoop(); diff --git a/engines/access/events.cpp b/engines/access/events.cpp index 0867b09765..6ffe67acfb 100644 --- a/engines/access/events.cpp +++ b/engines/access/events.cpp @@ -140,6 +140,8 @@ void EventsManager::pollEvents(bool skipTimers) { if (checkForNextTimerUpdate() && !skipTimers) nextTimer(); + _vm->_sound->checkSoundQueue(); + _wheelUp = _wheelDown = false; Common::Event event; diff --git a/engines/access/player.cpp b/engines/access/player.cpp index bcd553c6dc..e47daf532c 100644 --- a/engines/access/player.cpp +++ b/engines/access/player.cpp @@ -48,7 +48,6 @@ Player::Player(AccessEngine *vm) : Manager(vm), ImageEntry() { _playerSprites1 = nullptr; _manPal1 = nullptr; _frameNumber = 0; - _monData = nullptr; _rawTempL = 0; _rawXTemp = 0; _rawYTempL = 0; diff --git a/engines/access/player.h b/engines/access/player.h index 26caec681f..329cc15ed2 100644 --- a/engines/access/player.h +++ b/engines/access/player.h @@ -84,7 +84,6 @@ public: Direction _playerDirection; SpriteResource *_playerSprites; // Fields in original Player structure - byte *_monData; int _walkOffRight[PLAYER_DATA_COUNT]; int _walkOffLeft[PLAYER_DATA_COUNT]; int _walkOffUp[PLAYER_DATA_COUNT]; diff --git a/engines/access/scripts.cpp b/engines/access/scripts.cpp index 074c781352..1bd24894d7 100644 --- a/engines/access/scripts.cpp +++ b/engines/access/scripts.cpp @@ -794,7 +794,7 @@ void Scripts::cmdFreeSound() { charLoop(); _vm->_events->pollEvents(); - } while (!_vm->shouldQuit() && sound._playingSound); + } while (!_vm->shouldQuit() && sound.isSFXPlaying()); // Free the sounds while (sound._soundTable.size() > 0) { diff --git a/engines/access/sound.cpp b/engines/access/sound.cpp index a7d96dac9a..da267bdc4c 100644 --- a/engines/access/sound.cpp +++ b/engines/access/sound.cpp @@ -22,7 +22,6 @@ #include "common/algorithm.h" #include "audio/mixer.h" -#include "audio/audiostream.h" #include "audio/decoders/raw.h" #include "audio/decoders/wave.h" #include "access/access.h" @@ -31,8 +30,6 @@ namespace Access { SoundManager::SoundManager(AccessEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) { - _playingSound = false; - _isVoice = false; } SoundManager::~SoundManager() { @@ -44,11 +41,23 @@ void SoundManager::clearSounds() { for (uint i = 0; i < _soundTable.size(); ++i) delete _soundTable[i]._res; + _soundTable.clear(); + + if (_mixer->isSoundHandleActive(_effectsHandle)) + _mixer->stopHandle(_effectsHandle); + + if (_queue.size()) + _queue.remove_at(0); + + while (_queue.size()) { + delete _queue[0]; + _queue.remove_at(0); + } } -void SoundManager::queueSound(int idx, int fileNum, int subfile) { - debugC(1, kDebugSound, "queueSound(%d, %d, %d)", idx, fileNum, subfile); +void SoundManager::loadSoundTable(int idx, int fileNum, int subfile, int priority) { + debugC(1, kDebugSound, "loadSoundTable(%d, %d, %d)", idx, fileNum, subfile); Resource *soundResource; @@ -58,7 +67,7 @@ void SoundManager::queueSound(int idx, int fileNum, int subfile) { delete _soundTable[idx]._res; soundResource = _vm->_files->loadFile(fileNum, subfile); _soundTable[idx]._res = soundResource; - _soundTable[idx]._priority = 1; + _soundTable[idx]._priority = priority; } Resource *SoundManager::loadSound(int fileNum, int subfile) { @@ -77,19 +86,14 @@ void SoundManager::playSound(Resource *res, int priority) { debugC(1, kDebugSound, "playSound"); byte *resourceData = res->data(); - Audio::SoundHandle audioHandle; - Audio::RewindableAudioStream *audioStream = 0; assert(res->_size >= 32); - // HACK: Simulates queueing for the rare sounds played one after the other - while (_mixer->hasActiveChannelOfType(Audio::Mixer::kSFXSoundType)) - ; - if (READ_BE_UINT32(resourceData) == MKTAG('R','I','F','F')) { // CD version uses WAVE-files Common::SeekableReadStream *waveStream = new Common::MemoryReadStream(resourceData, res->_size, DisposeAfterUse::NO); - audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES); + Audio::RewindableAudioStream *audioStream = Audio::makeWAVStream(waveStream, DisposeAfterUse::YES); + _queue.push_back(audioStream); } else if (READ_BE_UINT32(resourceData) == MKTAG('S', 'T', 'E', 'V')) { // sound files have a fixed header of 32 bytes in total @@ -130,22 +134,34 @@ void SoundManager::playSound(Resource *res, int priority) { return; } - audioStream = Audio::makeRawStream(resourceData + 32, sampleSize, sampleRate, 0); + Audio::RewindableAudioStream *audioStream = Audio::makeRawStream(resourceData + 32, sampleSize, sampleRate, 0, DisposeAfterUse::NO); + _queue.push_back(audioStream); } else error("Unknown format"); - audioHandle = Audio::SoundHandle(); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &audioHandle, - audioStream, -1, _mixer->kMaxChannelVolume, 0, - DisposeAfterUse::NO); - - /* - Audio::QueuingAudioStream *audioStream = Audio::makeQueuingAudioStream(22050, false); - audioStream->queueBuffer(data, size, DisposeAfterUse::YES, 0); - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, audioStream, -1, - Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::YES, false); - */ + if (!_mixer->isSoundHandleActive(_effectsHandle)) + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_effectsHandle, + _queue[0], -1, _mixer->kMaxChannelVolume, 0, + DisposeAfterUse::YES); +} + +void SoundManager::checkSoundQueue() { + debugC(5, kDebugSound, "checkSoundQueue"); + + if (_queue.empty() || _mixer->isSoundHandleActive(_effectsHandle)) + return; + + _queue.remove_at(0); + + if (_queue.size()) + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_effectsHandle, + _queue[0], -1, _mixer->kMaxChannelVolume, 0, + DisposeAfterUse::YES); +} + +bool SoundManager::isSFXPlaying() { + return _mixer->isSoundHandleActive(_effectsHandle); } void SoundManager::loadSounds(Common::Array<RoomInfo::SoundIdent> &sounds) { diff --git a/engines/access/sound.h b/engines/access/sound.h index d0f4584fac..90f6656e26 100644 --- a/engines/access/sound.h +++ b/engines/access/sound.h @@ -24,6 +24,7 @@ #define ACCESS_SOUND_H #include "common/scummsys.h" +#include "audio/audiostream.h" #include "audio/mixer.h" #include "access/files.h" #include "audio/midiplayer.h" @@ -47,22 +48,23 @@ class SoundManager { private: AccessEngine *_vm; Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; + Audio::SoundHandle _effectsHandle; + Common::Array<Audio::RewindableAudioStream *> _queue; void clearSounds(); void playSound(Resource *res, int priority); public: Common::Array<SoundEntry> _soundTable; - bool _playingSound; - bool _isVoice; public: SoundManager(AccessEngine *vm, Audio::Mixer *mixer); ~SoundManager(); - void queueSound(int idx, int fileNum, int subfile); + void loadSoundTable(int idx, int fileNum, int subfile, int priority = 1); void playSound(int soundIndex); + void checkSoundQueue(); + bool isSFXPlaying(); Resource *loadSound(int fileNum, int subfile); void loadSounds(Common::Array<RoomInfo::SoundIdent> &sounds); diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index e7285d8112..3230b4e5d3 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -276,15 +276,13 @@ SaveStateList AgiMetaEngine::listSaves(const char *target) const { int AgiMetaEngine::getMaximumSaveSlot() const { return 999; } void AgiMetaEngine::removeSaveState(const char *target, int slot) const { - char fileName[MAXPATHLEN]; - sprintf(fileName, "%s.%03d", target, slot); + Common::String fileName = Common::String::format("%s.%03d", target, slot); g_system->getSavefileManager()->removeSavefile(fileName); } SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int slot) const { const uint32 AGIflag = MKTAG('A','G','I',':'); - char fileName[MAXPATHLEN]; - sprintf(fileName, "%s.%03d", target, slot); + Common::String fileName = Common::String::format("%s.%03d", target, slot); Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName); diff --git a/engines/agi/loader_v2.cpp b/engines/agi/loader_v2.cpp index 787eeaa0c7..693c53c2bf 100644 --- a/engines/agi/loader_v2.cpp +++ b/engines/agi/loader_v2.cpp @@ -135,13 +135,13 @@ int AgiLoader_v2::unloadResource(int t, int n) { */ uint8 *AgiLoader_v2::loadVolRes(struct AgiDir *agid) { uint8 *data = NULL; - char x[MAXPATHLEN], *path; + char x[6]; Common::File fp; unsigned int sig; + Common::String path; - sprintf(x, "vol.%i", agid->volume); - path = x; - debugC(3, kDebugLevelResources, "Vol res: path = %s", path); + path = Common::String::format("vol.%i", agid->volume); + debugC(3, kDebugLevelResources, "Vol res: path = %s", path.c_str()); if (agid->offset != _EMPTY && fp.open(path)) { debugC(3, kDebugLevelResources, "loading resource at offset %d", agid->offset); diff --git a/engines/agi/loader_v3.cpp b/engines/agi/loader_v3.cpp index fa135e5476..39759b4649 100644 --- a/engines/agi/loader_v3.cpp +++ b/engines/agi/loader_v3.cpp @@ -198,14 +198,13 @@ int AgiLoader_v3::unloadResource(int t, int n) { * NULL is returned if unsucsessful. */ uint8 *AgiLoader_v3::loadVolRes(AgiDir *agid) { - char x[MAXPATHLEN]; + char x[8]; uint8 *data = NULL, *compBuffer; Common::File fp; Common::String path; debugC(3, kDebugLevelResources, "(%p)", (void *)agid); - sprintf(x, "vol.%i", agid->volume); - path = Common::String(_vm->_game.name) + x; + path = Common::String::format("%svol.%i", _vm->_game.name, agid->volume); if (agid->offset != _EMPTY && fp.open(path)) { fp.seek(agid->offset, SEEK_SET); diff --git a/engines/drascula/detection.cpp b/engines/drascula/detection.cpp index 833363669d..a84bd11cb1 100644 --- a/engines/drascula/detection.cpp +++ b/engines/drascula/detection.cpp @@ -382,8 +382,7 @@ SaveStateList DrasculaMetaEngine::listSaves(const char *target) const { } SaveStateDescriptor DrasculaMetaEngine::querySaveMetaInfos(const char *target, int slot) const { - char fileName[MAXPATHLEN]; - sprintf(fileName, "%s.%03d", target, slot); + Common::String fileName = Common::String::format("%s.%03d", target, slot); Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName); diff --git a/engines/engine.cpp b/engines/engine.cpp index c63437f800..24008dd073 100644 --- a/engines/engine.cpp +++ b/engines/engine.cpp @@ -45,6 +45,7 @@ #include "common/taskbar.h" #include "common/textconsole.h" #include "common/translation.h" +#include "common/singleton.h" #include "backends/keymapper/keymapper.h" @@ -101,6 +102,36 @@ static void defaultErrorHandler(const char *msg) { } } +// Chained games manager + +ChainedGamesManager::ChainedGamesManager() { + clear(); +} + +void ChainedGamesManager::clear() { + _chainedGames.clear(); +} + +void ChainedGamesManager::push(const Common::String target, const int slot) { + Game game; + game.target = target; + game.slot = slot; + _chainedGames.push(game); +} + +bool ChainedGamesManager::pop(Common::String &target, int &slot) { + if (_chainedGames.empty()) { + return false; + } + Game game = _chainedGames.pop(); + target = game.target; + slot = game.slot; + return true; +} + +namespace Common { +DECLARE_SINGLETON(ChainedGamesManager); +} Engine::Engine(OSystem *syst) : _system(syst), diff --git a/engines/engine.h b/engines/engine.h index e325cc1ba2..d3415d584c 100644 --- a/engines/engine.h +++ b/engines/engine.h @@ -27,6 +27,8 @@ #include "common/str.h" #include "common/language.h" #include "common/platform.h" +#include "common/queue.h" +#include "common/singleton.h" class OSystem; @@ -334,6 +336,32 @@ protected: }; +// Chained games + +/** + * Singleton class which manages chained games. A chained game is one that + * starts automatically, optionally loading a saved game, instead of returning + * to the launcher. + */ +class ChainedGamesManager : public Common::Singleton<ChainedGamesManager> { +private: + struct Game { + Common::String target; + int slot; + }; + + Common::Queue<Game> _chainedGames; + +public: + ChainedGamesManager(); + void clear(); + void push(const Common::String target, const int slot = -1); + bool pop(Common::String &target, int &slot); +}; + +/** Convenience shortcut for accessing the chained games manager. */ +#define ChainedGamesMan ChainedGamesManager::instance() + // FIXME: HACK for MidiEmu & error() extern Engine *g_engine; diff --git a/engines/fullpipe/inventory.cpp b/engines/fullpipe/inventory.cpp index e79f9c54df..f9b507c50b 100644 --- a/engines/fullpipe/inventory.cpp +++ b/engines/fullpipe/inventory.cpp @@ -126,7 +126,7 @@ void Inventory2::removeItem2(Scene *sceneObj, int itemId, int x, int y, int prio int idx = getInventoryItemIndexById(itemId); if (idx >= 0) { - if (_inventoryItems[idx]->itemId >> 16) { + if (_inventoryItems[idx]->count) { removeItem(itemId, 1); Scene *sc = g_fp->accessScene(_sceneId); diff --git a/engines/fullpipe/mgm.cpp b/engines/fullpipe/mgm.cpp index aacfd5452a..1c8ca2a7b1 100644 --- a/engines/fullpipe/mgm.cpp +++ b/engines/fullpipe/mgm.cpp @@ -155,13 +155,14 @@ void MGM::rebuildTables(int objId) { if (!obj) return; - for (uint i = 0; i < obj->_staticsList.size(); i++) + for (uint i = 0; i < obj->_staticsList.size(); i++) { _items[idx]->statics.push_back((Statics *)obj->_staticsList[i]); + _items[idx]->subItems.push_back(new MGMSubItem); + } + for (uint i = 0; i < obj->_movements.size(); i++) _items[idx]->movements1.push_back((Movement *)obj->_movements[i]); - - _items[idx]->subItems.clear(); } int MGM::getItemIndexById(int objId) { diff --git a/engines/fullpipe/scenes/scene04.cpp b/engines/fullpipe/scenes/scene04.cpp index fd1ececdf2..4a87ae5b87 100644 --- a/engines/fullpipe/scenes/scene04.cpp +++ b/engines/fullpipe/scenes/scene04.cpp @@ -949,7 +949,7 @@ void sceneHandler04_springWobble() { if (g_vars->scene04_bottleWeight < newdelta) g_vars->scene04_springOffset--; - if ((oldDynIndex > g_vars->scene04_bottleWeight && newdelta > g_vars->scene04_bottleWeight) || newdelta <= g_vars->scene04_bottleWeight) { + if ((oldDynIndex <= g_vars->scene04_bottleWeight && newdelta > g_vars->scene04_bottleWeight) || newdelta <= g_vars->scene04_bottleWeight) { g_vars->scene04_springDelay++; if (g_vars->scene04_springOffset && g_vars->scene04_springDelay > 1) { @@ -960,6 +960,8 @@ void sceneHandler04_springWobble() { Common::Point point; + int oldpos = g_vars->scene04_spring->getCurrDimensions(point)->y - oldDynIndex; + if (g_vars->scene04_dynamicPhaseIndex) { if (!g_vars->scene04_spring->_movement) g_vars->scene04_spring->startAnim(MV_SPR_LOWER, 0, -1); @@ -969,8 +971,9 @@ void sceneHandler04_springWobble() { g_vars->scene04_spring->changeStatics2(ST_SPR_UP); } - if (g_vars->scene04_dynamicPhaseIndex != oldDynIndex) - sceneHandler04_bottleUpdateObjects(oldDynIndex - g_vars->scene04_dynamicPhaseIndex); + if (g_vars->scene04_dynamicPhaseIndex != oldDynIndex) { + sceneHandler04_bottleUpdateObjects(oldpos - (g_vars->scene04_spring->getCurrDimensions(point)->y - g_vars->scene04_dynamicPhaseIndex)); + } } void sceneHandler04_leaveScene() { diff --git a/engines/fullpipe/scenes/scene16.cpp b/engines/fullpipe/scenes/scene16.cpp index e9d3a37efd..df005950d2 100644 --- a/engines/fullpipe/scenes/scene16.cpp +++ b/engines/fullpipe/scenes/scene16.cpp @@ -182,7 +182,7 @@ void sceneHandler16_fillMug() { mq = new MessageQueue(g_fp->_currentScene->getMessageQueueById(QU_SC16_BOYOUT), 0, 1); mq->replaceKeyCode(-1, g_vars->scene16_walkingBoy->_okeyCode); - if (!mq || mq->chain(g_vars->scene16_walkingBoy)) + if (mq->chain(g_vars->scene16_walkingBoy)) return; } else { if (!g_vars->scene16_walkingGirl) diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp index 880c2eb0df..de3e1ea728 100644 --- a/engines/fullpipe/statics.cpp +++ b/engines/fullpipe/statics.cpp @@ -1048,8 +1048,11 @@ MessageQueue *StaticANIObject::changeStatics1(int msgNum) { if (_flags & 1) _messageQueueId = mq->_id; } else { - if (!queueMessageQueue(mq)) + if (!queueMessageQueue(mq)) { + delete mq; + return 0; + } g_fp->_globalMessageQueueList->addMessageQueue(mq); } @@ -1594,6 +1597,12 @@ Movement::Movement(Movement *src, int *oldIdxs, int newSize, StaticANIObject *an newSize = src->_dynamicPhases.size(); } + if (!newSize) { + warning("Movement::Movement: newSize = 0"); + + return; + } + _framePosOffsets = (Common::Point **)calloc(newSize, sizeof(Common::Point *)); for (int i = 0; i < newSize; i++) diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index d6bdef946b..32d1a58765 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -2069,6 +2069,20 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::DE_DEU, Common::kPlatformDOS, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - German DOS (German+English, 5 1/4" floppies) + // SCI interpreter version S.old.114 (executable), VERSION is "1.056" + {"lsl3", "", { + {"resource.map", 0, "2468da5d664bb6ca3df866074a05e43c", 8910}, + {"resource.001", 0, "3827a9b17b926e12dcc336860f50612a", 163326}, + {"resource.002", 0, "3827a9b17b926e12dcc336860f50612a", 312436}, + {"resource.003", 0, "3827a9b17b926e12dcc336860f50612a", 347307}, + {"resource.004", 0, "3827a9b17b926e12dcc336860f50612a", 332369}, + {"resource.005", 0, "3827a9b17b926e12dcc336860f50612a", 347654}, + {"resource.006", 0, "3827a9b17b926e12dcc336860f50612a", 326011}, + {"resource.007", 0, "3827a9b17b926e12dcc336860f50612a", 309553}, + AD_LISTEND}, + Common::DE_DEU, Common::kPlatformDOS, ADGF_ADDENGLISH, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - French DOS (provided by richiefs in bug report #2670691, also includes english language) // Executable scanning reports "S.old.123" // SCI interpreter version 0.000.572 (just a guess) diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index c07dc925e0..527c8f0ae0 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -205,7 +205,7 @@ static kLanguage charToLanguage(const char c) { Common::String SciEngine::getSciLanguageString(const Common::String &str, kLanguage requestedLanguage, kLanguage *secondaryLanguage, uint16 *languageSplitter) const { kLanguage foundLanguage = K_LANG_NONE; - const byte *textPtr = (byte *)str.c_str(); + const byte *textPtr = (const byte *)str.c_str(); byte curChar = 0; byte curChar2 = 0; diff --git a/engines/sci/graphics/portrait.cpp b/engines/sci/graphics/portrait.cpp index 668de616fb..3311f47022 100644 --- a/engines/sci/graphics/portrait.cpp +++ b/engines/sci/graphics/portrait.cpp @@ -57,13 +57,39 @@ void Portrait::init() { // 4 bytes paletteSize (base 1) // -> 17 bytes // paletteSize bytes paletteData - // 14 bytes bitmap header - // -> 4 bytes unknown - // -> 2 bytes height - // -> 2 bytes width - // -> 6 bytes unknown - // height * width bitmap data - // another animation count times bitmap header and data + // + // bitmap-data follows, total of [animation count] + // 14 bytes bitmap header + // -> 4 bytes unknown + // -> 2 bytes height + // -> 2 bytes width + // -> 6 bytes unknown + // height * width bitmap data + // + // 4 bytes offset table size (may be larger than the actual known entries?!) + // 14 bytes all zeroes (dummy entry?!) + // + // 14 bytes for each entry + // -> 2 bytes displace X + // -> 2 bytes displace Y + // -> 2 bytes height (again) + // -> 2 bytes width (again) + // -> 6 bytes unknown (normally 01 00 00 00 00 00 for delta bitmaps, 00 00 00 00 00 00 for first bitmap) + // random data may be used as filler + // + // 4 bytes lip sync id table size (is [lip sync id count] * 4, should be 0x2E0 for all actors) + // 4 bytes per lip sync id + // -> 1 byte length of ID + // -> 3 bytes actual ID + // + // 4 bytes lip sync id data table size (seems to be the same for all actors, always 0x220 in size) + // 1 byte animation number or 0xFF as terminator + // 1 byte delay, if last byte was not terminator + // one array for every lip sync id + // + // 4 bytes appended, seem to be random + // 9E11120E for alex + // 9E9E9E9E for vizier int32 fileSize = 0; Common::SeekableReadStream *file = SearchMan.createReadStreamForMember("actors/" + _resourceName + ".bin"); @@ -202,8 +228,9 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint if (raveResource->size < 4000) { memcpy(debugPrint, raveResource->data, raveResource->size); debugPrint[raveResource->size] = 0; // set terminating NUL + debug("kPortrait: using actor %s", _resourceName.c_str()); debug("kPortrait (noun %d, verb %d, cond %d, seq %d)", noun, verb, cond, seq); - debug("kPortrait: %s", debugPrint); + debug("kPortrait: rave data is '%s'", debugPrint); } #endif @@ -273,6 +300,14 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint raveLipSyncData = NULL; } +#ifdef DEBUG_PORTRAIT + if (raveID & 0x0ff) { + debug("kPortrait: rave '%c%c' after %d ticks", raveID >> 8, raveID & 0x0ff, raveTicks); + } else if (raveID) { + debug("kPortrait: rave '%c' after %d ticks", raveID >> 8, raveTicks); + } +#endif + timerPosition += raveTicks; // Wait till syncTime passed, then show specific animation bitmap @@ -295,6 +330,8 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint timerPositionWithin = timerPosition; raveLipSyncTicks = *raveLipSyncData++; while ( (raveLipSyncData < _lipSyncDataOffsetTableEnd) && (raveLipSyncTicks != 0xFF) ) { + if (raveLipSyncTicks) + raveLipSyncTicks--; // 1 -> wait 0 ticks, 2 -> wait 1 tick, etc. timerPositionWithin += raveLipSyncTicks; do { @@ -308,6 +345,13 @@ void Portrait::doit(Common::Point position, uint16 resourceId, uint16 noun, uint } while ((curPosition != -1) && (curPosition < timerPositionWithin) && (!userAbort)); raveLipSyncBitmapNr = *raveLipSyncData++; +#ifdef DEBUG_PORTRAIT + if (!raveLipSyncTicks) { + debug("kPortrait: showing frame %d", raveLipSyncBitmapNr); + } else { + debug("kPortrait: showing frame %d after %d ticks", raveLipSyncBitmapNr, raveLipSyncTicks); + } +#endif // bitmap nr within sync data is base 1, we need base 0 raveLipSyncBitmapNr--; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 8b0e76332f..2f95bf7751 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -238,6 +238,8 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _vectorPutPixelPtr = &GfxScreen::putPixelDisplayUpscaled; _putPixelPtr = &GfxScreen::putPixelDisplayUpscaled; break; + case GFX_SCREEN_UPSCALED_DISABLED: + break; } } diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 0c0f6be73b..e5673c1803 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -149,7 +149,7 @@ void ScummEngine::requestSave(int slot, const Common::String &name) { void ScummEngine::requestLoad(int slot) { _saveLoadSlot = slot; - _saveTemporaryState = false; + _saveTemporaryState = (slot == 100); _saveLoadFlag = 2; // 2 for load } diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp index d2f4133f74..6c81f17f2f 100644 --- a/engines/scumm/script_v6.cpp +++ b/engines/scumm/script_v6.cpp @@ -2597,7 +2597,11 @@ void ScummEngine_v6::o6_kernelSetFunctions() { fadeIn(args[1]); break; case 8: - startManiac(); + if (startManiac()) { + // This is so that the surprised exclamation happens + // after we return to the game again, not before. + o6_breakHere(); + } break; case 9: killAllScriptsExceptCurrent(); diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 6040344c2c..7d927b0cda 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -2597,9 +2597,52 @@ void ScummEngine_v90he::runBootscript() { } #endif -void ScummEngine::startManiac() { - debug(0, "stub startManiac()"); - displayMessage(0, "%s", _("Usually, Maniac Mansion would start now. But ScummVM doesn't do that yet. To play it, go to 'Add Game' in the ScummVM start menu and select the 'Maniac' directory inside the Tentacle game directory.")); +bool ScummEngine::startManiac() { + Common::String currentPath = ConfMan.get("path"); + Common::String maniacTarget; + + if (!ConfMan.hasKey("easter_egg")) { + // Look for a game with a game path pointing to a 'Maniac' directory + // as a subdirectory to the current game. + Common::ConfigManager::DomainMap::iterator iter = ConfMan.beginGameDomains(); + for (; iter != ConfMan.endGameDomains(); ++iter) { + Common::ConfigManager::Domain &dom = iter->_value; + Common::String path = dom.getVal("path"); + + if (path.hasPrefix(currentPath)) { + path.erase(0, currentPath.size() + 1); + if (path.equalsIgnoreCase("maniac")) { + maniacTarget = dom.getVal("gameid"); + break; + } + } + } + } else { + maniacTarget = ConfMan.get("easter_egg"); + } + + if (!maniacTarget.empty()) { + // Request a temporary save game to be made. + _saveLoadFlag = 1; + _saveLoadSlot = 100; + _saveTemporaryState = true; + + // Set up the chanined games to Maniac Mansion, and then back + // to the current game again with that save slot. + ChainedGamesMan.push(maniacTarget); + ChainedGamesMan.push(ConfMan.getActiveDomainName(), 100); + + // Force a return to the launcher. This will start the first + // chained game. + Common::EventManager *eventMan = g_system->getEventManager(); + Common::Event event; + event.type = Common::EVENT_RTL; + eventMan->pushEvent(event); + return true; + } else { + displayMessage(0, "%s", _("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.")); + return false; + } } #pragma mark - diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 967909e505..30b4d61880 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -654,7 +654,7 @@ protected: int getScriptSlot(); void startScene(int room, Actor *a, int b); - void startManiac(); + bool startManiac(); public: void runScript(int script, bool freezeResistant, bool recursive, int *lvarptr, int cycle = 0); diff --git a/engines/sword25/kernel/outputpersistenceblock.cpp b/engines/sword25/kernel/outputpersistenceblock.cpp index 9003b5d58a..3e25fce5c7 100644 --- a/engines/sword25/kernel/outputpersistenceblock.cpp +++ b/engines/sword25/kernel/outputpersistenceblock.cpp @@ -41,6 +41,13 @@ OutputPersistenceBlock::OutputPersistenceBlock() { _data.reserve(INITIAL_BUFFER_SIZE); } +void OutputPersistenceBlock::write(const void *data, uint32 size) { + writeMarker(BLOCK_MARKER); + + write(size); + rawWrite(data, size); +} + void OutputPersistenceBlock::write(int32 value) { writeMarker(SINT_MARKER); value = TO_LE_32(value); diff --git a/engines/sword25/kernel/outputpersistenceblock.h b/engines/sword25/kernel/outputpersistenceblock.h index c7d8dfc6aa..bdbd20d3e0 100644 --- a/engines/sword25/kernel/outputpersistenceblock.h +++ b/engines/sword25/kernel/outputpersistenceblock.h @@ -41,6 +41,7 @@ class OutputPersistenceBlock : public PersistenceBlock { public: OutputPersistenceBlock(); + void write(const void *data, uint32 size); void write(int32 value); void write(uint32 value); void write(float value); diff --git a/engines/sword25/module.mk b/engines/sword25/module.mk index 234baec165..0842eb9aa8 100644 --- a/engines/sword25/module.mk +++ b/engines/sword25/module.mk @@ -82,9 +82,10 @@ MODULE_OBJS := \ util/lua/lvm.o \ util/lua/lzio.o \ util/lua/scummvm_file.o \ - util/pluto/pdep.o \ - util/pluto/pluto.o \ - util/pluto/plzio.o + util/double_serialization.o \ + util/lua_persistence_util.o \ + util/lua_persist.o \ + util/lua_unpersist.o # This module can be built as a plugin ifeq ($(ENABLE_SWORD25), DYNAMIC_PLUGIN) diff --git a/engines/sword25/script/luascript.cpp b/engines/sword25/script/luascript.cpp index f62a08005b..e93289596b 100644 --- a/engines/sword25/script/luascript.cpp +++ b/engines/sword25/script/luascript.cpp @@ -29,7 +29,7 @@ * */ -#include "common/array.h" +#include "common/memstream.h" #include "common/debug-channels.h" #include "sword25/sword25.h" @@ -43,7 +43,7 @@ #include "sword25/util/lua/lua.h" #include "sword25/util/lua/lualib.h" #include "sword25/util/lua/lauxlib.h" -#include "sword25/util/pluto/pluto.h" +#include "sword25/util/lua_persistence.h" namespace Sword25 { @@ -112,10 +112,6 @@ bool LuaScriptEngine::init() { // Place the error handler function in the Lua registry, and remember the index _pcallErrorhandlerRegistryIndex = luaL_ref(_state, LUA_REGISTRYINDEX); - // Initialize the Pluto-Persistence library - luaopen_pluto(_state); - lua_pop(_state, 1); - // Initialize debugging callback if (DebugMan.isDebugChannelEnabled(kDebugScript)) { int mask = 0; @@ -383,19 +379,8 @@ bool pushPermanentsTable(lua_State *L, PERMANENT_TABLE_TYPE tableType) { return true; } -} - -namespace { -int chunkwriter(lua_State *L, const void *p, size_t sz, void *ud) { - Common::Array<byte> & chunkData = *reinterpret_cast<Common::Array<byte> * >(ud); - const byte *buffer = reinterpret_cast<const byte *>(p); - while (sz--) - chunkData.push_back(*buffer++); - - return 1; -} -} +} // End of anonymous namespace bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters @@ -409,12 +394,12 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { pushPermanentsTable(_state, PTT_PERSIST); lua_getglobal(_state, "_G"); - // Lua persists and stores the data in a Common::Array - Common::Array<byte> chunkData; - pluto_persist(_state, chunkwriter, &chunkData); + // Lua persists and stores the data in a WriteStream + Common::MemoryWriteStreamDynamic writeStream; + Lua::persistLua(_state, &writeStream); // Persistenzdaten in den Writer schreiben. - writer.writeByteArray(chunkData); + writer.write(writeStream.getData(), writeStream.size()); // Die beiden Tabellen vom Stack nehmen. lua_pop(_state, 2); @@ -424,24 +409,6 @@ bool LuaScriptEngine::persist(OutputPersistenceBlock &writer) { namespace { -struct ChunkreaderData { - void *BufferPtr; - size_t Size; - bool BufferReturned; -}; - -const char *chunkreader(lua_State *L, void *ud, size_t *sz) { - ChunkreaderData &cd = *reinterpret_cast<ChunkreaderData *>(ud); - - if (!cd.BufferReturned) { - cd.BufferReturned = true; - *sz = cd.Size; - return reinterpret_cast<const char *>(cd.BufferPtr); - } else { - return 0; - } -} - void clearGlobalTable(lua_State *L, const char **exceptions) { // Iterate over all elements of the global table lua_pushvalue(L, LUA_GLOBALSINDEX); @@ -479,7 +446,8 @@ void clearGlobalTable(lua_State *L, const char **exceptions) { // Perform garbage collection, so that all removed elements are deleted lua_gc(L, LUA_GCCOLLECT, 0); } -} + +} // End of anonymous namespace bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // Empty the Lua stack. pluto_persist() xepects that the stack is empty except for its parameters @@ -512,14 +480,9 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // Persisted Lua data Common::Array<byte> chunkData; reader.readByteArray(chunkData); + Common::MemoryReadStream readStream(&chunkData[0], chunkData.size(), DisposeAfterUse::NO); - // Chunk-Reader initialisation. It is used with pluto_unpersist to restore read data - ChunkreaderData cd; - cd.BufferPtr = &chunkData[0]; - cd.Size = chunkData.size(); - cd.BufferReturned = false; - - pluto_unpersist(_state, chunkreader, &cd); + Lua::unpersistLua(_state, &readStream); // Permanents-Table is removed from stack lua_remove(_state, -2); @@ -527,7 +490,7 @@ bool LuaScriptEngine::unpersist(InputPersistenceBlock &reader) { // The read elements in the global table about lua_pushnil(_state); while (lua_next(_state, -2) != 0) { - // The referenec to the global table (_G) must not be overwritten, or ticks from Lua total + // The reference to the global table (_G) must not be overwritten, or ticks from Lua total bool isGlobalReference = lua_isstring(_state, -2) && strcmp(lua_tostring(_state, -2), "_G") == 0; if (!isGlobalReference) { lua_pushvalue(_state, -2); diff --git a/engines/sword25/util/double_serialization.cpp b/engines/sword25/util/double_serialization.cpp new file mode 100644 index 0000000000..48fd75cb33 --- /dev/null +++ b/engines/sword25/util/double_serialization.cpp @@ -0,0 +1,138 @@ +/* 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 "sword25/util/double_serialization.h" + +#include "common/scummsys.h" + + +namespace Util { + +SerializedDouble encodeDouble(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 31)); + + SerializedDouble returnValue; + returnValue.significandOne = significandOne; // SignificandOne + returnValue.signAndSignificandTwo = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + significandTwo; // SignificandTwo + returnValue.exponent = (int16)exponent; + return returnValue; +} + +double decodeDouble(SerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)value.exponent; + double expandedsignificandOne = (double)value.significandOne; + double expandedsignificandTwo = (double)(value.signAndSignificandTwo & 0x7FFFFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandTwo & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +uint64 encodeDouble_64(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the significand into the integer range + double shiftedsignificand = ldexp(abs(significand), 53); + + // Combine everything using the IEEE standard + uint64 uintsignificand = (uint64)shiftedsignificand; + return ((uint64)(value < 0 ? 1 : 0) << 63) | // Sign + ((uint64)(exponent + 1023) << 52) | // Exponent stored as an offset to 1023 + (uintsignificand & 0x000FFFFFFFFFFFFF); // significand with MSB inferred +} + +double decodeDouble_64(uint64 value) { + // Expand the exponent and significand + int exponent = (int)((value >> 52) & 0x7FF) - 1023; + double expandedsignificand = (double)(0x10000000000000 /* Inferred MSB */ | (value & 0x000FFFFFFFFFFFFF)); + + // Deflate the significand + int temp; + double significand = frexp(expandedsignificand, &temp); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value & 0x8000000000000000) == 0x8000000000000000) ? -returnValue : returnValue; +} + +CompactSerializedDouble encodeDouble_Compact(double value) { + // Split the value into its significand and exponent + int exponent; + double significand = frexp(value, &exponent); + + // Shift the the first part of the significand into the integer range + double shiftedsignificandPart = ldexp(abs(significand), 32); + uint32 significandOne = uint32(floor(shiftedsignificandPart)); + + // Shift the remainder of the significand into the integer range + shiftedsignificandPart -= significandOne; + uint32 significandTwo = (uint32)(ldexp(shiftedsignificandPart, 21)); + + CompactSerializedDouble returnValue; + returnValue.signAndSignificandOne = ((uint32)(value < 0 ? 1 : 0) << 31) | // Sign + (significandOne & 0x7FFFFFFF); // significandOne with MSB inferred + // Exponent stored as an offset to 1023 + returnValue.exponentAndSignificandTwo = ((uint32)(exponent + 1023) << 21) | significandTwo; + + return returnValue; +} + +double decodeDouble_Compact(CompactSerializedDouble value) { + // Expand the exponent and the parts of the significand + int exponent = (int)(value.exponentAndSignificandTwo >> 21) - 1023; + double expandedsignificandOne = (double)(0x80000000 /* Inferred MSB */ | (value.signAndSignificandOne & 0x7FFFFFFF)); + double expandedsignificandTwo = (double)(value.exponentAndSignificandTwo & 0x1FFFFF); + + // Deflate the significand + double shiftedsignificand = ldexp(expandedsignificandTwo, -21); + double significand = ldexp(expandedsignificandOne + shiftedsignificand, -32); + + // Re-calculate the actual double + double returnValue = ldexp(significand, exponent); + + // Check the sign bit and return + return ((value.signAndSignificandOne & 0x80000000) == 0x80000000) ? -returnValue : returnValue; +} + +} // End of namespace Sword25 diff --git a/engines/sword25/util/double_serialization.h b/engines/sword25/util/double_serialization.h new file mode 100644 index 0000000000..e90338c369 --- /dev/null +++ b/engines/sword25/util/double_serialization.h @@ -0,0 +1,95 @@ +/* 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 DOUBLE_SERIALIZATION_H +#define DOUBLE_SERIALIZATION_H + +#include "common/types.h" + + +namespace Util { + +struct SerializedDouble { + uint32 significandOne; + uint32 signAndSignificandTwo; + int16 exponent; +}; + +struct CompactSerializedDouble { + uint32 signAndSignificandOne; + uint32 exponentAndSignificandTwo; +}; + +/** + * Encodes a double as two uint32 and a one int16 + * + * Supports denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +SerializedDouble encodeDouble(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble(SerializedDouble value); + +/** + * Encodes a double as a uint64 + * + * Does NOT support denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +uint64 encodeDouble_64(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble_64(uint64 value); + +/** + * Encodes a double as two uint32 + * + * Does NOT support denormalized numbers. Does NOT support NaN, or Inf + * + * @param value The value to encode + * @return The encoded value + */ +CompactSerializedDouble encodeDouble_Compact(double value); +/** + * Decodes a previously encoded double + * + * @param value The value to decode + * @return The decoded value + */ +double decodeDouble_Compact(CompactSerializedDouble value); + +} // End of namespace Sword25 + +#endif diff --git a/engines/sword25/util/lua_persist.cpp b/engines/sword25/util/lua_persist.cpp new file mode 100644 index 0000000000..6fe88fe9a3 --- /dev/null +++ b/engines/sword25/util/lua_persist.cpp @@ -0,0 +1,804 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serialization.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +struct SerializationInfo { + lua_State *luaState; + Common::WriteStream *writeStream; + uint counter; +}; + +static void persist(SerializationInfo *info); + +static void persistBoolean(SerializationInfo *info); +static void persistNumber(SerializationInfo *info); +static void persistString(SerializationInfo *info); +static void persistTable(SerializationInfo *info); +static void persistFunction(SerializationInfo *info); +static void persistThread(SerializationInfo *info); +static void persistProto(SerializationInfo *info); +static void persistUpValue(SerializationInfo *info); +static void persistUserData(SerializationInfo *info); + + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream) { + SerializationInfo info; + info.luaState = luaState; + info.writeStream = writeStream; + info.counter = 1u; + + // The process starts with the lua stack as follows: + // >>>>> permTbl rootObj + // That's the table of permanents and the root object to be serialized + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 4); + assert(lua_gettop(luaState) == 2); + // And that the root isn't nil + assert(!lua_isnil(luaState, 2)); + + // Create a table to hold indexes of everything that's serialized + // This allows us to only serialize an object once + // Every other time, just reference the index + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl + + // Now we're going to make the table weakly keyed. This prevents the + // GC from visiting it and trying to mark things it doesn't want to + // mark in tables, e.g. upvalues. All objects in the table are + // a priori reachable, so it doesn't matter that we do this. + + // Create the metatable + lua_newtable(luaState); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_pushstring(luaState, "__mode"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" + + lua_pushstring(luaState, "k"); + // >>>>> permTbl rootObj indexTbl metaTbl "__mode" "k" + + lua_settable(luaState, 4); + // >>>>> permTbl rootObj indexTbl metaTbl + + lua_setmetatable(luaState, 3); + // >>>>> permTbl rootObj indexTbl + + // Swap the indexTable and the rootObj + lua_insert(luaState, 2); + // >>>>> permTbl indexTbl rootObj + + // Serialize the root recursively + persist(&info); + + // Return the stack back to the original state + lua_remove(luaState, 2); + // >>>>> permTbl rootObj +} + +static void persist(SerializationInfo *info) { + // The stack can potentially have many things on it + // The object we want to serialize is the item on the top of the stack + // >>>>> permTbl indexTbl rootObj ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // If the object has already been written, don't write it again + // Instead write the index of the object from the indexTbl + + // Check the indexTbl + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_rawget(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj ?index? + + // If the index isn't nil, the object has already been written + if (!lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + + // Retrieve the index from the stack + uint *index = (uint *)lua_touserdata(info->luaState, -1); + + // Write out the index + info->writeStream->writeUint32LE(*index); + + // Pop the index off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the index/nil off the stack + lua_pop(info->luaState, 1); + + // If the obj itself is nil, we represent it as an index of 0 + if (lua_isnil(info->luaState, -1)) { + // Write out a flag that indicates that it's an index + info->writeStream->writeByte(0); + // Write out the index + info->writeStream->writeUint32LE(0); + + return; + } + + // Write out a flag that indicates that this is a real object + info->writeStream->writeByte(1); + + // Add the object to the indexTbl + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + uint *ref = (uint *)lua_newuserdata(info->luaState, sizeof(uint)); + *ref = ++(info->counter); + // >>>>> permTbl indexTbl rootObj ...... obj obj index + + lua_rawset(info->luaState, 2); + // >>>>> permTbl indexTbl rootObj ...... obj + + + // Write out the index + info->writeStream->writeUint32LE(info->counter); + + + // Objects that are in the permanents table are serialized in a special way + + lua_pushvalue(info->luaState, -1); + // >>>>> permTbl indexTbl rootObj ...... obj obj + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl rootObj ...... obj obj ?permKey? + + if (!lua_isnil(info->luaState, -1)) { + // Write out the type + info->writeStream->writeSint32LE(PERMANENT_TYPE); + + // Serialize the key + persist(info); + + // Pop the key off the stack + lua_pop(info->luaState, 1); + + return; + } + + // Pop the nil off the stack + lua_pop(info->luaState, 1); + + // Query the type of the object + int objType = lua_type(info->luaState, -1); + + // Write it out + info->writeStream->writeSint32LE(objType); + + // Serialize the object by its type + + switch (objType) { + case LUA_TBOOLEAN: + persistBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + persistNumber(info); + break; + case LUA_TSTRING: + persistString(info); + break; + case LUA_TTABLE: + persistTable(info); + break; + case LUA_TFUNCTION: + persistFunction(info); + break; + case LUA_TTHREAD: + persistThread(info); + break; + case LUA_TPROTO: + persistProto(info); + break; + case LUA_TUPVAL: + persistUpValue(info); + break; + case LUA_TUSERDATA: + persistUserData(info); + break; + default: + assert(0); + } +} + +static void persistBoolean(SerializationInfo *info) { + int value = lua_toboolean(info->luaState, -1); + + info->writeStream->writeSint32LE(value); +} + +static void persistNumber(SerializationInfo *info) { + lua_Number value = lua_tonumber(info->luaState, -1); + + Util::SerializedDouble serializedValue(Util::encodeDouble(value)); + + info->writeStream->writeUint32LE(serializedValue.significandOne); + info->writeStream->writeUint32LE(serializedValue.signAndSignificandTwo); + info->writeStream->writeSint16LE(serializedValue.exponent); +} + +static void persistString(SerializationInfo *info) { + // Hard cast to a uint32 to force size_t to an explicit size + // *Theoretically* this could truncate, but if we have a 4gb string, we have bigger problems + uint32 length = static_cast<uint32>(lua_strlen(info->luaState, -1)); + info->writeStream->writeUint32LE(length); + + const char *str = lua_tostring(info->luaState, -1); + info->writeStream->write(str, length); +} + +/* Choose whether to do a regular or special persistence based on an object's + * metatable. "default" is whether the object, if it doesn't have a __persist + * entry, is literally persistable or not. + * Pushes the unpersist closure and returns true if special persistence is + * used. */ +static bool serializeSpecialObject(SerializationInfo *info, bool defaction) { + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 4); + + // Check whether we should persist literally, or via the __persist metafunction + if (!lua_getmetatable(info->luaState, -1)) { + if (defaction) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Not reached + } + } + + // >>>>> permTbl indexTbl ...... obj metaTbl + lua_pushstring(info->luaState, "__persist"); + // >>>>> permTbl indexTbl rootObj ...... obj metaTbl "__persist" + + lua_rawget(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj metaTbl ?__persist? + + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl nil + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + if (defaction) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + return false; + } else { + lua_pushstring(info->luaState, "Type not literally persistable by default"); + lua_error(info->luaState); + + return false; // Return false + } + + } else if (lua_isboolean(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... obj metaTbl bool + if (lua_toboolean(info->luaState, -1)) { + // Write out a flag declaring that the object isn't special and should be persisted normally + info->writeStream->writeSint32LE(0); + + // >>>>> permTbl indexTbl ...... obj metaTbl true */ + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return false; + } else { + lua_pushstring(info->luaState, "Metatable forbade persistence"); + lua_error(info->luaState); + + return false; // Not reached + } + } else if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist not nil, boolean, or function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl __persist + lua_pushvalue(info->luaState, -3); + // >>>>> permTbl indexTbl ...... obj metaTbl __persist obj + + // >>>>> permTbl indexTbl ...... obj metaTbl ?func? + + if (!lua_isfunction(info->luaState, -1)) { + lua_pushstring(info->luaState, "__persist function did not return a function"); + lua_error(info->luaState); + } + + // >>>>> permTbl indexTbl ...... obj metaTbl func + + // Write out a flag that the function exists + info->writeStream->writeSint32LE(1); + + // Serialize the function + persist(info); + + lua_pop(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj + + return true; +} + +static void persistTable(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... tbl + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 1)) { + return; + } + + // >>>>> permTbl indexTbl ...... tbl + + // First, serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl indexTbl ...... tbl metaTbl/nil */ + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl nil + + // Now, persist all k/v pairs + while (lua_next(info->luaState, -2)) { + // >>>>> permTbl indexTbl ...... tbl k v */ + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl k v k */ + + // Serialize the key + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k v */ + + // Serialize the value + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl k */ + } + + // >>>>> permTbl indexTbl ...... tbl + + // Terminate the list with a nil + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... tbl + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void persistFunction(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... func + Closure *cl = clvalue(getObject(info->luaState, -1)); + lua_checkstack(info->luaState, 2); + + if (cl->c.isC) { + /* It's a C function. For now, we aren't going to allow + * persistence of C closures, even if the "C proto" is + * already in the permanents table. */ + lua_pushstring(info->luaState, "Attempt to persist a C function"); + lua_error(info->luaState); + } else { + // It's a Lua closure + + // We don't really _NEED_ the number of upvals, but it'll simplify things a bit + info->writeStream->writeByte(cl->l.p->nups); + + // Serialize the prototype + pushProto(info->luaState, cl->l.p); + // >>>>> permTbl indexTbl ...... func proto */ + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // Serialize upvalue values (not the upvalue objects themselves) + for (byte i = 0; i < cl->l.p->nups; i++) { + // >>>>> permTbl indexTbl ...... func + pushUpValue(info->luaState, cl->l.upvals[i]); + // >>>>> permTbl indexTbl ...... func upval + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func + + // Serialize function environment + lua_getfenv(info->luaState, -1); + // >>>>> permTbl indexTbl ...... func fenv + + if (lua_equal(info->luaState, -1, LUA_GLOBALSINDEX)) { + // Function has the default fenv + + // >>>>> permTbl indexTbl ...... func _G + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + } + + // >>>>> permTbl indexTbl ...... func fenv/nil + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } +} + +static void persistThread(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... thread + lua_State *threadState = lua_tothread(info->luaState, -1); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, threadState->top - threadState->stack + 1); + + if (info->luaState == threadState) { + lua_pushstring(info->luaState, "Can't persist currently running thread"); + lua_error(info->luaState); + return; /* not reached */ + } + + // Persist the stack + + // We *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 stackSize = static_cast<uint32>(appendStackToStack_reverse(threadState, info->luaState)); + info->writeStream->writeUint32LE(stackSize); + + // >>>>> permTbl indexTbl ...... thread (reversed contents of thread stack) */ + for (; stackSize > 0; --stackSize) { + persist(info); + + lua_pop(info->luaState, 1); + } + + // >>>>> permTbl indexTbl ...... thread + + // Now, serialize the CallInfo stack + + // Again, we *could* have truncation here, but if we have more than 4 billion items on a stack, we have bigger problems + uint32 numFrames = static_cast<uint32>((threadState->ci - threadState->base_ci) + 1); + info->writeStream->writeUint32LE(numFrames); + + for (uint32 i = 0; i < numFrames; i++) { + CallInfo *ci = threadState->base_ci + i; + + // Same argument as above about truncation + uint32 stackBase = static_cast<uint32>(ci->base - threadState->stack); + uint32 stackFunc = static_cast<uint32>(ci->func - threadState->stack); + uint32 stackTop = static_cast<uint32>(ci->top - threadState->stack); + + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + info->writeStream->writeUint32LE(stackTop); + + info->writeStream->writeSint32LE(ci->nresults); + + uint32 savedpc = (ci != threadState->base_ci) ? static_cast<uint32>(ci->savedpc - ci_func(ci)->l.p->code) : 0u; + info->writeStream->writeUint32LE(savedpc); + } + + + // Serialize the state's other parameters, with the exception of upval stuff + + assert(threadState->nCcalls <= 1); + info->writeStream->writeByte(threadState->status); + + // Same argument as above about truncation + uint32 stackBase = static_cast<uint32>(threadState->base - threadState->stack); + uint32 stackFunc = static_cast<uint32>(threadState->top - threadState->stack); + info->writeStream->writeUint32LE(stackBase); + info->writeStream->writeUint32LE(stackFunc); + + // Same argument as above about truncation + uint32 stackOffset = static_cast<uint32>(threadState->errfunc); + info->writeStream->writeUint32LE(stackOffset); + + // Finally, record upvalues which need to be reopened + // See the comment above serializeUpVal() for why we do this + + UpVal *upVal; + + // >>>>> permTbl indexTbl ...... thread + for (GCObject *gcObject = threadState->openupval; gcObject != NULL; gcObject = upVal->next) { + upVal = gco2uv(gcObject); + + /* Make sure upvalue is really open */ + assert(upVal->v != &upVal->u.value); + + pushUpValue(info->luaState, upVal); + // >>>>> permTbl indexTbl ...... thread upVal + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + // Same argument as above about truncation + uint32 stackpos = static_cast<uint32>(upVal->v - threadState->stack); + info->writeStream->writeUint32LE(stackpos); + } + + // >>>>> permTbl indexTbl ...... thread + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... thread nil + + // Use nil as a terminator + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread +} + +static void persistProto(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... proto + Proto *proto = gco2p(getObject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Serialize constant refs */ + info->writeStream->writeSint32LE(proto->sizek); + + for (int i = 0; i < proto->sizek; ++i) { + pushObject(info->luaState, &proto->k[i]); + // >>>>> permTbl indexTbl ...... proto const + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize inner Proto refs + info->writeStream->writeSint32LE(proto->sizep); + + for (int i = 0; i < proto->sizep; ++i) + { + pushProto(info->luaState, proto->p[i]); + // >>>>> permTbl indexTbl ...... proto subProto */ + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + // >>>>> permTbl indexTbl ...... proto + + // Serialize the code + info->writeStream->writeSint32LE(proto->sizecode); + + uint32 len = static_cast<uint32>(sizeof(Instruction) * proto->sizecode); + info->writeStream->write(proto->code, len); + + + // Serialize upvalue names + info->writeStream->writeSint32LE(proto->sizeupvalues); + + for (int i = 0; i < proto->sizeupvalues; ++i) + { + pushString(info->luaState, proto->upvalues[i]); + // >>>>> permTbl indexTbl ...... proto str + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + + + // Serialize local variable infos + info->writeStream->writeSint32LE(proto->sizelocvars); + + for (int i = 0; i < proto->sizelocvars; ++i) { + pushString(info->luaState, proto->locvars[i].varname); + // >>>>> permTbl indexTbl ...... proto str + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + info->writeStream->writeSint32LE(proto->locvars[i].startpc); + info->writeStream->writeSint32LE(proto->locvars[i].endpc); + } + + + // Serialize source string + pushString(info->luaState, proto->source); + // >>>>> permTbl indexTbl ...... proto sourceStr + + persist(info); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Serialize line numbers + info->writeStream->writeSint32LE(proto->sizelineinfo); + + if (proto->sizelineinfo) { + uint32 len = static_cast<uint32>(sizeof(int) * proto->sizelineinfo); + info->writeStream->write(proto->lineinfo, len); + } + + // Serialize linedefined and lastlinedefined + info->writeStream->writeSint32LE(proto->linedefined); + info->writeStream->writeSint32LE(proto->lastlinedefined); + + + // Serialize misc values + info->writeStream->writeByte(proto->nups); + info->writeStream->writeByte(proto->numparams); + info->writeStream->writeByte(proto->is_vararg); + info->writeStream->writeByte(proto->maxstacksize); +} + +/* Upvalues are tricky. Here's why. + * + * A particular upvalue may be either "open", in which case its member v + * points into a thread's stack, or "closed" in which case it points to the + * upvalue itself. An upvalue is closed under any of the following conditions: + * -- The function that initially declared the variable "local" returns + * -- The thread in which the closure was created is garbage collected + * + * To make things wackier, just because a thread is reachable by Lua doesn't + * mean it's in our root set. We need to be able to treat an open upvalue + * from an unreachable thread as a closed upvalue. + * + * The solution: + * (a) For the purposes of serializing, don't indicate whether an upvalue is + * closed or not. + * (b) When unserializing, pretend that all upvalues are closed. + * (c) When serializing, persist all open upvalues referenced by a thread + * that is persisted, and tag each one with the corresponding stack position + * (d) When unserializing, "reopen" each of these upvalues as the thread is + * unserialized + */ +static void persistUpValue(SerializationInfo *info) { + // >>>>> permTbl indexTbl ...... upval + assert(ttype(getObject(info->luaState, -1)) == LUA_TUPVAL); + UpVal *upValue = gco2uv(getObject(info->luaState, -1)->value.gc); + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // We can't permit the upValue to linger around on the stack, as Lua + // will bail if its GC finds it. + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... + + pushObject(info->luaState, upValue->v); + // >>>>> permTbl indexTbl ...... obj + + persist(info); + // >>>>> permTbl indexTbl ...... obj +} + +static void persistUserData(SerializationInfo *info) { + // >>>>> permTbl rootObj ...... udata + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + // Test if the object needs special serialization + if (serializeSpecialObject(info, 0)) { + return; + } + + // Use literal persistence + + // Hard cast to a uint32 length + // This could lead to truncation, but if we have a 4gb block of data, we have bigger problems + uint32 length = static_cast<uint32>(uvalue(getObject(info->luaState, -1))->len); + info->writeStream->writeUint32LE(length); + + info->writeStream->write(lua_touserdata(info->luaState, -1), length); + + // Serialize the metatable (if any) + if (!lua_getmetatable(info->luaState, -1)) { + lua_pushnil(info->luaState); + } + + // >>>>> permTbl rootObj ...... udata metaTbl/nil + persist(info); + + lua_pop(info->luaState, 1); + /* perms reftbl ... udata */ +} + + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence.h b/engines/sword25/util/lua_persistence.h new file mode 100644 index 0000000000..53e3dee02e --- /dev/null +++ b/engines/sword25/util/lua_persistence.h @@ -0,0 +1,67 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +#ifndef LUA_PERSISTENCE_H +#define LUA_PERSISTENCE_H + +#include "sword25/util/lua/lua.h" + + +namespace Common { +class WriteStream; +class ReadStream; +} + + +namespace Lua { + +#define PERMANENT_TYPE 101 + +void persistLua(lua_State *luaState, Common::WriteStream *writeStream); +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_persistence_util.cpp b/engines/sword25/util/lua_persistence_util.cpp new file mode 100644 index 0000000000..958fb7ac3c --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.cpp @@ -0,0 +1,393 @@ +/* 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 distri8buted 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. + * + */ + +/** + * This code is heavily based on the pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence_util.h" + +#include "common/scummsys.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize) { + global_State *globalState = G(luaState); + + block = (*globalState->frealloc)(globalState->ud, block, osize, nsize); + globalState->totalbytes = (globalState->totalbytes - osize) + nsize; + + return block; +} + +void pushObject(lua_State *luaState, TValue *obj) { + setobj2s(luaState, luaState->top, obj); + + api_check(luaState, luaState->top < luaState->ci->top); + luaState->top++; +} + +void pushProto(lua_State *luaState, Proto *proto) { + TValue obj; + setptvalue(luaState, &obj, proto); + + pushObject(luaState, &obj); +} + +void pushUpValue(lua_State *luaState, UpVal *upval) { + TValue obj; + + obj.value.gc = cast(GCObject *, upval); + obj.tt = LUA_TUPVAL; + checkliveness(G(L), obj); + + pushObject(luaState, &obj); +} + +void pushString(lua_State *luaState, TString *str) { + TValue o; + setsvalue(luaState, &o, str); + + pushObject(luaState, &o); +} + +/* A simple reimplementation of the unfortunately static function luaA_index. + * Does not support the global table, registry, or upvalues. */ +StkId getObject(lua_State *luaState, int stackpos) { + if (stackpos > 0) { + lua_assert(luaState->base + stackpos - 1 < luaState->top); + return luaState->base + stackpos - 1; + } else { + lua_assert(L->top - stackpos >= L->base); + return luaState->top + stackpos; + } +} + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type) { + global_State *globalState = G(luaState); + + obj->gch.next = globalState->rootgc; + globalState->rootgc = obj; + obj->gch.marked = luaC_white(globalState); + obj->gch.tt = type; +} + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable) { + Closure *c = (Closure *)lua_malloc(luaState, sizeLclosure(numElements)); + lua_linkObjToGC(luaState, obj2gco(c), LUA_TFUNCTION); + + c->l.isC = 0; + c->l.env = elementTable; + c->l.nupvalues = cast_byte(numElements); + + while (numElements--) { + c->l.upvals[numElements] = NULL; + } + + return c; +} + +void pushClosure(lua_State *luaState, Closure *closure) { + TValue obj; + setclvalue(luaState, &obj, closure); + pushObject(luaState, &obj); +} + +Proto *createProto(lua_State *luaState) { + Proto *newProto = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_linkObjToGC(luaState, obj2gco(newProto), LUA_TPROTO); + + newProto->k = NULL; + newProto->sizek = 0; + newProto->p = NULL; + newProto->sizep = 0; + newProto->code = NULL; + newProto->sizecode = 0; + newProto->sizelineinfo = 0; + newProto->sizeupvalues = 0; + newProto->nups = 0; + newProto->upvalues = NULL; + newProto->numparams = 0; + newProto->is_vararg = 0; + newProto->maxstacksize = 0; + newProto->lineinfo = NULL; + newProto->sizelocvars = 0; + newProto->locvars = NULL; + newProto->linedefined = 0; + newProto->lastlinedefined = 0; + newProto->source = NULL; + + return newProto; +} + +TString *createString(lua_State *luaState, const char *str, size_t len) { + TString *res; + lua_pushlstring(luaState, str, len); + + res = rawtsvalue(luaState->top - 1); + lua_pop(luaState, 1); + + return res; +} + +Proto *makeFakeProto(lua_State *L, lu_byte nups) { + Proto *p = createProto(L); + + p->sizelineinfo = 1; + p->lineinfo = lua_newVector(L, 1, int); + p->lineinfo[0] = 1; + p->sizecode = 1; + p->code = lua_newVector(L, 1, Instruction); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->source = createString(L, "", 0); + p->maxstacksize = 2; + p->nups = nups; + p->sizek = 0; + p->sizep = 0; + + return p; +} + +UpVal *createUpValue(lua_State *luaState, int stackpos) { + UpVal *upValue = (UpVal *)lua_malloc(luaState, sizeof(UpVal)); + lua_linkObjToGC(luaState, (GCObject *)upValue, LUA_TUPVAL); + upValue->tt = LUA_TUPVAL; + upValue->v = &upValue->u.value; + upValue->u.l.prev = NULL; + upValue->u.l.next = NULL; + + const TValue *o2 = (TValue *)getObject(luaState, stackpos); + upValue->v->value = o2->value; + upValue->v->tt = o2->tt; + checkliveness(G(L), upValue->v); + + return upValue; +} + +void unboxUpValue(lua_State *luaState) { + // >>>>> ...... func + LClosure *lcl; + UpVal *uv; + + lcl = (LClosure *)clvalue(getObject(luaState, -1)); + uv = lcl->upvals[0]; + + lua_pop(luaState, 1); + // >>>>> ...... + + pushUpValue(luaState, uv); + // >>>>> ...... upValue +} + +size_t appendStackToStack_reverse(lua_State *from, lua_State *to) { + for (StkId id = from->top - 1; id >= from->stack; --id) { + setobj2s(to, to->top, id); + to->top++; + } + + return from->top - from->stack; +} + +void correctStack(lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + +void lua_reallocstack(lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + + lua_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack + newsize; + correctStack(L, oldstack); +} + +void lua_growstack(lua_State *L, int n) { + // Double size is enough? + if (n <= L->stacksize) { + lua_reallocstack(L, 2 * L->stacksize); + } else { + lua_reallocstack(L, L->stacksize + n); + } +} + +void lua_reallocCallInfo(lua_State *lauState, int newsize) { + CallInfo *oldci = lauState->base_ci; + lua_reallocvector(lauState, lauState->base_ci, lauState->size_ci, newsize, CallInfo); + + lauState->size_ci = newsize; + lauState->ci = (lauState->ci - oldci) + lauState->base_ci; + lauState->end_ci = lauState->base_ci + lauState->size_ci - 1; +} + +void GCUnlink(lua_State *luaState, GCObject *gco) { + GCObject *prevslot; + if (G(luaState)->rootgc == gco) { + G(luaState)->rootgc = G(luaState)->rootgc->gch.next; + return; + } + + prevslot = G(luaState)->rootgc; + while (prevslot->gch.next != gco) { + prevslot = prevslot->gch.next; + } + + prevslot->gch.next = prevslot->gch.next->gch.next; +} + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len) { + lua_pushlstring(luaState, str, len); + TString *luaStr = &(luaState->top - 1)->value.gc->ts; + + lua_pop(luaState, 1); + + return luaStr; +} + +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt) { + global_State *g = G(luaState); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + +Proto *lua_newproto(lua_State *luaState) { + Proto *f = (Proto *)lua_malloc(luaState, sizeof(Proto)); + lua_link(luaState, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + +UpVal *makeUpValue(lua_State *luaState, int stackPos) { + UpVal *uv = lua_new(luaState, UpVal); + lua_link(luaState, (GCObject *)uv, LUA_TUPVAL); + uv->tt = LUA_TUPVAL; + uv->v = &uv->u.value; + uv->u.l.prev = NULL; + uv->u.l.next = NULL; + + setobj(luaState, uv->v, getObject(luaState, stackPos)); + + return uv; +} + +void boxUpValue_start(lua_State *luaState) { + LClosure *closure; + closure = (LClosure *)lua_newLclosure(luaState, 1, hvalue(&luaState->l_gt)); + pushClosure(luaState, (Closure *)closure); + // >>>>> ...... func + closure->p = makeFakeProto(luaState, 1); + + // Temporarily initialize the upvalue to nil + lua_pushnil(luaState); + closure->upvals[0] = makeUpValue(luaState, -1); + lua_pop(luaState, 1); +} + +void boxUpValue_finish(lua_State *luaState) { + // >>>>> ...... func obj + LClosure *lcl = (LClosure *)clvalue(getObject(luaState, -2)); + + lcl->upvals[0]->u.value = *getObject(luaState, -1); + lua_pop(luaState, 1); + // >>>>> ...... func +} + +} // End of namespace Lua diff --git a/engines/sword25/util/lua_persistence_util.h b/engines/sword25/util/lua_persistence_util.h new file mode 100644 index 0000000000..4d0085e242 --- /dev/null +++ b/engines/sword25/util/lua_persistence_util.h @@ -0,0 +1,122 @@ +/* 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 distri8buted 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#ifndef LUA_PERISTENCE_UTIL_H +#define LUA_PERISTENCE_UTIL_H + + +struct lua_State; + +#include "lua/lobject.h" + +typedef TValue *StkId; + +namespace Lua { + +#define lua_malloc(luaState, nsize) lua_realloc(luaState, nullptr, 0, nsize) +#define lua_reallocv(luaState, block, on, n, e) lua_realloc(luaState, block, (on) * (e), (n) * (e)) +#define lua_reallocvector(luaState, vec, oldn, n, T) ((vec) = (T *)(lua_reallocv(luaState, vec, oldn, n, sizeof(T)))) +#define lua_newVector(luaState, num, T) ((T *)lua_reallocv(luaState, nullptr, 0, num, sizeof(T))) +#define lua_new(luaState,T) (T *)lua_malloc(luaState, sizeof(T)) + +void *lua_realloc(lua_State *luaState, void *block, size_t osize, size_t nsize); + +void pushObject(lua_State *luaState, TValue *obj); +void pushProto(lua_State *luaState, Proto *proto); +void pushUpValue(lua_State *luaState, UpVal *upval); +void pushString(lua_State *luaState, TString *str); + +StkId getObject(lua_State *luaState, int stackpos); + +void lua_linkObjToGC(lua_State *luaState, GCObject *obj, lu_byte type); + +#define sizeLclosure(n) ((sizeof(LClosure)) + sizeof(TValue *) * ((n) - 1)) + +Closure *lua_newLclosure(lua_State *luaState, int numElements, Table *elementTable); +void pushClosure(lua_State *luaState, Closure *closure); + +Proto *createProto(lua_State *luaState); +Proto *makeFakeProto(lua_State *L, lu_byte nups); + +TString *createString(lua_State *luaState, const char *str, size_t len); + +UpVal *createUpValue(lua_State *luaState, int stackpos); +void unboxUpValue(lua_State *luaState); + +/* Appends one stack to another stack, but the stack is reversed in the process */ +size_t appendStackToStack_reverse(lua_State *from, lua_State *to); +void correctStack(lua_State *L, TValue *oldstack); +void lua_reallocstack(lua_State *L, int newsize); +void lua_growstack(lua_State *L, int n); + +void lua_reallocCallInfo(lua_State *lauState, int newsize); + +/* Does basically the opposite of luaC_link(). + * Right now this function is rather inefficient; it requires traversing the + * entire root GC set in order to find one object. If the GC list were doubly + * linked this would be much easier, but there's no reason for Lua to have + * that. */ +void GCUnlink(lua_State *luaState, GCObject *gco); + +TString *lua_newlstr(lua_State *luaState, const char *str, size_t len); +void lua_link(lua_State *luaState, GCObject *o, lu_byte tt); +Proto *lua_newproto(lua_State *luaState) ; + +UpVal *makeUpValue(lua_State *luaState, int stackPos); +/** + * The GC is not fond of finding upvalues in tables. We get around this + * during persistence using a weakly keyed table, so that the GC doesn't + * bother to mark them. This won't work in unpersisting, however, since + * if we make the values weak they'll be collected (since nothing else + * references them). Our solution, during unpersisting, is to represent + * upvalues as dummy functions, each with one upvalue. + */ +void boxUpValue_start(lua_State *luaState); +void boxUpValue_finish(lua_State *luaState); + +} // End of namespace Lua + +#endif diff --git a/engines/sword25/util/lua_unpersist.cpp b/engines/sword25/util/lua_unpersist.cpp new file mode 100644 index 0000000000..ef0ef31041 --- /dev/null +++ b/engines/sword25/util/lua_unpersist.cpp @@ -0,0 +1,722 @@ +/* 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. + * + */ + +/** + * This code is heavily based on the Pluto code base. Copyright below + */ + +/* Tamed Pluto - Heavy-duty persistence for Lua + * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public + * domain. People making use of this software as part of an application + * are politely requested to email the author at sneftel@gmail.com + * with a brief description of the application, primarily to satisfy his + * curiosity. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Instrumented by Stefan Reich (info@luaos.net) + * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) + */ + + +#include "sword25/util/lua_persistence.h" + +#include "sword25/util/double_serialization.h" +#include "sword25/util/lua_persistence_util.h" + +#include "common/stream.h" + +#include "lua/lobject.h" +#include "lua/lstate.h" +#include "lua/lgc.h" +#include "lua/lopcodes.h" + + +namespace Lua { + +struct UnSerializationInfo { + lua_State *luaState; + Common::ReadStream *readStream; +}; + +static void unpersist(UnSerializationInfo *info); + +static void unpersistBoolean(UnSerializationInfo *info); +static void unpersistNumber(UnSerializationInfo *info); +static void unpersistString(UnSerializationInfo *info); +static void unpersistTable(UnSerializationInfo *info, int index); +static void unpersistFunction(UnSerializationInfo *info, int index); +static void unpersistThread(UnSerializationInfo *info, int index); +static void unpersistProto(UnSerializationInfo *info, int index); +static void unpersistUpValue(UnSerializationInfo *info, int index); +static void unpersistUserData(UnSerializationInfo *info, int index); +static void unpersistPermanent(UnSerializationInfo *info, int index); + + +void unpersistLua(lua_State *luaState, Common::ReadStream *readStream) { + UnSerializationInfo info; + info.luaState = luaState; + info.readStream = readStream; + + // The process starts with the lua stack as follows: + // >>>>> permTbl + // That's the table of permanents + + // Make sure there is enough room on the stack + lua_checkstack(luaState, 3); + + // Create a table to hold indexes of everything thats already been read + lua_newtable(luaState); + // >>>>> permTbl indexTbl + + // Prevent garbage collection while we unserialize + lua_gc(luaState, LUA_GCSTOP, 0); + + // Unserialize the root object + unpersist(&info); + // >>>>> permTbl indexTbl rootObj + + // Re-start garbage collection + lua_gc(luaState, LUA_GCRESTART, 0); + + // Remove the indexTbl + lua_replace(luaState, 2); + // >>>>> permTbl rootObj +} + +/* The object is left on the stack. This is primarily used by unserialize, but + * may be used by GCed objects that may incur cycles in order to preregister + * the object. */ +static void registerObjectInIndexTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... obj + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... obj index + + lua_pushvalue(info->luaState, -2); + // >>>>> permTbl indexTbl ...... obj index obj + + // Push the k/v pair into the indexTbl + lua_settable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... obj +} + +static void unpersist(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte isARealValue = info->readStream->readByte(); + if (isARealValue) { + int index = info->readStream->readSint32LE(); + int type = info->readStream->readSint32LE(); + + switch (type) { + case LUA_TBOOLEAN: + unpersistBoolean(info); + break; + case LUA_TLIGHTUSERDATA: + // You can't serialize a pointer + // It would be meaningless on the next run + assert(0); + break; + case LUA_TNUMBER: + unpersistNumber(info); + break; + case LUA_TSTRING: + unpersistString(info); + break; + case LUA_TTABLE: + unpersistTable(info, index); + break; + case LUA_TFUNCTION: + unpersistFunction(info, index); + break; + case LUA_TTHREAD: + unpersistThread(info, index); + break; + case LUA_TPROTO: + unpersistProto(info, index); + break; + case LUA_TUPVAL: + unpersistUpValue(info, index); + break; + case LUA_TUSERDATA: + unpersistUserData(info, index); + break; + case PERMANENT_TYPE: + unpersistPermanent(info, index); + break; + default: + assert(0); + } + + + // >>>>> permTbl indexTbl ...... obj + assert(lua_type(info->luaState, -1) == type || + type == PERMANENT_TYPE || + // Remember, upvalues get a special dispensation, as described in boxUpValue + (lua_type(info->luaState, -1) == LUA_TFUNCTION && type == LUA_TUPVAL)); + + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... obj + } else { + int index = info->readStream->readSint32LE(); + + if (index == 0) { + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... nil + } else { + // Fetch the object from the indexTbl + + lua_pushlightuserdata(info->luaState, (void *)index); + // >>>>> permTbl indexTbl ...... index + + lua_gettable(info->luaState, 2); + // >>>>> permTbl indexTbl ...... ?obj? + + assert(!lua_isnil(info->luaState, -1)); + } + // >>>>> permTbl indexTbl ...... obj/nil + } + + // >>>>> permTbl indexTbl ...... obj/nil +} + +static void unpersistBoolean(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int value = info->readStream->readSint32LE(); + + lua_pushboolean(info->luaState, value); + // >>>>> permTbl indexTbl ...... bool +} + +static void unpersistNumber(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + // Read the serialized double + Util::SerializedDouble serializedValue; + serializedValue.significandOne = info->readStream->readUint32LE(); + serializedValue.signAndSignificandTwo = info->readStream->readUint32LE(); + serializedValue.exponent = info->readStream->readSint16LE(); + + lua_Number value = Util::decodeDouble(serializedValue); + + lua_pushnumber(info->luaState, value); + // >>>>> permTbl indexTbl ...... num +} + +static void unpersistString(UnSerializationInfo *info) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + uint32 length = info->readStream->readUint32LE(); + char *string = new char[length]; + + info->readStream->read(string, length); + lua_pushlstring(info->luaState, string, length); + + // >>>>> permTbl indexTbl ...... string + + delete[] string; +} + +static void unserializeSpecialTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + unpersist(info); + + // >>>>> permTbl indexTbl ...... spfunc + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... tbl +} + +static void unserializeLiteralTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 3); + + // Preregister table for handling of cycles + lua_newtable(info->luaState); + + // >>>>> permTbl indexTbl ...... tbl + registerObjectInIndexTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + + // Unserialize metatable + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl ?metaTbl/nil? + + if (lua_istable(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl metaTbl + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... tbl + } else { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + } + // >>>>> permTbl indexTbl ...... tbl + + + while (1) { + // >>>>> permTbl indexTbl ...... tbl + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl key/nil + + // The table serialization is nil terminated + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... tbl nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... tbl + + break; + } + + // >>>>> permTbl indexTbl ...... tbl key + unpersist(info); + // >>>>> permTbl indexTbl ...... tbl value + + lua_rawset(info->luaState, -3); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unpersistTable(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 1); + + int isSpecial = info->readStream->readSint32LE(); + + if (isSpecial) { + unserializeSpecialTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } else { + unserializeLiteralTable(info, index); + // >>>>> permTbl indexTbl ...... tbl + } +} + +void unpersistFunction(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + byte numUpValues = info->readStream->readByte(); + + LClosure *lclosure = (LClosure *)lua_newLclosure(info->luaState, numUpValues, hvalue(&info->luaState->l_gt)); + pushClosure(info->luaState, (Closure *)lclosure); + // >>>>> permTbl indexTbl ...... func + + // Put *some* proto in the closure, before the GC can find it + lclosure->p = makeFakeProto(info->luaState, numUpValues); + + //Also, we need to temporarily fill the upvalues + lua_pushnil(info->luaState); + // >>>>> permTbl indexTbl ...... func nil + + for (byte i = 0; i < numUpValues; ++i) { + lclosure->upvals[i] = createUpValue(info->luaState, -1); + } + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + // I can't see offhand how a function would ever get to be self- + // referential, but just in case let's register it early + registerObjectInIndexTable(info, index); + + // Now that it's safe, we can get the real proto + unpersist(info); + // >>>>> permTbl indexTbl ...... func proto + + lclosure->p = gco2p(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + + for (byte i = 0; i < numUpValues; ++i) { + // >>>>> permTbl indexTbl ...... func + unpersist(info); + // >>>>> permTbl indexTbl ...... func func2 + + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... func upValue + lclosure->upvals[i] = gco2uv(getObject(info->luaState, -1)->value.gc); + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // Finally, the fenv + unpersist(info); + + // >>>>> permTbl indexTbl ...... func ?fenv/nil? + if (!lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... func fenv + lua_setfenv(info->luaState, -2); + // >>>>> permTbl indexTbl ...... func + } else { + // >>>>> permTbl indexTbl ...... func nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... func + } + + // >>>>> permTbl indexTbl ...... func +} + +void unpersistThread(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + lua_State *L2; + uint32 stacklimit = 0; + + L2 = lua_newthread(info->luaState); + lua_checkstack(info->luaState, 3); + + // L1: permTbl indexTbl ...... thread + // L2: (empty) + registerObjectInIndexTable(info, index); + + // First, deserialize the object stack + uint32 stackSize = info->readStream->readUint32LE(); + lua_growstack(info->luaState, (int)stackSize); + + // Make sure that the first stack element (a nil, representing + // the imaginary top-level C function) is written to the very, + // very bottom of the stack + L2->top--; + for (uint32 i = 0; i < stackSize; ++i) { + unpersist(info); + // L1: permTbl indexTbl ...... thread obj* + } + + lua_xmove(info->luaState, L2, stackSize); + // L1: permTbl indexTbl ...... thread + // L2: obj* + + // Hereafter, stacks refer to L1 + + + // Now, deserialize the CallInfo stack + + uint32 numFrames = info->readStream->readUint32LE(); + + lua_reallocCallInfo(L2, numFrames * 2); + for (uint32 i = 0; i < numFrames; ++i) { + CallInfo *ci = L2->base_ci + i; + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stackfunc = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + ci->nresults = info->readStream->readSint32LE(); + + uint32 savedpc = info->readStream->readUint32LE(); + + if (stacklimit < stacktop) { + stacklimit = stacktop; + } + + ci->base = L2->stack + stackbase; + ci->func = L2->stack + stackfunc; + ci->top = L2->stack + stacktop; + ci->savedpc = (ci != L2->base_ci) ? ci_func(ci)->l.p->code + savedpc : 0; + ci->tailcalls = 0; + + // Update the pointer each time, to keep the GC happy + L2->ci = ci; + } + + // >>>>> permTbl indexTbl ...... thread + // Deserialize the state's other parameters, with the exception of upval stuff + + L2->savedpc = L2->ci->savedpc; + L2->status = info->readStream->readByte(); + uint32 stackbase = info->readStream->readUint32LE(); + uint32 stacktop = info->readStream->readUint32LE(); + + + L2->errfunc = info->readStream->readUint32LE(); + + L2->base = L2->stack + stackbase; + L2->top = L2->stack + stacktop; + + // Finally, "reopen" upvalues. See serializeUpVal() for why we do this + UpVal *uv; + GCObject **nextslot = &L2->openupval; + global_State *g = G(L2); + + while (true) { + unpersist(info); + // >>>>> permTbl indexTbl ...... thread upVal/nil + + // The list is terminated by a nil + if (lua_isnil(info->luaState, -1)) { + // >>>>> permTbl indexTbl ...... thread nil + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + break; + } + + // >>>>> permTbl indexTbl ...... thread boxedUpVal + unboxUpValue(info->luaState); + // >>>>> permTbl indexTbl ...... thread boxedUpVal + + uv = &(getObject(info->luaState, -1)->value.gc->uv); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... thread + + uint32 stackpos = info->readStream->readUint32LE(); + uv->v = L2->stack + stackpos; + + GCUnlink(info->luaState, (GCObject *)uv); + + uv->marked = luaC_white(g); + *nextslot = (GCObject *)uv; + nextslot = &uv->next; + uv->u.l.prev = &G(L2)->uvhead; + uv->u.l.next = G(L2)->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + G(L2)->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + } + *nextslot = NULL; + + // The stack must be valid at least to the highest value among the CallInfos + // 'top' and the values up to there must be filled with 'nil' + lua_checkstack(L2, (int)stacklimit); + for (StkId o = L2->top; o <= L2->top + stacklimit; ++o) { + setnilvalue(o); + } +} + +void unpersistProto(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // We have to be careful. The GC expects a lot out of protos. In particular, we need + // to give the function a valid string for its source, and valid code, even before we + // actually read in the real code. + TString *source = lua_newlstr(info->luaState, "", 0); + Proto *p = lua_newproto(info->luaState); + p->source = source; + p->sizecode = 1; + p->code = (Instruction *)lua_reallocv(info->luaState, NULL, 0, 1, sizeof(Instruction)); + p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); + p->maxstacksize = 2; + p->sizek = 0; + p->sizep = 0; + + lua_checkstack(info->luaState, 2); + + pushProto(info->luaState, p); + // >>>>> permTbl indexTbl ...... proto + + // We don't need to register early, since protos can never ever be + // involved in cyclic references + + // Read in constant references + int sizek = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->k, 0, sizek, TValue); + for (int i = 0; i < sizek; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto k + + setobj2s(info->luaState, &p->k[i], getObject(info->luaState, -1)); + p->sizek++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + // Read in sub-proto references + + int sizep = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->p, 0, sizep, Proto *); + for (int i = 0; i < sizep; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto subproto + + p->p[i] = (Proto *)getObject(info->luaState, -1)->value.gc; + p->sizep++; + + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + // >>>>> permTbl indexTbl ...... proto + + + // Read in code + p->sizecode = info->readStream->readSint32LE(); + lua_reallocvector(info->luaState, p->code, 1, p->sizecode, Instruction); + info->readStream->read(p->code, sizeof(Instruction) * p->sizecode); + + + /* Read in upvalue names */ + p->sizeupvalues = info->readStream->readSint32LE(); + if (p->sizeupvalues) { + lua_reallocvector(info->luaState, p->upvalues, 0, p->sizeupvalues, TString *); + for (int i = 0; i < p->sizeupvalues; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto str + + p->upvalues[i] = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in local variable infos + p->sizelocvars = info->readStream->readSint32LE(); + if (p->sizelocvars) { + lua_reallocvector(info->luaState, p->locvars, 0, p->sizelocvars, LocVar); + for (int i = 0; i < p->sizelocvars; ++i) { + // >>>>> permTbl indexTbl ...... proto + unpersist(info); + // >>>>> permTbl indexTbl ...... proto str + + p->locvars[i].varname = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + p->locvars[i].startpc = info->readStream->readSint32LE(); + p->locvars[i].endpc = info->readStream->readSint32LE(); + } + } + // >>>>> permTbl indexTbl ...... proto + + // Read in source string + unpersist(info); + // >>>>> permTbl indexTbl ...... proto sourceStr + + p->source = lua_newlstr(info->luaState, lua_tostring(info->luaState, -1), strlen(lua_tostring(info->luaState, -1))); + lua_pop(info->luaState, 1); + // >>>>> permTbl indexTbl ...... proto + + // Read in line numbers + p->sizelineinfo = info->readStream->readSint32LE(); + if (p->sizelineinfo) { + lua_reallocvector(info->luaState, p->lineinfo, 0, p->sizelineinfo, int); + info->readStream->read(p->lineinfo, sizeof(int) * p->sizelineinfo); + } + + + /* Read in linedefined and lastlinedefined */ + p->linedefined = info->readStream->readSint32LE(); + p->lastlinedefined = info->readStream->readSint32LE(); + + // Read in misc values + p->nups = info->readStream->readByte(); + p->numparams = info->readStream->readByte(); + p->is_vararg = info->readStream->readByte(); + p->maxstacksize = info->readStream->readByte(); +} + +void unpersistUpValue(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + lua_checkstack(info->luaState, 2); + + boxUpValue_start(info->luaState); + // >>>>> permTbl indexTbl ...... func + registerObjectInIndexTable(info, index); + + unpersist(info); + // >>>>> permTbl indexTbl ...... func obj + + boxUpValue_finish(info->luaState); + // >>>>> permTbl indexTbl ...... func +} + +void unpersistUserData(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + int isspecial = info->readStream->readSint32LE(); + if (isspecial) { + unpersist(info); + // >>>>> permTbl indexTbl ...... specialFunc + + lua_call(info->luaState, 0, 1); + // >>>>> permTbl indexTbl ...... udata + } else { + uint32 length = info->readStream->readUint32LE(); + lua_newuserdata(info->luaState, length); + // >>>>> permTbl indexTbl ...... udata + registerObjectInIndexTable(info, index); + + info->readStream->read(lua_touserdata(info->luaState, -1), length); + + unpersist(info); + // >>>>> permTbl indexTbl ...... udata metaTable/nil + + lua_setmetatable(info->luaState, -2); + // >>>>> permTbl indexTbl ...... udata + } + // >>>>> permTbl indexTbl ...... udata +} + +void unpersistPermanent(UnSerializationInfo *info, int index) { + // >>>>> permTbl indexTbl ...... + + // Make sure there is enough room on the stack + lua_checkstack(info->luaState, 2); + + unpersist(info); + // >>>>> permTbl indexTbl ...... permKey + + lua_gettable(info->luaState, 1); + // >>>>> permTbl indexTbl ...... perm +} + +} // End of namespace Lua diff --git a/engines/sword25/util/pluto/CHANGELOG b/engines/sword25/util/pluto/CHANGELOG deleted file mode 100644 index 1be321f898..0000000000 --- a/engines/sword25/util/pluto/CHANGELOG +++ /dev/null @@ -1,37 +0,0 @@ -$Id$ - --- 2.4 -- -* Changed upval unboxing to allow upvals which contain func-housed cycles -* Added stack checking to all stack-growing functions -* Serialized debug information for functions - --- 2.3 -- -* Added LUALIB_API declaration for luaopen_pluto - --- 2.2 -- -* Rolled all internal Lua dependencies into the Pluto distribution -* Made the unit tests depend on dynamically loading Pluto - --- 2.1 -- -* Various fixes to make the GC happy -* stack size always expanded where necessary -* fixed some memory leaks -* GC disabled during unpersist -* callstack initialized for traversal - -This changelog is maintained as of version 2.0alpha1. -Earlier versions are changelogged on the LuaForge site. - --- 2.0 -- -* Fixed a few format changes to 5.1.3 -* Fixed myriad warnings -* GCC compliance: not incrementing cast results -* Fix for self-referring upvals -* Renamed loading function to work with Lua module system -* Loading tables with __newindex works -* unpersist makes buffer copy - --- 2.0alpha1 -- -* Fixed all outstanding 5.0->5.1 conversion issues -* Made heavier use of size_t in preference to int -* Fixed GC/Upval issue (thanks to Eric Jacobs) diff --git a/engines/sword25/util/pluto/FILEFORMAT b/engines/sword25/util/pluto/FILEFORMAT deleted file mode 100644 index e7716675c7..0000000000 --- a/engines/sword25/util/pluto/FILEFORMAT +++ /dev/null @@ -1,168 +0,0 @@ -$Id$ - -pluto_persist() produces a "hunk" of objects. Here's the file format adhered -to by the function, and expected by pluto_unpersist(). - -As a developer, I feel that where file format information is given it is of -utmost importance that that information precisely and accurately reflects the -actual operation of the application. Therefore, if you find any discrepancy -between this and actual operation, please lambast me thoroughly over email. - -Pseudo-C is used to express the file format. Padding is assumed to be -nonexistent. The keyword "one_of" is used to express a concept similar to -"union", except that its size is the size of the actual datatype chosen. Thus, -objects which contain, directly or indirectly, a one_of, may vary in size. - - -struct Object { - int firstTime; /* Whether this is the first time the object - is being referenced */ - one_of { - RealObject o; /* if firstTime == 1 */ - Reference r; /* if firstTime == 0 */ - }; -}; - -struct Reference { - int ref; /* The index the object was registered with */ -}; - -struct RealObject { - int type; /* The type of the object */ - one_of { - Boolean b; /* If type == LUA_TBOOLEAN */ - LightUserData l; /* If type == LUA_TLIGHTUSERDATA */ - Number n; /* If type == LUA_TNUMBER */ - String s; /* If type == LUA_TSTRING */ - Table t; /* If type == LUA_TTABLE */ - Function f; /* If type == LUA_TFUNCTION */ - Userdata u; /* If type == LUA_TUSERDATA */ - Thread th; /* If type == LUA_TTHREAD */ - Proto p; /* If type == LUA_TPROTO (from lobject.h) */ - Upval uv; /* If type == LUA_TUPVAL (from lobject.h) */ - }; /* The actual object */ -}; - -struct Boolean { - int32 bvalue; /* 0 for false, 1 for true */ -}; - -struct LightUserData { - void* luvalue; /* The actual, literal pointer */ -}; - -struct Number { - lua_Number nvalue; /* The actual number */ -}; - -struct String { - int length; /* The length of the string */ - char str[length]; /* The actual string (not null terminated) */ -}; - -struct Table { - int isspecial; /* 1 if SP is used; 0 otherwise */ - one_of { - Closure c; /* if isspecial == 1; closure to refill the table */ - LiteralTable t; /* if isspecial == 0; literal table info */ - }; -}; - -struct LiteralTable { - Object metatable; /* nil for default metatable */ - Pair p[]; /* key/value pairs */ - Object nil = nil; /* Nil reference to terminate */ -}; - -struct Pair { - Object key; - Object value; -}; - -struct Function { /* Actually a closure */ - lu_byte nups; /* Number of upvalues the function uses */ - Object proto; /* The proto this function uses */ - Object upvals[nups]; /* All upvalues */ - Object fenv; /* The FEnv (nil for the global table) -}; - -struct Upval { - Object obj; /* The object this upval refers to */ -} - -struct Userdata { - int isSpecial; /* 1 for special persistence, 0 for literal - one_of { - LiteralUserdata lu; /* if is_special is 0 */ - SpecialUserdata su; /* if is_special is 1 */ - }; -}; - -struct LiteralUserdata { - Object metatable; /* The metatable (nil for default) */ - int length; /* Size of the data */ - char data[length]; /* The actual data */ -}; - -struct SpecialUserdata { - int length; /* The size of the data */ - Object func; /* The closure used to fill the userdata */ -}; - -struct Thread { - int stacksize; /* The size of the stack filled with objects, - * including the "nil" that is hidden below - * the bottom of the stack visible to C */ - Object stack[stacksize];/* Indices of all stack values, bottom up */ - int callinfosize; /* Number of elements in the CallInfo stack */ - CallInfo callinfostack[callinfosize]; /* The CallInfo stack */ - int base; /* base = L->base - L->stack; */ - int top; /* top = L->top - L->stack; */ - OpenUpval openupvals[]; /* Upvalues to open */ - Object nil = nil; /* To terminate the open upvalues list */ -}; - -struct OpenUpval { - Object upval; /* The upvalue */ - int stackpos; /* The stack position to "reopen" it to */ - -}; - -struct CallInfo { - int base; /* base = ci->base - L->stack; */ - int top; /* top = ci->top - L->stack; */ - int pc; /* pc = ci->pc - proto->code; */ - int state; /* flags used by the CallInfo */ -}; - -struct Proto { - int sizek; /* Number of constants referenced */ - Object k[sizek]; /* Constants referenced */ - int sizep; /* Number of inner Protos referenced */ - Object p[sizep]; /* Inner Protos referenced */ - int sizecode; /* Number of instructions in code */ - Instruction code[sizecode]; /* The proto's code */ - ProtoDebug debuginfo; /* Debug information for the proto */ - lu_byte nups; /* Number of upvalues used */ - lu_byte numparams; /* Number of parameters taken */ - lu_byte is_vararg; /* 1 if function accepts varargs, 0 otherwise */ - lu_byte maxstacksize; /* Size of stack reserved for the function */ -}; - -struct ProtoDebug { - int sizeupvals; /* Number of upvalue names */ - Object upvals; /* Upvalue names */ - int sizelocvars; /* Number of local variable names */ - LocVar[sizelocvars]; /* Local variable names */ - Object source; /* The source code */ - int sizelineinfo; /* Number of opcode-line mappings */ - int lineinfo[sizelineinfo]; /* opcode-line mappings */ - int linedefined; /* Start of line range */ - int lastlinedefined; /* End of line range */ -}; - -struct LocVar { - Object name; /* Name of the local variable */ - int startpc; /* Point where variable is active */ - int endpc; /* Point where variable is dead */ -}; diff --git a/engines/sword25/util/pluto/README b/engines/sword25/util/pluto/README deleted file mode 100644 index 838fce498b..0000000000 --- a/engines/sword25/util/pluto/README +++ /dev/null @@ -1,133 +0,0 @@ -$Id$ - -PLUTO - Heavy duty persistence for Lua - -Pluto is a library which allows users to write arbitrarily large portions -of the "Lua universe" into a flat file, and later read them back into the -same or a different Lua universe. Object references are appropriately -handled, such that the file contains everything needed to recreate the -objects in question. - -Pluto has the following major features: -* Can persist any Lua function -* Can persist threads -* Works with any Lua chunkreader/chunkwriter -* Support for "invariant" permanent objects, of all datatypes -* Can invoke metafunctions for custom persistence of tables and userdata - -Pluto 2.2 requires Lua 5.1.3. If you need to use Pluto with Lua -5.0, please use version 1.2 of Pluto. - -Starting with version 2.2, Pluto no longer depends on the Lua sources. -Instead, it subsumes the required headers into its own codebase. -As a result, it may not work properly with Lua version 5.1.4 or later. - -Pluto may have bugs. Users are advised to define lua_assert in -luaconf.h to something useful when compiling in debug mode, to catch -assertions by Pluto and Lua. - -The Pluto library consists of two public functions. - -int pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) - -This function recursively persists the Lua object in stack position 2 -and all other objects which are directly or indirectly referenced by -it, except those referenced in the permanent object table. The data -is written using the chunk-writer given, and that writer is passed -the arbitrary pointer value ud. - -The Lua stack must contain exactly and only these two items, in order: - -1. A table of permanent objects, that should not be persisted. For each -permanent object, the object itself should be the key, and a unique -object of any type should be the value. Likely candidates for this table -include Lua functions (including those in the Lua libraries) that are -loaded at load-time. It must include all non-persistable objects that -are referenced by the object to be persisted. The table is not modified -by the function. Objects in this table are considered "opaque" and are -not examined or descended into. Objects should not appear in the table -multiple times; the result of doing this is undefined (though probably -harmless). NOTE: If you are planning to persist threads, keep in mind -that all yielded threads have coroutine.yield on the tops of their -stacks. Since it's a C function, it should be put here. For complex -permanents, it may be a good idea to use the __index meta-function of -the permanents table to "search" for permanents. - -2. The single object to be persisted. In many cases, this will be the -global table. For more flexibility, however, it may be something like a -table built for the occasion, with various values to keep track of. The -object may not be nil. - - -int pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) - -This function loads in a Lua object and places it on top of the stack. All -objects directly or indirectly referenced by it are also loaded. - -The Lua stack must contain, as its top value, a table of permanent -objects. This table should be like the permanent object table used when -persisting, but with the key and value of each pair reversed. These -objects are used as substitutes for those referenced in their positions -when persisting, and under most circumstances should be identical objects -to those referenced in the permanents table used for persisting. It's -okay for multiple keys to refer to the same object. - - -RUNNING PLUTO FROM LUA: -It is also possible to invoke pluto from a Lua script. The C function -pluto_open() will register pluto.persist and pluto.unpersist, lua functions -which operate on strings. The first takes a permanents table and a root -object, and returns a string; the second takes a permanents table and a -string, and returns the root object. - -An error will be raised if pluto.persist is called from a thread which is -itself referenced by the root object. - -SPECIAL PERSISTENCE: -Tables and userdata have special persistence semantics. These semantics are -keyed to the value of the object's metatable's __persist member, if any. This -member may be any of the following four values: -1. Boolean "true": The table or userdata is persisted literally; tables are -persisted member-by-member, and userdata are written out as literal data. -2. Boolean "false": An error is returned, indicating that the object cannot -be persisted. -3. A function: This function should take one argument, the object in question, -and return one result, a closure. This "fixup closure", in turn, will be -persisted, and during unpersistence will be called. The closure will be -responsible for recreating the object with the appropriate data, based on -its upvalues. -4. Nil, or no metatable. In the case of tables, the table is literally -persisted. In the case of userdata, an error is returned. - -Here's an example of special persistence for a simple 3d vector object: - -vec = { x = 2, y = 1, z = 4 } -setmetatable(vec, { __persist = function(oldtbl) - local x = oldtbl.x - local y = oldtbl.y - local z = oldtbl.z - local mt = getmetatable(oldtbl) - return function() - newtbl = {} - newtbl.x = x - newtbl.y = y - newtbl.z = z - setmetatable(newtbl, mt) - return newtbl - end -end }) - -Note how x, y, z, and the mt are explicitly pulled out of the table. It is -important that the fixup closure returned not reference the original table -directly, as that table would again be persisted as an upvalue, leading to an -infinite loop. Also note that the object's metatable is NOT automatically -persisted; it is necessary for the fixup closure to reset it, if it wants. - -LIMITATIONS/TODO: -* Light userdata are persisted literally, as their pointer values. This -may or may not be what you want. -* Closures of C functions may not be persisted. Once it becomes possible -to specify a C function "proto" as a permanent object, this restriction -will be relaxed. - -BUGS: None known. Emphasis on the 'known'. diff --git a/engines/sword25/util/pluto/THANKS b/engines/sword25/util/pluto/THANKS deleted file mode 100644 index 443713fa61..0000000000 --- a/engines/sword25/util/pluto/THANKS +++ /dev/null @@ -1,9 +0,0 @@ -Pluto is surprisingly robust and useful. This would not be the case without -the hard work and helpfulness of the following people, mentioned in no -particular order: - -Ivko Stanilov -Goran Adrinek -Eric Jacobs -Anolan Milanes -Malte Thiesen diff --git a/engines/sword25/util/pluto/pdep.cpp b/engines/sword25/util/pluto/pdep.cpp deleted file mode 100644 index a32c43b42d..0000000000 --- a/engines/sword25/util/pluto/pdep.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* This file is derived from the Lua source code. Please see lua.h for -the copyright statement. -*/ - -#include "pdep/pdep.h" - -#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} - -void pdep_pushobject (lua_State *L, const TValue *o) { - setobj2s(L, L->top, o); - api_incr_top(L); -} - -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { - global_State *g = G(L); - lua_assert((osize == 0) == (block == NULL)); - block = (*g->frealloc)(g->ud, block, osize, nsize); - lua_assert((nsize == 0) == (block == NULL)); - g->totalbytes = (g->totalbytes - osize) + nsize; - return block; -} - -void pdep_link (lua_State *L, GCObject *o, lu_byte tt) { - global_State *g = G(L); - o->gch.next = g->rootgc; - g->rootgc = o; - o->gch.marked = luaC_white(g); - o->gch.tt = tt; -} - -Proto *pdep_newproto (lua_State *L) { - Proto *f = pdep_new(L, Proto); - pdep_link(L, obj2gco(f), LUA_TPROTO); - f->k = NULL; - f->sizek = 0; - f->p = NULL; - f->sizep = 0; - f->code = NULL; - f->sizecode = 0; - f->sizelineinfo = 0; - f->sizeupvalues = 0; - f->nups = 0; - f->upvalues = NULL; - f->numparams = 0; - f->is_vararg = 0; - f->maxstacksize = 0; - f->lineinfo = NULL; - f->sizelocvars = 0; - f->locvars = NULL; - f->linedefined = 0; - f->lastlinedefined = 0; - f->source = NULL; - return f; -} - -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e) { - Closure *c = cast(Closure *, pdep_malloc(L, sizeLclosure(nelems))); - pdep_link(L, obj2gco(c), LUA_TFUNCTION); - c->l.isC = 0; - c->l.env = e; - c->l.nupvalues = cast_byte(nelems); - while (nelems--) c->l.upvals[nelems] = NULL; - return c; -} - -static void correctstack (lua_State *L, TValue *oldstack) { - CallInfo *ci; - GCObject *up; - L->top = (L->top - oldstack) + L->stack; - for (up = L->openupval; up != NULL; up = up->gch.next) - gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; - for (ci = L->base_ci; ci <= L->ci; ci++) { - ci->top = (ci->top - oldstack) + L->stack; - ci->base = (ci->base - oldstack) + L->stack; - ci->func = (ci->func - oldstack) + L->stack; - } - L->base = (L->base - oldstack) + L->stack; -} - - -void pdep_reallocstack (lua_State *L, int newsize) { - TValue *oldstack = L->stack; - int realsize = newsize + 1 + EXTRA_STACK; - lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); - pdep_reallocvector(L, L->stack, L->stacksize, realsize, TValue); - L->stacksize = realsize; - L->stack_last = L->stack+newsize; - correctstack(L, oldstack); -} - -void pdep_growstack (lua_State *L, int n) { - if (n <= L->stacksize) /* double size is enough? */ - pdep_reallocstack(L, 2*L->stacksize); - else - pdep_reallocstack(L, L->stacksize + n); -} - -void pdep_reallocCI (lua_State *L, int newsize) { - CallInfo *oldci = L->base_ci; - pdep_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); - L->size_ci = newsize; - L->ci = (L->ci - oldci) + L->base_ci; - L->end_ci = L->base_ci + L->size_ci - 1; -} - -TString *pdep_newlstr (lua_State *L, const char *str, size_t l) { - TString *res; - lua_pushlstring(L, str, l); - res = rawtsvalue(L->top-1); - lua_pop(L, 1); - return res; -} diff --git a/engines/sword25/util/pluto/pdep/README b/engines/sword25/util/pluto/pdep/README deleted file mode 100644 index 3592754da0..0000000000 --- a/engines/sword25/util/pluto/pdep/README +++ /dev/null @@ -1,5 +0,0 @@ -These files are directly copied from the Lua distribution, with the -exception of lzio.h, which is s/lua{ZM}/pdep/g and has an include removed. - -As such, unlike the rest of Pluto, they are released under the -same terms as Lua. See "lua.h" for the copyright notice. diff --git a/engines/sword25/util/pluto/pdep/lzio.h b/engines/sword25/util/pluto/pdep/lzio.h deleted file mode 100644 index 2e37f8d202..0000000000 --- a/engines/sword25/util/pluto/pdep/lzio.h +++ /dev/null @@ -1,65 +0,0 @@ -/* -** $Id$ -** Buffered streams -** See Copyright Notice in lua.h -*/ - - -#ifndef lzio_h -#define lzio_h - -#include "sword25/util/lua/lua.h" - - -#define EOZ (-1) /* end of stream */ - -typedef struct Zio ZIO; - -#define char2int(c) cast(int, cast(unsigned char, (c))) - -#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : pdep_fill(z)) - -typedef struct Mbuffer { - char *buffer; - size_t n; - size_t buffsize; -} Mbuffer; - -#define pdep_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) - -#define pdep_buffer(buff) ((buff)->buffer) -#define pdep_sizebuffer(buff) ((buff)->buffsize) -#define pdep_bufflen(buff) ((buff)->n) - -#define pdep_resetbuffer(buff) ((buff)->n = 0) - - -#define pdep_resizebuffer(L, buff, size) \ - (pdep_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ - (buff)->buffsize = size) - -#define pdep_freebuffer(L, buff) pdep_resizebuffer(L, buff, 0) - - -LUAI_FUNC char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n); -LUAI_FUNC void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, - void *data); -LUAI_FUNC size_t pdep_read (ZIO* z, void* b, size_t n); /* read next n bytes */ -LUAI_FUNC int pdep_lookahead (ZIO *z); - - - -/* --------- Private Part ------------------ */ - -struct Zio { - size_t n; /* bytes still unread */ - const char *p; /* current position in buffer */ - lua_Reader reader; - void* data; /* additional data */ - lua_State *L; /* Lua state (for reader) */ -}; - - -LUAI_FUNC int pdep_fill (ZIO *z); - -#endif diff --git a/engines/sword25/util/pluto/pdep/pdep.h b/engines/sword25/util/pluto/pdep/pdep.h deleted file mode 100644 index 664fc812b5..0000000000 --- a/engines/sword25/util/pluto/pdep/pdep.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef PDEP_H -#define PDEP_H - -#include "sword25/util/lua/lua.h" -#include "sword25/util/pluto/pdep/lzio.h" -#include "sword25/util/lua/ldo.h" -#include "sword25/util/lua/lfunc.h" -#include "sword25/util/lua/lgc.h" -#include "sword25/util/lua/llimits.h" -#include "sword25/util/lua/lobject.h" -#include "sword25/util/lua/lopcodes.h" -#include "sword25/util/lua/lstate.h" -#include "sword25/util/lua/lstring.h" -#include "sword25/util/lua/lauxlib.h" - - -#define pdep_reallocv(L,b,on,n,e) \ - pdep_realloc_(L, (b), (on)*(e), (n)*(e)) -#define pdep_reallocvector(L, v,oldn,n,t) \ - ((v)=cast(t *, pdep_reallocv(L, v, oldn, n, sizeof(t)))) -#define pdep_freearray(L, b, n, t) pdep_reallocv(L, (b), n, 0, sizeof(t)) -#define pdep_newvector(L,n,t) \ - cast(t *, pdep_reallocv(L, NULL, 0, n, sizeof(t))) -#define pdep_new(L,t) cast(t *, pdep_malloc(L, sizeof(t))) -#define pdep_malloc(L,t) pdep_realloc_(L, NULL, 0, (t)) -#define pdep_checkstack(L,n) \ - if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ - pdep_growstack(L, n); \ - else pdep_reallocstack(L, L->stacksize - EXTRA_STACK - 1); - - -void pdep_pushobject (lua_State *L, const TValue *o); -void *pdep_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize); -void pdep_link (lua_State *L, GCObject *o, lu_byte tt); -Proto *pdep_newproto (lua_State *L); -Closure *pdep_newLclosure (lua_State *L, int nelems, Table *e); -void pdep_reallocstack (lua_State *L, int newsize); -void pdep_growstack (lua_State *L, int n); -void pdep_reallocCI (lua_State *L, int newsize); -TString *pdep_newlstr (lua_State *L, const char *str, size_t l); - -#endif diff --git a/engines/sword25/util/pluto/pluto.cpp b/engines/sword25/util/pluto/pluto.cpp deleted file mode 100644 index cbe16b0d5b..0000000000 --- a/engines/sword25/util/pluto/pluto.cpp +++ /dev/null @@ -1,2083 +0,0 @@ -/* $Id$ */ - -/* Tamed Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - * Instrumented by Stefan Reich (info@luaos.net) - * for Mobile Lua (http://luaos.net/pages/mobile-lua.php) - */ - -#include "sword25/util/lua/lua.h" -#include "pluto.h" - -#undef TOTEXT - -#define USE_PDEP - -#ifdef USE_PDEP -#include "pdep/pdep.h" -#define LIF(prefix, name) pdep ## _ ## name -#else -#include "lapi.h" -#include "ldo.h" -#include "lfunc.h" -#include "lgc.h" -#include "llimits.h" -#include "lmem.h" -#include "lobject.h" -#include "lopcodes.h" -#include "lstate.h" -#include "lstring.h" -#include "lauxlib.h" -#define LIF(prefix, name) lua ## prefix ## _ ## name -#endif - -#include <string.h> - - -/* Define this if you want size_t values to be written in 64-bit - (even on 32-bit systems). Should eliminate at least one source of - 32/64 bit incompatibility. */ -#define SIZES64 - - -/* #define PLUTO_DEBUG */ - - -#ifdef SIZES64 -#define VERSION "Tamed Pluto 1.0 with SIZES64 flag" -#else -#define VERSION "Tamed Pluto 1.0" -#endif - - -#ifdef PLUTO_DEBUG -#include <stdio.h> -#endif - -#define PLUTO_TPERMANENT 101 - -#define verify(x) { int v = (int)((x)); v=v; lua_assert(v); } - -#define NUMTYPES 9 -static const char* typenames[] = { - "nil", - "boolean", - "lightuserdata", - "number", - "string", - "table", - "function", - "userdata", - "thread" -}; - -static int humanReadable = 0; -#define hrBufSize 200 -static char hrBuf[hrBufSize]; - -typedef struct PersistInfo_t { - lua_State *L; - int counter; - lua_Chunkwriter writer; - void *ud; -#ifdef PLUTO_DEBUG - int level; -#endif -} PersistInfo; - -#ifdef PLUTO_DEBUG -void printindent(int indent) -{ - int il; - for(il=0; il<indent; il++) { - printf(" "); - } -} -#endif - -/* lua_Chunkwriter signature: (lua_State *L, const void *p, size_t sz, void *ud). - ud is a pointer to the WriterInfo struct (holds the buffer pointer) -*/ - -static void pi_write(PersistInfo *pi, const void *p, size_t size, void *ud) { - if (humanReadable) { - uint i; - snprintf(hrBuf, hrBufSize, " pi_write %d ", (int) size); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - snprintf(hrBuf, hrBufSize, "%X%X", (b >> 4) & 0xF, b & 0xF); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } - snprintf(hrBuf, hrBufSize, "\n"); - pi->writer(pi->L, hrBuf, strlen(hrBuf), ud); - } else { - pi->writer(pi->L, p, size, ud); - } -#ifdef TOTEXT - int i; - printf(" pi_write %d ", (int) size); - for (i = 0; i < size; i++) { - char b = ((char *)p)[i]; - printf("%X%X", (b >> 4) & 0xF, b & 0xF); - } - printf("\n"); -#endif -} - -static void hrOut(PersistInfo *pi) { - pi->writer(pi->L, hrBuf, strlen(hrBuf), pi->ud); -} - -static void write_size(PersistInfo *pi, size_t *val) -{ -#ifdef SIZES64 - int64 longval; /* yeah, you really need long long to get 8 bytes on win32... duh. */ - longval = *val; - pi_write(pi, &longval, 8, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size64 %ld\n", longval); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size64 %ld\n", longval); -#endif -#else - pi_write(pi, val, sizeof(size_t), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "write_size %ld\n", *((size_t *)val)); - hrOut(pi); - } -#ifdef TOTEXT - printf("write_size %ld\n", *val); -#endif -#endif -} - -static void read_size(ZIO *zio, size_t *val) -{ -#ifdef SIZES64 - int64 longval; - verify(LIF(Z,read)(zio, &longval, 8) == 0); - *val = longval; -#else - verify(LIF(Z,read)(zio, val, sizeof(size_t)) == 0); -#endif -} - - -/* Mutual recursion requires prototype */ -static void persist(PersistInfo *pi); - -/* A simple reimplementation of the unfortunately static function luaA_index. - * Does not support the global table, registry, or upvalues. */ -static StkId getobject(lua_State *L, int stackpos) -{ - if(stackpos > 0) { - lua_assert(L->base+stackpos-1 < L->top); - return L->base+stackpos-1; - } else { - lua_assert(L->top-stackpos >= L->base); - return L->top+stackpos; - } -} - -/* Choose whether to do a regular or special persistence based on an object's - * metatable. "default" is whether the object, if it doesn't have a __persist - * entry, is literally persistable or not. - * Pushes the unpersist closure and returns true if special persistence is - * used. */ -static int persistspecialobject(PersistInfo *pi, int defaction) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 4); - /* Check whether we should persist literally, or via the __persist - * metafunction */ - if(!lua_getmetatable(pi->L, -1)) { - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - } - } - /* perms reftbl sptbl ... obj mt */ - lua_pushstring(pi->L, "__persist"); - /* perms reftbl sptbl ... obj mt "__persist" */ - lua_rawget(pi->L, -2); - /* perms reftbl sptbl ... obj mt __persist? */ - if(lua_isnil(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt nil */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - if(defaction) { - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero2\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero2\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Type not literally persistable by default"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(lua_isboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt bool */ - if(lua_toboolean(pi->L, -1)) { - /* perms reftbl sptbl ... obj mt true */ - lua_pop(pi->L, 2); - /* perms reftbl sptbl ... obj */ - { - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_zero3\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_zero3\n"); -#endif - } - return 0; - } else { - lua_pushstring(pi->L, "Metatable forbade persistence"); - lua_error(pi->L); - return 0; /* not reached */ - } - } else if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist not nil, boolean, or function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt __persist */ - lua_pushvalue(pi->L, -3); - /* perms reftbl ... obj mt __persist obj */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(pi->L, (void *)pi->writer); - lua_pushlightuserdata(pi->L, pi->ud); - /* perms reftbl ... obj mt __persist obj ud */ - lua_call(pi->L, 3, 1); - /* perms reftbl ... obj mt func? */ -#else - lua_call(pi->L, 1, 1); - /* perms reftbl ... obj mt func? */ -#endif - /* perms reftbl ... obj mt func? */ - if(!lua_isfunction(pi->L, -1)) { - lua_pushstring(pi->L, "__persist function did not return a function"); - lua_error(pi->L); - } - /* perms reftbl ... obj mt func */ - { - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistspecialobject_write_one\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistspecialobject_write_one\n"); -#endif - } - persist(pi); - /* perms reftbl ... obj mt func */ - lua_pop(pi->L, 2); - /* perms reftbl ... obj */ - return 1; -} - -static void persisttable(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persisttable\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persisttable\n"); -#endif - - /* perms reftbl ... tbl */ - lua_checkstack(pi->L, 3); - if(persistspecialobject(pi, 1)) { - /* perms reftbl ... tbl */ - return; - } - /* perms reftbl ... tbl */ - /* First, persist the metatable (if any) */ - if(!lua_getmetatable(pi->L, -1)) { - lua_pushnil(pi->L); - } - /* perms reftbl ... tbl mt/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ - - /* Now, persist all k/v pairs */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - while(lua_next(pi->L, -2)) { - /* perms reftbl ... tbl k v */ - lua_pushvalue(pi->L, -2); - /* perms reftbl ... tbl k v k */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k v */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl k */ - } - /* perms reftbl ... tbl */ - /* Terminate list */ - lua_pushnil(pi->L); - /* perms reftbl ... tbl nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... tbl */ -} - -static void persistuserdata(PersistInfo *pi) { - /* perms reftbl ... udata */ - lua_checkstack(pi->L, 2); - if(persistspecialobject(pi, 0)) { - /* perms reftbl ... udata */ - return; - } else { - /* Use literal persistence */ - size_t length = uvalue(getobject(pi->L, -1))->len; - write_size(pi, &length); - pi_write(pi, lua_touserdata(pi->L, -1), length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistuserdata %ld\n", (long) length); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistuserdata %ld\n", (long) length); -#endif - if(!lua_getmetatable(pi->L, -1)) { - /* perms reftbl ... udata */ - lua_pushnil(pi->L); - /* perms reftbl ... udata mt/nil */ - } - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... udata */ - } -} - - -static Proto *toproto(lua_State *L, int stackpos) -{ - return gco2p(getobject(L, stackpos)->value.gc); -} - -static UpVal *toupval(lua_State *L, int stackpos) -{ - lua_assert(ttype(getobject(L, stackpos)) == LUA_TUPVAL); - return gco2uv(getobject(L, stackpos)->value.gc); -} - -static void pushproto(lua_State *L, Proto *proto) -{ - TValue o; - setptvalue(L, &o, proto); - LIF(A,pushobject)(L, &o); -} - -#define setuvvalue(L,obj,x) \ - { TValue *i_o=(obj); \ - i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUPVAL; \ - checkliveness(G(L),i_o); } - -static void pushupval(lua_State *L, UpVal *upval) -{ - TValue o; - setuvvalue(L, &o, upval); - LIF(A,pushobject)(L, &o); -} - -static void pushclosure(lua_State *L, Closure *closure) -{ - TValue o; - setclvalue(L, &o, closure); - LIF(A,pushobject)(L, &o); -} - -static void pushstring(lua_State *L, TString *s) -{ - TValue o; - setsvalue(L, &o, s); - LIF(A,pushobject)(L, &o); -} - -static void persistfunction(PersistInfo *pi) -{ - /* perms reftbl ... func */ - Closure *cl = clvalue(getobject(pi->L, -1)); - lua_checkstack(pi->L, 2); - if(cl->c.isC) { - /* It's a C function. For now, we aren't going to allow - * persistence of C closures, even if the "C proto" is - * already in the permanents table. */ - lua_pushstring(pi->L, "Attempt to persist a C function"); - lua_error(pi->L); - } else { - /* It's a Lua closure. */ - { - /* We don't really _NEED_ the number of upvals, - * but it'll simplify things a bit */ - pi_write(pi, &cl->l.p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistfunction_number_upvalues %d\n", (int) cl->l.p->nups); -#endif - } - /* Persist prototype */ - { - pushproto(pi->L, cl->l.p); - /* perms reftbl ... func proto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* Persist upvalue values (not the upvalue objects - * themselves) */ - { - int i; - for(i=0; i<cl->l.p->nups; i++) { - /* perms reftbl ... func */ - pushupval(pi->L, cl->l.upvals[i]); - /* perms reftbl ... func upval */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - } - /* Persist function environment */ - { - lua_getfenv(pi->L, -1); - /* perms reftbl ... func fenv */ - if(lua_equal(pi->L, -1, LUA_GLOBALSINDEX)) { - /* Function has the default fenv */ - /* perms reftbl ... func _G */ - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - lua_pushnil(pi->L); - /* perms reftbl ... func nil */ - } - /* perms reftbl ... func fenv/nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... func */ - } - } -} - - -/* Upvalues are tricky. Here's why. - * - * A particular upvalue may be either "open", in which case its member v - * points into a thread's stack, or "closed" in which case it points to the - * upvalue itself. An upvalue is closed under any of the following conditions: - * -- The function that initially declared the variable "local" returns - * -- The thread in which the closure was created is garbage collected - * - * To make things wackier, just because a thread is reachable by Lua doesn't - * mean it's in our root set. We need to be able to treat an open upvalue - * from an unreachable thread as a closed upvalue. - * - * The solution: - * (a) For the purposes of persisting, don't indicate whether an upvalue is - * closed or not. - * (b) When unpersisting, pretend that all upvalues are closed. - * (c) When persisting, persist all open upvalues referenced by a thread - * that is persisted, and tag each one with the corresponding stack position - * (d) When unpersisting, "reopen" each of these upvalues as the thread is - * unpersisted - */ -static void persistupval(PersistInfo *pi) -{ - /* perms reftbl ... upval */ - UpVal *uv = toupval(pi->L, -1); - lua_checkstack(pi->L, 1); - - /* We can't permit the upval to linger around on the stack, as Lua - * will bail if its GC finds it. */ - - lua_pop(pi->L, 1); - /* perms reftbl ... */ - LIF(A,pushobject)(pi->L, uv->v); - /* perms reftbl ... obj */ - persist(pi); - /* perms reftbl ... obj */ -} - -static void persistproto(PersistInfo *pi) -{ - /* perms reftbl ... proto */ - Proto *p = toproto(pi->L, -1); - lua_checkstack(pi->L, 2); - - /* Persist constant refs */ - { - int i; - pi_write(pi, &p->sizek, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizek %d\n", p->sizek); - hrOut(pi); - } - #ifdef TOTEXT - printf("persistproto_sizek %d\n", p->sizek); - #endif - for(i=0; i<p->sizek; i++) { - LIF(A,pushobject)(pi->L, &p->k[i]); - /* perms reftbl ... proto const */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* serialize inner Proto refs */ - { - int i; - pi_write(pi, &p->sizep, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizep %d\n", p->sizep); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizep %d\n", p->sizep); -#endif - for(i=0; i<p->sizep; i++) - { - pushproto(pi->L, p->p[i]); - /* perms reftbl ... proto subproto */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... proto */ - } - } - /* perms reftbl ... proto */ - - /* Serialize code */ - { - int len; - pi_write(pi, &p->sizecode, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizecode %d\n", p->sizecode); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizecode %d\n", p->sizecode); -#endif - len = sizeof(Instruction) * p->sizecode; - pi_write(pi, p->code, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_code %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_code %d\n", len); -#endif - } - - /* Serialize upvalue names */ - { - int i; - pi_write(pi, &p->sizeupvalues, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_upvalues %d\n", p->sizeupvalues); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_upvalues %d\n", p->sizeupvalues); -#endif - for(i=0; i<p->sizeupvalues; i++) - { - pushstring(pi->L, p->upvalues[i]); - persist(pi); - lua_pop(pi->L, 1); - } - } - /* Serialize local variable infos */ - { - int i; - pi_write(pi, &p->sizelocvars, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelocvars %d\n", p->sizelocvars); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelocvars %d\n", p->sizelocvars); -#endif - for(i=0; i<p->sizelocvars; i++) - { - pushstring(pi->L, p->locvars[i].varname); - persist(pi); - lua_pop(pi->L, 1); - - pi_write(pi, &p->locvars[i].startpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_startpc %d\n", p->locvars[i].startpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_startpc %d\n", p->locvars[i].startpc); -#endif - pi_write(pi, &p->locvars[i].endpc, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_endpc %d\n", p->locvars[i].endpc); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_endpc %d\n", p->locvars[i].endpc); -#endif - } - } - - /* Serialize source string */ - pushstring(pi->L, p->source); - persist(pi); - lua_pop(pi->L, 1); - - /* Serialize line numbers */ - { - pi_write(pi, &p->sizelineinfo, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_sizelineinfo %d\n", p->sizelineinfo); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_sizelineinfo %d\n", p->sizelineinfo); -#endif - if (p->sizelineinfo) - { - int len; - len = sizeof(int) * p->sizelineinfo; - pi_write(pi, p->lineinfo, len, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lineinfo %d\n", len); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lineinfo %d\n", len); -#endif - } - } - - /* Serialize linedefined and lastlinedefined */ - pi_write(pi, &p->linedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_linedefined %d\n", p->linedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_linedefined %d\n", p->linedefined); -#endif - pi_write(pi, &p->lastlinedefined, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_lastlinedefined %d\n", p->lastlinedefined); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_lastlinedefined %d\n", p->lastlinedefined); -#endif - - /* Serialize misc values */ - { - pi_write(pi, &p->nups, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_nups %d\n", (int) p->nups); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_nups %d\n", (int) p->nups); -#endif - pi_write(pi, &p->numparams, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_numparams %d\n", (int) p->numparams); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_numparams %d\n", (int) p->numparams); -#endif - pi_write(pi, &p->is_vararg, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_is_vararg %d\n", (int) p->is_vararg); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_is_vararg %d\n", (int) p->is_vararg); -#endif - pi_write(pi, &p->maxstacksize, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistproto_maxstacksize %d\n", (int) p->maxstacksize); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistproto_maxstacksize %d\n", (int) p->maxstacksize); -#endif - } - /* We do not currently persist upvalue names, local variable names, - * variable lifetimes, line info, or source code. */ -} - -/* Copies a stack, but the stack is reversed in the process - */ -static size_t revappendstack(lua_State *from, lua_State *to) -{ - StkId o; - for(o=from->top-1; o>=from->stack; o--) { - setobj2s(to, to->top, o); - to->top++; - } - return from->top - from->stack; -} - -/* Persist all stack members - */ -static void persistthread(PersistInfo *pi) -{ - size_t posremaining; - lua_State *L2; - /* perms reftbl ... thr */ - L2 = lua_tothread(pi->L, -1); - lua_checkstack(pi->L, L2->top - L2->stack + 1); - if(pi->L == L2) { - lua_pushstring(pi->L, "Can't persist currently running thread"); - lua_error(pi->L); - return; /* not reached */ - } - - /* Persist the stack */ - posremaining = revappendstack(L2, pi->L); - /* perms reftbl ... thr (rev'ed contents of L2) */ - write_size(pi, &posremaining); - for(; posremaining > 0; posremaining--) { - persist(pi); - lua_pop(pi->L, 1); - } - /* perms reftbl ... thr */ - /* Now, persist the CallInfo stack. */ - { - size_t i, numframes = (L2->ci - L2->base_ci) + 1; - write_size(pi, &numframes); - for(i=0; i<numframes; i++) { - CallInfo *ci = L2->base_ci + i; - size_t stackbase = ci->base - L2->stack; - size_t stackfunc = ci->func - L2->stack; - size_t stacktop = ci->top - L2->stack; - size_t savedpc = (ci != L2->base_ci) ? - ci->savedpc - ci_func(ci)->l.p->code : - 0; - write_size(pi, &stackbase); - write_size(pi, &stackfunc); - write_size(pi, &stacktop); - pi_write(pi, &ci->nresults, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread %d\n", ci->nresults); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread %d\n", ci->nresults); -#endif - write_size(pi, &savedpc); - } - } - - /* Serialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase = L2->base - L2->stack; - size_t stacktop = L2->top - L2->stack; - lua_assert(L2->nCcalls <= 1); - pi_write(pi, &L2->status, sizeof(lu_byte), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistthread_status %d\n", (int) L2->status); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistthread_status %d\n", (int) L2->status); -#endif - write_size(pi, &stackbase); - write_size(pi, &stacktop); - - // ptrdiff_t changes sizes based on 32/64 bit - // Hard cast to 64 bit size if SIZE64 is defined -#ifdef SIZES64 - uint64 ptrIndex = static_cast<uint64>(L2->errfunc); - pi_write(pi, &ptrIndex, sizeof(uint64), pi->ud); -#else - pi_write(pi, &L2->errfunc, sizeof(ptrdiff_t), pi->ud); -#endif - //write_size(pi, (size_t *)&L2->errfunc); - } - - /* Finally, record upvalues which need to be reopened */ - /* See the comment above persistupval() for why we do this */ - { - GCObject *gco; - UpVal *uv; - /* perms reftbl ... thr */ - for(gco = L2->openupval; gco != NULL; gco = uv->next) { - size_t stackpos; - uv = gco2uv(gco); - - /* Make sure upvalue is really open */ - lua_assert(uv->v != &uv->u.value); - pushupval(pi->L, uv); - /* perms reftbl ... thr uv */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - stackpos = uv->v - L2->stack; - write_size(pi, &stackpos); - } - /* perms reftbl ... thr */ - lua_pushnil(pi->L); - /* perms reftbl ... thr nil */ - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... thr */ - } - /* perms reftbl ... thr */ -} - -static void persistboolean(PersistInfo *pi) -{ - int b = lua_toboolean(pi->L, -1); - pi_write(pi, &b, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistboolean %d\n", b); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistboolean %d\n", b); -#endif -} - -static void persistlightuserdata(PersistInfo *pi) -{ - void *p = lua_touserdata(pi->L, -1); - pi_write(pi, &p, sizeof(void *), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistlightuserdata %p\n", p); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistlightuserdata %d\n", (int) p); -#endif -} - -static void persistnumber(PersistInfo *pi) -{ - lua_Number n = lua_tonumber(pi->L, -1); - pi_write(pi, &n, sizeof(lua_Number), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); - hrOut(pi); - } -#ifdef TOTEXT - printf("persistnumber %d (%d)\n", (int) n, (int) sizeof(lua_Number)); -#endif -} - -static void persiststring(PersistInfo *pi) -{ - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring\n"); -#endif - size_t length = lua_strlen(pi->L, -1); - write_size(pi, &length); - const char* s = lua_tostring(pi->L, -1); - pi_write(pi, s, length, pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persiststring %d \"%s\"\n", (int)length, s); - hrOut(pi); - } -#ifdef TOTEXT - printf("persiststring %d \"%s\"\n", length, s); -#endif -} - -/* Top-level delegating persist function - */ -static void persist(PersistInfo *pi) -{ - /* perms reftbl ... obj */ - lua_checkstack(pi->L, 2); - /* If the object has already been written, write a reference to it */ - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_rawget(pi->L, 2); - /* perms reftbl ... obj ref? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj ref */ - int zero = 0; - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_seenobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_seenobject\n"); -#endif - int *ref = (int *)lua_touserdata(pi->L, -1); - pi_write(pi, ref, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_touserdata_ref %d\n", ref); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_touserdata_ref %d\n", ref); -#endif - lua_pop(pi->L, 1); - /* perms reftbl ... obj ref */ -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 %d\n", ref); -#endif - return; - } - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - /* If the object is nil, write the pseudoreference 0 */ - if(lua_isnil(pi->L, -1)) { - int zero = 0; - /* firsttime */ - pi_write(pi, &zero, sizeof(int), pi->ud); - /* ref */ - pi_write(pi, &zero, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_nil (last 2 lines)\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_nil (last 2 lines)\n"); -#endif -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("0 0\n"); -#endif - return; - } - { - /* indicate that it's the first time */ - int one = 1; - pi_write(pi, &one, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_newobject\n"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_newobject\n"); -#endif - } - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - int *ref = (int *)lua_newuserdata(pi->L, sizeof(int)); - *ref = ++(pi->counter); - /* perms reftbl ... obj obj ref */ - lua_rawset(pi->L, 2); - /* perms reftbl ... obj */ - - pi_write(pi, &pi->counter, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_counter %d\n", pi->counter); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_counter %d\n", pi->counter); -#endif - - - /* At this point, we'll give the permanents table a chance to play. */ - { - lua_pushvalue(pi->L, -1); - /* perms reftbl ... obj obj */ - lua_gettable(pi->L, 1); - /* perms reftbl ... obj permkey? */ - if(!lua_isnil(pi->L, -1)) { - /* perms reftbl ... obj permkey */ - int type = PLUTO_TPERMANENT; -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d PERM\n", pi->counter); - pi->level++; -#endif - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist_permtype %d\n", type); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist_permtype %d\n", type); -#endif - persist(pi); - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - pi->level--; -#endif - return; - } else { - /* perms reftbl ... obj nil */ - lua_pop(pi->L, 1); - /* perms reftbl ... obj */ - } - /* perms reftbl ... obj */ - } - { - int type = lua_type(pi->L, -1); - pi_write(pi, &type, sizeof(int), pi->ud); - if (humanReadable) { - snprintf(hrBuf, hrBufSize, "persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); - hrOut(pi); - } -#ifdef TOTEXT - printf("persist %s\n", type >= 0 && type < NUMTYPES ? typenames[type] : "?"); -#endif - -#ifdef PLUTO_DEBUG - printindent(pi->level); - printf("1 %d %d\n", pi->counter, type); - pi->level++; -#endif - } - - switch(lua_type(pi->L, -1)) { - case LUA_TBOOLEAN: - persistboolean(pi); - break; - case LUA_TLIGHTUSERDATA: - persistlightuserdata(pi); - break; - case LUA_TNUMBER: - persistnumber(pi); - break; - case LUA_TSTRING: - persiststring(pi); - break; - case LUA_TTABLE: - persisttable(pi); - break; - case LUA_TFUNCTION: - persistfunction(pi); - break; - case LUA_TTHREAD: - persistthread(pi); - break; - case LUA_TPROTO: - persistproto(pi); - break; - case LUA_TUPVAL: - persistupval(pi); - break; - case LUA_TUSERDATA: - persistuserdata(pi); - break; - default: - lua_assert(0); - } -#ifdef PLUTO_DEBUG - pi->level--; -#endif -} - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud) -{ - PersistInfo pi; - - pi.counter = 0; - pi.L = L; - pi.writer = writer; - pi.ud = ud; -#ifdef PLUTO_DEBUG - pi.level = 0; -#endif - - lua_checkstack(L, 4); - /* perms? rootobj? ...? */ - lua_assert(lua_gettop(L) == 2); - /* perms rootobj */ - lua_assert(!lua_isnil(L, 2)); - /* perms rootobj */ - lua_newtable(L); - /* perms rootobj reftbl */ - - /* Now we're going to make the table weakly keyed. This prevents the - * GC from visiting it and trying to mark things it doesn't want to - * mark in tables, e.g. upvalues. All objects in the table are - * a priori reachable, so it doesn't matter that we do this. */ - lua_newtable(L); - /* perms rootobj reftbl mt */ - lua_pushstring(L, "__mode"); - /* perms rootobj reftbl mt "__mode" */ - lua_pushstring(L, "k"); - /* perms rootobj reftbl mt "__mode" "k" */ - lua_settable(L, 4); - /* perms rootobj reftbl mt */ - lua_setmetatable(L, 3); - /* perms rootobj reftbl */ - lua_insert(L, 2); - /* perms reftbl rootobj */ - persist(&pi); - /* perms reftbl rootobj */ - lua_remove(L, 2); - /* perms rootobj */ -} - -typedef struct WriterInfo_t { - char* buf; - size_t buflen; -} WriterInfo; - -static int bufwriter (lua_State *L, const void *p, size_t sz, void *ud) { - const char *cp = (const char *)p; - WriterInfo *wi = (WriterInfo *)ud; - - LIF(M,reallocvector)(L, wi->buf, wi->buflen, wi->buflen+sz, char); - while(sz) - { - /* how dearly I love ugly C pointer twiddling */ - wi->buf[wi->buflen++] = *cp++; - sz--; - } - return 0; -} - -int persist_l(lua_State *L) -{ - /* perms? rootobj? ...? */ - WriterInfo wi; - - wi.buf = NULL; - wi.buflen = 0; - - lua_settop(L, 2); - /* perms? rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms rootobj */ - - pluto_persist(L, bufwriter, &wi); - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, wi.buf, wi.buflen); - /* str */ - pdep_freearray(L, wi.buf, wi.buflen, char); - return 1; -} - -typedef struct UnpersistInfo_t { - lua_State *L; - ZIO zio; -#ifdef PLUTO_DEBUG - int level; -#endif -} UnpersistInfo; - -static void unpersist(UnpersistInfo *upi); - -/* The object is left on the stack. This is primarily used by unpersist, but - * may be used by GCed objects that may incur cycles in order to preregister - * the object. */ -static void registerobject(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... obj */ - lua_checkstack(upi->L, 2); - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... obj ref */ - lua_pushvalue(upi->L, -2); - /* perms reftbl ... obj ref obj */ - lua_settable(upi->L, 2); - /* perms reftbl ... obj */ -} - -static void unpersistboolean(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int b; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &b, sizeof(int)) == 0); - lua_pushboolean(upi->L, b); - /* perms reftbl ... bool */ -} - -static void unpersistlightuserdata(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - void *p; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &p, sizeof(void *)) == 0); - lua_pushlightuserdata(upi->L, p); - /* perms reftbl ... ludata */ -} - -static void unpersistnumber(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_Number n; - lua_checkstack(upi->L, 1); - verify(LIF(Z,read)(&upi->zio, &n, sizeof(lua_Number)) == 0); - lua_pushnumber(upi->L, n); - /* perms reftbl ... num */ -} - -static void unpersiststring(UnpersistInfo *upi) -{ - /* perms reftbl sptbl ref */ - /*int length;*/ - size_t length; - char* string; - lua_checkstack(upi->L, 1); - /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(int)) == 0);*/ - /*verify(LIF(Z,read)(&upi->zio, &length, sizeof(size_t)) == 0);*/ - read_size(&upi->zio, &length); - string = pdep_newvector(upi->L, length, char); - verify(LIF(Z,read)(&upi->zio, string, length) == 0); - lua_pushlstring(upi->L, string, length); - /* perms reftbl sptbl ref str */ - pdep_freearray(upi->L, string, length, char); -} - -static void unpersistspecialtable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ - lua_call(upi->L, 0, 1); - /* perms reftbl ... tbl? */ - lua_assert(lua_istable(upi->L, -1)); - /* perms reftbl ... tbl */ -} - -static void unpersistliteraltable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 3); - /* Preregister table for handling of cycles */ - lua_newtable(upi->L); - /* perms reftbl ... tbl */ - registerobject(ref, upi); - /* perms reftbl ... tbl */ - /* Unpersist metatable */ - { - unpersist(upi); - /* perms reftbl ... tbl mt/nil? */ - if(lua_istable(upi->L, -1)) { - /* perms reftbl ... tbl mt */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... tbl */ - } else { - /* perms reftbl ... tbl nil? */ - lua_assert(lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } - - while(1) - { - /* perms reftbl ... tbl */ - unpersist(upi); - /* perms reftbl ... tbl key/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... tbl nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... tbl */ - break; - } - /* perms reftbl ... tbl key */ - unpersist(upi); - /* perms reftbl ... tbl key value? */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl ... tbl key value */ - lua_rawset(upi->L, -3); - /* perms reftbl ... tbl */ - } -} - -static void unpersisttable(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 1); - { - int isspecial; - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersistspecialtable(ref, upi); - /* perms reftbl ... tbl */ - } else { - unpersistliteraltable(ref, upi); - /* perms reftbl ... tbl */ - } - /* perms reftbl ... tbl */ - } -} - -static UpVal *makeupval(lua_State *L, int stackpos) -{ - UpVal *uv = pdep_new(L, UpVal); - pdep_link(L, (GCObject *)uv, LUA_TUPVAL); - uv->tt = LUA_TUPVAL; - uv->v = &uv->u.value; - uv->u.l.prev = NULL; - uv->u.l.next = NULL; - setobj(L, uv->v, getobject(L, stackpos)); - return uv; -} - -static Proto *makefakeproto(lua_State *L, lu_byte nups) -{ - Proto *p = pdep_newproto(L); - p->sizelineinfo = 1; - p->lineinfo = pdep_newvector(L, 1, int); - p->lineinfo[0] = 1; - p->sizecode = 1; - p->code = pdep_newvector(L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->source = pdep_newlstr(L, "", 0); - p->maxstacksize = 2; - p->nups = nups; - p->sizek = 0; - p->sizep = 0; - - return p; -} - -/* The GC is not fond of finding upvalues in tables. We get around this - * during persistence using a weakly keyed table, so that the GC doesn't - * bother to mark them. This won't work in unpersisting, however, since - * if we make the values weak they'll be collected (since nothing else - * references them). Our solution, during unpersisting, is to represent - * upvalues as dummy functions, each with one upvalue. */ -static void boxupval_start(lua_State *L) -{ - LClosure *lcl; - lcl = (LClosure *)pdep_newLclosure(L, 1, hvalue(&L->l_gt)); - pushclosure(L, (Closure *)lcl); - /* ... func */ - lcl->p = makefakeproto(L, 1); - - /* Temporarily initialize the upvalue to nil */ - - lua_pushnil(L); - lcl->upvals[0] = makeupval(L, -1); - lua_pop(L, 1); -} - -static void boxupval_finish(lua_State *L) -{ - /* ... func obj */ - LClosure *lcl = (LClosure *) clvalue(getobject(L, -2)); - - lcl->upvals[0]->u.value = *getobject(L, -1); - lua_pop(L, 1); -} - - -static void unboxupval(lua_State *L) -{ - /* ... func */ - LClosure *lcl; - UpVal *uv; - - lcl = (LClosure *)clvalue(getobject(L, -1)); - uv = lcl->upvals[0]; - lua_pop(L, 1); - /* ... */ - pushupval(L, uv); - /* ... upval */ -} - -static void unpersistfunction(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - LClosure *lcl; - int i; - lu_byte nupvalues; - lua_checkstack(upi->L, 2); - - verify(LIF(Z,read)(&upi->zio, &nupvalues, sizeof(lu_byte)) == 0); - - lcl = (LClosure *)pdep_newLclosure(upi->L, nupvalues, hvalue(&upi->L->l_gt)); - pushclosure(upi->L, (Closure *)lcl); - - /* perms reftbl ... func */ - /* Put *some* proto in the closure, before the GC can find it */ - lcl->p = makefakeproto(upi->L, nupvalues); - - /* Also, we need to temporarily fill the upvalues */ - lua_pushnil(upi->L); - /* perms reftbl ... func nil */ - for(i=0; i<nupvalues; i++) { - lcl->upvals[i] = makeupval(upi->L, -1); - } - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - /* I can't see offhand how a function would ever get to be self- - * referential, but just in case let's register it early */ - registerobject(ref, upi); - - /* Now that it's safe, we can get the real proto */ - unpersist(upi); - /* perms reftbl ... func proto? */ - lua_assert(lua_type(upi->L, -1) == LUA_TPROTO); - /* perms reftbl ... func proto */ - lcl->p = toproto(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - - for(i=0; i<nupvalues; i++) { - /* perms reftbl ... func */ - unpersist(upi); - /* perms reftbl ... func func2 */ - unboxupval(upi->L); - /* perms reftbl ... func upval */ - lcl->upvals[i] = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ - - /* Finally, the fenv */ - unpersist(upi); - /* perms reftbl ... func fenv/nil? */ - lua_assert(lua_type(upi->L, -1) == LUA_TNIL || - lua_type(upi->L, -1) == LUA_TTABLE); - /* perms reftbl ... func fenv/nil */ - if(!lua_isnil(upi->L, -1)) { - /* perms reftbl ... func fenv */ - lua_setfenv(upi->L, -2); - /* perms reftbl ... func */ - } else { - /* perms reftbl ... func nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... func */ - } - /* perms reftbl ... func */ -} - -static void unpersistupval(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - - boxupval_start(upi->L); - /* perms reftbl ... func */ - registerobject(ref, upi); - - unpersist(upi); - /* perms reftbl ... func obj */ - boxupval_finish(upi->L); - /* perms reftbl ... func */ -} - -static void unpersistproto(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - Proto *p; - int i; - int sizep, sizek; - - /* We have to be careful. The GC expects a lot out of protos. In - * particular, we need to give the function a valid string for its - * source, and valid code, even before we actually read in the real - * code. */ - TString *source = pdep_newlstr(upi->L, "", 0); - p = pdep_newproto(upi->L); - p->source = source; - p->sizecode=1; - p->code = pdep_newvector(upi->L, 1, Instruction); - p->code[0] = CREATE_ABC(OP_RETURN, 0, 1, 0); - p->maxstacksize = 2; - p->sizek = 0; - p->sizep = 0; - - lua_checkstack(upi->L, 2); - - pushproto(upi->L, p); - /* perms reftbl ... proto */ - /* We don't need to register early, since protos can never ever be - * involved in cyclic references */ - - /* Read in constant references */ - { - verify(LIF(Z,read)(&upi->zio, &sizek, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->k, 0, sizek, TValue); - for(i=0; i<sizek; i++) { - /* perms reftbl ... proto */ - unpersist(upi); - /* perms reftbl ... proto k */ - setobj2s(upi->L, &p->k[i], getobject(upi->L, -1)); - p->sizek++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - /* Read in sub-proto references */ - { - verify(LIF(Z,read)(&upi->zio, &sizep, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->p, 0, sizep, Proto*); - for(i=0; i<sizep; i++) { - /* perms reftbl ... proto */ - unpersist(upi); - /* perms reftbl ... proto subproto */ - p->p[i] = toproto(upi->L, -1); - p->sizep++; - lua_pop(upi->L, 1); - /* perms reftbl ... proto */ - } - /* perms reftbl ... proto */ - } - - /* Read in code */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizecode, sizeof(int)) == 0); - LIF(M,reallocvector)(upi->L, p->code, 1, p->sizecode, Instruction); - verify(LIF(Z,read)(&upi->zio, p->code, - sizeof(Instruction) * p->sizecode) == 0); - } - - /* Read in upvalue names */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizeupvalues, sizeof(int)) == 0); - if (p->sizeupvalues) - { - LIF(M,reallocvector)(upi->L, p->upvalues, 0, p->sizeupvalues, TString *); - for(i=0; i<p->sizeupvalues; i++) - { - unpersist(upi); - p->upvalues[i] = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - } - } - } - - /* Read in local variable infos */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelocvars, sizeof(int)) == 0); - if (p->sizelocvars) - { - LIF(M,reallocvector)(upi->L, p->locvars, 0, p->sizelocvars, LocVar); - for(i=0; i<p->sizelocvars; i++) - { - unpersist(upi); - p->locvars[i].varname = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].startpc, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->locvars[i].endpc, sizeof(int)) == 0); - } - } - } - - /* Read in source string*/ - unpersist(upi); - p->source = pdep_newlstr(upi->L, lua_tostring(upi->L, -1), strlen(lua_tostring(upi->L, -1))); - lua_pop(upi->L, 1); - - /* Read in line numbers */ - { - verify(LIF(Z,read)(&upi->zio, &p->sizelineinfo, sizeof(int)) == 0); - if (p->sizelineinfo) - { - LIF(M,reallocvector)(upi->L, p->lineinfo, 0, p->sizelineinfo, int); - verify(LIF(Z,read)(&upi->zio, p->lineinfo, - sizeof(int) * p->sizelineinfo) == 0); - } - } - - /* Read in linedefined and lastlinedefined */ - verify(LIF(Z,read)(&upi->zio, &p->linedefined, sizeof(int)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->lastlinedefined, sizeof(int)) == 0); - - /* Read in misc values */ - { - verify(LIF(Z,read)(&upi->zio, &p->nups, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->numparams, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->is_vararg, sizeof(lu_byte)) == 0); - verify(LIF(Z,read)(&upi->zio, &p->maxstacksize, sizeof(lu_byte)) == 0); - } -} - - -/* Does basically the opposite of luaC_link(). - * Right now this function is rather inefficient; it requires traversing the - * entire root GC set in order to find one object. If the GC list were doubly - * linked this would be much easier, but there's no reason for Lua to have - * that. */ -static void gcunlink(lua_State *L, GCObject *gco) -{ - GCObject *prevslot; - if(G(L)->rootgc == gco) { - G(L)->rootgc = G(L)->rootgc->gch.next; - return; - } - - prevslot = G(L)->rootgc; - while(prevslot->gch.next != gco) { - lua_assert(prevslot->gch.next != NULL); - prevslot = prevslot->gch.next; - } - - prevslot->gch.next = prevslot->gch.next->gch.next; -} - -/* FIXME __ALL__ field ordering */ -static void unpersistthread(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_State *L2; - size_t stacklimit = 0; - L2 = lua_newthread(upi->L); - lua_checkstack(upi->L, 3); - /* L1: perms reftbl ... thr */ - /* L2: (empty) */ - registerobject(ref, upi); - - /* First, deserialize the object stack. */ - { - size_t i, stacksize; - read_size(&upi->zio, &stacksize); - LIF(D,growstack)(L2, (int)stacksize); - /* Make sure that the first stack element (a nil, representing - * the imaginary top-level C function) is written to the very, - * very bottom of the stack */ - L2->top--; - for(i=0; i<stacksize; i++) { - unpersist(upi); - /* L1: perms reftbl ... thr obj* */ - } - lua_xmove(upi->L, L2, stacksize); - /* L1: perms reftbl ... thr */ - /* L2: obj* */ - } - /* (hereafter, stacks refer to L1) */ - - /* Now, deserialize the CallInfo stack. */ - { - size_t i, numframes; - read_size(&upi->zio, &numframes); - LIF(D,reallocCI)(L2,numframes*2); - for(i=0; i<numframes; i++) { - CallInfo *ci = L2->base_ci + i; - size_t stackbase, stackfunc, stacktop, savedpc; - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stackfunc); - read_size(&upi->zio, &stacktop); - verify(LIF(Z,read)(&upi->zio, &ci->nresults, sizeof(int)) == 0); - read_size(&upi->zio, &savedpc); - - if(stacklimit < stacktop) - stacklimit = stacktop; - - ci->base = L2->stack+stackbase; - ci->func = L2->stack+stackfunc; - ci->top = L2->stack+stacktop; - ci->savedpc = (ci != L2->base_ci) ? - ci_func(ci)->l.p->code+savedpc : - 0; - ci->tailcalls = 0; - /* Update the pointer each time, to keep the GC - * happy*/ - L2->ci = ci; - } - } - /* perms reftbl ... thr */ - /* Deserialize the state's other parameters, with the exception of upval stuff */ - { - size_t stackbase, stacktop; - L2->savedpc = L2->ci->savedpc; - verify(LIF(Z,read)(&upi->zio, &L2->status, sizeof(lu_byte)) == 0); - read_size(&upi->zio, &stackbase); - read_size(&upi->zio, &stacktop); - -#ifdef SIZES64 - uint64 value; - verify(LIF(Z,read)(&upi->zio, &value, sizeof(uint64)) == 0); - - L2->errfunc = static_cast<ptrdiff_t>(value); -#else - verify(LIF(Z,read)(&upi->zio, &L2->errfunc, sizeof(ptrdiff_t)) == 0); -#endif - - //read_size(&upi->zio, (size_t *)&L2->errfunc); - L2->base = L2->stack + stackbase; - L2->top = L2->stack + stacktop; - } - /* Finally, "reopen" upvalues (see persistupval() for why) */ - { - UpVal* uv; - GCObject **nextslot = &L2->openupval; - global_State *g = G(L2); - while(1) { - size_t stackpos; - unpersist(upi); - /* perms reftbl ... thr uv/nil */ - if(lua_isnil(upi->L, -1)) { - /* perms reftbl ... thr nil */ - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - break; - } - /* perms reftbl ... thr boxeduv */ - unboxupval(upi->L); - /* perms reftbl ... thr uv */ - uv = toupval(upi->L, -1); - lua_pop(upi->L, 1); - /* perms reftbl ... thr */ - - read_size(&upi->zio, &stackpos); - uv->v = L2->stack + stackpos; - gcunlink(upi->L, (GCObject *)uv); - uv->marked = luaC_white(g); - *nextslot = (GCObject *)uv; - nextslot = &uv->next; - uv->u.l.prev = &G(L2)->uvhead; - uv->u.l.next = G(L2)->uvhead.u.l.next; - uv->u.l.next->u.l.prev = uv; - G(L2)->uvhead.u.l.next = uv; - lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - } - *nextslot = NULL; - } - - /* The stack must be valid at least to the highest value among the CallInfos */ - /* 'top' and the values up to there must be filled with 'nil' */ - { - StkId o; - LIF(D,checkstack)(L2, (int)stacklimit); - for (o = L2->top; o <= L2->top + stacklimit; o++) - setnilvalue(o); - } -} - -static void unpersistuserdata(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int isspecial; - lua_checkstack(upi->L, 2); - verify(LIF(Z,read)(&upi->zio, &isspecial, sizeof(int)) == 0); - if(isspecial) { - unpersist(upi); - /* perms reftbl ... spfunc? */ - lua_assert(lua_isfunction(upi->L, -1)); - /* perms reftbl ... spfunc */ -#ifdef PLUTO_PASS_USERDATA_TO_PERSIST - lua_pushlightuserdata(upi->L, &upi->zio); - lua_call(upi->L, 1, 1); -#else - lua_call(upi->L, 0, 1); -#endif - /* perms reftbl ... udata? */ -/* This assertion might not be necessary; it's conceivable, for - * example, that the SP function might decide to return a table - * with equivalent functionality. For the time being, we'll - * ignore this possibility in favor of stricter and more testable - * requirements. */ - lua_assert(lua_isuserdata(upi->L, -1)); - /* perms reftbl ... udata */ - } else { - size_t length; - read_size(&upi->zio, &length); - - lua_newuserdata(upi->L, length); - /* perms reftbl ... udata */ - registerobject(ref, upi); - verify(LIF(Z,read)(&upi->zio, lua_touserdata(upi->L, -1), length) == 0); - - unpersist(upi); - /* perms reftbl ... udata mt/nil? */ - lua_assert(lua_istable(upi->L, -1) || lua_isnil(upi->L, -1)); - /* perms reftbl ... udata mt/nil */ - lua_setmetatable(upi->L, -2); - /* perms reftbl ... udata */ - } - /* perms reftbl ... udata */ -} - -static void unpersistpermanent(int ref, UnpersistInfo *upi) -{ - /* perms reftbl ... */ - lua_checkstack(upi->L, 2); - unpersist(upi); - /* perms reftbl permkey */ - lua_gettable(upi->L, 1); - /* perms reftbl perm? */ - /* We assume currently that the substituted permanent value - * shouldn't be nil. This may be a bad assumption. Real-life - * experience is needed to evaluate this. */ - lua_assert(!lua_isnil(upi->L, -1)); - /* perms reftbl perm */ -} - -#if 0 -/* For debugging only; not called when lua_assert is empty */ -static int inreftable(lua_State *L, int ref) -{ - int res; - lua_checkstack(L, 1); - /* perms reftbl ... */ - lua_pushlightuserdata(L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(L, 2); - /* perms reftbl ... obj? */ - res = !lua_isnil(L, -1); - lua_pop(L, 1); - /* perms reftbl ... */ - return res; -} -#endif - -static void unpersist(UnpersistInfo *upi) -{ - /* perms reftbl ... */ - int firstTime; - int stacksize = lua_gettop(upi->L); stacksize = stacksize; /* DEBUG */ - lua_checkstack(upi->L, 2); - LIF(Z,read)(&upi->zio, &firstTime, sizeof(int)); - if(firstTime) { - int ref; - int type; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); - lua_assert(!inreftable(upi->L, ref)); - LIF(Z,read)(&upi->zio, &type, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("1 %d %d\n", ref, type); - upi->level++; -#endif - switch(type) { - case LUA_TBOOLEAN: - unpersistboolean(upi); - break; - case LUA_TLIGHTUSERDATA: - unpersistlightuserdata(upi); - break; - case LUA_TNUMBER: - unpersistnumber(upi); - break; - case LUA_TSTRING: - unpersiststring(upi); - break; - case LUA_TTABLE: - unpersisttable(ref, upi); - break; - case LUA_TFUNCTION: - unpersistfunction(ref, upi); - break; - case LUA_TTHREAD: - unpersistthread(ref, upi); - break; - case LUA_TPROTO: - unpersistproto(ref, upi); - break; - case LUA_TUPVAL: - unpersistupval(ref, upi); - break; - case LUA_TUSERDATA: - unpersistuserdata(ref, upi); - break; - case PLUTO_TPERMANENT: - unpersistpermanent(ref, upi); - break; - default: - lua_assert(0); - } - /* perms reftbl ... obj */ - lua_assert(lua_type(upi->L, -1) == type || - type == PLUTO_TPERMANENT || - /* Remember, upvalues get a special dispensation, as - * described in boxupval */ - (lua_type(upi->L, -1) == LUA_TFUNCTION && - type == LUA_TUPVAL)); - registerobject(ref, upi); - /* perms reftbl ... obj */ -#ifdef PLUTO_DEBUG - upi->level--; -#endif - } else { - int ref; - LIF(Z,read)(&upi->zio, &ref, sizeof(int)); -#ifdef PLUTO_DEBUG - printindent(upi->level); - printf("0 %d\n", ref); -#endif - if(ref == 0) { - lua_pushnil(upi->L); - /* perms reftbl ... nil */ - } else { - lua_pushlightuserdata(upi->L, (void *)ref); - /* perms reftbl ... ref */ - lua_gettable(upi->L, 2); - /* perms reftbl ... obj? */ - lua_assert(!lua_isnil(upi->L, -1)); - } - /* perms reftbl ... obj/nil */ - } - /* perms reftbl ... obj/nil */ - lua_assert(lua_gettop(upi->L) == stacksize + 1); -} - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud) -{ - /* We use the graciously provided ZIO (what the heck does the Z stand - * for?) library so that we don't have to deal with the reader directly. - * Letting the reader function decide how much data to return can be - * very unpleasant. - */ - UnpersistInfo upi; - upi.L = L; -#ifdef PLUTO_DEBUG - upi.level = 0; -#endif - - lua_checkstack(L, 3); - LIF(Z,init)(L, &upi.zio, reader, ud); - - /* perms */ - lua_newtable(L); - /* perms reftbl */ - lua_gc(L, LUA_GCSTOP, 0); - unpersist(&upi); - lua_gc(L, LUA_GCRESTART, 0); - /* perms reftbl rootobj */ - lua_replace(L, 2); - /* perms rootobj */ -} - -typedef struct LoadInfo_t { - char *buf; - size_t size; -} LoadInfo; - - -static const char *bufreader(lua_State *L, void *ud, size_t *sz) { - LoadInfo *li = (LoadInfo *)ud; - if(li->size == 0) { - return NULL; - } - *sz = li->size; - li->size = 0; - return li->buf; -} - -int unpersist_l(lua_State *L) -{ - LoadInfo li; - char const *origbuf; - char *tempbuf; - size_t bufsize; - /* perms? str? ...? */ - lua_settop(L, 2); - /* perms? str? */ - origbuf = luaL_checklstring(L, 2, &bufsize); - tempbuf = LIF(M,newvector)(L, bufsize, char); - memcpy(tempbuf, origbuf, bufsize); - - li.buf = tempbuf; - li.size = bufsize; - - /* perms? str */ - lua_pop(L, 1); - /* perms? */ - luaL_checktype(L, 1, LUA_TTABLE); - /* perms */ - pluto_unpersist(L, bufreader, &li); - /* perms rootobj */ - LIF(M,freearray)(L, tempbuf, bufsize, char); - return 1; -} - -/* Stefan's first C function for Lua! :) - Returns a string describing the Pluto version you're using. */ - -int version_l(lua_State *L) -{ - const char *version = VERSION; - - lua_settop(L, 0); - /* (empty) */ - lua_pushlstring(L, version, strlen(version)); - /* str */ - return 1; -} - -/* Set human-readable output on or off. */ -int human_l(lua_State *L) -{ - /* flag? ...? */ - lua_settop(L, 1); - /* flag? */ - /*luaL_checktype(L, 1, LUA_TBOOLEAN);*/ - /* flag */ - - humanReadable = lua_toboolean(L, 1); - - lua_settop(L, 0); - /* (empty) */ - return 0; -} - -static luaL_reg pluto_reg[] = { - { "persist", persist_l }, - { "unpersist", unpersist_l }, - { "version", version_l }, - { "human", human_l }, - { NULL, NULL } -}; - -LUALIB_API int luaopen_pluto(lua_State *L) { - luaL_openlib(L, "pluto", pluto_reg, 0); - return 1; -} diff --git a/engines/sword25/util/pluto/pluto.h b/engines/sword25/util/pluto/pluto.h deleted file mode 100644 index 3674842d44..0000000000 --- a/engines/sword25/util/pluto/pluto.h +++ /dev/null @@ -1,25 +0,0 @@ -/* $Id$ */ - -/* Pluto - Heavy-duty persistence for Lua - * Copyright (C) 2004 by Ben Sunshine-Hill, and released into the public - * domain. People making use of this software as part of an application - * are politely requested to email the author at sneftel@gmail.com - * with a brief description of the application, primarily to satisfy his - * curiosity. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* lua.h must be included before this file */ - -void pluto_persist(lua_State *L, lua_Chunkwriter writer, void *ud); - -void pluto_unpersist(lua_State *L, lua_Chunkreader reader, void *ud); - -LUALIB_API int luaopen_pluto(lua_State *L); diff --git a/engines/sword25/util/pluto/plzio.cpp b/engines/sword25/util/pluto/plzio.cpp deleted file mode 100644 index 21f69a9e8d..0000000000 --- a/engines/sword25/util/pluto/plzio.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* -** $Id$ -** a generic input stream interface -** See Copyright Notice in lua.h -*/ - - -#include <string.h> - -#define lzio_c -#define LUA_CORE - -#include "pdep/pdep.h" - -int pdep_fill (ZIO *z) { - size_t size; - lua_State *L = z->L; - const char *buff; - lua_unlock(L); - buff = z->reader(L, z->data, &size); - lua_lock(L); - if (buff == NULL || size == 0) return EOZ; - z->n = size - 1; - z->p = buff; - return char2int(*(z->p++)); -} - - -int pdep_lookahead (ZIO *z) { - if (z->n == 0) { - if (pdep_fill(z) == EOZ) - return EOZ; - else { - z->n++; /* pdep_fill removed first byte; put back it */ - z->p--; - } - } - return char2int(*z->p); -} - - -void pdep_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { - z->L = L; - z->reader = reader; - z->data = data; - z->n = 0; - z->p = NULL; -} - - -/* --------------------------------------------------------------- read --- */ -size_t pdep_read (ZIO *z, void *b, size_t n) { - while (n) { - size_t m; - if (pdep_lookahead(z) == EOZ) - return n; /* return number of missing bytes */ - m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ - memcpy(b, z->p, m); - z->n -= m; - z->p += m; - b = (char *)b + m; - n -= m; - } - return 0; -} - -/* ------------------------------------------------------------------------ */ -char *pdep_openspace (lua_State *L, Mbuffer *buff, size_t n) { - if (n > buff->buffsize) { - if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; - pdep_resizebuffer(L, buff, n); - } - return buff->buffer; -} diff --git a/engines/zvision/core/console.cpp b/engines/zvision/core/console.cpp index 07d3114ec8..b5e542d777 100644 --- a/engines/zvision/core/console.cpp +++ b/engines/zvision/core/console.cpp @@ -79,12 +79,14 @@ bool Console::cmdLoadSound(int argc, const char **argv) { Audio::AudioStream *soundStream = makeRawZorkStream(argv[1], _engine); Audio::SoundHandle handle; _engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle, soundStream, -1, 100, 0, DisposeAfterUse::YES, false, false); - } else if (argc == 4) { int isStereo = atoi(argv[3]); Common::File *file = new Common::File(); - file->open(argv[1]); + if (!_engine->getSearchManager()->openFile(*file, argv[1])) { + warning("File not found: %s", argv[1]); + return true; + } Audio::AudioStream *soundStream = makeRawZorkStream(file, atoi(argv[2]), isStereo == 0 ? false : true); Audio::SoundHandle handle; @@ -104,8 +106,10 @@ bool Console::cmdRawToWav(int argc, const char **argv) { } Common::File file; - if (!file.open(argv[1])) + if (!_engine->getSearchManager()->openFile(file, argv[1])) { + warning("File not found: %s", argv[1]); return true; + } Audio::AudioStream *audioStream = makeRawZorkStream(argv[1], _engine); @@ -134,6 +138,10 @@ bool Console::cmdRawToWav(int argc, const char **argv) { output.writeUint32LE(file.size() * 2); int16 *buffer = new int16[file.size()]; audioStream->readBuffer(buffer, file.size()); +#ifndef SCUMM_LITTLE_ENDIAN + for (int i = 0; i < file.size(); ++i) + buffer[i] = TO_LE_16(buffer[i]); +#endif output.write(buffer, file.size() * 2); delete[] buffer; diff --git a/engines/zvision/core/events.cpp b/engines/zvision/core/events.cpp index 6e8cf1fe4f..7804130e2a 100644 --- a/engines/zvision/core/events.cpp +++ b/engines/zvision/core/events.cpp @@ -28,8 +28,9 @@ #include "zvision/graphics/cursors/cursor_manager.h" #include "zvision/graphics/render_manager.h" #include "zvision/scripting/script_manager.h" -#include "zvision/core/menu.h" +#include "zvision/scripting/menu.h" #include "zvision/sound/zork_raw.h" +#include "zvision/text/string_manager.h" #include "common/events.h" #include "common/system.h" @@ -39,23 +40,50 @@ namespace ZVision { +void ZVision::pushKeyToCheatBuf(uint8 key) { + for (int i = 0; i < KEYBUF_SIZE - 1; i++) + _cheatBuffer[i] = _cheatBuffer[i + 1]; + + _cheatBuffer[KEYBUF_SIZE - 1] = key; +} + +bool ZVision::checkCode(const char *code) { + int codeLen = strlen(code); + + if (codeLen > KEYBUF_SIZE) + return false; + + for (int i = 0; i < codeLen; i++) + if (code[i] != _cheatBuffer[KEYBUF_SIZE - codeLen + i] && code[i] != '?') + return false; + + return true; +} + +uint8 ZVision::getBufferedKey(uint8 pos) { + if (pos >= KEYBUF_SIZE) + return 0; + else + return _cheatBuffer[KEYBUF_SIZE - pos - 1]; +} + void ZVision::shortKeys(Common::Event event) { if (event.kbd.hasFlags(Common::KBD_CTRL)) { switch (event.kbd.keycode) { case Common::KEYCODE_s: - if (getMenuBarEnable() & menuBar_Save) + if (_menu->getEnable() & kMenubarSave) _scriptManager->changeLocation('g', 'j', 's', 'e', 0); break; case Common::KEYCODE_r: - if (getMenuBarEnable() & menuBar_Restore) + if (_menu->getEnable() & kMenubarRestore) _scriptManager->changeLocation('g', 'j', 'r', 'e', 0); break; case Common::KEYCODE_p: - if (getMenuBarEnable() & menuBar_Settings) + if (_menu->getEnable() & kMenubarSettings) _scriptManager->changeLocation('g', 'j', 'p', 'e', 0); break; case Common::KEYCODE_q: - if (getMenuBarEnable() & menuBar_Exit) + if (_menu->getEnable() & kMenubarExit) ifQuit(); break; default: @@ -70,11 +98,11 @@ void ZVision::cheatCodes(uint8 key) { if (getGameId() == GID_GRANDINQUISITOR) { if (checkCode("IMNOTDEAF")) { // Unknown cheat - showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented")); + _renderManager->showDebugMsg(Common::String::format("IMNOTDEAF cheat or debug, not implemented")); } if (checkCode("3100OPB")) { - showDebugMsg(Common::String::format("Current location: %c%c%c%c", + _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c", _scriptManager->getStateValue(StateKey_World), _scriptManager->getStateValue(StateKey_Room), _scriptManager->getStateValue(StateKey_Node), @@ -91,9 +119,9 @@ void ZVision::cheatCodes(uint8 key) { } // There are 3 more cheats in script files: - // - "EAT ME": gjcr.scr - // - "WHOAMI": hp1e.scr - // - "HUISOK": uh1f.scr + // - "WHOAMI": gjcr.scr + // - "HUISOK": hp1e.scr + // - "EAT ME": uh1f.scr } else if (getGameId() == GID_NEMESIS) { if (checkCode("CHLOE")) { _scriptManager->changeLocation('t', 'm', '2', 'g', 0); @@ -101,7 +129,7 @@ void ZVision::cheatCodes(uint8 key) { } if (checkCode("77MASSAVE")) { - showDebugMsg(Common::String::format("Current location: %c%c%c%c", + _renderManager->showDebugMsg(Common::String::format("Current location: %c%c%c%c", _scriptManager->getStateValue(StateKey_World), _scriptManager->getStateValue(StateKey_Room), _scriptManager->getStateValue(StateKey_Node), @@ -130,15 +158,18 @@ void ZVision::cheatCodes(uint8 key) { } } - if (checkCode("FRAME")) - showDebugMsg(Common::String::format("FPS: ???, not implemented")); + if (checkCode("FRAME")) { + Common::String fpsStr = Common::String::format("FPS: %d", getFPS()); + _renderManager->showDebugMsg(fpsStr); + } + if (checkCode("COMPUTERARCH")) + _renderManager->showDebugMsg("COMPUTERARCH: var-viewer not implemented"); + + // This cheat essentially toggles the GOxxxx cheat below if (checkCode("XYZZY")) _scriptManager->setStateValue(StateKey_DebugCheats, 1 - _scriptManager->getStateValue(StateKey_DebugCheats)); - if (checkCode("COMPUTERARCH")) - showDebugMsg(Common::String::format("COMPUTERARCH: var-viewer not implemented")); - if (_scriptManager->getStateValue(StateKey_DebugCheats) == 1) if (checkCode("GO????")) _scriptManager->changeLocation(getBufferedKey(3), @@ -212,6 +243,11 @@ void ZVision::processEvents() { _scriptManager->getStateValue(StateKey_KbdRotateSpeed)) * 2; break; + case Common::KEYCODE_F10: { + Common::String fpsStr = Common::String::format("FPS: %d", getFPS()); + _renderManager->showDebugMsg(fpsStr); + } + break; default: break; } @@ -296,18 +332,21 @@ void ZVision::onMouseMove(const Common::Point &pos) { if (clippedPos.x >= _workingWindow.left && clippedPos.x < _workingWindow.left + ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((clippedPos.x - (ROTATION_SCREEN_EDGE_OFFSET + _workingWindow.left)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.left)) - mspeed).toInt(), -1); + _cursorManager->changeCursor(CursorIndex_Left); cursorWasChanged = true; } else if (clippedPos.x <= _workingWindow.right && clippedPos.x > _workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((clippedPos.x - (_workingWindow.right - ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MAX((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (clippedPos.x - _workingWindow.right + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1); _cursorManager->changeCursor(CursorIndex_Right); cursorWasChanged = true; @@ -318,18 +357,20 @@ void ZVision::onMouseMove(const Common::Point &pos) { if (clippedPos.y >= _workingWindow.top && clippedPos.y < _workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((clippedPos.y - (_workingWindow.top + ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MIN(((Common::Rational(mspeed, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.top)) - mspeed).toInt(), -1); _cursorManager->changeCursor(CursorIndex_UpArr); cursorWasChanged = true; } else if (clippedPos.y <= _workingWindow.bottom && clippedPos.y > _workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET) { int16 mspeed = _scriptManager->getStateValue(StateKey_RotateSpeed) >> 4; - if (mspeed <= 0) - mspeed = 400 >> 4; - _mouseVelocity = (((clippedPos.y - (_workingWindow.bottom - ROTATION_SCREEN_EDGE_OFFSET)) << 7) / ROTATION_SCREEN_EDGE_OFFSET * mspeed) >> 7; + if (mspeed <= 0) { + mspeed = 25; + } + _mouseVelocity = MAX((Common::Rational(MAX_ROTATION_SPEED, ROTATION_SCREEN_EDGE_OFFSET) * (pos.y - _workingWindow.bottom + ROTATION_SCREEN_EDGE_OFFSET)).toInt(), 1); _cursorManager->changeCursor(CursorIndex_DownArr); cursorWasChanged = true; @@ -450,4 +491,12 @@ uint8 ZVision::getZvisionKey(Common::KeyCode scummKeyCode) { return 0; } +bool ZVision::ifQuit() { + if (_renderManager->askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) { + quitGame(); + return true; + } + return false; +} + } // End of namespace ZVision diff --git a/engines/zvision/detection.cpp b/engines/zvision/detection.cpp index ebf5bdcfdd..1eaff83413 100644 --- a/engines/zvision/detection.cpp +++ b/engines/zvision/detection.cpp @@ -26,7 +26,7 @@ #include "zvision/zvision.h" #include "zvision/detection.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/script_manager.h" #include "common/translation.h" @@ -55,6 +55,11 @@ static const PlainGameDescriptor zVisionGames[] = { namespace ZVision { +#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1 +#define GAMEOPTION_DOUBLE_FPS GUIO_GAMEOPTIONS2 +#define GAMEOPTION_ENABLE_VENUS GUIO_GAMEOPTIONS3 +#define GAMEOPTION_DISABLE_ANIM_WHILE_TURNING GUIO_GAMEOPTIONS4 + static const ZVisionGameDescription gameDescriptions[] = { { @@ -66,7 +71,7 @@ static const ZVisionGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformDOS, ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) }, GID_NEMESIS }, @@ -80,7 +85,7 @@ static const ZVisionGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_DEMO, - GUIO1(GUIO_NONE) + GUIO4(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_ENABLE_VENUS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) }, GID_NEMESIS }, @@ -94,35 +99,35 @@ static const ZVisionGameDescription gameDescriptions[] = { Common::EN_ANY, Common::kPlatformWindows, ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) }, GID_GRANDINQUISITOR }, { - // Zork Grand Inquisitor English demo version + // Zork Grand Inquisitor English DVD version { "zgi", - "Demo", - AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239), + "DVD", + AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326), Common::EN_ANY, Common::kPlatformWindows, - ADGF_DEMO, - GUIO1(GUIO_NONE) + ADGF_NO_FLAGS, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) }, GID_GRANDINQUISITOR }, { - // Zork Grand Inquisitor English DVD version + // Zork Grand Inquisitor English demo version { "zgi", - "DVD", - AD_ENTRY1s("SCRIPTS.ZFS", "03157a3399513bfaaf8dc6d5ab798b36", 8433326), + "Demo", + AD_ENTRY1s("SCRIPTS.ZFS", "71a2494fd2fb999347deb13401e9b998", 304239), Common::EN_ANY, Common::kPlatformWindows, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) + ADGF_DEMO, + GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_DOUBLE_FPS, GAMEOPTION_DISABLE_ANIM_WHILE_TURNING) }, GID_GRANDINQUISITOR }, @@ -140,23 +145,53 @@ static const char *directoryGlobs[] = { 0 }; -static const ExtraGuiOption ZVisionExtraGuiOption = { - _s("Use original save/load screens"), - _s("Use the original save/load screens, instead of the ScummVM ones"), - "originalsaveload", - false -}; +static const ADExtraGuiOptionsMap optionsList[] = { + { + GAMEOPTION_ORIGINAL_SAVELOAD, + { + _s("Use original save/load screens"), + _s("Use the original save/load screens, instead of the ScummVM ones"), + "originalsaveload", + false + } + }, + + { + GAMEOPTION_DOUBLE_FPS, + { + _s("Double FPS"), + _s("Halve the update delay"), + "doublefps", + false + } + }, + + { + GAMEOPTION_ENABLE_VENUS, + { + _s("Enable Venus"), + _s("Enable the Venus help system"), + "venusenabled", + true + } + }, -static const ExtraGuiOption ZVisionExtraGuiOption2 = { - _s("Double FPS"), - _s("Halve the update delay"), - "doublefps", - false + { + GAMEOPTION_DISABLE_ANIM_WHILE_TURNING, + { + _s("Disable animation while turning"), + _s("Disable animation while turning in panoramic mode"), + "noanimwhileturning", + false + } + }, + + AD_EXTRA_GUI_OPTIONS_TERMINATOR }; class ZVisionMetaEngine : public AdvancedMetaEngine { public: - ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), zVisionGames) { + ZVisionMetaEngine() : AdvancedMetaEngine(ZVision::gameDescriptions, sizeof(ZVision::ZVisionGameDescription), zVisionGames, optionsList) { _maxScanDepth = 2; _directoryGlobs = directoryGlobs; _singleid = "zvision"; @@ -172,7 +207,6 @@ public: virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const; SaveStateList listSaves(const char *target) const; virtual int getMaximumSaveSlot() const; void removeSaveState(const char *target, int slot) const; @@ -223,13 +257,6 @@ bool ZVisionMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADG return gd != 0; } -const ExtraGuiOptions ZVisionMetaEngine::getExtraGuiOptions(const Common::String &target) const { - ExtraGuiOptions options; - options.push_back(ZVisionExtraGuiOption); - options.push_back(ZVisionExtraGuiOption2); - return options; -} - SaveStateList ZVisionMetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); ZVision::SaveGameHeader header; @@ -308,6 +335,11 @@ SaveStateDescriptor ZVisionMetaEngine::querySaveMetaInfos(const char *target, in if (successfulRead) { SaveStateDescriptor desc(slot, header.saveName); + // Do not allow save slot 0 (used for auto-saving) to be deleted or + // overwritten. + desc.setDeletableFlag(slot != 0); + desc.setWriteProtectedFlag(slot == 0); + desc.setThumbnail(header.thumbnail); if (header.version > 0) { diff --git a/engines/zvision/core/save_manager.cpp b/engines/zvision/file/save_manager.cpp index 20bd39fde5..042fafd38e 100644 --- a/engines/zvision/core/save_manager.cpp +++ b/engines/zvision/file/save_manager.cpp @@ -22,8 +22,8 @@ #include "common/scummsys.h" -#include "zvision/core/save_manager.h" #include "zvision/zvision.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" @@ -78,9 +78,6 @@ bool SaveManager::scummVMSaveLoadDialog(bool isSave) { } void SaveManager::saveGame(uint slot, const Common::String &saveName) { - // The games only support 20 slots - //assert(slot <= 1 && slot <= 20); - Common::SaveFileManager *saveFileManager = g_system->getSavefileManager(); Common::OutSaveFile *file = saveFileManager->openForSaving(_engine->generateSaveFileName(slot)); @@ -90,6 +87,8 @@ void SaveManager::saveGame(uint slot, const Common::String &saveName) { file->finalize(); delete file; + + _lastSaveTime = g_system->getMillis(); } void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::MemoryWriteStreamDynamic *stream) { @@ -102,6 +101,8 @@ void SaveManager::saveGame(uint slot, const Common::String &saveName, Common::Me file->finalize(); delete file; + + _lastSaveTime = g_system->getMillis(); } void SaveManager::saveGameBuffered(uint slot, const Common::String &saveName) { @@ -112,19 +113,10 @@ void SaveManager::saveGameBuffered(uint slot, const Common::String &saveName) { } void SaveManager::autoSave() { - Common::OutSaveFile *file = g_system->getSavefileManager()->openForSaving(_engine->generateAutoSaveFileName()); - - writeSaveGameHeader(file, "auto"); - - _engine->getScriptManager()->serialize(file); - - // Cleanup - file->finalize(); - delete file; + saveGame(0, "Auto save"); } void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::String &saveName) { - file->writeUint32BE(SAVEGAME_ID); // Write version @@ -148,9 +140,6 @@ void SaveManager::writeSaveGameHeader(Common::OutSaveFile *file, const Common::S } Common::Error SaveManager::loadGame(uint slot) { - // The games only support 20 slots - //assert(slot <= 1 && slot <= 20); - Common::SeekableReadStream *saveFile = getSlotFile(slot); if (saveFile == 0) { return Common::kPathDoesNotExist; @@ -226,7 +215,13 @@ bool SaveManager::readSaveGameHeader(Common::InSaveFile *in, SaveGameHeader &hea // Check that the save version isn't newer than this binary if (header.version > SAVE_VERSION) { uint tempVersion = header.version; - GUI::MessageDialog dialog(Common::String::format("This save file uses version %u, but this engine only supports up to version %d. You will need an updated version of the engine to use this save file.", tempVersion, SAVE_VERSION), "OK"); + GUI::MessageDialog dialog( + Common::String::format( + "This save file uses version %u, but this engine only " + "supports up to version %d. You will need an updated version " + "of the engine to use this save file.", tempVersion, SAVE_VERSION + ), + "OK"); dialog.runModal(); } diff --git a/engines/zvision/core/save_manager.h b/engines/zvision/file/save_manager.h index 75841331e7..fc8db67566 100644 --- a/engines/zvision/core/save_manager.h +++ b/engines/zvision/file/save_manager.h @@ -48,13 +48,18 @@ struct SaveGameHeader { class SaveManager { public: - SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL) {} + SaveManager(ZVision *engine) : _engine(engine), _tempSave(NULL), _lastSaveTime(0) {} ~SaveManager() { flushSaveBuffer(); } + uint32 getLastSaveTime() const { + return _lastSaveTime; + } + private: ZVision *_engine; + uint32 _lastSaveTime; static const uint32 SAVEGAME_ID; enum { @@ -67,8 +72,7 @@ private: public: /** * Called every room change. Saves the state of the room just before - * we switched rooms. Uses ZVision::generateAutoSaveFileName() to - * create the save file name. + * the room changes. */ void autoSave(); /** diff --git a/engines/zvision/file/search_manager.cpp b/engines/zvision/file/search_manager.cpp index 7a907df39c..ec250ff648 100644 --- a/engines/zvision/file/search_manager.cpp +++ b/engines/zvision/file/search_manager.cpp @@ -169,8 +169,13 @@ void SearchManager::loadZix(const Common::String &name) { line.trim(); if (line.matchString("----------*", true)) break; - else if (line.matchString("DIR:*", true)) { + else if (line.matchString("DIR:*", true) || line.matchString("CD0:*", true) || line.matchString("CD1:*", true)) { Common::String path(line.c_str() + 5); + // Check if INQUIS.ZIX refers to the ZGI folder, and check the game + // root folder instead + if (path.hasPrefix("zgi\\")) + path = Common::String(path.c_str() + 4); + Common::Archive *arc; char tempPath[128]; strcpy(tempPath, path.c_str()); diff --git a/engines/zvision/graphics/cursors/cursor.cpp b/engines/zvision/graphics/cursors/cursor.cpp index 07323b45c4..f32c68645d 100644 --- a/engines/zvision/graphics/cursors/cursor.cpp +++ b/engines/zvision/graphics/cursors/cursor.cpp @@ -36,35 +36,6 @@ ZorkCursor::ZorkCursor() _hotspotY(0) { } -ZorkCursor::ZorkCursor(const Common::String &fileName) - : _width(0), - _height(0), - _hotspotX(0), - _hotspotY(0) { - Common::File file; - if (!file.open(fileName)) - return; - - uint32 magic = file.readUint32BE(); - if (magic != MKTAG('Z', 'C', 'R', '1')) { - warning("%s is not a Zork Cursor file", fileName.c_str()); - return; - } - - _hotspotX = file.readUint16LE(); - _hotspotY = file.readUint16LE(); - _width = file.readUint16LE(); - _height = file.readUint16LE(); - - uint dataSize = _width * _height * sizeof(uint16); - _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); - uint32 bytesRead = file.read(_surface.getPixels(), dataSize); - assert(bytesRead == dataSize); - - // Convert to RGB 565 - _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); -} - ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) : _width(0), _height(0), @@ -86,12 +57,15 @@ ZorkCursor::ZorkCursor(ZVision *engine, const Common::String &fileName) _height = file.readUint16LE(); uint dataSize = _width * _height * sizeof(uint16); - _surface.create(_width, _height, Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0)); + _surface.create(_width, _height, engine->_resourcePixelFormat); uint32 bytesRead = file.read(_surface.getPixels(), dataSize); assert(bytesRead == dataSize); - // Convert to RGB 565 - _surface.convertToInPlace(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); +#ifndef SCUMM_LITTLE_ENDIAN + int16 *buffer = (int16 *)_surface.getPixels(); + for (uint32 i = 0; i < dataSize / 2; ++i) + buffer[i] = FROM_LE_16(buffer[i]); +#endif } ZorkCursor::ZorkCursor(const ZorkCursor &other) { diff --git a/engines/zvision/graphics/cursors/cursor.h b/engines/zvision/graphics/cursors/cursor.h index 0c1e99411f..6e0083520a 100644 --- a/engines/zvision/graphics/cursors/cursor.h +++ b/engines/zvision/graphics/cursors/cursor.h @@ -39,7 +39,6 @@ namespace ZVision { class ZorkCursor { public: ZorkCursor(); - ZorkCursor(const Common::String &fileName); ZorkCursor(ZVision *engine, const Common::String &fileName); ZorkCursor(const ZorkCursor &other); ~ZorkCursor(); diff --git a/engines/zvision/graphics/cursors/cursor_manager.cpp b/engines/zvision/graphics/cursors/cursor_manager.cpp index 92fd461c72..c364426bad 100644 --- a/engines/zvision/graphics/cursors/cursor_manager.cpp +++ b/engines/zvision/graphics/cursors/cursor_manager.cpp @@ -45,7 +45,7 @@ const char *CursorManager::_zNemCursorFileNames[NUM_CURSORS] = { "00act", "arrow "hright", "hup", "00idle", "left", "right", "ssurr", "stilt", "turn", "up" }; -CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat) +CursorManager::CursorManager(ZVision *engine, const Graphics::PixelFormat pixelFormat) : _engine(engine), _pixelFormat(pixelFormat), _cursorIsPushed(false), @@ -106,7 +106,7 @@ void CursorManager::initialize() { } void CursorManager::changeCursor(const ZorkCursor &cursor) { - CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, _pixelFormat); + CursorMan.replaceCursor(cursor.getSurface(), cursor.getWidth(), cursor.getHeight(), cursor.getHotspotX(), cursor.getHotspotY(), cursor.getKeyColor(), false, &_pixelFormat); } void CursorManager::cursorDown(bool pushed) { diff --git a/engines/zvision/graphics/cursors/cursor_manager.h b/engines/zvision/graphics/cursors/cursor_manager.h index bbfa085c23..35c605baf8 100644 --- a/engines/zvision/graphics/cursors/cursor_manager.h +++ b/engines/zvision/graphics/cursors/cursor_manager.h @@ -58,7 +58,7 @@ enum CursorIndex { */ class CursorManager { public: - CursorManager(ZVision *engine, const Graphics::PixelFormat *pixelFormat); + CursorManager(ZVision *engine, const Graphics::PixelFormat pixelFormat); private: static const int NUM_CURSORS = 18; @@ -67,7 +67,7 @@ private: ZorkCursor _cursors[NUM_CURSORS + 2][2]; ZVision *_engine; - const Graphics::PixelFormat *_pixelFormat; + const Graphics::PixelFormat _pixelFormat; bool _cursorIsPushed; int _item; int _lastitem; diff --git a/engines/zvision/graphics/effects/fog.cpp b/engines/zvision/graphics/effects/fog.cpp index f59e82a4a0..32a01915d3 100644 --- a/engines/zvision/graphics/effects/fog.cpp +++ b/engines/zvision/graphics/effects/fog.cpp @@ -31,7 +31,7 @@ namespace ZVision { FogFx::FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds): - Effect(engine, key, region, ported) { + GraphicsEffect(engine, key, region, ported) { _map = Map; @@ -79,10 +79,10 @@ const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) { if (it->inEffect) { // Not 100% equivalent, but looks nice and not buggy uint8 sr, sg, sb; - _engine->_pixelFormat.colorToRGB(lineBuf[i], sr, sg, sb); + _engine->_resourcePixelFormat.colorToRGB(lineBuf[i], sr, sg, sb); uint16 fogColor = *(uint16 *)_fog.getBasePtr((i + _pos) % _fog.w, j); uint8 dr, dg, db; - _engine->_pixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db); + _engine->_resourcePixelFormat.colorToRGB(_colorMap[fogColor & 0x1F], dr, dg, db); uint16 fr = dr + sr; if (fr > 255) fr = 255; @@ -92,7 +92,7 @@ const Graphics::Surface *FogFx::draw(const Graphics::Surface &srcSubRect) { uint16 fb = db + sb; if (fb > 255) fb = 255; - lineBuf[i] = _engine->_pixelFormat.RGBToColor(fr, fg, fb); + lineBuf[i] = _engine->_resourcePixelFormat.RGBToColor(fr, fg, fb); } cnt++; if (cnt >= it->count) { @@ -138,14 +138,14 @@ void FogFx::update() { // Not 100% equivalent, but looks nice and not buggy - _colorMap[31] = _engine->_pixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3); + _colorMap[31] = _engine->_resourcePixelFormat.RGBToColor(_r << 3, _g << 3, _b << 3); for (uint8 i = 0; i < 31; i++) { float perc = (float)i / 31.0; uint8 cr = (float)_r * perc; uint8 cg = (float)_g * perc; uint8 cb = (float)_b * perc; - _colorMap[i] = _engine->_pixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3); + _colorMap[i] = _engine->_resourcePixelFormat.RGBToColor(cr << 3, cg << 3, cb << 3); } } diff --git a/engines/zvision/graphics/effects/fog.h b/engines/zvision/graphics/effects/fog.h index 45d6f9596d..498347609e 100644 --- a/engines/zvision/graphics/effects/fog.h +++ b/engines/zvision/graphics/effects/fog.h @@ -23,13 +23,14 @@ #ifndef ZVISION_FOG_H #define ZVISION_FOG_H -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class FogFx : public Effect { +// Used by Zork: Nemesis for the mixing chamber gas effect in the gas puzzle (location tt5e, when the blinds are down) +class FogFx : public GraphicsEffect { public: FogFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, const Common::String &clouds); diff --git a/engines/zvision/graphics/effects/light.cpp b/engines/zvision/graphics/effects/light.cpp index 00b3811d65..39341687f8 100644 --- a/engines/zvision/graphics/effects/light.cpp +++ b/engines/zvision/graphics/effects/light.cpp @@ -30,7 +30,7 @@ namespace ZVision { LightFx::LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD, int8 maxD): - Effect(engine, key, region, ported) { + GraphicsEffect(engine, key, region, ported) { _map = Map; _delta = delta; _up = true; @@ -59,10 +59,10 @@ const Graphics::Surface *LightFx::draw(const Graphics::Surface &srcSubRect) { if (_pos < 0) { uint8 cc = ((-_pos) & 0x1F) << 3; - dcolor = _engine->_pixelFormat.RGBToColor(cc, cc, cc); + dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc); } else { uint8 cc = (_pos & 0x1F) << 3; - dcolor = _engine->_pixelFormat.RGBToColor(cc, cc, cc); + dcolor = _engine->_resourcePixelFormat.RGBToColor(cc, cc, cc); } for (uint16 j = 0; j < _surface.h; j++) { diff --git a/engines/zvision/graphics/effects/light.h b/engines/zvision/graphics/effects/light.h index ae87d66cb3..cd73a585ec 100644 --- a/engines/zvision/graphics/effects/light.h +++ b/engines/zvision/graphics/effects/light.h @@ -23,13 +23,13 @@ #ifndef LIGHTFX_H_INCLUDED #define LIGHTFX_H_INCLUDED -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class LightFx : public Effect { +class LightFx : public GraphicsEffect { public: LightFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, EffectMap *Map, int8 delta, int8 minD = -127, int8 maxD = 127); diff --git a/engines/zvision/graphics/effects/wave.cpp b/engines/zvision/graphics/effects/wave.cpp index 1b3aa040e8..cec631611b 100644 --- a/engines/zvision/graphics/effects/wave.cpp +++ b/engines/zvision/graphics/effects/wave.cpp @@ -30,7 +30,7 @@ namespace ZVision { WaveFx::WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd): - Effect(engine, key, region, ported) { + GraphicsEffect(engine, key, region, ported) { _frame = 0; _frameCount = frames; diff --git a/engines/zvision/graphics/effects/wave.h b/engines/zvision/graphics/effects/wave.h index 2e813ed5b6..8e912372d7 100644 --- a/engines/zvision/graphics/effects/wave.h +++ b/engines/zvision/graphics/effects/wave.h @@ -24,13 +24,13 @@ #define WAVEFX_H_INCLUDED #include "common/array.h" -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class WaveFx : public Effect { +class WaveFx : public GraphicsEffect { public: WaveFx(ZVision *engine, uint32 key, Common::Rect region, bool ported, int16 frames, int16 centerX, int16 centerY, float ampl, float waveln, float spd); diff --git a/engines/zvision/graphics/effect.h b/engines/zvision/graphics/graphics_effect.h index c6653c6037..bfa266b11d 100644 --- a/engines/zvision/graphics/effect.h +++ b/engines/zvision/graphics/graphics_effect.h @@ -20,8 +20,8 @@ * */ -#ifndef EFFECT_H_INCLUDED -#define EFFECT_H_INCLUDED +#ifndef GRAPHICS_EFFECT_H_INCLUDED +#define GRAPHICS_EFFECT_H_INCLUDED #include "common/rect.h" #include "common/list.h" @@ -33,13 +33,13 @@ namespace ZVision { class ZVision; -class Effect { +class GraphicsEffect { public: - Effect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) { - _surface.create(_region.width(), _region.height(), _engine->_pixelFormat); + GraphicsEffect(ZVision *engine, uint32 key, Common::Rect region, bool ported) : _engine(engine), _key(key), _region(region), _ported(ported) { + _surface.create(_region.width(), _region.height(), _engine->_resourcePixelFormat); } - virtual ~Effect() {} + virtual ~GraphicsEffect() {} uint32 getKey() { return _key; @@ -80,4 +80,4 @@ typedef Common::List<EffectMapUnit> EffectMap; } // End of namespace ZVision -#endif // EFFECT_H_INCLUDED +#endif // GRAPHICS_EFFECT_H_INCLUDED diff --git a/engines/zvision/graphics/render_manager.cpp b/engines/zvision/graphics/render_manager.cpp index b9305f5dcc..a1cc8ac53c 100644 --- a/engines/zvision/graphics/render_manager.cpp +++ b/engines/zvision/graphics/render_manager.cpp @@ -39,7 +39,7 @@ namespace ZVision { -RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat) +RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat, bool doubleFPS) : _engine(engine), _system(engine->_system), _workingWidth(workingWindow.width()), @@ -51,7 +51,8 @@ RenderManager::RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowH _backgroundWidth(0), _backgroundHeight(0), _backgroundOffset(0), - _renderTable(_workingWidth, _workingHeight) { + _renderTable(_workingWidth, _workingHeight), + _doubleFPS(doubleFPS) { _backgroundSurface.create(_workingWidth, _workingHeight, _pixelFormat); _effectSurface.create(_workingWidth, _workingHeight, _pixelFormat); @@ -103,7 +104,8 @@ void RenderManager::renderSceneToScreen() { post = (*it)->draw(_currentBackgroundImage.getSubArea(rect)); else post = (*it)->draw(_effectSurface.getSubArea(rect)); - blitSurfaceToSurface(*post, _effectSurface, screenSpaceLocation.left, screenSpaceLocation.top); + Common::Rect empty; + blitSurfaceToSurface(*post, empty, _effectSurface, screenSpaceLocation.left, screenSpaceLocation.top); screenSpaceLocation.clip(windowRect); if (_backgroundSurfaceDirtyRect .isEmpty()) { _backgroundSurfaceDirtyRect = screenSpaceLocation; @@ -127,14 +129,29 @@ void RenderManager::renderSceneToScreen() { } if (!outWndDirtyRect.isEmpty()) { - _system->copyRectToScreen(out->getBasePtr(outWndDirtyRect.left, outWndDirtyRect.top), out->pitch, - outWndDirtyRect.left + _workingWindow.left, - outWndDirtyRect.top + _workingWindow.top, - outWndDirtyRect.width(), - outWndDirtyRect.height()); + Common::Rect rect( + outWndDirtyRect.left + _workingWindow.left, + outWndDirtyRect.top + _workingWindow.top, + outWndDirtyRect.left + _workingWindow.left + outWndDirtyRect.width(), + outWndDirtyRect.top + _workingWindow.top + outWndDirtyRect.height() + ); + copyToScreen(*out, rect, outWndDirtyRect.left, outWndDirtyRect.top); } } +void RenderManager::copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop) { + // Convert the surface to RGB565, if needed + Graphics::Surface *outSurface = surface.convertTo(_engine->_screenPixelFormat); + _system->copyRectToScreen(outSurface->getBasePtr(srcLeft, srcTop), + outSurface->pitch, + rect.left, + rect.top, + rect.width(), + rect.height()); + outSurface->free(); + delete outSurface; +} + void RenderManager::renderImageToBackground(const Common::String &fileName, int16 destX, int16 destY) { Graphics::Surface surface; readImageToSurface(fileName, surface); @@ -162,97 +179,8 @@ void RenderManager::renderImageToBackground(const Common::String &fileName, int1 } void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination) { - Common::File file; - - if (!_engine->getSearchManager()->openFile(file, fileName)) { - warning("Could not open file %s", fileName.c_str()); - return; - } - - // Read the magic number - // Some files are true TGA, while others are TGZ - uint32 fileType = file.readUint32BE(); - - uint32 imageWidth; - uint32 imageHeight; - Image::TGADecoder tga; - uint16 *buffer; bool isTransposed = _renderTable.getRenderState() == RenderTable::PANORAMA; - // All ZVision images are in RGB 555 - Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - destination.format = pixelFormat555; - - bool isTGZ; - - // Check for TGZ files - if (fileType == MKTAG('T', 'G', 'Z', '\0')) { - isTGZ = true; - - // TGZ files have a header and then Bitmap data that is compressed with LZSS - uint32 decompressedSize = file.readSint32LE(); - imageWidth = file.readSint32LE(); - imageHeight = file.readSint32LE(); - - LzssReadStream lzssStream(&file); - buffer = (uint16 *)(new uint16[decompressedSize]); - lzssStream.read(buffer, decompressedSize); - } else { - isTGZ = false; - - // Reset the cursor - file.seek(0); - - // Decode - if (!tga.loadStream(file)) { - warning("Error while reading TGA image"); - return; - } - - Graphics::Surface tgaSurface = *(tga.getSurface()); - imageWidth = tgaSurface.w; - imageHeight = tgaSurface.h; - - buffer = (uint16 *)tgaSurface.getPixels(); - } - - // Flip the width and height if transposed - if (isTransposed) { - uint16 temp = imageHeight; - imageHeight = imageWidth; - imageWidth = temp; - } - - // If the destination internal buffer is the same size as what we're copying into it, - // there is no need to free() and re-create - if (imageWidth != destination.w || imageHeight != destination.h) { - destination.create(imageWidth, imageHeight, pixelFormat555); - } - - // If transposed, 'un-transpose' the data while copying it to the destination - // Otherwise, just do a simple copy - if (isTransposed) { - uint16 *dest = (uint16 *)destination.getPixels(); - - for (uint32 y = 0; y < imageHeight; ++y) { - uint32 columnIndex = y * imageWidth; - - for (uint32 x = 0; x < imageWidth; ++x) { - dest[columnIndex + x] = buffer[x * imageHeight + y]; - } - } - } else { - memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); - } - - // Cleanup - if (isTGZ) { - delete[] buffer; - } else { - tga.destroy(); - } - - // Convert in place to RGB 565 from RGB 555 - destination.convertToInPlace(_pixelFormat); + readImageToSurface(fileName, destination, isTransposed); } void RenderManager::readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed) { @@ -272,8 +200,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: Image::TGADecoder tga; uint16 *buffer; // All ZVision images are in RGB 555 - Graphics::PixelFormat pixelFormat555 = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); - destination.format = pixelFormat555; + destination.format = _engine->_resourcePixelFormat; bool isTGZ; @@ -282,13 +209,17 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: isTGZ = true; // TGZ files have a header and then Bitmap data that is compressed with LZSS - uint32 decompressedSize = file.readSint32LE(); + uint32 decompressedSize = file.readSint32LE() / 2; imageWidth = file.readSint32LE(); imageHeight = file.readSint32LE(); LzssReadStream lzssStream(&file); buffer = (uint16 *)(new uint16[decompressedSize]); - lzssStream.read(buffer, decompressedSize); + lzssStream.read(buffer, 2 * decompressedSize); +#ifndef SCUMM_LITTLE_ENDIAN + for (uint32 i = 0; i < decompressedSize; ++i) + buffer[i] = FROM_LE_16(buffer[i]); +#endif } else { isTGZ = false; @@ -318,7 +249,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: // If the destination internal buffer is the same size as what we're copying into it, // there is no need to free() and re-create if (imageWidth != destination.w || imageHeight != destination.h) { - destination.create(imageWidth, imageHeight, pixelFormat555); + destination.create(imageWidth, imageHeight, _engine->_resourcePixelFormat); } // If transposed, 'un-transpose' the data while copying it to the destination @@ -334,7 +265,7 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: } } } else { - memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * _pixelFormat.bytesPerPixel); + memcpy(destination.getPixels(), buffer, imageWidth * imageHeight * destination.format.bytesPerPixel); } // Cleanup @@ -343,9 +274,6 @@ void RenderManager::readImageToSurface(const Common::String &fileName, Graphics: } else { tga.destroy(); } - - // Convert in place to RGB 565 from RGB 555 - destination.convertToInPlace(_pixelFormat); } const Common::Point RenderManager::screenSpaceToImageSpace(const Common::Point &point) { @@ -459,10 +387,6 @@ void RenderManager::scaleBuffer(const void *src, void *dst, uint32 srcWidth, uin } void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y) { - - if (src.format != dst.format) - return; - Common::Rect srcRect = _srcRect; if (srcRect.isEmpty()) srcRect = Common::Rect(src.w, src.h); @@ -473,8 +397,10 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (srcRect.isEmpty() || !srcRect.isValidRect()) return; + Graphics::Surface *srcAdapted = src.convertTo(dst.format); + // Copy srcRect from src surface to dst surface - const byte *srcBuffer = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top); int xx = _x; int yy = _y; @@ -484,8 +410,11 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (yy < 0) yy = 0; - if (_x >= dst.w || _y >= dst.h) + if (_x >= dst.w || _y >= dst.h) { + srcAdapted->free(); + delete srcAdapted; return; + } byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); @@ -493,17 +422,16 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com int32 h = srcRect.height(); for (int32 y = 0; y < h; y++) { - memcpy(dstBuffer, srcBuffer, w * src.format.bytesPerPixel); - srcBuffer += src.pitch; + memcpy(dstBuffer, srcBuffer, w * srcAdapted->format.bytesPerPixel); + srcBuffer += srcAdapted->pitch; dstBuffer += dst.pitch; } + + srcAdapted->free(); + delete srcAdapted; } void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey) { - - if (src.format != dst.format) - return; - Common::Rect srcRect = _srcRect; if (srcRect.isEmpty()) srcRect = Common::Rect(src.w, src.h); @@ -514,10 +442,11 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (srcRect.isEmpty() || !srcRect.isValidRect()) return; - uint32 _keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1); + Graphics::Surface *srcAdapted = src.convertTo(dst.format); + uint32 keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1); // Copy srcRect from src surface to dst surface - const byte *srcBuffer = (const byte *)src.getBasePtr(srcRect.left, srcRect.top); + const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top); int xx = _x; int yy = _y; @@ -527,8 +456,11 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com if (yy < 0) yy = 0; - if (_x >= dst.w || _y >= dst.h) + if (_x >= dst.w || _y >= dst.h) { + srcAdapted->free(); + delete srcAdapted; return; + } byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); @@ -536,12 +468,12 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com int32 h = srcRect.height(); for (int32 y = 0; y < h; y++) { - switch (src.format.bytesPerPixel) { + switch (srcAdapted->format.bytesPerPixel) { case 1: { const uint *srcTemp = (const uint *)srcBuffer; uint *dstTemp = (uint *)dstBuffer; for (int32 x = 0; x < w; x++) { - if (*srcTemp != _keycolor) + if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; @@ -553,7 +485,7 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com const uint16 *srcTemp = (const uint16 *)srcBuffer; uint16 *dstTemp = (uint16 *)dstBuffer; for (int32 x = 0; x < w; x++) { - if (*srcTemp != _keycolor) + if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; @@ -565,7 +497,7 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com const uint32 *srcTemp = (const uint32 *)srcBuffer; uint32 *dstTemp = (uint32 *)dstBuffer; for (int32 x = 0; x < w; x++) { - if (*srcTemp != _keycolor) + if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; @@ -576,35 +508,20 @@ void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Com default: break; } - srcBuffer += src.pitch; + srcBuffer += srcAdapted->pitch; dstBuffer += dst.pitch; } -} - -void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, dst, x, y); -} -void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y, uint32 colorkey) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, dst, x, y, colorkey); + srcAdapted->free(); + delete srcAdapted; } -void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y) { +void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey) { Common::Rect empt; - blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y); - Common::Rect dirty(src.w, src.h); - dirty.translate(x, y); - if (_backgroundDirtyRect.isEmpty()) - _backgroundDirtyRect = dirty; + if (colorkey >= 0) + blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y, colorkey); else - _backgroundDirtyRect.extend(dirty); -} - -void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y, colorkey); + blitSurfaceToSurface(src, empt, _currentBackgroundImage, x, y); Common::Rect dirty(src.w, src.h); dirty.translate(x, y); if (_backgroundDirtyRect.isEmpty()) @@ -613,23 +530,10 @@ void RenderManager::blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, _backgroundDirtyRect.extend(dirty); } -void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect) { - if (src.w == _dstRect.width() && src.h == _dstRect.height()) - blitSurfaceToBkg(src, _dstRect.left, _dstRect.top); - else { - Graphics::Surface *tmp = new Graphics::Surface; - tmp->create(_dstRect.width(), _dstRect.height(), src.format); - scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height()); - blitSurfaceToBkg(*tmp, _dstRect.left, _dstRect.top); - tmp->free(); - delete tmp; - } -} - -void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, uint32 colorkey) { - if (src.w == _dstRect.width() && src.h == _dstRect.height()) +void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey) { + if (src.w == _dstRect.width() && src.h == _dstRect.height()) { blitSurfaceToBkg(src, _dstRect.left, _dstRect.top, colorkey); - else { + } else { Graphics::Surface *tmp = new Graphics::Surface; tmp->create(_dstRect.width(), _dstRect.height(), src.format); scaleBuffer(src.getPixels(), tmp->getPixels(), src.w, src.h, src.format.bytesPerPixel, _dstRect.width(), _dstRect.height()); @@ -639,20 +543,12 @@ void RenderManager::blitSurfaceToBkgScaled(const Graphics::Surface &src, const C } } -void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y) { +void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey) { Common::Rect empt; - blitSurfaceToSurface(src, empt, _menuSurface, x, y); - Common::Rect dirty(src.w, src.h); - dirty.translate(x, y); - if (_menuSurfaceDirtyRect.isEmpty()) - _menuSurfaceDirtyRect = dirty; + if (colorkey >= 0) + blitSurfaceToSurface(src, empt, _menuSurface, x, y, colorkey); else - _menuSurfaceDirtyRect.extend(dirty); -} - -void RenderManager::blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey) { - Common::Rect empt; - blitSurfaceToSurface(src, empt, _menuSurface, x, y, colorkey); + blitSurfaceToSurface(src, empt, _menuSurface, x, y); Common::Rect dirty(src.w, src.h); dirty.translate(x, y); if (_menuSurfaceDirtyRect.isEmpty()) @@ -676,28 +572,18 @@ Graphics::Surface *RenderManager::getBkgRect(Common::Rect &rect) { return srf; } -Graphics::Surface *RenderManager::loadImage(Common::String &file) { +Graphics::Surface *RenderManager::loadImage(Common::String file) { Graphics::Surface *tmp = new Graphics::Surface; readImageToSurface(file, *tmp); return tmp; } -Graphics::Surface *RenderManager::loadImage(const char *file) { - Common::String str = Common::String(file); - return loadImage(str); -} - -Graphics::Surface *RenderManager::loadImage(Common::String &file, bool transposed) { +Graphics::Surface *RenderManager::loadImage(Common::String file, bool transposed) { Graphics::Surface *tmp = new Graphics::Surface; readImageToSurface(file, *tmp, transposed); return tmp; } -Graphics::Surface *RenderManager::loadImage(const char *file, bool transposed) { - Common::String str = Common::String(file); - return loadImage(str, transposed); -} - void RenderManager::prepareBackground() { _backgroundDirtyRect.clip(_backgroundWidth, _backgroundHeight); RenderTable::RenderState state = _renderTable.getRenderState(); @@ -788,12 +674,15 @@ void RenderManager::clearMenuSurface(const Common::Rect &r) { void RenderManager::renderMenuToScreen() { if (!_menuSurfaceDirtyRect.isEmpty()) { _menuSurfaceDirtyRect.clip(Common::Rect(_menuSurface.w, _menuSurface.h)); - if (!_menuSurfaceDirtyRect.isEmpty()) - _system->copyRectToScreen(_menuSurface.getBasePtr(_menuSurfaceDirtyRect.left, _menuSurfaceDirtyRect.top), _menuSurface.pitch, - _menuSurfaceDirtyRect.left + _menuArea.left, - _menuSurfaceDirtyRect.top + _menuArea.top, - _menuSurfaceDirtyRect.width(), - _menuSurfaceDirtyRect.height()); + if (!_menuSurfaceDirtyRect.isEmpty()) { + Common::Rect rect( + _menuSurfaceDirtyRect.left + _menuArea.left, + _menuSurfaceDirtyRect.top + _menuArea.top, + _menuSurfaceDirtyRect.left + _menuArea.left + _menuSurfaceDirtyRect.width(), + _menuSurfaceDirtyRect.top + _menuArea.top + _menuSurfaceDirtyRect.height() + ); + copyToScreen(_menuSurface, rect, _menuSurfaceDirtyRect.left, _menuSurfaceDirtyRect.top); + } _menuSurfaceDirtyRect = Common::Rect(); } } @@ -813,18 +702,9 @@ uint16 RenderManager::createSubArea(const Common::Rect &area) { } uint16 RenderManager::createSubArea() { - _subid++; - - OneSubtitle sub; - sub.redraw = false; - sub.timer = -1; - sub.todelete = false; - sub.r = Common::Rect(_subtitleArea.left, _subtitleArea.top, _subtitleArea.right, _subtitleArea.bottom); - sub.r.translate(-_workingWindow.left, -_workingWindow.top); - - _subsList[_subid] = sub; - - return _subid; + Common::Rect r(_subtitleArea.left, _subtitleArea.top, _subtitleArea.right, _subtitleArea.bottom); + r.translate(-_workingWindow.left, -_workingWindow.top); + return createSubArea(r); } void RenderManager::deleteSubArea(uint16 id) { @@ -868,20 +748,23 @@ void RenderManager::processSubs(uint16 deltatime) { OneSubtitle *sub = &it->_value; if (sub->txt.size()) { Graphics::Surface *rndr = new Graphics::Surface(); - rndr->create(sub->r.width(), sub->r.height(), _pixelFormat); + rndr->create(sub->r.width(), sub->r.height(), _engine->_resourcePixelFormat); _engine->getTextRenderer()->drawTxtInOneLine(sub->txt, *rndr); - blitSurfaceToSurface(*rndr, _subtitleSurface, sub->r.left - _subtitleArea.left + _workingWindow.left, sub->r.top - _subtitleArea.top + _workingWindow.top); + Common::Rect empty; + blitSurfaceToSurface(*rndr, empty, _subtitleSurface, sub->r.left - _subtitleArea.left + _workingWindow.left, sub->r.top - _subtitleArea.top + _workingWindow.top); rndr->free(); delete rndr; } sub->redraw = false; } - _system->copyRectToScreen(_subtitleSurface.getPixels(), _subtitleSurface.pitch, - _subtitleArea.left, - _subtitleArea.top, - _subtitleSurface.w, - _subtitleSurface.h); + Common::Rect rect( + _subtitleArea.left, + _subtitleArea.top, + _subtitleArea.left + _subtitleSurface.w, + _subtitleArea.top + _subtitleSurface.h + ); + copyToScreen(_subtitleSurface, rect, 0, 0); } } @@ -889,7 +772,7 @@ Common::Point RenderManager::getBkgSize() { return Common::Point(_backgroundWidth, _backgroundHeight); } -void RenderManager::addEffect(Effect *_effect) { +void RenderManager::addEffect(GraphicsEffect *_effect) { _effects.push_back(_effect); } @@ -1072,9 +955,221 @@ void RenderManager::markDirty() { _backgroundDirtyRect = Common::Rect(_backgroundWidth, _backgroundHeight); } +#if 0 void RenderManager::bkgFill(uint8 r, uint8 g, uint8 b) { _currentBackgroundImage.fillRect(Common::Rect(_currentBackgroundImage.w, _currentBackgroundImage.h), _currentBackgroundImage.format.RGBToColor(r, g, b)); markDirty(); } +#endif + +void RenderManager::timedMessage(const Common::String &str, uint16 milsecs) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, str); + processSubs(0); + renderSceneToScreen(); + deleteSubArea(msgid, milsecs); +} + +bool RenderManager::askQuestion(const Common::String &str) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, str); + processSubs(0); + renderSceneToScreen(); + _engine->stopClock(); + + int result = 0; + + while (result == 0) { + Common::Event evnt; + while (_engine->getEventManager()->pollEvent(evnt)) { + if (evnt.type == Common::EVENT_KEYDOWN) { + switch (evnt.kbd.keycode) { + case Common::KEYCODE_y: + result = 2; + break; + case Common::KEYCODE_n: + result = 1; + break; + default: + break; + } + } + } + _system->updateScreen(); + if (_doubleFPS) + _system->delayMillis(33); + else + _system->delayMillis(66); + } + deleteSubArea(msgid); + _engine->startClock(); + return result == 2; +} + +void RenderManager::delayedMessage(const Common::String &str, uint16 milsecs) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, str); + processSubs(0); + renderSceneToScreen(); + _engine->stopClock(); + + uint32 stopTime = _system->getMillis() + milsecs; + while (_system->getMillis() < stopTime) { + Common::Event evnt; + while (_engine->getEventManager()->pollEvent(evnt)) { + if (evnt.type == Common::EVENT_KEYDOWN && + (evnt.kbd.keycode == Common::KEYCODE_SPACE || + evnt.kbd.keycode == Common::KEYCODE_RETURN || + evnt.kbd.keycode == Common::KEYCODE_ESCAPE)) + break; + } + _system->updateScreen(); + if (_doubleFPS) + _system->delayMillis(33); + else + _system->delayMillis(66); + } + deleteSubArea(msgid); + _engine->startClock(); +} + +void RenderManager::showDebugMsg(const Common::String &msg, int16 delay) { + uint16 msgid = createSubArea(); + updateSubArea(msgid, msg); + deleteSubArea(msgid, delay); +} + +void RenderManager::updateRotation() { + int16 _velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity(); + ScriptManager *scriptManager = _engine->getScriptManager(); + + if (_doubleFPS) + _velocity /= 2; + + if (_velocity) { + RenderTable::RenderState renderState = _renderTable.getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition + (_renderTable.getPanoramaReverse() ? -_velocity : _velocity); + + int16 zeroPoint = _renderTable.getPanoramaZeroPoint(); + if (startPosition >= zeroPoint && newPosition < zeroPoint) + scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) - 1); + if (startPosition <= zeroPoint && newPosition > zeroPoint) + scriptManager->setStateValue(StateKey_Rounds, scriptManager->getStateValue(StateKey_Rounds) + 1); + + int16 screenWidth = getBkgSize().x; + if (screenWidth) + newPosition %= screenWidth; + + if (newPosition < 0) + newPosition += screenWidth; + + setBackgroundPosition(newPosition); + } else if (renderState == RenderTable::TILT) { + int16 startPosition = scriptManager->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition + _velocity; + + int16 screenHeight = getBkgSize().y; + int16 tiltGap = _renderTable.getTiltGap(); + + if (newPosition >= (screenHeight - tiltGap)) + newPosition = screenHeight - tiltGap; + if (newPosition <= tiltGap) + newPosition = tiltGap; + + setBackgroundPosition(newPosition); + } + } +} + +void RenderManager::checkBorders() { + RenderTable::RenderState renderState = _renderTable.getRenderState(); + if (renderState == RenderTable::PANORAMA) { + int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition; + + int16 screenWidth = getBkgSize().x; + + if (screenWidth) + newPosition %= screenWidth; + + if (newPosition < 0) + newPosition += screenWidth; + + if (startPosition != newPosition) + setBackgroundPosition(newPosition); + } else if (renderState == RenderTable::TILT) { + int16 startPosition = _engine->getScriptManager()->getStateValue(StateKey_ViewPos); + + int16 newPosition = startPosition; + + int16 screenHeight = getBkgSize().y; + int16 tiltGap = _renderTable.getTiltGap(); + + if (newPosition >= (screenHeight - tiltGap)) + newPosition = screenHeight - tiltGap; + if (newPosition <= tiltGap) + newPosition = tiltGap; + + if (startPosition != newPosition) + setBackgroundPosition(newPosition); + } +} + +void RenderManager::rotateTo(int16 _toPos, int16 _time) { + if (_renderTable.getRenderState() != RenderTable::PANORAMA) + return; + + if (_time == 0) + _time = 1; + + int32 maxX = getBkgSize().x; + int32 curX = getCurrentBackgroundOffset(); + int32 dx = 0; + + if (curX == _toPos) + return; + + if (curX > _toPos) { + if (curX - _toPos > maxX / 2) + dx = (_toPos + (maxX - curX)) / _time; + else + dx = -(curX - _toPos) / _time; + } else { + if (_toPos - curX > maxX / 2) + dx = -((maxX - _toPos) + curX) / _time; + else + dx = (_toPos - curX) / _time; + } + + _engine->stopClock(); + + for (int16 i = 0; i <= _time; i++) { + if (i == _time) + curX = _toPos; + else + curX += dx; + + if (curX < 0) + curX = maxX - curX; + else if (curX >= maxX) + curX %= maxX; + + setBackgroundPosition(curX); + + prepareBackground(); + renderSceneToScreen(); + + _system->updateScreen(); + + _system->delayMillis(500 / _time); + } + + _engine->startClock(); +} } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_manager.h b/engines/zvision/graphics/render_manager.h index 29bbd8f411..c22f9a78c9 100644 --- a/engines/zvision/graphics/render_manager.h +++ b/engines/zvision/graphics/render_manager.h @@ -24,14 +24,14 @@ #define ZVISION_RENDER_MANAGER_H #include "zvision/graphics/render_table.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "common/rect.h" #include "common/hashmap.h" #include "graphics/surface.h" -#include "effect.h" +#include "graphics_effect.h" class OSystem; @@ -48,7 +48,7 @@ namespace ZVision { class RenderManager { public: - RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat); + RenderManager(ZVision *engine, uint32 windowWidth, uint32 windowHeight, const Common::Rect workingWindow, const Graphics::PixelFormat pixelFormat, bool doubleFPS); ~RenderManager(); private: @@ -61,7 +61,7 @@ private: }; typedef Common::HashMap<uint16, OneSubtitle> SubtitleMap; - typedef Common::List<Effect *> EffectsList; + typedef Common::List<GraphicsEffect *> EffectsList; private: ZVision *_engine; @@ -137,6 +137,7 @@ private: // Visual effects list EffectsList _effects; + bool _doubleFPS; public: void initialize(); @@ -146,6 +147,8 @@ public: */ void renderSceneToScreen(); + void copyToScreen(const Graphics::Surface &surface, Common::Rect &rect, int16 srcLeft, int16 srcTop); + /** * Blits the image or a portion of the image to the background. * @@ -227,20 +230,15 @@ public: // Blitting surface-to-surface methods void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int x, int y); void blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey); - void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y); - void blitSurfaceToSurface(const Graphics::Surface &src, Graphics::Surface &dst, int x, int y, uint32 colorkey); // Blitting surface-to-background methods - void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y); - void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, uint32 colorkey); + void blitSurfaceToBkg(const Graphics::Surface &src, int x, int y, int32 colorkey = -1); // Blitting surface-to-background methods with scale - void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect); - void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, uint32 colorkey); + void blitSurfaceToBkgScaled(const Graphics::Surface &src, const Common::Rect &_dstRect, int32 colorkey = -1); // Blitting surface-to-menu methods - void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y); - void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, uint32 colorkey); + void blitSurfaceToMenu(const Graphics::Surface &src, int x, int y, int32 colorkey = -1); // Subtitles methods @@ -265,10 +263,8 @@ public: Graphics::Surface *getBkgRect(Common::Rect &rect); // Load image into new surface - Graphics::Surface *loadImage(const char *file); - Graphics::Surface *loadImage(Common::String &file); - Graphics::Surface *loadImage(const char *file, bool transposed); - Graphics::Surface *loadImage(Common::String &file, bool transposed); + Graphics::Surface *loadImage(Common::String file); + Graphics::Surface *loadImage(Common::String file, bool transposed); // Clear whole/area of menu surface void clearMenuSurface(); @@ -281,8 +277,7 @@ public: void prepareBackground(); /** - * Reads an image file pixel data into a Surface buffer. In the process - * it converts the pixel data from RGB 555 to RGB 565. Also, if the image + * Reads an image file pixel data into a Surface buffer. Also, if the image * is transposed, it will un-transpose the pixel data. The function will * call destination::create() if the dimensions of destination do not match * up with the dimensions of the image. @@ -293,8 +288,7 @@ public: void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination); /** - * Reads an image file pixel data into a Surface buffer. In the process - * it converts the pixel data from RGB 555 to RGB 565. Also, if the image + * Reads an image file pixel data into a Surface buffer. Also, if the image * is transposed, it will un-transpose the pixel data. The function will * call destination::create() if the dimensions of destination do not match * up with the dimensions of the image. @@ -306,7 +300,7 @@ public: void readImageToSurface(const Common::String &fileName, Graphics::Surface &destination, bool transposed); // Add visual effect to effects list - void addEffect(Effect *_effect); + void addEffect(GraphicsEffect *_effect); // Delete effect(s) by ID (ID equal to slot of action:region that create this effect) void deleteEffect(uint32 ID); @@ -328,8 +322,19 @@ public: // Mark whole background surface as dirty void markDirty(); - // Fille background surface by color +#if 0 + // Fill background surface by color void bkgFill(uint8 r, uint8 g, uint8 b); +#endif + + bool askQuestion(const Common::String &str); + void delayedMessage(const Common::String &str, uint16 milsecs); + void timedMessage(const Common::String &str, uint16 milsecs); + void showDebugMsg(const Common::String &msg, int16 delay = 3000); + + void checkBorders(); + void rotateTo(int16 to, int16 time); + void updateRotation(); }; } // End of namespace ZVision diff --git a/engines/zvision/graphics/render_table.cpp b/engines/zvision/graphics/render_table.cpp index c30e0bd472..df73247344 100644 --- a/engines/zvision/graphics/render_table.cpp +++ b/engines/zvision/graphics/render_table.cpp @@ -81,27 +81,6 @@ const Common::Point RenderTable::convertWarpedCoordToFlatCoord(const Common::Poi return newPoint; } -uint16 mixTwoRGB(uint16 colorOne, uint16 colorTwo, float percentColorOne) { - assert(percentColorOne < 1.0f); - - float rOne = float((colorOne & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); - float rTwo = float((colorTwo & Graphics::ColorMasks<555>::kRedMask) >> Graphics::ColorMasks<555>::kRedShift); - float gOne = float((colorOne & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); - float gTwo = float((colorTwo & Graphics::ColorMasks<555>::kGreenMask) >> Graphics::ColorMasks<555>::kGreenShift); - float bOne = float((colorOne & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); - float bTwo = float((colorTwo & Graphics::ColorMasks<555>::kBlueMask) >> Graphics::ColorMasks<555>::kBlueShift); - - float rFinal = rOne * percentColorOne + rTwo * (1.0f - percentColorOne); - float gFinal = gOne * percentColorOne + gTwo * (1.0f - percentColorOne); - float bFinal = bOne * percentColorOne + bTwo * (1.0f - percentColorOne); - - uint16 returnColor = (byte(rFinal + 0.5f) << Graphics::ColorMasks<555>::kRedShift) | - (byte(gFinal + 0.5f) << Graphics::ColorMasks<555>::kGreenShift) | - (byte(bFinal + 0.5f) << Graphics::ColorMasks<555>::kBlueShift); - - return returnColor; -} - void RenderTable::mutateImage(uint16 *sourceBuffer, uint16 *destBuffer, uint32 destWidth, const Common::Rect &subRect) { uint32 destOffset = 0; diff --git a/engines/zvision/module.mk b/engines/zvision/module.mk index 00652f0824..93fba2879d 100644 --- a/engines/zvision/module.mk +++ b/engines/zvision/module.mk @@ -4,10 +4,9 @@ MODULE_OBJS := \ core/console.o \ core/clock.o \ core/events.o \ - core/menu.o \ - core/save_manager.o \ detection.o \ file/lzss_read_stream.o \ + file/save_manager.o \ file/search_manager.o \ file/zfs_archive.o \ graphics/cursors/cursor_manager.o \ @@ -17,8 +16,6 @@ MODULE_OBJS := \ graphics/effects/wave.o \ graphics/render_manager.o \ graphics/render_table.o \ - graphics/subtitles.o \ - graphics/truetype_font.o \ scripting/actions.o \ scripting/control.o \ scripting/controls/fist_control.o \ @@ -32,19 +29,22 @@ MODULE_OBJS := \ scripting/controls/slot_control.o \ scripting/controls/titler_control.o \ scripting/inventory.o \ + scripting/menu.o \ scripting/scr_file_handling.o \ scripting/script_manager.o \ - scripting/sidefx/animation_node.o \ - scripting/sidefx/distort_node.o \ - scripting/sidefx/music_node.o \ - scripting/sidefx/region_node.o \ - scripting/sidefx/syncsound_node.o \ - scripting/sidefx/timer_node.o \ - scripting/sidefx/ttytext_node.o \ + scripting/effects/animation_effect.o \ + scripting/effects/distort_effect.o \ + scripting/effects/music_effect.o \ + scripting/effects/region_effect.o \ + scripting/effects/syncsound_effect.o \ + scripting/effects/timer_effect.o \ + scripting/effects/ttytext_effect.o \ sound/midi.o \ sound/zork_raw.o \ text/string_manager.o \ + text/subtitles.o \ text/text.o \ + text/truetype_font.o \ video/rlf_decoder.o \ video/video.o \ video/zork_avi_decoder.o \ diff --git a/engines/zvision/scripting/actions.cpp b/engines/zvision/scripting/actions.cpp index 0422a2c028..a61fa26223 100644 --- a/engines/zvision/scripting/actions.cpp +++ b/engines/zvision/scripting/actions.cpp @@ -29,20 +29,21 @@ #include "zvision/graphics/render_manager.h" #include "zvision/sound/zork_raw.h" #include "zvision/video/zork_avi_decoder.h" -#include "zvision/scripting/sidefx/timer_node.h" -#include "zvision/scripting/sidefx/music_node.h" -#include "zvision/scripting/sidefx/syncsound_node.h" -#include "zvision/scripting/sidefx/animation_node.h" -#include "zvision/scripting/sidefx/distort_node.h" -#include "zvision/scripting/sidefx/ttytext_node.h" -#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/file/save_manager.h" +#include "zvision/scripting/menu.h" +#include "zvision/scripting/effects/timer_effect.h" +#include "zvision/scripting/effects/music_effect.h" +#include "zvision/scripting/effects/syncsound_effect.h" +#include "zvision/scripting/effects/animation_effect.h" +#include "zvision/scripting/effects/distort_effect.h" +#include "zvision/scripting/effects/ttytext_effect.h" +#include "zvision/scripting/effects/region_effect.h" #include "zvision/scripting/controls/titler_control.h" #include "zvision/graphics/render_table.h" -#include "zvision/graphics/effect.h" +#include "zvision/graphics/graphics_effect.h" #include "zvision/graphics/effects/fog.h" #include "zvision/graphics/effects/light.h" #include "zvision/graphics/effects/wave.h" -#include "zvision/core/save_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" #include "common/file.h" @@ -105,8 +106,8 @@ ActionAttenuate::ActionAttenuate(ZVision *engine, int32 slotkey, const Common::S } bool ActionAttenuate::execute() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_key); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { + ScriptingEffect *fx = _engine->getScriptManager()->getSideFX(_key); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { MusicNode *mus = (MusicNode *)fx; mus->setVolume(255 - (abs(_attenuation) >> 7)); } @@ -156,8 +157,8 @@ ActionCrossfade::ActionCrossfade(ZVision *engine, int32 slotkey, const Common::S bool ActionCrossfade::execute() { if (_keyOne) { - SideFX *fx = _engine->getScriptManager()->getSideFX(_keyOne); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { + ScriptingEffect *fx = _engine->getScriptManager()->getSideFX(_keyOne); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { MusicNode *mus = (MusicNode *)fx; if (_oneStartVolume >= 0) mus->setVolume((_oneStartVolume * 255) / 100); @@ -167,8 +168,8 @@ bool ActionCrossfade::execute() { } if (_keyTwo) { - SideFX *fx = _engine->getScriptManager()->getSideFX(_keyTwo); - if (fx && fx->getType() == SideFX::SIDEFX_AUDIO) { + ScriptingEffect *fx = _engine->getScriptManager()->getSideFX(_keyTwo); + if (fx && fx->getType() == ScriptingEffect::SCRIPTING_EFFECT_AUDIO) { MusicNode *mus = (MusicNode *)fx; if (_twoStartVolume >= 0) mus->setVolume((_twoStartVolume * 255) / 100); @@ -243,23 +244,6 @@ bool ActionDisableControl::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionDisableVenus -////////////////////////////////////////////////////////////////////////////// - -ActionDisableVenus::ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { - _key = 0; - - sscanf(line.c_str(), "%d", &_key); -} - -bool ActionDisableVenus::execute() { - _engine->getScriptManager()->setStateValue(_key, 0); - - return true; -} - -////////////////////////////////////////////////////////////////////////////// // ActionDisplayMessage ////////////////////////////////////////////////////////////////////////////// @@ -295,7 +279,7 @@ bool ActionDissolve::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionDistort +// ActionDistort - only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30) ////////////////////////////////////////////////////////////////////////////// ActionDistort::ActionDistort(ZVision *engine, int32 slotkey, const Common::String &line) : @@ -363,7 +347,7 @@ ActionInventory::ActionInventory(ZVision *engine, int32 slotkey, const Common::S _key = 0; char buf[25]; - sscanf(line.c_str(), "%25s %d", buf, &_key); + sscanf(line.c_str(), "%24s %d", buf, &_key); if (strcmp(buf, "add") == 0) { _type = 0; @@ -406,7 +390,7 @@ bool ActionInventory::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionKill +// ActionKill - only used by ZGI ////////////////////////////////////////////////////////////////////////////// ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &line) : @@ -414,31 +398,31 @@ ActionKill::ActionKill(ZVision *engine, int32 slotkey, const Common::String &lin _key = 0; _type = 0; char keytype[25]; - sscanf(line.c_str(), "%25s", keytype); + sscanf(line.c_str(), "%24s", keytype); if (keytype[0] == '"') { if (!scumm_stricmp(keytype, "\"ANIM\"")) - _type = SideFX::SIDEFX_ANIM; + _type = ScriptingEffect::SCRIPTING_EFFECT_ANIM; else if (!scumm_stricmp(keytype, "\"AUDIO\"")) - _type = SideFX::SIDEFX_AUDIO; + _type = ScriptingEffect::SCRIPTING_EFFECT_AUDIO; else if (!scumm_stricmp(keytype, "\"DISTORT\"")) - _type = SideFX::SIDEFX_DISTORT; + _type = ScriptingEffect::SCRIPTING_EFFECT_DISTORT; else if (!scumm_stricmp(keytype, "\"PANTRACK\"")) - _type = SideFX::SIDEFX_PANTRACK; + _type = ScriptingEffect::SCRIPTING_EFFECT_PANTRACK; else if (!scumm_stricmp(keytype, "\"REGION\"")) - _type = SideFX::SIDEFX_REGION; + _type = ScriptingEffect::SCRIPTING_EFFECT_REGION; else if (!scumm_stricmp(keytype, "\"TIMER\"")) - _type = SideFX::SIDEFX_TIMER; + _type = ScriptingEffect::SCRIPTING_EFFECT_TIMER; else if (!scumm_stricmp(keytype, "\"TTYTEXT\"")) - _type = SideFX::SIDEFX_TTYTXT; + _type = ScriptingEffect::SCRIPTING_EFFECT_TTYTXT; else if (!scumm_stricmp(keytype, "\"ALL\"")) - _type = SideFX::SIDEFX_ALL; + _type = ScriptingEffect::SCRIPTING_EFFECT_ALL; } else _key = atoi(keytype); } bool ActionKill::execute() { if (_type) - _engine->getScriptManager()->killSideFxType((SideFX::SideFXType)_type); + _engine->getScriptManager()->killSideFxType((ScriptingEffect::ScriptingEffectType)_type); else _engine->getScriptManager()->killSideFx(_key); return true; @@ -456,7 +440,7 @@ ActionMenuBarEnable::ActionMenuBarEnable(ZVision *engine, int32 slotkey, const C } bool ActionMenuBarEnable::execute() { - _engine->menuBarEnable(_menus); + _engine->getMenuHandler()->setEnable(_menus); return true; } @@ -475,9 +459,11 @@ ActionMusic::ActionMusic(ZVision *engine, int32 slotkey, const Common::String &l uint loop = 0; uint volume = 255; - sscanf(line.c_str(), "%u %25s %u %u", &type, fileNameBuffer, &loop, &volume); + sscanf(line.c_str(), "%u %24s %u %u", &type, fileNameBuffer, &loop, &volume); - // type 4 are midi sound effect files + // Type 4 actions are MIDI commands, not files. These are only used by + // Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well + // as vr) if (type == 4) { _midi = true; int note; @@ -577,14 +563,14 @@ ActionPreloadAnimation::ActionPreloadAnimation(ZVision *engine, int32 slotkey, c char fileName[25]; - // The two %*u are always 0 and dont seem to have a use - sscanf(line.c_str(), "%25s %*u %*u %d %d", fileName, &_mask, &_framerate); + // The two %*u are usually 0 and dont seem to have a use + sscanf(line.c_str(), "%24s %*u %*u %d %d", fileName, &_mask, &_framerate); - if (_mask > 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); - _mask = _engine->_pixelFormat.RGBToColor(r, g, b); - } + // Mask 0 means "no transparency" in this case. Since we use a common blitting + // code for images and animations, we set it to -1 to avoid confusion with + // color 0, which is used as a mask in some images + if (_mask == 0) + _mask = -1; _fileName = Common::String(fileName); } @@ -594,10 +580,10 @@ ActionPreloadAnimation::~ActionPreloadAnimation() { } bool ActionPreloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + AnimationEffect *nod = (AnimationEffect *)_engine->getScriptManager()->getSideFX(_slotKey); if (!nod) { - nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate, false); + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate, false); _engine->getScriptManager()->addSideFX(nod); } else nod->stop(); @@ -617,9 +603,9 @@ ActionUnloadAnimation::ActionUnloadAnimation(ZVision *engine, int32 slotkey, con } bool ActionUnloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_key); + AnimationEffect *nod = (AnimationEffect *)_engine->getScriptManager()->getSideFX(_key); - if (nod && nod->getType() == SideFX::SIDEFX_ANIM) + if (nod && nod->getType() == ScriptingEffect::SCRIPTING_EFFECT_ANIM) _engine->getScriptManager()->deleteSideFx(_key); return true; @@ -645,14 +631,14 @@ ActionPlayAnimation::ActionPlayAnimation(ZVision *engine, int32 slotkey, const C // The two %*u are always 0 and dont seem to have a use sscanf(line.c_str(), - "%25s %u %u %u %u %u %u %d %*u %*u %d %d", + "%24s %u %u %u %u %u %u %d %*u %*u %d %d", fileName, &_x, &_y, &_x2, &_y2, &_start, &_end, &_loopCount, &_mask, &_framerate); - if (_mask > 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(_mask, r, g, b); - _mask = _engine->_pixelFormat.RGBToColor(r, g, b); - } + // Mask 0 means "no transparency" in this case. Since we use a common blitting + // code for images and animations, we set it to -1 to avoid confusion with + // color 0, which is used as a mask in some images + if (_mask == 0) + _mask = -1; _fileName = Common::String(fileName); } @@ -662,10 +648,10 @@ ActionPlayAnimation::~ActionPlayAnimation() { } bool ActionPlayAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_slotKey); + AnimationEffect *nod = (AnimationEffect *)_engine->getScriptManager()->getSideFX(_slotKey); if (!nod) { - nod = new AnimationNode(_engine, _slotKey, _fileName, _mask, _framerate); + nod = new AnimationEffect(_engine, _slotKey, _fileName, _mask, _framerate); _engine->getScriptManager()->addSideFX(nod); } else nod->stop(); @@ -697,7 +683,7 @@ ActionPlayPreloadAnimation::ActionPlayPreloadAnimation(ZVision *engine, int32 sl } bool ActionPlayPreloadAnimation::execute() { - AnimationNode *nod = (AnimationNode *)_engine->getScriptManager()->getSideFX(_controlKey); + AnimationEffect *nod = (AnimationEffect *)_engine->getScriptManager()->getSideFX(_controlKey); if (nod) nod->addPlayNode(_slotKey, _x1, _y1, _x2, _y2, _startFrame, _endFrame, _loopCount); @@ -716,7 +702,7 @@ bool ActionQuit::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionRegion +// ActionRegion - only used by Zork: Nemesis ////////////////////////////////////////////////////////////////////////////// ActionRegion::ActionRegion(ZVision *engine, int32 slotkey, const Common::String &line) : @@ -745,13 +731,13 @@ bool ActionRegion::execute() { if (_engine->getScriptManager()->getSideFX(_slotKey)) return true; - Effect *effct = NULL; + GraphicsEffect *effect = NULL; switch (_type) { case 0: { uint16 centerX, centerY, frames; double amplitude, waveln, speed; sscanf(_custom.c_str(), "%hu,%hu,%hu,%lf,%lf,%lf,", ¢erX, ¢erY, &frames, &litude, &waveln, &speed); - effct = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); + effect = new WaveFx(_engine, _slotKey, _rect, _unk1, frames, centerX, centerY, amplitude, waveln, speed); } break; case 1: { @@ -763,7 +749,7 @@ bool ActionRegion::execute() { int8 minD; int8 maxD; EffectMap *_map = _engine->getRenderManager()->makeEffectMap(Common::Point(aX, aY), aD, _rect, &minD, &maxD); - effct = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); + effect = new LightFx(_engine, _slotKey, _rect, _unk1, _map, atoi(_custom.c_str()), minD, maxD); } break; case 9: { @@ -779,16 +765,16 @@ bool ActionRegion::execute() { _rect.setHeight(tempMask.h); EffectMap *_map = _engine->getRenderManager()->makeEffectMap(tempMask, 0); - effct = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); + effect = new FogFx(_engine, _slotKey, _rect, _unk1, _map, Common::String(buf)); } break; default: break; } - if (effct) { - _engine->getScriptManager()->addSideFX(new RegionNode(_engine, _slotKey, effct, _delay)); - _engine->getRenderManager()->addEffect(effct); + if (effect) { + _engine->getScriptManager()->addSideFX(new RegionNode(_engine, _slotKey, effect, _delay)); + _engine->getRenderManager()->addEffect(effect); } return true; @@ -846,7 +832,7 @@ ActionRotateTo::ActionRotateTo(ZVision *engine, int32 slotkey, const Common::Str } bool ActionRotateTo::execute() { - _engine->rotateTo(_toPos, _time); + _engine->getRenderManager()->rotateTo(_toPos, _time); return true; } @@ -861,21 +847,12 @@ ActionSetPartialScreen::ActionSetPartialScreen(ZVision *engine, int32 slotkey, c _y = 0; char fileName[25]; - int color; - sscanf(line.c_str(), "%u %u %25s %*u %d", &_x, &_y, fileName, &color); + sscanf(line.c_str(), "%u %u %24s %*u %d", &_x, &_y, fileName, &_backgroundColor); _fileName = Common::String(fileName); - if (color >= 0) { - byte r, g, b; - Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0).colorToRGB(color, r, g, b); - _backgroundColor = _engine->_pixelFormat.RGBToColor(r, g, b); - } else { - _backgroundColor = color; - } - - if (color > 65535) { + if (_backgroundColor > 65535) { warning("Background color for ActionSetPartialScreen is bigger than a uint16"); } } @@ -907,7 +884,7 @@ bool ActionSetPartialScreen::execute() { ActionSetScreen::ActionSetScreen(ZVision *engine, int32 slotkey, const Common::String &line) : ResultAction(engine, slotkey) { char fileName[25]; - sscanf(line.c_str(), "%25s", fileName); + sscanf(line.c_str(), "%24s", fileName); _fileName = Common::String(fileName); } @@ -919,24 +896,6 @@ bool ActionSetScreen::execute() { } ////////////////////////////////////////////////////////////////////////////// -// ActionSetVenus -////////////////////////////////////////////////////////////////////////////// - -ActionSetVenus::ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line) : - ResultAction(engine, slotkey) { - _key = 0; - - sscanf(line.c_str(), "%d", &_key); -} - -bool ActionSetVenus::execute() { - if (_engine->getScriptManager()->getStateValue(_key)) - _engine->getScriptManager()->setStateValue(StateKey_Venus, _key); - - return true; -} - -////////////////////////////////////////////////////////////////////////////// // ActionStop ////////////////////////////////////////////////////////////////////////////// @@ -966,7 +925,7 @@ ActionStreamVideo::ActionStreamVideo(ZVision *engine, int32 slotkey, const Commo char fileName[25]; uint skipline = 0; //skipline - render video with skip every second line, not skippable. - sscanf(line.c_str(), "%25s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); + sscanf(line.c_str(), "%24s %u %u %u %u %u %u", fileName, &_x1, &_y1, &_x2, &_y2, &_flags, &skipline); _fileName = Common::String(fileName); _skippable = true; @@ -1017,23 +976,19 @@ ActionSyncSound::ActionSyncSound(ZVision *engine, int32 slotkey, const Common::S char fileName[25]; int notUsed = 0; - sscanf(line.c_str(), "%d %d %25s", &_syncto, ¬Used, fileName); + sscanf(line.c_str(), "%d %d %24s", &_syncto, ¬Used, fileName); _fileName = Common::String(fileName); } bool ActionSyncSound::execute() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_syncto); + ScriptingEffect *fx = _engine->getScriptManager()->getSideFX(_syncto); if (!fx) return true; - if (!(fx->getType() & SideFX::SIDEFX_ANIM)) + if (!(fx->getType() & ScriptingEffect::SCRIPTING_EFFECT_ANIM)) return true; - AnimationNode *animnode = (AnimationNode *)fx; - if (animnode->getFrameDelay() > 200) // Hack for fix incorrect framedelay in some animpreload - animnode->setNewFrameDelay(66); // ~15fps - _engine->getScriptManager()->addSideFX(new SyncSoundNode(_engine, _slotKey, _fileName, _syncto)); return true; } diff --git a/engines/zvision/scripting/actions.h b/engines/zvision/scripting/actions.h index 292e25e19c..8d43309b74 100644 --- a/engines/zvision/scripting/actions.h +++ b/engines/zvision/scripting/actions.h @@ -124,14 +124,6 @@ private: uint8 _action; }; -class ActionDebug : public ResultAction { -public: - ActionDebug(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: -}; - class ActionDelayRender : public ResultAction { public: ActionDelayRender(ZVision *engine, int32 slotkey, const Common::String &line); @@ -150,15 +142,6 @@ private: uint32 _key; }; -class ActionDisableVenus : public ResultAction { -public: - ActionDisableVenus(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: - int32 _key; -}; - class ActionDisplayMessage : public ResultAction { public: ActionDisplayMessage(ZVision *engine, int32 slotkey, const Common::String &line); @@ -338,7 +321,7 @@ private: uint16 _unk2; }; -// TODO: See if this exists in ZGI. It doesn't in ZNem +// Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j) class ActionUnloadAnimation : public ResultAction { public: ActionUnloadAnimation(ZVision *engine, int32 slotkey, const Common::String &line); @@ -397,15 +380,6 @@ private: Common::String _fileName; }; -class ActionSetVenus : public ResultAction { -public: - ActionSetVenus(ZVision *engine, int32 slotkey, const Common::String &line); - bool execute(); - -private: - int32 _key; -}; - class ActionStop : public ResultAction { public: ActionStop(ZVision *engine, int32 slotkey, const Common::String &line); diff --git a/engines/zvision/scripting/control.cpp b/engines/zvision/scripting/control.cpp index 127f35ef12..81123eb99b 100644 --- a/engines/zvision/scripting/control.cpp +++ b/engines/zvision/scripting/control.cpp @@ -72,6 +72,7 @@ void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream & renderTable->generateRenderTable(); } +// Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view) void Control::parseTiltControl(ZVision *engine, Common::SeekableReadStream &stream) { RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); renderTable->setRenderState(RenderTable::TILT); diff --git a/engines/zvision/scripting/control.h b/engines/zvision/scripting/control.h index 803d0cf1ce..108b83fd00 100644 --- a/engines/zvision/scripting/control.h +++ b/engines/zvision/scripting/control.h @@ -36,6 +36,11 @@ namespace ZVision { class ZVision; +/** + * The base class for all Controls. + * + * Controls are the things that the user interacts with. Ex: A lever on the door + */ class Control { public: diff --git a/engines/zvision/scripting/controls/fist_control.cpp b/engines/zvision/scripting/controls/fist_control.cpp index 34a64b4298..4a8e8b1bbd 100644 --- a/engines/zvision/scripting/controls/fist_control.cpp +++ b/engines/zvision/scripting/controls/fist_control.cpp @@ -46,10 +46,6 @@ FistControl::FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream _order = 0; _fistnum = 0; - _frameCur = -1; - _frameEnd = -1; - _frameTime = 0; - _lastRenderedFrame = -1; _animationId = 0; clearFistArray(_fistsUp); @@ -95,41 +91,23 @@ FistControl::~FistControl() { _entries.clear(); } -void FistControl::renderFrame(uint frameNumber) { - if ((int32)frameNumber == _lastRenderedFrame) - return; - - _lastRenderedFrame = frameNumber; - - const Graphics::Surface *frameData; - - if (_animation) { - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _anmRect); - } -} - bool FistControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - if (_frameCur >= 0 && _frameEnd >= 0) - if (_frameCur <= _frameEnd) { - _frameTime -= deltaTimeInMillis; - - if (_frameTime <= 0) { - _frameTime = 1000.0 / _animation->getDuration().framerate(); - - renderFrame(_frameCur); - - _frameCur++; + if (_animation && _animation->isPlaying()) { + if (_animation->endOfVideo()) { + _animation->stop(); + _engine->getScriptManager()->setStateValue(_animationId, 2); + return false; + } - if (_frameCur > _frameEnd) - _engine->getScriptManager()->setStateValue(_animationId, 2); - } + if (_animation->needsUpdate()) { + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _anmRect); } + } return false; } @@ -160,9 +138,12 @@ bool FistControl::onMouseUp(const Common::Point &screenSpacePos, const Common::P for (int i = 0; i < _numEntries; i++) if (_entries[i]._bitsStrt == oldStatus && _entries[i]._bitsEnd == _fiststatus) { - _frameCur = _entries[i]._anmStrt; - _frameEnd = _entries[i]._anmEnd; - _frameTime = 0; + if (_animation) { + _animation->stop(); + _animation->seekToFrame(_entries[i]._anmStrt); + _animation->setEndFrame(_entries[i]._anmEnd); + _animation->start(); + } _engine->getScriptManager()->setStateValue(_animationId, 1); _engine->getScriptManager()->setStateValue(_soundKey, _entries[i]._sound); diff --git a/engines/zvision/scripting/controls/fist_control.h b/engines/zvision/scripting/controls/fist_control.h index bad2daa6d5..d7cbcb1f71 100644 --- a/engines/zvision/scripting/controls/fist_control.h +++ b/engines/zvision/scripting/controls/fist_control.h @@ -34,7 +34,7 @@ namespace Video { namespace ZVision { -// Only used in Zork Nemesis, it handles the door lock puzzle with the skeletal fingers (td60, td90, td9e) +// Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e) class FistControl : public Control { public: FistControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -64,10 +64,6 @@ private: Video::VideoDecoder *_animation; Common::Rect _anmRect; int32 _soundKey; - int32 _frameCur; - int32 _frameEnd; - int32 _frameTime; - int32 _lastRenderedFrame; int32 _animationId; public: @@ -76,7 +72,6 @@ public: bool process(uint32 deltaTimeInMillis); private: - void renderFrame(uint frameNumber); void readDescFile(const Common::String &fileName); void clearFistArray(Common::Array< Common::Array<Common::Rect> > &arr); uint32 readBits(const char *str); diff --git a/engines/zvision/scripting/controls/hotmov_control.cpp b/engines/zvision/scripting/controls/hotmov_control.cpp index e77272ec73..182447a990 100644 --- a/engines/zvision/scripting/controls/hotmov_control.cpp +++ b/engines/zvision/scripting/controls/hotmov_control.cpp @@ -41,10 +41,7 @@ HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadSt : Control(engine, key, CONTROL_HOTMOV) { _animation = NULL; _cycle = 0; - _curFrame = -1; - _lastRenderedFrame = -1; _frames.clear(); - _frameTime = 0; _cyclesCount = 0; _framesCount = 0; @@ -78,6 +75,7 @@ HotMovControl::HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadSt sscanf(values.c_str(), "%s", filename); values = Common::String(filename); _animation = _engine->loadAnimation(values); + _animation->start(); } else if (param.matchString("venus_id", true)) { _venusId = atoi(values.c_str()); } @@ -95,41 +93,26 @@ HotMovControl::~HotMovControl() { _frames.clear(); } -void HotMovControl::renderFrame(uint frameNumber) { - if ((int)frameNumber == _lastRenderedFrame) - return; - - _lastRenderedFrame = frameNumber; - - const Graphics::Surface *frameData; - - if (_animation) { - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); - } -} - bool HotMovControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; if (_cycle < _cyclesCount) { - _frameTime -= deltaTimeInMillis; + if (_animation && _animation->endOfVideo()) { + _cycle++; - if (_frameTime <= 0) { - _curFrame++; - if (_curFrame >= _framesCount) { - _curFrame = 0; - _cycle++; - } - if (_cycle != _cyclesCount) - renderFrame(_curFrame); - else + if (_cycle == _cyclesCount) { _engine->getScriptManager()->setStateValue(_key, 2); + return false; + } + + _animation->rewind(); + } - _frameTime = 1000.0 / _animation->getDuration().framerate(); + if (_animation && _animation->needsUpdate()) { + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkgScaled(*frameData, _rectangle); } } @@ -140,8 +123,11 @@ bool HotMovControl::onMouseMove(const Common::Point &screenSpacePos, const Commo if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_animation) + return false; + if (_cycle < _cyclesCount) { - if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) { _engine->getCursorManager()->changeCursor(CursorIndex_Active); return true; } @@ -154,8 +140,11 @@ bool HotMovControl::onMouseUp(const Common::Point &screenSpacePos, const Common: if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; + if (!_animation) + return false; + if (_cycle < _cyclesCount) { - if (_frames[_curFrame].contains(backgroundImageSpacePos)) { + if (_frames[_animation->getCurFrame()].contains(backgroundImageSpacePos)) { setVenus(); _engine->getScriptManager()->setStateValue(_key, 1); return true; diff --git a/engines/zvision/scripting/controls/hotmov_control.h b/engines/zvision/scripting/controls/hotmov_control.h index 640fab00c0..99d1fd0979 100644 --- a/engines/zvision/scripting/controls/hotmov_control.h +++ b/engines/zvision/scripting/controls/hotmov_control.h @@ -34,7 +34,7 @@ namespace Video { namespace ZVision { -// Only used in Zork Nemesis, it handles movies where the player needs to click on something (mj7g, vw3g) +// Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g) class HotMovControl : public Control { public: HotMovControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -42,9 +42,6 @@ public: private: int32 _framesCount; - int32 _frameTime; - int32 _curFrame; - int32 _lastRenderedFrame; int32 _cycle; int32 _cyclesCount; Video::VideoDecoder *_animation; @@ -56,7 +53,6 @@ public: bool process(uint32 deltaTimeInMillis); private: - void renderFrame(uint frameNumber); void readHsFile(const Common::String &fileName); }; diff --git a/engines/zvision/scripting/controls/input_control.cpp b/engines/zvision/scripting/controls/input_control.cpp index e75cc15743..47da27fa08 100644 --- a/engines/zvision/scripting/controls/input_control.cpp +++ b/engines/zvision/scripting/controls/input_control.cpp @@ -46,9 +46,7 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre _enterPressed(false), _readOnly(false), _txtWidth(0), - _animation(NULL), - _frameDelay(0), - _frame(-1) { + _animation(NULL) { // Loop until we find the closing brace Common::String line = stream.readLine(); _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); @@ -96,11 +94,10 @@ InputControl::InputControl(ZVision *engine, uint32 key, Common::SeekableReadStre } else if (param.matchString("cursor_animation", true)) { char fileName[25]; - sscanf(values.c_str(), "%25s %*u", fileName); + sscanf(values.c_str(), "%24s %*u", fileName); _animation = _engine->loadAnimation(fileName); - _frame = -1; - _frameDelay = 0; + _animation->start(); } else if (param.matchString("focus", true)) { _focused = true; _engine->getScriptManager()->setFocusControlKey(_key); @@ -199,7 +196,7 @@ bool InputControl::process(uint32 deltaTimeInMillis) { // Blit the text using the RenderManager Graphics::Surface txt; - txt.create(_textRectangle.width(), _textRectangle.height(), _engine->_pixelFormat); + txt.create(_textRectangle.width(), _textRectangle.height(), _engine->_resourcePixelFormat); if (!_readOnly || !_focused) _txtWidth = _engine->getTextRenderer()->drawTxt(_currentInputText, _stringInit, txt); @@ -212,18 +209,12 @@ bool InputControl::process(uint32 deltaTimeInMillis) { } if (_animation && !_readOnly && _focused) { - bool needDraw = true;// = _textChanged; - _frameDelay -= deltaTimeInMillis; - if (_frameDelay <= 0) { - _frame = (_frame + 1) % _animation->getFrameCount(); - _frameDelay = 1000.0 / _animation->getDuration().framerate(); - needDraw = true; - } + if (_animation->endOfVideo()) + _animation->rewind(); - if (needDraw) { - _animation->seekToFrame(_frame); + if (_animation->needsUpdate()) { const Graphics::Surface *srf = _animation->decodeNextFrame(); - uint32 xx = _textRectangle.left + _txtWidth; + int16 xx = _textRectangle.left + _txtWidth; if (xx >= _textRectangle.left + (_textRectangle.width() - (int16)_animation->getWidth())) xx = _textRectangle.left + _textRectangle.width() - (int16)_animation->getWidth(); _engine->getRenderManager()->blitSurfaceToBkg(*srf, xx, _textRectangle.top); diff --git a/engines/zvision/scripting/controls/lever_control.cpp b/engines/zvision/scripting/controls/lever_control.cpp index 8faa18357c..bef51f0e91 100644 --- a/engines/zvision/scripting/controls/lever_control.cpp +++ b/engines/zvision/scripting/controls/lever_control.cpp @@ -64,12 +64,12 @@ LeverControl::LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStre while (!stream.eos() && !line.contains('}')) { if (param.matchString("descfile", true)) { char levFileName[25]; - sscanf(values.c_str(), "%25s", levFileName); + sscanf(values.c_str(), "%24s", levFileName); parseLevFile(levFileName); } else if (param.matchString("cursor", true)) { char cursorName[25]; - sscanf(values.c_str(), "%25s", cursorName); + sscanf(values.c_str(), "%24s", cursorName); _cursor = _engine->getCursorManager()->getCursorId(Common::String(cursorName)); } diff --git a/engines/zvision/scripting/controls/lever_control.h b/engines/zvision/scripting/controls/lever_control.h index fdf4a649dc..8787234c51 100644 --- a/engines/zvision/scripting/controls/lever_control.h +++ b/engines/zvision/scripting/controls/lever_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e) class LeverControl : public Control { public: LeverControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/controls/paint_control.cpp b/engines/zvision/scripting/controls/paint_control.cpp index df06bb814e..62dde3d170 100644 --- a/engines/zvision/scripting/controls/paint_control.cpp +++ b/engines/zvision/scripting/controls/paint_control.cpp @@ -114,12 +114,18 @@ PaintControl::PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStre PaintControl::~PaintControl() { // Clear the state value back to 0 //_engine->getScriptManager()->setStateValue(_key, 0); - if (_paint) + if (_paint) { + _paint->free(); delete _paint; - if (_brush) + } + if (_brush) { + _brush->free(); delete _brush; - if (_bkg) + } + if (_bkg) { + _bkg->free(); delete _bkg; + } } bool PaintControl::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) { diff --git a/engines/zvision/scripting/controls/paint_control.h b/engines/zvision/scripting/controls/paint_control.h index 0e5b59b821..8c01f0e68a 100644 --- a/engines/zvision/scripting/controls/paint_control.h +++ b/engines/zvision/scripting/controls/paint_control.h @@ -32,7 +32,7 @@ namespace ZVision { -// Only used in Zork Nemesis, it's the painting puzzle screen in Lucien's room in Irondune (ch4g) +// Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g) class PaintControl : public Control { public: PaintControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/controls/safe_control.cpp b/engines/zvision/scripting/controls/safe_control.cpp index 71be692431..6ba34106d0 100644 --- a/engines/zvision/scripting/controls/safe_control.cpp +++ b/engines/zvision/scripting/controls/safe_control.cpp @@ -49,10 +49,7 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream _outerRadiusSqr = 0; _zeroPointer = 0; _startPointer = 0; - _curFrame = -1; _targetFrame = 0; - _frameTime = 0; - _lastRenderedFrame = -1; // Loop until we find the closing brace Common::String line = stream.readLine(); @@ -64,6 +61,7 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream while (!stream.eos() && !line.contains('}')) { if (param.matchString("animation", true)) { _animation = _engine->loadAnimation(values); + _animation->start(); } else if (param.matchString("rectangle", true)) { int x; int y; @@ -104,7 +102,9 @@ SafeControl::SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream _engine->getScriptManager()->trimCommentsAndWhiteSpace(&line); getParams(line, param, values); } - renderFrame(_curState); + + if (_animation) + _animation->seekToFrame(_curState); } SafeControl::~SafeControl() { @@ -113,44 +113,20 @@ SafeControl::~SafeControl() { } -void SafeControl::renderFrame(uint frameNumber) { - if (frameNumber == 0) { - _lastRenderedFrame = frameNumber; - } else if ((int16)frameNumber < _lastRenderedFrame) { - _lastRenderedFrame = frameNumber; - frameNumber = (_statesCount * 2) - frameNumber; - } else { - _lastRenderedFrame = frameNumber; - } - - const Graphics::Surface *frameData; - int x = _rectangle.left; - int y = _rectangle.top; - - _animation->seekToFrame(frameNumber); - frameData = _animation->decodeNextFrame(); - if (frameData) - _engine->getRenderManager()->blitSurfaceToBkg(*frameData, x, y); -} - bool SafeControl::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getStateFlag(_key) & Puzzle::DISABLED) return false; - if (_curFrame != _targetFrame) { - _frameTime -= deltaTimeInMillis; - - if (_frameTime <= 0) { - if (_curFrame < _targetFrame) { - _curFrame++; - renderFrame(_curFrame); - } else if (_curFrame > _targetFrame) { - _curFrame--; - renderFrame(_curFrame); - } - _frameTime = 1000.0 / _animation->getDuration().framerate(); - } + if (_animation && _animation->getCurFrame() != _targetFrame && _animation->needsUpdate()) { + // If we're past the target frame, move back one + if (_animation->getCurFrame() > _targetFrame) + _animation->seekToFrame(_animation->getCurFrame() - 1); + + const Graphics::Surface *frameData = _animation->decodeNextFrame(); + if (frameData) + _engine->getRenderManager()->blitSurfaceToBkg(*frameData, _rectangle.left, _rectangle.top); } + return false; } @@ -187,7 +163,8 @@ bool SafeControl::onMouseUp(const Common::Point &screenSpacePos, const Common::P int16 tmp2 = (m_state + _curState - _zeroPointer + _statesCount - 1) % _statesCount; - _curFrame = (_curState + _statesCount - _startPointer) % _statesCount; + if (_animation) + _animation->seekToFrame((_curState + _statesCount - _startPointer) % _statesCount); _curState = (_statesCount * 2 + tmp2) % _statesCount; diff --git a/engines/zvision/scripting/controls/safe_control.h b/engines/zvision/scripting/controls/safe_control.h index 6e1095e304..3e8c17635c 100644 --- a/engines/zvision/scripting/controls/safe_control.h +++ b/engines/zvision/scripting/controls/safe_control.h @@ -34,6 +34,7 @@ namespace Video { namespace ZVision { +// Only used in Zork Nemesis, handles the safe in the Asylum (ac4g) class SafeControl : public Control { public: SafeControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); @@ -51,19 +52,12 @@ private: int32 _outerRadiusSqr; int16 _zeroPointer; int16 _startPointer; - int16 _curFrame; int16 _targetFrame; - int32 _frameTime; - - int16 _lastRenderedFrame; public: bool onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos); bool process(uint32 deltaTimeInMillis); - -private: - void renderFrame(uint frameNumber); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/controls/save_control.cpp b/engines/zvision/scripting/controls/save_control.cpp index b35611feca..6cedddffeb 100644 --- a/engines/zvision/scripting/controls/save_control.cpp +++ b/engines/zvision/scripting/controls/save_control.cpp @@ -29,7 +29,8 @@ #include "zvision/scripting/script_manager.h" #include "zvision/text/string_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" +#include "zvision/graphics/render_manager.h" #include "common/str.h" #include "common/stream.h" @@ -97,18 +98,18 @@ bool SaveControl::process(uint32 deltaTimeInMillis) { if (inp->getText().size() > 0) { bool toSave = true; if (iter->exist) - if (!_engine->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) + if (!_engine->getRenderManager()->askQuestion(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEXIST))) toSave = false; if (toSave) { // FIXME: At this point, the screen shows the save control, so the save game thumbnails will always // show the save control _engine->getSaveManager()->saveGameBuffered(iter->saveId, inp->getText()); - _engine->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); + _engine->getRenderManager()->delayedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVED), 2000); _engine->getScriptManager()->changeLocation(_engine->getScriptManager()->getLastMenuLocation()); } } else { - _engine->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); + _engine->getRenderManager()->timedMessage(_engine->getStringManager()->getTextLine(StringManager::ZVISION_STR_SAVEEMPTY), 2000); } } else { _engine->getSaveManager()->loadGame(iter->saveId); diff --git a/engines/zvision/scripting/controls/titler_control.cpp b/engines/zvision/scripting/controls/titler_control.cpp index 10ba0af655..542e0a0b67 100644 --- a/engines/zvision/scripting/controls/titler_control.cpp +++ b/engines/zvision/scripting/controls/titler_control.cpp @@ -67,14 +67,16 @@ TitlerControl::TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadSt if (!_rectangle.isEmpty()) { _surface = new Graphics::Surface; - _surface->create(_rectangle.width(), _rectangle.height(), _engine->_pixelFormat); + _surface->create(_rectangle.width(), _rectangle.height(), _engine->_resourcePixelFormat); _surface->fillRect(Common::Rect(_surface->w, _surface->h), 0); } } TitlerControl::~TitlerControl() { - if (_surface) + if (_surface) { + _surface->free(); delete _surface; + } } void TitlerControl::setString(int strLine) { diff --git a/engines/zvision/scripting/controls/titler_control.h b/engines/zvision/scripting/controls/titler_control.h index 86bb398b3c..dd96e4a846 100644 --- a/engines/zvision/scripting/controls/titler_control.h +++ b/engines/zvision/scripting/controls/titler_control.h @@ -32,7 +32,7 @@ namespace ZVision { -// Only used in Zork Nemesis - it's the death screen with the Restore/Exit buttons +// Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons class TitlerControl : public Control { public: TitlerControl(ZVision *engine, uint32 key, Common::SeekableReadStream &stream); diff --git a/engines/zvision/scripting/sidefx/animation_node.cpp b/engines/zvision/scripting/effects/animation_effect.cpp index 3a21227d1a..511a0db353 100644 --- a/engines/zvision/scripting/sidefx/animation_node.cpp +++ b/engines/zvision/scripting/effects/animation_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/animation_node.h" +#include "zvision/scripting/effects/animation_effect.h" #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" @@ -33,20 +33,27 @@ namespace ZVision { -AnimationNode::AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse) - : SideFX(engine, controlKey, SIDEFX_ANIM), - _DisposeAfterUse(DisposeAfterUse), +AnimationEffect::AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse) + : ScriptingEffect(engine, controlKey, SCRIPTING_EFFECT_ANIM), + _disposeAfterUse(disposeAfterUse), _mask(mask), _animation(NULL) { _animation = engine->loadAnimation(fileName); - _frmDelay = 1000.0 / _animation->getDuration().framerate(); - if (frate > 0) - _frmDelay = 1000.0 / frate; + if (frate > 0) { + _frmDelayOverride = (int32)(1000.0 / frate); + + // WORKAROUND: We do not allow the engine to delay more than 66 msec + // per frame (15fps max) + if (_frmDelayOverride > 66) + _frmDelayOverride = 66; + } else { + _frmDelayOverride = 0; + } } -AnimationNode::~AnimationNode() { +AnimationEffect::~AnimationEffect() { if (_animation) delete _animation; @@ -56,60 +63,83 @@ AnimationNode::~AnimationNode() { if (it != _playList.end()) { _engine->getScriptManager()->setStateValue((*it).slot, 2); - if ((*it)._scaled) + if ((*it)._scaled) { + (*it)._scaled->free(); delete(*it)._scaled; + } } _playList.clear(); } -bool AnimationNode::process(uint32 deltaTimeInMillis) { +bool AnimationEffect::process(uint32 deltaTimeInMillis) { + ScriptManager *scriptManager = _engine->getScriptManager(); + RenderManager *renderManager = _engine->getRenderManager(); + RenderTable::RenderState renderState = renderManager->getRenderTable()->getRenderState(); + bool isPanorama = (renderState == RenderTable::PANORAMA); + int16 velocity = _engine->getMouseVelocity() + _engine->getKeyboardVelocity(); + + // Do not update animation nodes in panoramic mode while turning, if the user + // has set this option + if (scriptManager->getStateValue(StateKey_NoTurnAnim) == 1 && isPanorama && velocity) + return false; + PlayNodes::iterator it = _playList.begin(); if (it != _playList.end()) { playnode *nod = &(*it); - nod->_delay -= deltaTimeInMillis; - if (nod->_delay <= 0) { - nod->_delay += _frmDelay; - - const Graphics::Surface *frame = NULL; - - if (nod->_curFrame == -1) { // Start of new playlist node - nod->_curFrame = nod->start; - - _animation->seekToFrame(nod->_curFrame); - frame = _animation->decodeNextFrame(); - - nod->_delay = _frmDelay; - if (nod->slot) - _engine->getScriptManager()->setStateValue(nod->slot, 1); - } else { - nod->_curFrame++; - - if (nod->_curFrame > nod->stop) { - nod->loop--; - - if (nod->loop == 0) { - if (nod->slot >= 0) - _engine->getScriptManager()->setStateValue(nod->slot, 2); - if (nod->_scaled) - delete nod->_scaled; - _playList.erase(it); - return _DisposeAfterUse; - } - - nod->_curFrame = nod->start; - _animation->seekToFrame(nod->_curFrame); + if (nod->_curFrame == -1) { + // The node is just beginning playback + nod->_curFrame = nod->start; + + _animation->start(); + _animation->seekToFrame(nod->start); + _animation->setEndFrame(nod->stop); + + nod->_delay = deltaTimeInMillis; // Force the frame to draw + if (nod->slot) + scriptManager->setStateValue(nod->slot, 1); + } else if (_animation->endOfVideo()) { + // The node has reached the end; check if we need to loop + nod->loop--; + + if (nod->loop == 0) { + if (nod->slot >= 0) + scriptManager->setStateValue(nod->slot, 2); + if (nod->_scaled) { + nod->_scaled->free(); + delete nod->_scaled; } + _playList.erase(it); + return _disposeAfterUse; + } - frame = _animation->decodeNextFrame(); + nod->_curFrame = nod->start; + _animation->seekToFrame(nod->start); + } + + // Check if we need to draw a frame + bool needsUpdate = false; + if (_frmDelayOverride == 0) { + // If not overridden, use the VideoDecoder's check + needsUpdate = _animation->needsUpdate(); + } else { + // Otherwise, implement our own timing + nod->_delay -= deltaTimeInMillis; + + if (nod->_delay <= 0) { + nod->_delay += _frmDelayOverride; + needsUpdate = true; } + } - if (frame) { + if (needsUpdate) { + const Graphics::Surface *frame = _animation->decodeNextFrame(); + if (frame) { uint32 dstw; uint32 dsth; - if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + if (isPanorama) { dstw = nod->pos.height(); dsth = nod->pos.width(); } else { @@ -126,6 +156,7 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { if (frame->w > dstw || frame->h > dsth || (frame->w == dstw / 2 && frame->h == dsth / 2)) { if (nod->_scaled) if (nod->_scaled->w != dstw || nod->_scaled->h != dsth) { + nod->_scaled->free(); delete nod->_scaled; nod->_scaled = NULL; } @@ -135,22 +166,17 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { nod->_scaled->create(dstw, dsth, frame->format); } - _engine->getRenderManager()->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); + renderManager->scaleBuffer(frame->getPixels(), nod->_scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, dstw, dsth); frame = nod->_scaled; } - if (_engine->getRenderManager()->getRenderTable()->getRenderState() == RenderTable::PANORAMA) { + if (isPanorama) { Graphics::Surface *transposed = RenderManager::tranposeSurface(frame); - if (_mask > 0) - _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); - else - _engine->getRenderManager()->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top); + renderManager->blitSurfaceToBkg(*transposed, nod->pos.left, nod->pos.top, _mask); + transposed->free(); delete transposed; } else { - if (_mask > 0) - _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); - else - _engine->getRenderManager()->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top); + renderManager->blitSurfaceToBkg(*frame, nod->pos.left, nod->pos.top, _mask); } } } @@ -159,16 +185,12 @@ bool AnimationNode::process(uint32 deltaTimeInMillis) { return false; } -void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { +void AnimationEffect::addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops) { playnode nod; nod.loop = loops; nod.pos = Common::Rect(x, y, x2 + 1, y2 + 1); nod.start = startFrame; - nod.stop = endFrame; - - if (nod.stop >= (int)_animation->getFrameCount()) - nod.stop = _animation->getFrameCount() - 1; - + nod.stop = CLIP<int>(endFrame, 0, _animation->getFrameCount() - 1); nod.slot = slot; nod._curFrame = -1; nod._delay = 0; @@ -176,12 +198,14 @@ void AnimationNode::addPlayNode(int32 slot, int x, int y, int x2, int y2, int st _playList.push_back(nod); } -bool AnimationNode::stop() { +bool AnimationEffect::stop() { PlayNodes::iterator it = _playList.begin(); if (it != _playList.end()) { _engine->getScriptManager()->setStateValue((*it).slot, 2); - if ((*it)._scaled) + if ((*it)._scaled) { + (*it)._scaled->free(); delete(*it)._scaled; + } } _playList.clear(); @@ -190,21 +214,4 @@ bool AnimationNode::stop() { return false; } -void AnimationNode::setNewFrameDelay(int32 newDelay) { - if (newDelay > 0) { - PlayNodes::iterator it = _playList.begin(); - if (it != _playList.end()) { - playnode *nod = &(*it); - float percent = (float)nod->_delay / (float)_frmDelay; - nod->_delay = percent * newDelay; // Scale to new max - } - - _frmDelay = newDelay; - } -} - -int32 AnimationNode::getFrameDelay() { - return _frmDelay; -} - } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/animation_node.h b/engines/zvision/scripting/effects/animation_effect.h index 3adfd91f32..fd6e24ab8b 100644 --- a/engines/zvision/scripting/sidefx/animation_node.h +++ b/engines/zvision/scripting/effects/animation_effect.h @@ -23,7 +23,7 @@ #ifndef ZVISION_ANIMATION_NODE_H #define ZVISION_ANIMATION_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "common/rect.h" #include "common/list.h" @@ -39,10 +39,10 @@ namespace ZVision { class ZVision; -class AnimationNode : public SideFX { +class AnimationEffect : public ScriptingEffect { public: - AnimationNode(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool DisposeAfterUse = true); - ~AnimationNode(); + AnimationEffect(ZVision *engine, uint32 controlKey, const Common::String &fileName, int32 mask, int32 frate, bool disposeAfterUse = true); + ~AnimationEffect(); struct playnode { Common::Rect pos; @@ -61,10 +61,10 @@ private: PlayNodes _playList; int32 _mask; - bool _DisposeAfterUse; + bool _disposeAfterUse; Video::VideoDecoder *_animation; - int32 _frmDelay; + int32 _frmDelayOverride; public: bool process(uint32 deltaTimeInMillis); @@ -72,9 +72,6 @@ public: void addPlayNode(int32 slot, int x, int y, int x2, int y2, int startFrame, int endFrame, int loops = 1); bool stop(); - - void setNewFrameDelay(int32 newDelay); - int32 getFrameDelay(); }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/distort_node.cpp b/engines/zvision/scripting/effects/distort_effect.cpp index 0d5c8b1ed5..78c4a1b9a8 100644 --- a/engines/zvision/scripting/sidefx/distort_node.cpp +++ b/engines/zvision/scripting/effects/distort_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/distort_node.h" +#include "zvision/scripting/effects/distort_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -34,7 +34,7 @@ namespace ZVision { DistortNode::DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale) - : SideFX(engine, key, SIDEFX_DISTORT) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_DISTORT) { _angle = _engine->getRenderManager()->getRenderTable()->getAngle(); _linScale = _engine->getRenderManager()->getRenderTable()->getLinscale(); @@ -65,7 +65,6 @@ DistortNode::~DistortNode() { } bool DistortNode::process(uint32 deltaTimeInMillis) { - float updTime = deltaTimeInMillis / (1000.0 / 60.0); if (_incr) diff --git a/engines/zvision/scripting/sidefx/distort_node.h b/engines/zvision/scripting/effects/distort_effect.h index 787a69bdde..c64f10e6ff 100644 --- a/engines/zvision/scripting/sidefx/distort_node.h +++ b/engines/zvision/scripting/effects/distort_effect.h @@ -23,13 +23,13 @@ #ifndef ZVISION_DISTORT_NODE_H #define ZVISION_DISTORT_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" namespace ZVision { class ZVision; -class DistortNode : public SideFX { +class DistortNode : public ScriptingEffect { public: DistortNode(ZVision *engine, uint32 key, int16 speed, float startAngle, float endAngle, float startLineScale, float endLineScale); ~DistortNode(); diff --git a/engines/zvision/scripting/sidefx/music_node.cpp b/engines/zvision/scripting/effects/music_effect.cpp index 56598189f6..102f330305 100644 --- a/engines/zvision/scripting/sidefx/music_node.cpp +++ b/engines/zvision/scripting/effects/music_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/music_node.h" +#include "zvision/scripting/effects/music_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -37,7 +37,7 @@ namespace ZVision { MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool loop, int8 volume) - : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { + : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) { _loop = loop; _volume = volume; _crossfade = false; @@ -88,7 +88,7 @@ MusicNode::MusicNode(ZVision *engine, uint32 key, Common::String &filename, bool } MusicNode::~MusicNode() { - if (!_loaded) + if (_loaded) _engine->_mixer->stopHandle(_handle); if (_key != StateKey_NotSet) _engine->getScriptManager()->setStateValue(_key, 2); @@ -137,7 +137,7 @@ bool MusicNode::process(uint32 deltaTimeInMillis) { if (_pantrack || _volume != _newvol) setVolume(_newvol); - if (_sub) + if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1) _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); } return false; @@ -179,26 +179,26 @@ void MusicNode::setVolume(uint8 newVolume) { } PanTrackNode::PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos) - : SideFX(engine, key, SIDEFX_PANTRACK) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_PANTRACK) { _slot = slot; - SideFX *fx = _engine->getScriptManager()->getSideFX(slot); - if (fx && fx->getType() == SIDEFX_AUDIO) { + ScriptingEffect *fx = _engine->getScriptManager()->getSideFX(slot); + if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) { MusicNodeBASE *mus = (MusicNodeBASE *)fx; mus->setPanTrack(pos); } } PanTrackNode::~PanTrackNode() { - SideFX *fx = _engine->getScriptManager()->getSideFX(_slot); - if (fx && fx->getType() == SIDEFX_AUDIO) { + ScriptingEffect *fx = _engine->getScriptManager()->getSideFX(_slot); + if (fx && fx->getType() == SCRIPTING_EFFECT_AUDIO) { MusicNodeBASE *mus = (MusicNodeBASE *)fx; mus->unsetPanTrack(); } } MusicMidiNode::MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume) - : MusicNodeBASE(engine, key, SIDEFX_AUDIO) { + : MusicNodeBASE(engine, key, SCRIPTING_EFFECT_AUDIO) { _volume = volume; _prog = program; _noteNumber = note; diff --git a/engines/zvision/scripting/sidefx/music_node.h b/engines/zvision/scripting/effects/music_effect.h index 09bdc3707e..31d538f668 100644 --- a/engines/zvision/scripting/sidefx/music_node.h +++ b/engines/zvision/scripting/effects/music_effect.h @@ -24,8 +24,8 @@ #define ZVISION_MUSIC_NODE_H #include "audio/mixer.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/text/subtitles.h" namespace Common { class String; @@ -33,9 +33,9 @@ class String; namespace ZVision { -class MusicNodeBASE : public SideFX { +class MusicNodeBASE : public ScriptingEffect { public: - MusicNodeBASE(ZVision *engine, uint32 key, SideFXType type) : SideFX(engine, key, type) {} + MusicNodeBASE(ZVision *engine, uint32 key, ScriptingEffectType type) : ScriptingEffect(engine, key, type) {} ~MusicNodeBASE() {} /** @@ -91,6 +91,7 @@ private: bool _loaded; }; +// Only used by Zork: Nemesis, for the flute and piano puzzles (tj4e and ve6f, as well as vr) class MusicMidiNode : public MusicNodeBASE { public: MusicMidiNode(ZVision *engine, uint32 key, int8 program, int8 note, int8 volume); @@ -120,7 +121,7 @@ private: int8 _prog; }; -class PanTrackNode : public SideFX { +class PanTrackNode : public ScriptingEffect { public: PanTrackNode(ZVision *engine, uint32 key, uint32 slot, int16 pos); ~PanTrackNode(); diff --git a/engines/zvision/scripting/sidefx/region_node.cpp b/engines/zvision/scripting/effects/region_effect.cpp index de613d8af2..78061cf4de 100644 --- a/engines/zvision/scripting/sidefx/region_node.cpp +++ b/engines/zvision/scripting/effects/region_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/region_node.h" +#include "zvision/scripting/effects/region_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -30,8 +30,8 @@ namespace ZVision { -RegionNode::RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay) - : SideFX(engine, key, SIDEFX_REGION) { +RegionNode::RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay) + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_REGION) { _effect = effect; _delay = delay; _timeLeft = 0; diff --git a/engines/zvision/scripting/sidefx/region_node.h b/engines/zvision/scripting/effects/region_effect.h index ec716b6e3e..4fc16224ff 100644 --- a/engines/zvision/scripting/sidefx/region_node.h +++ b/engines/zvision/scripting/effects/region_effect.h @@ -25,16 +25,16 @@ #include "graphics/surface.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/effect.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/graphics/graphics_effect.h" namespace ZVision { class ZVision; -class RegionNode : public SideFX { +class RegionNode : public ScriptingEffect { public: - RegionNode(ZVision *engine, uint32 key, Effect *effect, uint32 delay); + RegionNode(ZVision *engine, uint32 key, GraphicsEffect *effect, uint32 delay); ~RegionNode(); /** @@ -49,7 +49,7 @@ public: private: int32 _timeLeft; uint32 _delay; - Effect *_effect; + GraphicsEffect *_effect; }; } // End of namespace ZVision diff --git a/engines/zvision/scripting/sidefx/syncsound_node.cpp b/engines/zvision/scripting/effects/syncsound_effect.cpp index c1f139694b..70ba97deb8 100644 --- a/engines/zvision/scripting/sidefx/syncsound_node.cpp +++ b/engines/zvision/scripting/effects/syncsound_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/syncsound_node.h" +#include "zvision/scripting/effects/syncsound_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -36,7 +36,7 @@ namespace ZVision { SyncSoundNode::SyncSoundNode(ZVision *engine, uint32 key, Common::String &filename, int32 syncto) - : SideFX(engine, key, SIDEFX_AUDIO) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_AUDIO) { _syncto = syncto; _sub = NULL; @@ -76,7 +76,7 @@ bool SyncSoundNode::process(uint32 deltaTimeInMillis) { if (_engine->getScriptManager()->getSideFX(_syncto) == NULL) return stop(); - if (_sub) + if (_sub && _engine->getScriptManager()->getStateValue(StateKey_Subtitles) == 1) _sub->process(_engine->_mixer->getSoundElapsedTime(_handle) / 100); } return false; diff --git a/engines/zvision/scripting/sidefx/syncsound_node.h b/engines/zvision/scripting/effects/syncsound_effect.h index 7cd02a8aef..0eabff77a3 100644 --- a/engines/zvision/scripting/sidefx/syncsound_node.h +++ b/engines/zvision/scripting/effects/syncsound_effect.h @@ -24,15 +24,15 @@ #define ZVISION_SYNCSOUND_NODE_H #include "audio/mixer.h" -#include "zvision/scripting/sidefx.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting/scripting_effect.h" +#include "zvision/text/subtitles.h" namespace Common { class String; } namespace ZVision { -class SyncSoundNode : public SideFX { +class SyncSoundNode : public ScriptingEffect { public: SyncSoundNode(ZVision *engine, uint32 key, Common::String &file, int32 syncto); ~SyncSoundNode(); diff --git a/engines/zvision/scripting/sidefx/timer_node.cpp b/engines/zvision/scripting/effects/timer_effect.cpp index 170f6e7472..778f9dec6c 100644 --- a/engines/zvision/scripting/sidefx/timer_node.cpp +++ b/engines/zvision/scripting/effects/timer_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/effects/timer_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -32,7 +32,7 @@ namespace ZVision { TimerNode::TimerNode(ZVision *engine, uint32 key, uint timeInSeconds) - : SideFX(engine, key, SIDEFX_TIMER) { + : ScriptingEffect(engine, key, SCRIPTING_EFFECT_TIMER) { _timeLeft = 0; if (_engine->getGameId() == GID_NEMESIS) diff --git a/engines/zvision/scripting/sidefx/timer_node.h b/engines/zvision/scripting/effects/timer_effect.h index 7a26aff251..5e45d54d7d 100644 --- a/engines/zvision/scripting/sidefx/timer_node.h +++ b/engines/zvision/scripting/effects/timer_effect.h @@ -23,13 +23,13 @@ #ifndef ZVISION_TIMER_NODE_H #define ZVISION_TIMER_NODE_H -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" namespace ZVision { class ZVision; -class TimerNode : public SideFX { +class TimerNode : public ScriptingEffect { public: TimerNode(ZVision *engine, uint32 key, uint timeInSeconds); ~TimerNode(); diff --git a/engines/zvision/scripting/sidefx/ttytext_node.cpp b/engines/zvision/scripting/effects/ttytext_effect.cpp index 2930118524..c60b3aa8c5 100644 --- a/engines/zvision/scripting/sidefx/ttytext_node.cpp +++ b/engines/zvision/scripting/effects/ttytext_effect.cpp @@ -22,7 +22,7 @@ #include "common/scummsys.h" -#include "zvision/scripting/sidefx/ttytext_node.h" +#include "zvision/scripting/effects/ttytext_effect.h" #include "zvision/zvision.h" #include "zvision/scripting/script_manager.h" @@ -35,7 +35,7 @@ namespace ZVision { ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay) : - SideFX(engine, key, SIDEFX_TTYTXT), + ScriptingEffect(engine, key, SCRIPTING_EFFECT_TTYTXT), _fnt(engine) { _delay = delay; _r = r; @@ -56,7 +56,7 @@ ttyTextNode::ttyTextNode(ZVision *engine, uint32 key, const Common::String &file delete infile; } - _img.create(_r.width(), _r.height(), _engine->_pixelFormat); + _img.create(_r.width(), _r.height(), _engine->_resourcePixelFormat); _style._sharp = true; _style.readAllStyle(_txtbuf); _style.setFont(_fnt); @@ -158,7 +158,7 @@ void ttyTextNode::newline() { } void ttyTextNode::outchar(uint16 chr) { - uint32 clr = _engine->_pixelFormat.RGBToColor(_style._red, _style._green, _style._blue); + uint32 clr = _engine->_resourcePixelFormat.RGBToColor(_style._red, _style._green, _style._blue); if (_dx + _fnt.getCharWidth(chr) > _r.width()) newline(); diff --git a/engines/zvision/scripting/sidefx/ttytext_node.h b/engines/zvision/scripting/effects/ttytext_effect.h index b6cbed3e34..8d8a2518c7 100644 --- a/engines/zvision/scripting/sidefx/ttytext_node.h +++ b/engines/zvision/scripting/effects/ttytext_effect.h @@ -26,16 +26,16 @@ #include "common/rect.h" #include "graphics/surface.h" -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "zvision/text/text.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" namespace Common { class String; } namespace ZVision { -class ttyTextNode : public SideFX { +class ttyTextNode : public ScriptingEffect { public: ttyTextNode(ZVision *engine, uint32 key, const Common::String &file, const Common::Rect &r, int32 delay); ~ttyTextNode(); diff --git a/engines/zvision/core/menu.cpp b/engines/zvision/scripting/menu.cpp index 31e0d71370..16aa57e3ae 100644 --- a/engines/zvision/core/menu.cpp +++ b/engines/zvision/scripting/menu.cpp @@ -20,26 +20,22 @@ * */ -#include "common/scummsys.h" - -#include "zvision/core/menu.h" - #include "zvision/graphics/render_manager.h" +#include "zvision/scripting/menu.h" namespace ZVision { enum { - SLOT_START_SLOT = 151, - SLOT_SPELL_1 = 191, - SLOT_USER_CHOSE_THIS_SPELL = 205, - SLOT_REVERSED_SPELLBOOK = 206 + kMainMenuSave = 0, + kMainMenuLoad = 1, + kMainMenuPrefs = 2, + kMainMenuExit = 3 }; enum { - menu_MAIN_SAVE = 0, - menu_MAIN_REST = 1, - menu_MAIN_PREF = 2, - menu_MAIN_EXIT = 3 + kMenuItem = 0, + kMenuMagic = 1, + kMenuMain = 2 }; MenuHandler::MenuHandler(ZVision *engine) { @@ -122,8 +118,8 @@ MenuZGI::~MenuZGI() { void MenuZGI::onMouseUp(const Common::Point &Pos) { if (Pos.y < 40) { switch (menuMouseFocus) { - case menu_ITEM: - if (menuBarFlag & menuBar_Items) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) { int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); if (itemCount == 0) itemCount = 20; @@ -131,13 +127,13 @@ void MenuZGI::onMouseUp(const Common::Point &Pos) { for (int i = 0; i < itemCount; i++) { int itemspace = (600 - 28) / itemCount; - if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, - scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0, + scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) { int32 mouseItem = _engine->getScriptManager()->getStateValue(StateKey_InventoryItem); if (mouseItem >= 0 && mouseItem < 0xE0) { _engine->getScriptManager()->inventoryDrop(mouseItem); - _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i)); - _engine->getScriptManager()->setStateValue(SLOT_START_SLOT + i, mouseItem); + _engine->getScriptManager()->inventoryAdd(_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i)); + _engine->getScriptManager()->setStateValue(StateKey_Inv_StartSlot + i, mouseItem); redraw = true; } @@ -146,62 +142,62 @@ void MenuZGI::onMouseUp(const Common::Point &Pos) { } break; - case menu_MAGIC: - if (menuBarFlag & menuBar_Magic) { + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) { for (int i = 0; i < 12; i++) { - uint itemnum = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + uint itemnum = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i); if (itemnum != 0) { - if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1) itemnum = 0xEE + i; else itemnum = 0xE0 + i; } if (itemnum) if (_engine->getScriptManager()->getStateValue(StateKey_InventoryItem) == 0 || _engine->getScriptManager()->getStateValue(StateKey_InventoryItem) >= 0xE0) - if (Common::Rect(668 + 47 * i - scrollPos[menu_MAGIC], 0, - 668 + 47 * i - scrollPos[menu_MAGIC] + 28, 32).contains(Pos)) - _engine->getScriptManager()->setStateValue(SLOT_USER_CHOSE_THIS_SPELL, itemnum); + if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0, + 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) + _engine->getScriptManager()->setStateValue(StateKey_Active_Spell, itemnum); } } break; - case menu_MAIN: + case kMenuMain: // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(320 + 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->ifQuit(); } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(320 , - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->getScriptManager()->changeLocation('g', 'j', 'p', 'e', 0); } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(320 - 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->getScriptManager()->changeLocation('g', 'j', 'r', 'e', 0); } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(320 - 135 * 2, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 - 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { + scrollPos[kMenuMain] + 32).contains(Pos)) { _engine->getScriptManager()->changeLocation('g', 'j', 's', 'e', 0); } break; @@ -216,8 +212,8 @@ void MenuZGI::onMouseMove(const Common::Point &Pos) { redraw = true; inmenu = true; switch (menuMouseFocus) { - case menu_ITEM: - if (menuBarFlag & menuBar_Items) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) { int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); if (itemCount == 0) itemCount = 20; @@ -231,78 +227,78 @@ void MenuZGI::onMouseMove(const Common::Point &Pos) { for (int i = 0; i < itemCount; i++) { int itemspace = (600 - 28) / itemCount; - if (Common::Rect(scrollPos[menu_ITEM] + itemspace * i, 0, - scrollPos[menu_ITEM] + itemspace * i + 28, 32).contains(Pos)) { + if (Common::Rect(scrollPos[kMenuItem] + itemspace * i, 0, + scrollPos[kMenuItem] + itemspace * i + 28, 32).contains(Pos)) { mouseOnItem = i; break; } } if (lastItem != mouseOnItem) - if (_engine->getScriptManager()->getStateValue(SLOT_START_SLOT + mouseOnItem) || - _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + lastItem)) + if (_engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + mouseOnItem) || + _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + lastItem)) redraw = true; } break; - case menu_MAGIC: - if (menuBarFlag & menuBar_Magic) { + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) { int lastItem = mouseOnItem; mouseOnItem = -1; for (int i = 0; i < 12; i++) { - if (Common::Rect(668 + 47 * i - scrollPos[menu_MAGIC], 0, - 668 + 47 * i - scrollPos[menu_MAGIC] + 28, 32).contains(Pos)) { + if (Common::Rect(668 + 47 * i - scrollPos[kMenuMagic], 0, + 668 + 47 * i - scrollPos[kMenuMagic] + 28, 32).contains(Pos)) { mouseOnItem = i; break; } } if (lastItem != mouseOnItem) - if (_engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + mouseOnItem) || - _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + lastItem)) + if (_engine->getScriptManager()->getStateValue(StateKey_Spell_1 + mouseOnItem) || + _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + lastItem)) redraw = true; } break; - case menu_MAIN: { + case kMenuMain: { int lastItem = mouseOnItem; mouseOnItem = -1; // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(320 + 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_EXIT; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuExit; } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(320 , - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 + 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_PREF; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuPrefs; } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(320 - 135, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_REST; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuLoad; } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(320 - 135 * 2, - scrollPos[menu_MAIN], + scrollPos[kMenuMain], 320 - 135, - scrollPos[menu_MAIN] + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_SAVE; + scrollPos[kMenuMain] + 32).contains(Pos)) { + mouseOnItem = kMainMenuSave; } if (lastItem != mouseOnItem) @@ -313,25 +309,25 @@ void MenuZGI::onMouseMove(const Common::Point &Pos) { default: int cur_menu = menuMouseFocus; if (Common::Rect(64, 0, 64 + 512, 8).contains(Pos)) { // Main - menuMouseFocus = menu_MAIN; - scrolled[menu_MAIN] = false; - scrollPos[menu_MAIN] = menuback[menu_MAIN][1].h - menuback[menu_MAIN][0].h; + menuMouseFocus = kMenuMain; + scrolled[kMenuMain] = false; + scrollPos[kMenuMain] = menuback[kMenuMain][1].h - menuback[kMenuMain][0].h; _engine->getScriptManager()->setStateValue(StateKey_MenuState, 2); } - if (menuBarFlag & menuBar_Magic) + if (menuBarFlag & kMenubarMagic) if (Common::Rect(640 - 28, 0, 640, 32).contains(Pos)) { // Magic - menuMouseFocus = menu_MAGIC; - scrolled[menu_MAGIC] = false; - scrollPos[menu_MAGIC] = 28; + menuMouseFocus = kMenuMagic; + scrolled[kMenuMagic] = false; + scrollPos[kMenuMagic] = 28; _engine->getScriptManager()->setStateValue(StateKey_MenuState, 3); } - if (menuBarFlag & menuBar_Items) + if (menuBarFlag & kMenubarItems) if (Common::Rect(0, 0, 28, 32).contains(Pos)) { // Items - menuMouseFocus = menu_ITEM; - scrolled[menu_ITEM] = false; - scrollPos[menu_ITEM] = 28 - 600; + menuMouseFocus = kMenuItem; + scrolled[kMenuItem] = false; + scrollPos[kMenuItem] = 28 - 600; _engine->getScriptManager()->setStateValue(StateKey_MenuState, 1); } @@ -356,24 +352,24 @@ void MenuZGI::process(uint32 deltatime) { clean = false; } switch (menuMouseFocus) { - case menu_ITEM: - if (menuBarFlag & menuBar_Items) - if (!scrolled[menu_ITEM]) { + case kMenuItem: + if (menuBarFlag & kMenubarItems) + if (!scrolled[kMenuItem]) { redraw = true; float scrl = 600.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos [menu_ITEM] += scrl; + scrollPos [kMenuItem] += scrl; - if (scrollPos[menu_ITEM] >= 0) { - scrolled[menu_ITEM] = true; - scrollPos [menu_ITEM] = 0; + if (scrollPos[kMenuItem] >= 0) { + scrolled[kMenuItem] = true; + scrollPos [kMenuItem] = 0; } } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][0], scrollPos[menu_ITEM], 0); + _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuItem][0], scrollPos[kMenuItem], 0); int itemCount = _engine->getScriptManager()->getStateValue(StateKey_Inv_TotalSlots); if (itemCount == 0) @@ -389,7 +385,7 @@ void MenuZGI::process(uint32 deltatime) { if (mouseOnItem == i) inrect = true; - uint curItemId = _engine->getScriptManager()->getStateValue(SLOT_START_SLOT + i); + uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Inv_StartSlot + i); if (curItemId != 0) { if (itemId[i] != curItemId) { @@ -402,9 +398,9 @@ void MenuZGI::process(uint32 deltatime) { } if (inrect) - _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[menu_ITEM] + itemspace * i, 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][1], scrollPos[kMenuItem] + itemspace * i, 0, 0); else - _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[menu_ITEM] + itemspace * i, 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*items[i][0], scrollPos[kMenuItem] + itemspace * i, 0, 0); } else { if (items[i][0]) { @@ -425,24 +421,24 @@ void MenuZGI::process(uint32 deltatime) { } break; - case menu_MAGIC: - if (menuBarFlag & menuBar_Magic) - if (!scrolled[menu_MAGIC]) { + case kMenuMagic: + if (menuBarFlag & kMenubarMagic) + if (!scrolled[kMenuMagic]) { redraw = true; float scrl = 600.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos [menu_MAGIC] += scrl; + scrollPos [kMenuMagic] += scrl; - if (scrollPos[menu_MAGIC] >= 600) { - scrolled[menu_MAGIC] = true; - scrollPos [menu_MAGIC] = 600; + if (scrollPos[kMenuMagic] >= 600) { + scrolled[kMenuMagic] = true; + scrollPos [kMenuMagic] = 600; } } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][0], 640 - scrollPos[menu_MAGIC], 0); + _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMagic][0], 640 - scrollPos[kMenuMagic], 0); for (int i = 0; i < 12; i++) { bool inrect = false; @@ -450,9 +446,9 @@ void MenuZGI::process(uint32 deltatime) { if (mouseOnItem == i) inrect = true; - uint curItemId = _engine->getScriptManager()->getStateValue(SLOT_SPELL_1 + i); + uint curItemId = _engine->getScriptManager()->getStateValue(StateKey_Spell_1 + i); if (curItemId) { - if (_engine->getScriptManager()->getStateValue(SLOT_REVERSED_SPELLBOOK) == 1) + if (_engine->getScriptManager()->getStateValue(StateKey_Reversed_Spellbooc) == 1) curItemId = 0xEE + i; else curItemId = 0xE0 + i; @@ -469,9 +465,9 @@ void MenuZGI::process(uint32 deltatime) { } if (inrect) - _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[menu_MAGIC], 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][1], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0); else - _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[menu_MAGIC], 0, 0); + _engine->getRenderManager()->blitSurfaceToMenu(*magic[i][0], 668 + 47 * i - scrollPos[kMenuMagic], 0, 0); } else { if (magic[i][0]) { @@ -491,47 +487,47 @@ void MenuZGI::process(uint32 deltatime) { } break; - case menu_MAIN: - if (!scrolled[menu_MAIN]) { + case kMenuMain: + if (!scrolled[kMenuMain]) { redraw = true; float scrl = 32.0 * 2.0 * (deltatime / 1000.0); if (scrl == 0) scrl = 1.0; - scrollPos [menu_MAIN] += scrl; + scrollPos [kMenuMain] += scrl; - if (scrollPos[menu_MAIN] >= 0) { - scrolled[menu_MAIN] = true; - scrollPos [menu_MAIN] = 0; + if (scrollPos[kMenuMain] >= 0) { + scrolled[kMenuMain] = true; + scrollPos [kMenuMain] = 0; } } if (redraw) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAIN][0], 30, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMain][0], 30, scrollPos[kMenuMain]); - if (menuBarFlag & menuBar_Exit) { - if (mouseOnItem == menu_MAIN_EXIT) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_EXIT][1], 320 + 135, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarExit) { + if (mouseOnItem == kMainMenuExit) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuExit][1], 320 + 135, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_EXIT][0], 320 + 135, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuExit][0], 320 + 135, scrollPos[kMenuMain]); } - if (menuBarFlag & menuBar_Settings) { - if (mouseOnItem == menu_MAIN_PREF) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_PREF][1], 320, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarSettings) { + if (mouseOnItem == kMainMenuPrefs) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuPrefs][1], 320, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_PREF][0], 320, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuPrefs][0], 320, scrollPos[kMenuMain]); } - if (menuBarFlag & menuBar_Restore) { - if (mouseOnItem == menu_MAIN_REST) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_REST][1], 320 - 135, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarRestore) { + if (mouseOnItem == kMainMenuLoad) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuLoad][1], 320 - 135, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_REST][0], 320 - 135, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuLoad][0], 320 - 135, scrollPos[kMenuMain]); } - if (menuBarFlag & menuBar_Save) { - if (mouseOnItem == menu_MAIN_SAVE) - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_SAVE][1], 320 - 135 * 2, scrollPos[menu_MAIN]); + if (menuBarFlag & kMenubarSave) { + if (mouseOnItem == kMainMenuSave) + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuSave][1], 320 - 135 * 2, scrollPos[kMenuMain]); else - _engine->getRenderManager()->blitSurfaceToMenu(menubar[menu_MAIN_SAVE][0], 320 - 135 * 2, scrollPos[menu_MAIN]); + _engine->getRenderManager()->blitSurfaceToMenu(menubar[kMainMenuSave][0], 320 - 135 * 2, scrollPos[kMenuMain]); } redraw = false; } @@ -539,13 +535,13 @@ void MenuZGI::process(uint32 deltatime) { default: if (redraw) { if (inmenu) { - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAIN][1], 30, 0); + _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMain][1], 30, 0); - if (menuBarFlag & menuBar_Items) - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_ITEM][1], 0, 0); + if (menuBarFlag & kMenubarItems) + _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuItem][1], 0, 0); - if (menuBarFlag & menuBar_Magic) - _engine->getRenderManager()->blitSurfaceToMenu(menuback[menu_MAGIC][1], 640 - 28, 0); + if (menuBarFlag & kMenubarMagic) + _engine->getRenderManager()->blitSurfaceToMenu(menuback[kMenuMagic][1], 640 - 28, 0); } redraw = false; } @@ -587,7 +583,7 @@ static const int16 buts[4][2] = { {120 , 64}, {144, 184}, {128, 328}, {120, 456} void MenuNemesis::onMouseUp(const Common::Point &Pos) { if (Pos.y < 40) { // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(buts[3][1], scrollPos, buts[3][0] + buts[3][1], @@ -598,7 +594,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(buts[2][1], scrollPos, buts[2][0] + buts[2][1], @@ -609,7 +605,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(buts[1][1], scrollPos, buts[1][0] + buts[1][1], @@ -620,7 +616,7 @@ void MenuNemesis::onMouseUp(const Common::Point &Pos) { } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(buts[0][1], scrollPos, buts[0][0] + buts[0][1], @@ -644,39 +640,39 @@ void MenuNemesis::onMouseMove(const Common::Point &Pos) { mouseOnItem = -1; // Exit - if (menuBarFlag & menuBar_Exit) + if (menuBarFlag & kMenubarExit) if (Common::Rect(buts[3][1], scrollPos, buts[3][0] + buts[3][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_EXIT; + mouseOnItem = kMainMenuExit; } // Settings - if (menuBarFlag & menuBar_Settings) + if (menuBarFlag & kMenubarSettings) if (Common::Rect(buts[2][1], scrollPos, buts[2][0] + buts[2][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_PREF; + mouseOnItem = kMainMenuPrefs; } // Load - if (menuBarFlag & menuBar_Restore) + if (menuBarFlag & kMenubarRestore) if (Common::Rect(buts[1][1], scrollPos, buts[1][0] + buts[1][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_REST; + mouseOnItem = kMainMenuLoad; } // Save - if (menuBarFlag & menuBar_Save) + if (menuBarFlag & kMenubarSave) if (Common::Rect(buts[0][1], scrollPos, buts[0][0] + buts[0][1], scrollPos + 32).contains(Pos)) { - mouseOnItem = menu_MAIN_SAVE; + mouseOnItem = kMainMenuSave; } if (lastItem != mouseOnItem) { @@ -721,20 +717,20 @@ void MenuNemesis::process(uint32 deltatime) { if (redraw) { _engine->getRenderManager()->blitSurfaceToMenu(menubar, 64, scrollPos); - if (menuBarFlag & menuBar_Exit) - if (mouseOnItem == menu_MAIN_EXIT) + if (menuBarFlag & kMenubarExit) + if (mouseOnItem == kMainMenuExit) _engine->getRenderManager()->blitSurfaceToMenu(but[3][frm], buts[3][1], scrollPos); - if (menuBarFlag & menuBar_Settings) - if (mouseOnItem == menu_MAIN_PREF) + if (menuBarFlag & kMenubarSettings) + if (mouseOnItem == kMainMenuPrefs) _engine->getRenderManager()->blitSurfaceToMenu(but[2][frm], buts[2][1], scrollPos); - if (menuBarFlag & menuBar_Restore) - if (mouseOnItem == menu_MAIN_REST) + if (menuBarFlag & kMenubarRestore) + if (mouseOnItem == kMainMenuLoad) _engine->getRenderManager()->blitSurfaceToMenu(but[1][frm], buts[1][1], scrollPos); - if (menuBarFlag & menuBar_Save) - if (mouseOnItem == menu_MAIN_SAVE) + if (menuBarFlag & kMenubarSave) + if (mouseOnItem == kMainMenuSave) _engine->getRenderManager()->blitSurfaceToMenu(but[0][frm], buts[0][1], scrollPos); redraw = false; diff --git a/engines/zvision/core/menu.h b/engines/zvision/scripting/menu.h index ebe0bb50ac..a88587966f 100644 --- a/engines/zvision/core/menu.h +++ b/engines/zvision/scripting/menu.h @@ -32,12 +32,12 @@ namespace ZVision { enum menuBar { - menuBar_Exit = 0x1, - menuBar_Settings = 0x2, - menuBar_Restore = 0x4, - menuBar_Save = 0x8, - menuBar_Items = 0x100, - menuBar_Magic = 0x200 + kMenubarExit = 0x1, + kMenubarSettings = 0x2, + kMenubarRestore = 0x4, + kMenubarSave = 0x8, + kMenubarItems = 0x100, + kMenubarMagic = 0x200 }; class MenuHandler { @@ -84,12 +84,6 @@ private: bool scrolled[3]; int16 scrollPos[3]; - enum { - menu_ITEM = 0, - menu_MAGIC = 1, - menu_MAIN = 2 - }; - bool clean; bool redraw; diff --git a/engines/zvision/scripting/scr_file_handling.cpp b/engines/zvision/scripting/scr_file_handling.cpp index 47b8b0aa39..227c43557c 100644 --- a/engines/zvision/scripting/scr_file_handling.cpp +++ b/engines/zvision/scripting/scr_file_handling.cpp @@ -216,6 +216,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("animpreload", true)) { actionList.push_back(new ActionPreloadAnimation(_engine, slot, args)); } else if (act.matchString("animunload", true)) { + // Only used by ZGI (locations cd6e, cd6k, dg2f, dg4e, dv1j) actionList.push_back(new ActionUnloadAnimation(_engine, slot, args)); } else if (act.matchString("attenuate", true)) { actionList.push_back(new ActionAttenuate(_engine, slot, args)); @@ -234,12 +235,13 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("disable_control", true)) { actionList.push_back(new ActionDisableControl(_engine, slot, args)); } else if (act.matchString("disable_venus", true)) { - actionList.push_back(new ActionDisableVenus(_engine, slot, args)); + // Not used. Purposely left empty } else if (act.matchString("display_message", true)) { actionList.push_back(new ActionDisplayMessage(_engine, slot, args)); } else if (act.matchString("dissolve", true)) { actionList.push_back(new ActionDissolve(_engine)); } else if (act.matchString("distort", true)) { + // Only used by Zork: Nemesis for the "treatment" puzzle in the Sanitarium (aj30) actionList.push_back(new ActionDistort(_engine, slot, args)); } else if (act.matchString("enable_control", true)) { actionList.push_back(new ActionEnableControl(_engine, slot, args)); @@ -248,6 +250,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("inventory", true)) { actionList.push_back(new ActionInventory(_engine, slot, args)); } else if (act.matchString("kill", true)) { + // Only used by ZGI actionList.push_back(new ActionKill(_engine, slot, args)); } else if (act.matchString("menu_bar_enable", true)) { actionList.push_back(new ActionMenuBarEnable(_engine, slot, args)); @@ -264,6 +267,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("random", true)) { actionList.push_back(new ActionRandom(_engine, slot, args)); } else if (act.matchString("region", true)) { + // Only used by Zork: Nemesis actionList.push_back(new ActionRegion(_engine, slot, args)); } else if (act.matchString("restore_game", true)) { actionList.push_back(new ActionRestoreGame(_engine, slot, args)); @@ -276,7 +280,7 @@ void ScriptManager::parseResults(Common::SeekableReadStream &stream, Common::Lis } else if (act.matchString("set_screen", true)) { actionList.push_back(new ActionSetScreen(_engine, slot, args)); } else if (act.matchString("set_venus", true)) { - actionList.push_back(new ActionSetVenus(_engine, slot, args)); + // Not used. Purposely left empty } else if (act.matchString("stop", true)) { actionList.push_back(new ActionStop(_engine, slot, args)); } else if (act.matchString("streamvideo", true)) { @@ -345,29 +349,32 @@ Control *ScriptManager::parseControl(Common::String &line, Common::SeekableReadS Control::parsePanoramaControl(_engine, stream); return NULL; } else if (controlType.equalsIgnoreCase("tilt")) { + // Only used in Zork Nemesis, handles tilt controls (ZGI doesn't have a tilt view) Control::parseTiltControl(_engine, stream); return NULL; - } else if (controlType.equalsIgnoreCase("lever")) { - return new LeverControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("slot")) { return new SlotControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("input")) { return new InputControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("save")) { return new SaveControl(_engine, key, stream); + } else if (controlType.equalsIgnoreCase("lever")) { + // Only used in Zork Nemesis, handles draggable levers (te2e, tm7e, tp2e, tt2e, tz2e) + return new LeverControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("safe")) { + // Only used in Zork Nemesis, handles the safe in the Asylum (ac4g) return new SafeControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("hotmovie")) { - // Only used in Zork Nemesis, it handles movies where the player needs to click on something (mj7g, vw3g) + // Only used in Zork Nemesis, handles movies where the player needs to click on something (mj7g, vw3g) return new HotMovControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("fist")) { - // Only used in Zork Nemesis, it handles the door lock puzzle with the skeletal fingers (td60, td90, td9e) + // Only used in Zork Nemesis, handles the door lock puzzle with the skeletal fingers (td9e) return new FistControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("paint")) { - // Only used in Zork Nemesis, it's the painting puzzle screen in Lucien's room in Irondune (ch4g) + // Only used in Zork Nemesis, handles the painting puzzle screen in Lucien's room in Irondune (ch4g) return new PaintControl(_engine, key, stream); } else if (controlType.equalsIgnoreCase("titler")) { - // Only used in Zork Nemesis - it's the death screen with the Restore/Exit buttons (cjde) + // Only used in Zork Nemesis, handles the death screen with the Restore/Exit buttons (cjde) return new TitlerControl(_engine, key, stream); } return NULL; diff --git a/engines/zvision/scripting/script_manager.cpp b/engines/zvision/scripting/script_manager.cpp index 4c1e69072d..464e8bfe4d 100644 --- a/engines/zvision/scripting/script_manager.cpp +++ b/engines/zvision/scripting/script_manager.cpp @@ -27,9 +27,10 @@ #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/scripting/actions.h" -#include "zvision/scripting/sidefx/timer_node.h" +#include "zvision/scripting/menu.h" +#include "zvision/scripting/effects/timer_effect.h" #include "common/algorithm.h" #include "common/hashmap.h" @@ -381,11 +382,11 @@ void ScriptManager::setFocusControlKey(uint32 key) { _currentlyFocusedControl = key; } -void ScriptManager::addSideFX(SideFX *fx) { +void ScriptManager::addSideFX(ScriptingEffect *fx) { _activeSideFx.push_back(fx); } -SideFX *ScriptManager::getSideFX(uint32 key) { +ScriptingEffect *ScriptManager::getSideFX(uint32 key) { for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) { if ((*iter)->getKey() == key) { return (*iter); @@ -429,7 +430,7 @@ void ScriptManager::killSideFx(uint32 key) { } } -void ScriptManager::killSideFxType(SideFX::SideFXType type) { +void ScriptManager::killSideFxType(ScriptingEffect::ScriptingEffectType type) { for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) { if ((*iter)->getType() & type) { (*iter)->kill(); @@ -568,10 +569,6 @@ void ScriptManager::ChangeLocationReal() { } else { if (_currentLocation.world == 'g' && _currentLocation.room == 'j') _engine->getSaveManager()->flushSaveBuffer(); - else { - // Auto save - //_engine->getSaveManager()->autoSave(); - } } setStateValue(StateKey_World, _nextLocation.world); @@ -583,7 +580,7 @@ void ScriptManager::ChangeLocationReal() { _referenceTable.clear(); addPuzzlesToReferenceTable(universe); - _engine->menuBarEnable(0xFFFF); + _engine->getMenuHandler()->setEnable(0xFFFF); if (_nextLocation.world != _currentLocation.world) { cleanScriptScope(nodeview); @@ -652,7 +649,7 @@ void ScriptManager::ChangeLocationReal() { execScope(nodeview); } - _engine->checkBorders(); + _engine->getRenderManager()->checkBorders(); } void ScriptManager::serialize(Common::WriteStream *stream) { diff --git a/engines/zvision/scripting/script_manager.h b/engines/zvision/scripting/script_manager.h index 1e308faf0d..78c1b77dea 100644 --- a/engines/zvision/scripting/script_manager.h +++ b/engines/zvision/scripting/script_manager.h @@ -25,7 +25,7 @@ #include "zvision/scripting/puzzle.h" #include "zvision/scripting/control.h" -#include "zvision/scripting/sidefx.h" +#include "zvision/scripting/scripting_effect.h" #include "common/hashmap.h" #include "common/queue.h" @@ -94,7 +94,12 @@ enum StateKey { StateKey_Inv_Cnt_Slot = 100, StateKey_Inv_1_Slot = 101, StateKey_Inv_49_Slot = 149, - StateKey_Inv_TotalSlots = 150 + // ZGI only + StateKey_Inv_TotalSlots = 150, + StateKey_Inv_StartSlot = 151, + StateKey_Spell_1 = 191, + StateKey_Active_Spell = 205, + StateKey_Reversed_Spellbooc = 206 }; struct Location { @@ -111,7 +116,7 @@ typedef Common::List<Puzzle *> PuzzleList; typedef Common::Queue<Puzzle *> PuzzleQueue; typedef Common::List<Control *> ControlList; typedef Common::HashMap<uint32, int32> StateMap; -typedef Common::List<SideFX *> SideFXList; +typedef Common::List<ScriptingEffect *> SideFXList; typedef Common::List<Common::Event> EventList; class ScriptManager { @@ -191,12 +196,12 @@ public: // Only change focus control without call focus/unfocus. void setFocusControlKey(uint32 key); - void addSideFX(SideFX *fx); - SideFX *getSideFX(uint32 key); + void addSideFX(ScriptingEffect *fx); + ScriptingEffect *getSideFX(uint32 key); void deleteSideFx(uint32 key); void stopSideFx(uint32 key); void killSideFx(uint32 key); - void killSideFxType(SideFX::SideFXType type); + void killSideFxType(ScriptingEffect::ScriptingEffectType type); void addEvent(Common::Event); void flushEvent(Common::EventType type); @@ -282,7 +287,7 @@ public: void inventoryDrop(int16 item); void inventoryCycle(); - // TODO: Make this private. It was only made public so Console::cmdParseAllScrFiles() could use it +private: /** * Parses a script file into triggers and events * @@ -291,7 +296,6 @@ public: */ void parseScrFile(const Common::String &fileName, ScriptScope &scope); -private: /** * Parses the stream into a Puzzle object * Helper method for parseScrFile. diff --git a/engines/zvision/scripting/sidefx.h b/engines/zvision/scripting/scripting_effect.h index 5bb14f0cdd..0af1d9c21c 100644 --- a/engines/zvision/scripting/sidefx.h +++ b/engines/zvision/scripting/scripting_effect.h @@ -20,8 +20,8 @@ * */ -#ifndef SIDEFX_H_INCLUDED -#define SIDEFX_H_INCLUDED +#ifndef SCRIPTING_EFFECT_H_INCLUDED +#define SCRIPTING_EFFECT_H_INCLUDED namespace Common { class SeekableReadStream; @@ -33,29 +33,39 @@ namespace ZVision { class ZVision; -class SideFX { +/** + * The base class that represents effects created from Actions. + * This class is virtual. + * + * Detailed Description: + * A scene has Controls. By interacting with the controls, the user + * causes Actions to execute. Certain Actions create 'effects', for + * example, a sound or an animation. This is the base class for + * those effects. + */ +class ScriptingEffect { public: - enum SideFXType { - SIDEFX_ANIM = 1, - SIDEFX_AUDIO = 2, - SIDEFX_DISTORT = 4, - SIDEFX_PANTRACK = 8, - SIDEFX_REGION = 16, - SIDEFX_TIMER = 32, - SIDEFX_TTYTXT = 64, - SIDEFX_UNK = 128, - SIDEFX_ALL = 255 + enum ScriptingEffectType { + SCRIPTING_EFFECT_ANIM = 1, + SCRIPTING_EFFECT_AUDIO = 2, + SCRIPTING_EFFECT_DISTORT = 4, + SCRIPTING_EFFECT_PANTRACK = 8, + SCRIPTING_EFFECT_REGION = 16, + SCRIPTING_EFFECT_TIMER = 32, + SCRIPTING_EFFECT_TTYTXT = 64, + SCRIPTING_EFFECT_UNKNOWN = 128, + SCRIPTING_EFFECT_ALL = 255 }; - SideFX() : _engine(0), _key(0), _type(SIDEFX_UNK) {} - SideFX(ZVision *engine, uint32 key, SideFXType type) : _engine(engine), _key(key), _type(type) {} - virtual ~SideFX() {} + ScriptingEffect() : _engine(0), _key(0), _type(SCRIPTING_EFFECT_UNKNOWN) {} + ScriptingEffect(ZVision *engine, uint32 key, ScriptingEffectType type) : _engine(engine), _key(key), _type(type) {} + virtual ~ScriptingEffect() {} uint32 getKey() { return _key; } - SideFXType getType() { + ScriptingEffectType getType() { return _type; } @@ -103,7 +113,7 @@ public: protected: ZVision *_engine; uint32 _key; - SideFXType _type; + ScriptingEffectType _type; // Static member functions public: @@ -111,4 +121,4 @@ public: }; } // End of namespace ZVision -#endif // SIDEFX_H_INCLUDED +#endif // SCRIPTING_EFFECT_H_INCLUDED diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp index b2c88b34df..6d1980b1af 100644 --- a/engines/zvision/sound/zork_raw.cpp +++ b/engines/zvision/sound/zork_raw.cpp @@ -213,7 +213,6 @@ RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag dis } int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { - int32 bytesRead = _streamReader.readBuffer(buffer, _stream.get(), numSamples); if (_stream->eos()) @@ -241,16 +240,10 @@ Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stre return new RawZorkStream(rate, stereo, disposeAfterUse, stream); } -Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse) { - return makeRawZorkStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, stereo, DisposeAfterUse::YES); -} - Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { Common::File *file = new Common::File(); - assert(engine->getSearchManager()->openFile(*file, filePath)); + if (!engine->getSearchManager()->openFile(*file, filePath)) + error("File not found: %s", filePath.c_str()); // Get the file name Common::StringTokenizer tokenizer(filePath, "/\\"); diff --git a/engines/zvision/sound/zork_raw.h b/engines/zvision/sound/zork_raw.h index 0b408d818c..892bad4d5f 100644 --- a/engines/zvision/sound/zork_raw.h +++ b/engines/zvision/sound/zork_raw.h @@ -123,20 +123,6 @@ public: }; /** - * Creates an audio stream, which plays from the given buffer. - * - * @param buffer Buffer to play from. - * @param size Size of the buffer in bytes. - * @param rate Rate of the sound data. - * @param dispose AfterUse Whether to free the buffer after use (with free!). - * @return The new SeekableAudioStream (or 0 on failure). - */ -Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, - int rate, - bool stereo, - DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); - -/** * Creates an audio stream, which plays from the given stream. * * @param stream Stream object to play from. diff --git a/engines/zvision/text/string_manager.h b/engines/zvision/text/string_manager.h index b77ad65040..f4564ee1ec 100644 --- a/engines/zvision/text/string_manager.h +++ b/engines/zvision/text/string_manager.h @@ -24,7 +24,7 @@ #define ZVISION_STRING_MANAGER_H #include "zvision/detection.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" namespace Graphics { class FontManager; diff --git a/engines/zvision/graphics/subtitles.cpp b/engines/zvision/text/subtitles.cpp index d2c56f0991..acf4c37c2f 100644 --- a/engines/zvision/graphics/subtitles.cpp +++ b/engines/zvision/text/subtitles.cpp @@ -21,7 +21,7 @@ */ #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/text/subtitles.h" #include "zvision/file/search_manager.h" #include "zvision/text/text.h" diff --git a/engines/zvision/graphics/subtitles.h b/engines/zvision/text/subtitles.h index c3da6583a4..c3da6583a4 100644 --- a/engines/zvision/graphics/subtitles.h +++ b/engines/zvision/text/subtitles.h diff --git a/engines/zvision/text/text.cpp b/engines/zvision/text/text.cpp index 08b57914c2..a5ed044424 100644 --- a/engines/zvision/text/text.cpp +++ b/engines/zvision/text/text.cpp @@ -33,7 +33,7 @@ #include "zvision/text/text.h" #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "zvision/scripting/script_manager.h" namespace ZVision { @@ -44,12 +44,16 @@ cTxtStyle::cTxtStyle() { _green = 255; _red = 255; _bold = false; +#if 0 + _newline = false; _escapement = 0; +#endif _italic = false; _justify = TXT_JUSTIFY_LEFT; - _newline = false; _size = 12; +#if 0 _skipcolor = false; +#endif _strikeout = false; _underline = false; _statebox = 0; @@ -115,10 +119,12 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { } } } else if (token.matchString("newline", true)) { +#if 0 if ((retval & TXT_RET_NEWLN) == 0) _newline = 0; _newline++; +#endif retval |= TXT_RET_NEWLN; } else if (token.matchString("point", true)) { if (!tokenizer.empty()) { @@ -132,8 +138,10 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { } else if (token.matchString("escapement", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); +#if 0 int32 tmp = atoi(token.c_str()); _escapement = tmp; +#endif } } else if (token.matchString("italic", true)) { if (!tokenizer.empty()) { @@ -198,11 +206,13 @@ txtReturn cTxtStyle::parseStyle(const Common::String &strin, int16 ln) { } else if (token.matchString("skipcolor", true)) { if (!tokenizer.empty()) { token = tokenizer.nextToken(); +#if 0 if (token.matchString("on", true)) { _skipcolor = true; } else if (token.matchString("off", true)) { _skipcolor = false; } +#endif } } else if (token.matchString("image", true)) { // Not used @@ -288,7 +298,7 @@ void cTxtStyle::setFont(StyledTTFont &font) { Graphics::Surface *TextRenderer::render(StyledTTFont &fnt, const Common::String &txt, cTxtStyle &style) { style.setFontStyle(fnt); - uint32 clr = _engine->_pixelFormat.RGBToColor(style._red, style._green, style._blue); + uint32 clr = _engine->_resourcePixelFormat.RGBToColor(style._red, style._green, style._blue); return fnt.renderSolidText(txt, clr); } @@ -307,7 +317,7 @@ int32 TextRenderer::drawTxt(const Common::String &txt, cTxtStyle &fontStyle, Gra dst.fillRect(Common::Rect(dst.w, dst.h), 0); - uint32 clr = _engine->_pixelFormat.RGBToColor(fontStyle._red, fontStyle._green, fontStyle._blue); + uint32 clr = _engine->_resourcePixelFormat.RGBToColor(fontStyle._red, fontStyle._green, fontStyle._blue); int16 w; @@ -452,15 +462,16 @@ void TextRenderer::drawTxtInOneLine(const Common::String &text, Graphics::Surfac j++; } dx = 0; + Common::Rect empty; for (int32 jj = 0; jj < j; jj++) { if (TxtJustify[i] == TXT_JUSTIFY_LEFT) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], empty, dst, dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); else if (TxtJustify[i] == TXT_JUSTIFY_CENTER) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, ((dst.w - width) / 2) + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], empty, dst, ((dst.w - width) / 2) + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); else if (TxtJustify[i] == TXT_JUSTIFY_RIGHT) - _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], dst, dst.w - width + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); + _engine->getRenderManager()->blitSurfaceToSurface(*TxtSurfaces[i][jj], empty, dst, dst.w - width + dx, dy + TxtPoint[i] - TxtSurfaces[i][jj]->h, 0); dx += TxtSurfaces[i][jj]->w; } @@ -495,10 +506,12 @@ Common::String readWideLine(Common::SeekableReadStream &stream) { } else if (value >= 0x80 && value < 0x800) { asciiString += (char)(0xC0 | ((value >> 6) & 0x1F)); asciiString += (char)(0x80 | (value & 0x3F)); - } else if (value >= 0x800 && value < 0x10000) { + } else if (value >= 0x800 && value < 0x10000 && value != 0xCCCC) { asciiString += (char)(0xE0 | ((value >> 12) & 0xF)); asciiString += (char)(0x80 | ((value >> 6) & 0x3F)); asciiString += (char)(0x80 | (value & 0x3F)); + } else if (value == 0xCCCC) { + // Ignore, this character is used as newline sometimes } else if (value >= 0x10000 && value < 0x200000) { asciiString += (char)(0xF0); asciiString += (char)(0x80 | ((value >> 12) & 0x3F)); diff --git a/engines/zvision/text/text.h b/engines/zvision/text/text.h index c3c60f6b76..c942b8141a 100644 --- a/engines/zvision/text/text.h +++ b/engines/zvision/text/text.h @@ -25,7 +25,7 @@ #define ZVISION_TEXT_H #include "zvision/detection.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "zvision/zvision.h" namespace ZVision { @@ -61,13 +61,17 @@ public: uint8 _red; // 0-255 uint8 _green; // 0-255 uint8 _blue; // 0-255 +#if 0 int8 _newline; int8 _escapement; +#endif bool _italic; bool _bold; bool _underline; bool _strikeout; +#if 0 bool _skipcolor; +#endif int32 _statebox; bool _sharp; // char image ?? diff --git a/engines/zvision/graphics/truetype_font.cpp b/engines/zvision/text/truetype_font.cpp index 5c8aa03f6a..622a02a6a8 100644 --- a/engines/zvision/graphics/truetype_font.cpp +++ b/engines/zvision/text/truetype_font.cpp @@ -32,7 +32,7 @@ #include "zvision/zvision.h" #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" namespace ZVision { @@ -62,15 +62,15 @@ bool StyledTTFont::loadFont(const Common::String &fontName, int32 point) { }; const FontStyle systemFonts[] = { - { "*times new roman*", "times", "FreeSerif", "Italic" }, - { "*times*", "times", "FreeSerif", "Italic" }, + { "*times new roman*", "times", "FreeSerif", "Italic" }, + { "*times*", "times", "FreeSerif", "Italic" }, { "*century schoolbook*", "censcbk", "FreeSerif", "Italic" }, - { "*garamond*", "gara", "FreeSerif", "Italic" }, - { "*courier new*", "cour", "FreeMono", "Oblique" }, - { "*courier*", "cour", "FreeMono", "Oblique" }, - { "*ZorkDeath*", "cour", "FreeMono", "Oblique" }, - { "*arial*", "arial", "FreeSans", "Oblique" }, - { "*ZorkNormal*", "arial", "FreeSans", "Oblique" }, + { "*garamond*", "gara", "FreeSerif", "Italic" }, + { "*courier new*", "cour", "FreeMono", "Oblique" }, + { "*courier*", "cour", "FreeMono", "Oblique" }, + { "*ZorkDeath*", "cour", "FreeMono", "Oblique" }, + { "*arial*", "arial", "FreeSans", "Oblique" }, + { "*ZorkNormal*", "arial", "FreeSans", "Oblique" }, }; Common::String newFontName; @@ -108,7 +108,7 @@ bool StyledTTFont::loadFont(const Common::String &fontName, int32 point) { bool sharp = (_style & STTF_SHARP) == STTF_SHARP; Common::File file; - if (!file.open(newFontName) && !file.open(freeFontName)) + if (!file.open(newFontName) && !file.open(freeFontName) && !_engine->getSearchManager()->openFile(file, newFontName) && !_engine->getSearchManager()->openFile(file, freeFontName)) error("Unable to open font file %s (free alternative: %s)", newFontName.c_str(), freeFontName.c_str()); Graphics::Font *_newFont = Graphics::loadTTFFont(file, point, 60, (sharp ? Graphics::kTTFRenderModeMonochrome : Graphics::kTTFRenderModeNormal)); // 66 dpi for 640 x 480 on 14" display @@ -218,7 +218,7 @@ Graphics::Surface *StyledTTFont::renderSolidText(const Common::String &str, uint if (_font) { int16 w = _font->getStringWidth(str); if (w && w < 1024) { - tmp->create(w, _font->getFontHeight(), _engine->_pixelFormat); + tmp->create(w, _font->getFontHeight(), _engine->_resourcePixelFormat); drawString(tmp, str, 0, 0, w, color); } } diff --git a/engines/zvision/graphics/truetype_font.h b/engines/zvision/text/truetype_font.h index b5fac4af8a..b5fac4af8a 100644 --- a/engines/zvision/graphics/truetype_font.h +++ b/engines/zvision/text/truetype_font.h diff --git a/engines/zvision/video/rlf_decoder.cpp b/engines/zvision/video/rlf_decoder.cpp index bdb5dc18bc..db598a25b6 100644 --- a/engines/zvision/video/rlf_decoder.cpp +++ b/engines/zvision/video/rlf_decoder.cpp @@ -30,8 +30,6 @@ #include "common/debug.h" #include "common/endian.h" -#include "graphics/colormasks.h" - namespace ZVision { RLFDecoder::~RLFDecoder() { @@ -41,9 +39,13 @@ RLFDecoder::~RLFDecoder() { bool RLFDecoder::loadStream(Common::SeekableReadStream *stream) { close(); - addTrack(new RLFVideoTrack(stream)); - - return true; + // Check if the stream is valid + if (stream && !stream->err() && stream->readUint32BE() == MKTAG('F', 'E', 'L', 'R')) { + addTrack(new RLFVideoTrack(stream)); + return true; + } else { + return false; + } } RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) @@ -54,7 +56,7 @@ RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) _height(0), _frameTime(0), _frames(0), - _curFrame(0), + _displayedFrame(-1), _frameBufferByteSize(0) { if (!readHeader()) { @@ -62,7 +64,7 @@ RLFDecoder::RLFVideoTrack::RLFVideoTrack(Common::SeekableReadStream *stream) return; } - _currentFrameBuffer.create(_width, _height, Graphics::createPixelFormat<565>()); + _currentFrameBuffer.create(_width, _height, getPixelFormat()); _frameBufferByteSize = _width * _height * sizeof(uint16); _frames = new Frame[_frameCount]; @@ -83,10 +85,6 @@ RLFDecoder::RLFVideoTrack::~RLFVideoTrack() { } bool RLFDecoder::RLFVideoTrack::readHeader() { - if (_readStream->readUint32BE() != MKTAG('F', 'E', 'L', 'R')) { - return false; - } - // Read the header _readStream->readUint32LE(); // Size1 _readStream->readUint32LE(); // Unknown1 @@ -163,16 +161,11 @@ bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) { uint frame = getFrameAtTime(time); assert(frame < (int)_frameCount); - if ((uint)_curFrame == frame) + if ((uint)_displayedFrame == frame) return true; - if (frame < 0) { - _curFrame = 0; - return false; - } - - int closestFrame = _curFrame; - int distance = (int)frame - _curFrame; + int closestFrame = _displayedFrame; + int distance = (int)frame - closestFrame; if (distance < 0) { for (uint i = 0; i < _completeFrames.size(); ++i) { @@ -196,19 +189,18 @@ bool RLFDecoder::RLFVideoTrack::seek(const Audio::Timestamp &time) { applyFrameToCurrent(i); } - _curFrame = frame; + _displayedFrame = frame - 1; return true; } const Graphics::Surface *RLFDecoder::RLFVideoTrack::decodeNextFrame() { - // When an animation ends, rewind - if (_curFrame == (int)_frameCount) - seek(Audio::Timestamp(0, getFrameRate().toInt())); + if (_displayedFrame >= (int)_frameCount) + return NULL; - applyFrameToCurrent(_curFrame); + _displayedFrame++; + applyFrameToCurrent(_displayedFrame); - _curFrame++; return &_currentFrameBuffer; } @@ -242,10 +234,7 @@ void RLFDecoder::RLFVideoTrack::decodeMaskedRunLengthEncoding(int8 *source, int8 return; } - byte r, g, b; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 destColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); - WRITE_UINT16(dest + destOffset, destColor); + WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset)); sourceOffset += 2; destOffset += 2; @@ -289,10 +278,7 @@ void RLFDecoder::RLFVideoTrack::decodeSimpleRunLengthEncoding(int8 *source, int8 return; } - byte r, g, b; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 destColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); - WRITE_UINT16(dest + destOffset, destColor); + WRITE_UINT16(dest + destOffset, READ_LE_UINT16(source + sourceOffset)); sourceOffset += 2; destOffset += 2; @@ -306,9 +292,7 @@ void RLFDecoder::RLFVideoTrack::decodeSimpleRunLengthEncoding(int8 *source, int8 return; } - byte r, g, b; - Graphics::colorToRGB<Graphics::ColorMasks<555> >(READ_LE_UINT16(source + sourceOffset), r, g, b); - uint16 sampleColor = Graphics::RGBToColor<Graphics::ColorMasks<565> >(r, g, b); + uint16 sampleColor = READ_LE_UINT16(source + sourceOffset); sourceOffset += 2; numberOfCopy = numberOfSamples + 2; diff --git a/engines/zvision/video/rlf_decoder.h b/engines/zvision/video/rlf_decoder.h index f0f31c2128..8b8cbaecd5 100644 --- a/engines/zvision/video/rlf_decoder.h +++ b/engines/zvision/video/rlf_decoder.h @@ -45,15 +45,15 @@ private: uint16 getWidth() const { return _width; } uint16 getHeight() const { return _height; } - Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); /*RGB 565*/ } - int getCurFrame() const { return _curFrame; } + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); /* RGB 555 */ } + int getCurFrame() const { return _displayedFrame; } int getFrameCount() const { return _frameCount; } const Graphics::Surface *decodeNextFrame(); bool isSeekable() const { return true; } bool seek(const Audio::Timestamp &time); protected: - Common::Rational getFrameRate() const { return Common::Rational(60, _frameTime); } + Common::Rational getFrameRate() const { return Common::Rational(1000, _frameTime); } private: enum EncodingType { @@ -121,7 +121,7 @@ private: Frame *_frames; Common::Array<uint> _completeFrames; - int _curFrame; + int _displayedFrame; Graphics::Surface _currentFrameBuffer; uint32 _frameBufferByteSize; diff --git a/engines/zvision/video/video.cpp b/engines/zvision/video/video.cpp index 189fb22194..0913b28818 100644 --- a/engines/zvision/video/video.cpp +++ b/engines/zvision/video/video.cpp @@ -29,7 +29,8 @@ #include "zvision/zvision.h" #include "zvision/core/clock.h" #include "zvision/graphics/render_manager.h" -#include "zvision/graphics/subtitles.h" +#include "zvision/scripting//script_manager.h" +#include "zvision/text/subtitles.h" #include "zvision/video/rlf_decoder.h" #include "zvision/video/zork_avi_decoder.h" @@ -48,7 +49,12 @@ Video::VideoDecoder *ZVision::loadAnimation(const Common::String &fileName) { error("Unknown suffix for animation %s", fileName.c_str()); Common::File *_file = getSearchManager()->openFile(tmpFileName); - animation->loadStream(_file); + if (!_file) + error("Error opening %s", tmpFileName.c_str()); + + bool loaded = animation->loadStream(_file); + if (!loaded) + error("Error loading animation %s", tmpFileName.c_str()); return animation; } @@ -70,6 +76,7 @@ void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, uint16 y = _workingWindow.top + dst.top; uint16 finalWidth = dst.width() < _workingWindow.width() ? dst.width() : _workingWindow.width(); uint16 finalHeight = dst.height() < _workingWindow.height() ? dst.height() : _workingWindow.height(); + bool showSubs = (_scriptManager->getStateValue(StateKey_Subtitles) == 1); _clock.stop(); vid.start(); @@ -101,7 +108,7 @@ void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, if (vid.needsUpdate()) { const Graphics::Surface *frame = vid.decodeNextFrame(); - if (sub) + if (sub && showSubs) sub->process(vid.getCurFrame()); if (frame) { @@ -109,7 +116,8 @@ void ZVision::playVideo(Video::VideoDecoder &vid, const Common::Rect &destRect, _renderManager->scaleBuffer(frame->getPixels(), scaled->getPixels(), frame->w, frame->h, frame->format.bytesPerPixel, scaled->w, scaled->h); frame = scaled; } - _system->copyRectToScreen((const byte *)frame->getPixels(), frame->pitch, x, y, finalWidth, finalHeight); + Common::Rect rect = Common::Rect(x, y, x + finalWidth, y + finalHeight); + _renderManager->copyToScreen(*frame, rect, 0, 0); _renderManager->processSubs(0); } } diff --git a/engines/zvision/video/zork_avi_decoder.cpp b/engines/zvision/video/zork_avi_decoder.cpp index 67fab0a114..5618250d79 100644 --- a/engines/zvision/video/zork_avi_decoder.cpp +++ b/engines/zvision/video/zork_avi_decoder.cpp @@ -45,8 +45,14 @@ void ZorkAVIDecoder::ZorkAVIAudioTrack::queueSound(Common::SeekableReadStream *s RawChunkStream::RawChunk chunk = decoder->readNextChunk(stream); delete stream; - if (chunk.data) - _audStream->queueBuffer((byte *)chunk.data, chunk.size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN | Audio::FLAG_STEREO); + if (chunk.data) { + byte flags = Audio::FLAG_16BITS | Audio::FLAG_STEREO; +#ifdef SCUMM_LITTLE_ENDIAN + // RawChunkStream produces native endianness int16 + flags |= Audio::FLAG_LITTLE_ENDIAN; +#endif + _audStream->queueBuffer((byte *)chunk.data, chunk.size, DisposeAfterUse::YES, flags); + } } else { AVIAudioTrack::queueSound(stream); } diff --git a/engines/zvision/zvision.cpp b/engines/zvision/zvision.cpp index c32cceb26e..54991aced3 100644 --- a/engines/zvision/zvision.cpp +++ b/engines/zvision/zvision.cpp @@ -27,13 +27,13 @@ #include "zvision/scripting/script_manager.h" #include "zvision/graphics/render_manager.h" #include "zvision/graphics/cursors/cursor_manager.h" -#include "zvision/core/save_manager.h" +#include "zvision/file/save_manager.h" #include "zvision/text/string_manager.h" #include "zvision/detection.h" -#include "zvision/core/menu.h" +#include "zvision/scripting/menu.h" #include "zvision/file/search_manager.h" #include "zvision/text/text.h" -#include "zvision/graphics/truetype_font.h" +#include "zvision/text/truetype_font.h" #include "zvision/sound/midi.h" #include "zvision/file/zfs_archive.h" @@ -46,42 +46,41 @@ #include "common/system.h" #include "common/file.h" +#include "gui/message.h" #include "engines/util.h" - #include "audio/mixer.h" namespace ZVision { -#define ZVISION_SETTINGS_KEYS_COUNT 17 +#define ZVISION_SETTINGS_KEYS_COUNT 11 struct zvisionIniSettings { const char *name; int16 slot; - int16 deflt; + int16 defaultValue; // -1: use the bool value + bool defaultBoolValue; + bool allowEditing; } settingsKeys[ZVISION_SETTINGS_KEYS_COUNT] = { - {"ZVision_KeyboardTurnSpeed", StateKey_KbdRotateSpeed, 5}, - {"ZVision_PanaRotateSpeed", StateKey_RotateSpeed, 540}, - {"ZVision_QSoundEnabled", StateKey_Qsound, 1}, - {"ZVision_VenusEnabled", StateKey_VenusEnable, 1}, - {"ZVision_HighQuality", StateKey_HighQuality, 1}, - {"ZVision_Platform", StateKey_Platform, 0}, - {"ZVision_InstallLevel", StateKey_InstallLevel, 0}, - {"ZVision_CountryCode", StateKey_CountryCode, 0}, - {"ZVision_CPU", StateKey_CPU, 1}, - {"ZVision_MovieCursor", StateKey_MovieCursor, 1}, - {"ZVision_NoAnimWhileTurning", StateKey_NoTurnAnim, 0}, - {"ZVision_Win958", StateKey_WIN958, 0}, - {"ZVision_ShowErrorDialogs", StateKey_ShowErrorDlg, 0}, - {"ZVision_ShowSubtitles", StateKey_Subtitles, 1}, - {"ZVision_DebugCheats", StateKey_DebugCheats, 0}, - {"ZVision_JapaneseFonts", StateKey_JapanFonts, 0}, - {"ZVision_Brightness", StateKey_Brightness, 0} + // Hardcoded settings + {"countrycode", StateKey_CountryCode, 0, false, false}, // always 0 = US, subtitles are shown for codes 0 - 4, unused + {"lineskipvideo", StateKey_VideoLineSkip, 0, false, false}, // video line skip, 0 = default, 1 = always, 2 = pixel double when possible, unused + {"installlevel", StateKey_InstallLevel, 0, false, false}, // 0 = full, checked by universe.scr + {"highquality", StateKey_HighQuality, -1, true, false}, // high panorama quality, unused + {"qsoundenabled", StateKey_Qsound, -1, true, false}, // 1 = enable QSound - TODO: not supported yet + {"debugcheats", StateKey_DebugCheats, -1, true, false}, // always start with the GOxxxx cheat enabled + // Editable settings + {"keyboardturnspeed", StateKey_KbdRotateSpeed, 5, false, true}, + {"panarotatespeed", StateKey_RotateSpeed, 540, false, true}, // checked by universe.scr + {"noanimwhileturning", StateKey_NoTurnAnim, -1, false, true}, // toggle playing animations during pana rotation + {"venusenabled", StateKey_VenusEnable, -1, true, true}, + {"subtitles", StateKey_Subtitles, -1, true, true} }; ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), - _pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /*RGB 565*/ + _resourcePixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0), /* RGB 555 */ + _screenPixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), /* RGB 565 */ _desiredFrameTime(33), /* ~30 fps */ _clock(_system), _scriptManager(nullptr), @@ -95,12 +94,14 @@ ZVision::ZVision(OSystem *syst, const ZVisionGameDescription *gameDesc) _menu(nullptr), _searchManager(nullptr), _textRenderer(nullptr), - _halveDelay(false), + _doubleFPS(false), _audioId(0), _frameRenderDelay(2), _keyboardVelocity(0), _mouseVelocity(0), - _videoIsPlaying(false) { + _videoIsPlaying(false), + _renderedFrameCount(0), + _fps(0) { debug(1, "ZVision::ZVision"); @@ -124,24 +125,45 @@ ZVision::~ZVision() { delete _cursorManager; delete _stringManager; delete _saveManager; - delete _renderManager; delete _scriptManager; + delete _renderManager; // should be deleted after the script manager delete _rnd; delete _midiManager; + getTimerManager()->removeTimerProc(&fpsTimerCallback); + // Remove all of our debug levels DebugMan.clearAllDebugChannels(); } void ZVision::registerDefaultSettings() { - for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) - ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].deflt); + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) { + if (settingsKeys[i].allowEditing) { + if (settingsKeys[i].defaultValue >= 0) + ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].defaultValue); + else + ConfMan.registerDefault(settingsKeys[i].name, settingsKeys[i].defaultBoolValue); + } + } + + ConfMan.registerDefault("originalsaveload", false); ConfMan.registerDefault("doublefps", false); } void ZVision::loadSettings() { - for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) - _scriptManager->setStateValue(settingsKeys[i].slot, ConfMan.getInt(settingsKeys[i].name)); + int16 value = 0; + bool boolValue = false; + + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) { + if (settingsKeys[i].defaultValue >= 0) { + value = (settingsKeys[i].allowEditing) ? ConfMan.getInt(settingsKeys[i].name) : settingsKeys[i].defaultValue; + } else { + boolValue = (settingsKeys[i].allowEditing) ? ConfMan.getBool(settingsKeys[i].name) : settingsKeys[i].defaultBoolValue; + value = (boolValue) ? 1 : 0; + } + + _scriptManager->setStateValue(settingsKeys[i].slot, value); + } if (getGameId() == GID_NEMESIS) _scriptManager->setStateValue(StateKey_ExecScopeStyle, 1); @@ -150,8 +172,15 @@ void ZVision::loadSettings() { } void ZVision::saveSettings() { - for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) - ConfMan.setInt(settingsKeys[i].name, _scriptManager->getStateValue(settingsKeys[i].slot)); + for (int i = 0; i < ZVISION_SETTINGS_KEYS_COUNT; i++) { + if (settingsKeys[i].allowEditing) { + if (settingsKeys[i].defaultValue >= 0) + ConfMan.setInt(settingsKeys[i].name, _scriptManager->getStateValue(settingsKeys[i].slot)); + else + ConfMan.setBool(settingsKeys[i].name, (_scriptManager->getStateValue(settingsKeys[i].slot) == 1)); + } + } + ConfMan.flushToDisk(); } @@ -182,17 +211,17 @@ void ZVision::initialize() { } else if (_gameDescription->gameId == GID_NEMESIS) _searchManager->loadZix("NEMESIS.ZIX"); - initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_pixelFormat); + initGraphics(WINDOW_WIDTH, WINDOW_HEIGHT, true, &_screenPixelFormat); // Register random source _rnd = new Common::RandomSource("zvision"); // Create managers _scriptManager = new ScriptManager(this); - _renderManager = new RenderManager(this, WINDOW_WIDTH, WINDOW_HEIGHT, _workingWindow, _pixelFormat); + _renderManager = new RenderManager(this, WINDOW_WIDTH, WINDOW_HEIGHT, _workingWindow, _resourcePixelFormat, _doubleFPS); _saveManager = new SaveManager(this); _stringManager = new StringManager(this); - _cursorManager = new CursorManager(this, &_pixelFormat); + _cursorManager = new CursorManager(this, _resourcePixelFormat); _textRenderer = new TextRenderer(this); _midiManager = new MidiManager(); @@ -212,7 +241,10 @@ void ZVision::initialize() { // Create debugger console. It requires GFX to be initialized _console = new Console(this); - _halveDelay = ConfMan.getBool("doublefps"); + _doubleFPS = ConfMan.getBool("doublefps"); + + // Initialize FPS timer callback + getTimerManager()->installTimerProc(&fpsTimerCallback, 1000000, this, "zvisionFPS"); } Common::Error ZVision::run() { @@ -222,6 +254,22 @@ Common::Error ZVision::run() { if (ConfMan.hasKey("save_slot")) _saveManager->loadGame(ConfMan.getInt("save_slot")); + // Before starting, make absolutely sure that the user has copied the needed fonts + if (!Common::File::exists("arial.ttf") && !Common::File::exists("FreeSans.ttf") && !_searchManager->hasFile("arial.ttf") && !_searchManager->hasFile("FreeSans.ttf") ) { + GUI::MessageDialog dialog( + "Before playing this game, you'll need to copy the required " + "fonts into ScummVM's extras directory, or into the game directory. " + "On Windows, you'll need the following font files from the Windows " + "font directory: Times New Roman, Century Schoolbook, Garamond, " + "Courier New and Arial. Alternatively, you can download the GNU " + "FreeFont package. You'll need all the fonts from that package, " + "i.e., FreeMono, FreeSans and FreeSerif." + ); + dialog.runModal(); + quitGame(); + return Common::kUnknownError; + } + // Main loop while (!shouldQuit()) { _clock.update(); @@ -231,7 +279,7 @@ Common::Error ZVision::run() { _cursorManager->setItemID(_scriptManager->getStateValue(StateKey_InventoryItem)); processEvents(); - updateRotation(); + _renderManager->updateRotation(); _scriptManager->update(deltaTime); _menu->process(deltaTime); @@ -243,8 +291,9 @@ Common::Error ZVision::run() { _renderManager->renderSceneToScreen(); // Update the screen - if (_frameRenderDelay <= 0) { + if (canRender()) { _system->updateScreen(); + _renderedFrameCount++; } else { _frameRenderDelay--; } @@ -254,85 +303,18 @@ Common::Error ZVision::run() { // Ensure non-negative delay = delay < 0 ? 0 : delay; - if (_halveDelay) { + if (_doubleFPS) { delay >>= 1; } - _system->delayMillis(delay); - } - - return Common::kNoError; -} - -bool ZVision::askQuestion(const Common::String &str) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, str); - _renderManager->processSubs(0); - _renderManager->renderSceneToScreen(); - _clock.stop(); - - int result = 0; - - while (result == 0) { - Common::Event evnt; - while (_eventMan->pollEvent(evnt)) { - if (evnt.type == Common::EVENT_KEYDOWN) { - switch (evnt.kbd.keycode) { - case Common::KEYCODE_y: - result = 2; - break; - case Common::KEYCODE_n: - result = 1; - break; - default: - break; - } - } + if (canSaveGameStateCurrently() && shouldPerformAutoSave(_saveManager->getLastSaveTime())) { + _saveManager->autoSave(); } - _system->updateScreen(); - if (_halveDelay) - _system->delayMillis(33); - else - _system->delayMillis(66); - } - _renderManager->deleteSubArea(msgid); - _clock.start(); - return result == 2; -} -void ZVision::delayedMessage(const Common::String &str, uint16 milsecs) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, str); - _renderManager->processSubs(0); - _renderManager->renderSceneToScreen(); - _clock.stop(); - - uint32 stopTime = _system->getMillis() + milsecs; - while (_system->getMillis() < stopTime) { - Common::Event evnt; - while (_eventMan->pollEvent(evnt)) { - if (evnt.type == Common::EVENT_KEYDOWN && - (evnt.kbd.keycode == Common::KEYCODE_SPACE || - evnt.kbd.keycode == Common::KEYCODE_RETURN || - evnt.kbd.keycode == Common::KEYCODE_ESCAPE)) - break; - } - _system->updateScreen(); - if (_halveDelay) - _system->delayMillis(33); - else - _system->delayMillis(66); + _system->delayMillis(delay); } - _renderManager->deleteSubArea(msgid); - _clock.start(); -} -void ZVision::timedMessage(const Common::String &str, uint16 milsecs) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, str); - _renderManager->processSubs(0); - _renderManager->renderSceneToScreen(); - _renderManager->deleteSubArea(msgid, milsecs); + return Common::kNoError; } void ZVision::pauseEngineIntern(bool pause) { @@ -349,10 +331,6 @@ Common::String ZVision::generateSaveFileName(uint slot) { return Common::String::format("%s.%03u", _targetName.c_str(), slot); } -Common::String ZVision::generateAutoSaveFileName() { - return Common::String::format("%s.auto", _targetName.c_str()); -} - void ZVision::setRenderDelay(uint delay) { _frameRenderDelay = delay; } @@ -361,188 +339,23 @@ bool ZVision::canRender() { return _frameRenderDelay <= 0; } -void ZVision::updateRotation() { - int16 _velocity = _mouseVelocity + _keyboardVelocity; - - if (_halveDelay) - _velocity /= 2; - - if (_velocity) { - RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); - if (renderState == RenderTable::PANORAMA) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition + (_renderManager->getRenderTable()->getPanoramaReverse() ? -_velocity : _velocity); - - int16 zeroPoint = _renderManager->getRenderTable()->getPanoramaZeroPoint(); - if (startPosition >= zeroPoint && newPosition < zeroPoint) - _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) - 1); - if (startPosition <= zeroPoint && newPosition > zeroPoint) - _scriptManager->setStateValue(StateKey_Rounds, _scriptManager->getStateValue(StateKey_Rounds) + 1); - - int16 screenWidth = _renderManager->getBkgSize().x; - if (screenWidth) - newPosition %= screenWidth; - - if (newPosition < 0) - newPosition += screenWidth; - - _renderManager->setBackgroundPosition(newPosition); - } else if (renderState == RenderTable::TILT) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition + _velocity; - - int16 screenHeight = _renderManager->getBkgSize().y; - int16 tiltGap = _renderManager->getRenderTable()->getTiltGap(); - - if (newPosition >= (screenHeight - tiltGap)) - newPosition = screenHeight - tiltGap; - if (newPosition <= tiltGap) - newPosition = tiltGap; - - _renderManager->setBackgroundPosition(newPosition); - } - } +GUI::Debugger *ZVision::getDebugger() { + return _console; } -void ZVision::checkBorders() { - RenderTable::RenderState renderState = _renderManager->getRenderTable()->getRenderState(); - if (renderState == RenderTable::PANORAMA) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition; +void ZVision::syncSoundSettings() { + Engine::syncSoundSettings(); - int16 screenWidth = _renderManager->getBkgSize().x; - - if (screenWidth) - newPosition %= screenWidth; - - if (newPosition < 0) - newPosition += screenWidth; - - if (startPosition != newPosition) - _renderManager->setBackgroundPosition(newPosition); - } else if (renderState == RenderTable::TILT) { - int16 startPosition = _scriptManager->getStateValue(StateKey_ViewPos); - - int16 newPosition = startPosition; - - int16 screenHeight = _renderManager->getBkgSize().y; - int16 tiltGap = _renderManager->getRenderTable()->getTiltGap(); - - if (newPosition >= (screenHeight - tiltGap)) - newPosition = screenHeight - tiltGap; - if (newPosition <= tiltGap) - newPosition = tiltGap; - - if (startPosition != newPosition) - _renderManager->setBackgroundPosition(newPosition); - } -} - -void ZVision::rotateTo(int16 _toPos, int16 _time) { - if (_renderManager->getRenderTable()->getRenderState() != RenderTable::PANORAMA) - return; - - if (_time == 0) - _time = 1; - - int32 maxX = _renderManager->getBkgSize().x; - int32 curX = _renderManager->getCurrentBackgroundOffset(); - int32 dx = 0; - - if (curX == _toPos) - return; - - if (curX > _toPos) { - if (curX - _toPos > maxX / 2) - dx = (_toPos + (maxX - curX)) / _time; - else - dx = -(curX - _toPos) / _time; - } else { - if (_toPos - curX > maxX / 2) - dx = -((maxX - _toPos) + curX) / _time; - else - dx = (_toPos - curX) / _time; - } - - _clock.stop(); - - for (int16 i = 0; i <= _time; i++) { - if (i == _time) - curX = _toPos; - else - curX += dx; - - if (curX < 0) - curX = maxX - curX; - else if (curX >= maxX) - curX %= maxX; - - _renderManager->setBackgroundPosition(curX); - - _renderManager->prepareBackground(); - _renderManager->renderSceneToScreen(); - - _system->updateScreen(); - - _system->delayMillis(500 / _time); - } - - _clock.start(); -} - -void ZVision::menuBarEnable(uint16 menus) { - if (_menu) - _menu->setEnable(menus); -} - -uint16 ZVision::getMenuBarEnable() { - if (_menu) - return _menu->getEnable(); - return 0; -} - -bool ZVision::ifQuit() { - if (askQuestion(_stringManager->getTextLine(StringManager::ZVISION_STR_EXITPROMT))) { - quitGame(); - return true; - } - return false; -} - -void ZVision::pushKeyToCheatBuf(uint8 key) { - for (int i = 0; i < KEYBUF_SIZE - 1; i++) - _cheatBuffer[i] = _cheatBuffer[i + 1]; - - _cheatBuffer[KEYBUF_SIZE - 1] = key; + _scriptManager->setStateValue(StateKey_Subtitles, ConfMan.getBool("subtitles") ? 1 : 0); } -bool ZVision::checkCode(const char *code) { - int codeLen = strlen(code); - - if (codeLen > KEYBUF_SIZE) - return false; - - for (int i = 0; i < codeLen; i++) - if (code[i] != _cheatBuffer[KEYBUF_SIZE - codeLen + i] && code[i] != '?') - return false; - - return true; -} - -uint8 ZVision::getBufferedKey(uint8 pos) { - if (pos >= KEYBUF_SIZE) - return 0; - else - return _cheatBuffer[KEYBUF_SIZE - pos - 1]; +void ZVision::fpsTimerCallback(void *refCon) { + ((ZVision *)refCon)->fpsTimer(); } -void ZVision::showDebugMsg(const Common::String &msg, int16 delay) { - uint16 msgid = _renderManager->createSubArea(); - _renderManager->updateSubArea(msgid, msg); - _renderManager->deleteSubArea(msgid, delay); +void ZVision::fpsTimer() { + _fps = _renderedFrameCount; + _renderedFrameCount = 0; } } // End of namespace ZVision diff --git a/engines/zvision/zvision.h b/engines/zvision/zvision.h index 7ea10ed64d..ad22ddaaa2 100644 --- a/engines/zvision/zvision.h +++ b/engines/zvision/zvision.h @@ -41,6 +41,17 @@ namespace Video { class VideoDecoder; } +/** + * This is the namespace of the ZVision engine. + * + * Status of this engine: complete + * + * Games using this engine: + * - Zork Nemesis: The Forbidden Lands + * - Zork: Grand Inquisitor + * + */ + namespace ZVision { struct ZVisionGameDescription; @@ -68,7 +79,8 @@ public: * edges of this Rectangle */ Common::Rect _workingWindow; - const Graphics::PixelFormat _pixelFormat; + const Graphics::PixelFormat _resourcePixelFormat; + const Graphics::PixelFormat _screenPixelFormat; private: enum { @@ -118,9 +130,11 @@ private: Common::Event _event; int _frameRenderDelay; + int _renderedFrameCount; + int _fps; int16 _mouseVelocity; int16 _keyboardVelocity; - bool _halveDelay; + bool _doubleFPS; bool _videoIsPlaying; uint8 _cheatBuffer[KEYBUF_SIZE]; @@ -154,15 +168,32 @@ public: MidiManager *getMidiManager() const { return _midiManager; } + MenuHandler *getMenuHandler() const { + return _menu; + } Common::RandomSource *getRandomSource() const { return _rnd; } ZVisionGameId getGameId() const { return _gameDescription->gameId; } + int16 getKeyboardVelocity() const { + return _keyboardVelocity; + } + int16 getMouseVelocity() const { + return _mouseVelocity; + } uint8 getZvisionKey(Common::KeyCode scummKeyCode); + void startClock() { + _clock.start(); + } + + void stopClock() { + _clock.stop(); + } + /** * Play a video until it is finished. This is a blocking call. It will call * _clock.stop() when the video starts and _clock.start() when the video finishes. @@ -175,29 +206,24 @@ public: void playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect = Common::Rect(0, 0, 0, 0), bool skippable = true, Subtitle *sub = NULL); Video::VideoDecoder *loadAnimation(const Common::String &fileName); - void rotateTo(int16 to, int16 time); - Common::String generateSaveFileName(uint slot); - Common::String generateAutoSaveFileName(); - - bool askQuestion(const Common::String &str); - void delayedMessage(const Common::String &str, uint16 milsecs); - void timedMessage(const Common::String &str, uint16 milsecs); void setRenderDelay(uint); bool canRender(); + static void fpsTimerCallback(void *refCon); + void fpsTimer(); + int getFPS() const { + return _fps; + } + + GUI::Debugger *getDebugger(); + void syncSoundSettings(); void loadSettings(); void saveSettings(); - void menuBarEnable(uint16 menus); - uint16 getMenuBarEnable(); - bool ifQuit(); - void checkBorders(); - void showDebugMsg(const Common::String &msg, int16 delay = 3000); - // Engine features bool hasFeature(EngineFeature f) const; bool canLoadGameStateCurrently(); @@ -214,7 +240,6 @@ private: void processEvents(); void onMouseMove(const Common::Point &pos); - void updateRotation(); void registerDefaultSettings(); void shortKeys(Common::Event); diff --git a/gui/credits.h b/gui/credits.h index 0610609983..c7d9199db9 100644 --- a/gui/credits.h +++ b/gui/credits.h @@ -324,6 +324,8 @@ static const char *credits[] = { "", "C1""ZVision", "C0""Adrian Astley", +"C0""Filippos Karapetis", +"C0""Anton Yarcev", "", "", "C1""Backend Teams", diff --git a/gui/debugger.cpp b/gui/debugger.cpp index 216bd626fe..466681e89d 100644 --- a/gui/debugger.cpp +++ b/gui/debugger.cpp @@ -522,11 +522,25 @@ struct ArchiveMemberLess { bool Debugger::cmdMd5(int argc, const char **argv) { if (argc < 2) { - debugPrintf("md5 <filename | pattern>\n"); + debugPrintf("md5 [-n length] <filename | pattern>\n"); } else { + uint32 length = 0; + uint paramOffset = 0; + + // If the user supplied an -n parameter, set the bytes to read + if (!strcmp(argv[1], "-n")) { + // Make sure that we have at least two more parameters + if (argc < 4) { + debugPrintf("md5 [-n length] <filename | pattern>\n"); + return true; + } + length = atoi(argv[2]); + paramOffset = 2; + } + // Assume that spaces are part of a single filename. - Common::String filename = argv[1]; - for (int i = 2; i < argc; i++) { + Common::String filename = argv[1 + paramOffset]; + for (int i = 2 + paramOffset; i < argc; i++) { filename = filename + " " + argv[i]; } Common::ArchiveMemberList list; @@ -536,9 +550,9 @@ bool Debugger::cmdMd5(int argc, const char **argv) { } else { sort(list.begin(), list.end(), ArchiveMemberLess()); for (Common::ArchiveMemberList::iterator iter = list.begin(); iter != list.end(); ++iter) { - Common::ReadStream *stream = (*iter)->createReadStream(); - Common::String md5 = Common::computeStreamMD5AsString(*stream, 0); - debugPrintf("%s %s\n", md5.c_str(), (*iter)->getDisplayName().c_str()); + Common::SeekableReadStream *stream = (*iter)->createReadStream(); + Common::String md5 = Common::computeStreamMD5AsString(*stream, length); + debugPrintf("%s %s %d\n", md5.c_str(), (*iter)->getDisplayName().c_str(), stream->size()); delete stream; } } @@ -548,11 +562,25 @@ bool Debugger::cmdMd5(int argc, const char **argv) { bool Debugger::cmdMd5Mac(int argc, const char **argv) { if (argc < 2) { - debugPrintf("md5mac <base filename>\n"); + debugPrintf("md5mac [-n length] <base filename>\n"); } else { + uint32 length = 0; + uint paramOffset = 0; + + // If the user supplied an -n parameter, set the bytes to read + if (!strcmp(argv[1], "-n")) { + // Make sure that we have at least two more parameters + if (argc < 4) { + debugPrintf("md5mac [-n length] <base filename>\n"); + return true; + } + length = atoi(argv[2]); + paramOffset = 2; + } + // Assume that spaces are part of a single filename. - Common::String filename = argv[1]; - for (int i = 2; i < argc; i++) { + Common::String filename = argv[1 + paramOffset]; + for (int i = 2 + paramOffset; i < argc; i++) { filename = filename + " " + argv[i]; } Common::MacResManager macResMan; @@ -568,13 +596,13 @@ bool Debugger::cmdMd5Mac(int argc, const char **argv) { } else { // The resource fork is probably the most relevant one. if (macResMan.hasResFork()) { - Common::String md5 = macResMan.computeResForkMD5AsString(0); - debugPrintf("%s %s (resource)\n", md5.c_str(), macResMan.getBaseFileName().c_str()); + Common::String md5 = macResMan.computeResForkMD5AsString(length); + debugPrintf("%s %s (resource) %d\n", md5.c_str(), macResMan.getBaseFileName().c_str(), macResMan.getResForkDataSize()); } if (macResMan.hasDataFork()) { - Common::ReadStream *stream = macResMan.getDataFork(); - Common::String md5 = Common::computeStreamMD5AsString(*stream, 0); - debugPrintf("%s %s (data)\n", md5.c_str(), macResMan.getBaseFileName().c_str()); + Common::SeekableReadStream *stream = macResMan.getDataFork(); + Common::String md5 = Common::computeStreamMD5AsString(*stream, length); + debugPrintf("%s %s (data) %d\n", md5.c_str(), macResMan.getBaseFileName().c_str(), stream->size()); } } macResMan.close(); diff --git a/gui/saveload-dialog.cpp b/gui/saveload-dialog.cpp index 339ec95c50..a333c5fe57 100644 --- a/gui/saveload-dialog.cpp +++ b/gui/saveload-dialog.cpp @@ -654,16 +654,25 @@ void SaveLoadChooserGrid::open() { // In case there was a gap found use the slot. if (lastSlot + 1 < curSlot) { - _nextFreeSaveSlot = lastSlot + 1; - break; + // Check that the save slot can be used for user saves. + SaveStateDescriptor desc = _metaEngine->querySaveMetaInfos(_target.c_str(), lastSlot + 1); + if (!desc.getWriteProtectedFlag()) { + _nextFreeSaveSlot = lastSlot + 1; + break; + } } lastSlot = curSlot; } // Use the next available slot otherwise. - if (_nextFreeSaveSlot == -1 && lastSlot + 1 < _metaEngine->getMaximumSaveSlot()) { - _nextFreeSaveSlot = lastSlot + 1; + const int maxSlot = _metaEngine->getMaximumSaveSlot(); + for (int i = lastSlot; _nextFreeSaveSlot == -1 && i < maxSlot; ++i) { + // Check that the save slot can be used for user saves. + SaveStateDescriptor desc = _metaEngine->querySaveMetaInfos(_target.c_str(), i + 1); + if (!desc.getWriteProtectedFlag()) { + _nextFreeSaveSlot = i + 1; + } } } diff --git a/test/common/endian.h b/test/common/endian.h index cba7618c43..f083d1248c 100644 --- a/test/common/endian.h +++ b/test/common/endian.h @@ -10,6 +10,18 @@ class EndianTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(MKTAG('A','B','C','D'), tag); } + void test_READ_BE_UINT64() { + const char data[8] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF}; + uint64 value = READ_BE_UINT64(data); + TS_ASSERT_EQUALS(value, 0x123456789ABCDEFFULL); + } + + void test_READ_LE_UINT64() { + const char data[8] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xFF}; + uint64 value = READ_LE_UINT64(data); + TS_ASSERT_EQUALS(value, 0xFFEDCBA978563412ULL); + } + void test_READ_BE_UINT32() { const char data[4] = { 0x12, 0x34, 0x56, 0x78 }; uint32 value = READ_BE_UINT32(data); diff --git a/test/common/memoryreadstream.h b/test/common/memoryreadstream.h index adef861a5e..79c4079e9b 100644 --- a/test/common/memoryreadstream.h +++ b/test/common/memoryreadstream.h @@ -60,28 +60,32 @@ class MemoryReadStreamTestSuite : public CxxTest::TestSuite { } void test_seek_read_le() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStream ms(contents, sizeof(contents)); TS_ASSERT_EQUALS(ms.readUint16LE(), 0x0201UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32LE(), 0x06050403UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0E0D0C0B0A090807ULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_be() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStream ms(contents, sizeof(contents)); TS_ASSERT_EQUALS(ms.readUint16BE(), 0x0102UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32BE(), 0x03040506UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0708090A0B0C0D0EULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } diff --git a/test/common/memoryreadstreamendian.h b/test/common/memoryreadstreamendian.h index 35e804c70b..515128ea2a 100644 --- a/test/common/memoryreadstreamendian.h +++ b/test/common/memoryreadstreamendian.h @@ -60,54 +60,62 @@ class MemoryReadStreamEndianTestSuite : public CxxTest::TestSuite { } void test_seek_read_le() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), false); TS_ASSERT_EQUALS(ms.readUint16LE(), 0x0201UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32LE(), 0x06050403UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0E0D0C0B0A090807ULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_be() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), false); TS_ASSERT_EQUALS(ms.readUint16BE(), 0x0102UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32BE(), 0x03040506UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0708090A0B0C0D0EULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_le2() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), false); TS_ASSERT_EQUALS(ms.readUint16(), 0x0201UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32(), 0x06050403UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0E0D0C0B0A090807ULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } void test_seek_read_be2() { - byte contents[] = { 1, 2, 3, 4, 5, 6, 7 }; + byte contents[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; Common::MemoryReadStreamEndian ms(contents, sizeof(contents), true); TS_ASSERT_EQUALS(ms.readUint16(), 0x0102UL); TS_ASSERT_EQUALS(ms.pos(), 2); TS_ASSERT_EQUALS(ms.readUint32(), 0x03040506UL); TS_ASSERT_EQUALS(ms.pos(), 6); - TS_ASSERT_EQUALS(ms.readByte(), 0x07); - TS_ASSERT_EQUALS(ms.pos(), 7); + TS_ASSERT_EQUALS(ms.readUint64LE(), 0x0708090A0B0C0D0EULL); + TS_ASSERT_EQUALS(ms.pos(), 14); + TS_ASSERT_EQUALS(ms.readByte(), 0x0F); + TS_ASSERT_EQUALS(ms.pos(), 15); TS_ASSERT(!ms.eos()); } }; diff --git a/video/video_decoder.cpp b/video/video_decoder.cpp index dce96aae03..a4bc5b81a2 100644 --- a/video/video_decoder.cpp +++ b/video/video_decoder.cpp @@ -530,7 +530,9 @@ Audio::Timestamp VideoDecoder::FixedRateVideoTrack::getFrameTime(uint frame) con // (which Audio::Timestamp doesn't support). Common::Rational frameRate = getFrameRate(); - if (frameRate == frameRate.toInt()) // The nice case (a whole number) + // Try to keep it in terms of the frame rate, if the frame rate is a whole + // number. + if (frameRate.getDenominator() == 1) return Audio::Timestamp(0, frame, frameRate.toInt()); // Convert as best as possible |