diff options
153 files changed, 22111 insertions, 1528 deletions
diff --git a/Makefile.wiz b/Makefile.wiz new file mode 100644 index 0000000000..5370fb3ac5 --- /dev/null +++ b/Makefile.wiz @@ -0,0 +1,179 @@ +srcdir ?= . + +DEFINES := -DHAVE_CONFIG_H +LDFLAGS := +INCLUDES := -I. -I$(srcdir) -I$(srcdir)/engines +LIBS := +OBJS := +DEPDIR := .deps + +CXX := arm-open2x-linux-g++ +CXXFLAGS := -mcpu=arm926ej-s -mtune=arm926ej-s -I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include -g -mcpu=arm926ej-s -mtune=arm926ej-s -mcpu=arm926ej-s -mtune=arm926ej-s -ansi -W -Wno-unused-parameter -Wconversion -pedantic +LD := arm-open2x-linux-g++ +LIBS += -L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib -lvorbisidec -L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib -lmad -L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib -lz -L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib -Wl,-rpath,/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib -lSDL -lpthread +RANLIB := arm-open2x-linux-ranlib +STRIP := arm-open2x-linux-strip +AR := arm-open2x-linux-ar cru +AS := arm-open2x-linux-as +ASFLAGS := -mfloat-abi=soft +WINDRES := arm-open2x-linux-windres +WINDRESFLAGS := +WIN32PATH=C:/scummvm +AOS4PATH=Games:ScummVM +STATICLIBPATH=/sw + +BACKEND := gp2xwiz +MODULES += backends/platform/gp2xwiz +MODULE_DIRS += +EXEEXT := .wiz +NASM := +NASMFLAGS := + +prefix = /usr/local +exec_prefix = ${prefix} +bindir = ${exec_prefix}/bin +datarootdir = ${prefix}/share +datadir = ${datarootdir}/scummvm +docdir = ${datarootdir}/doc/scummvm +libdir = ${exec_prefix}/lib +mandir = ${datarootdir}/man + +HAVE_GCC = 1 +USE_ARM_SOUND_ASM = 1 +USE_ARM_SMUSH_ASM = 1 +USE_ARM_GFX_ASM = 1 +USE_ARM_COSTUME_ASM = 1 +USE_ARM_SCALER_ASM = 1 +# VERBOSE_BUILD = 1 +# USE_MT32EMU = 1 +# USE_RGB_COLOR = 1 +USE_SCALERS = 1 +# USE_HQ_SCALERS = 1 +USE_INDEO3 = 1 +# USE_VORBIS = 1 +USE_TREMOR = 1 +# USE_FLAC = 1 +USE_MAD = 1 +USE_ZLIB = 1 +# USE_MPEG2 = 1 +# USE_NASM = 1 +HAVE_GCC3 = 1 +CXX_UPDATE_DEP_FLAG = -MMD -MF "$(*D)/$(DEPDIR)/$(*F).d" -MQ "$@" -MP + +DYNAMIC_MODULES := 1 +VERBOSE_BUILD := 1 + +ifeq ($(DYNAMIC_MODULES),1) +ENABLED = DYNAMIC_PLUGIN +PLUGIN_PREFIX := +PLUGIN_SUFFIX := .plugin +PLUGIN_EXTRA_DEPS = $(EXECUTABLE) +CXXFLAGS += -DDYNAMIC_MODULES +PLUGIN_LDFLAGS += --nostartfiles -Wl,-q,--just-symbols,scummvm.wiz,-Tbackends/platform/gp2xwiz/plugin.ld,--retain-symbols-file,backends/platform/gp2xwiz/plugin.syms -lstdc++ -lc +PRE_OBJS_FLAGS := -Wl,-whole-archive +POST_OBJS_FLAGS := -Wl,-no-whole-archive +#LIBS += -ldl +else +ENABLED = STATIC_PLUGIN +endif + +ENABLE_SCUMM = $(ENABLED) +ENABLE_SCUMM_7_8 = $(ENABLED) +ENABLE_HE = $(ENABLED) +ENABLE_AGI = $(ENABLED) +ENABLE_AGOS = $(ENABLED) +ENABLE_AGOS2 = $(ENABLED) +ENABLE_CINE = $(ENABLED) +ENABLE_CRUISE = $(ENABLED) +ENABLE_DRACI = $(ENABLED) +ENABLE_DRASCULA = $(ENABLED) +ENABLE_GOB = $(ENABLED) +ENABLE_GROOVIE = $(ENABLED) +ENABLE_GROOVIE2 = $(ENABLED) +ENABLE_KYRA = $(ENABLED) +ENABLE_LOL = $(ENABLED) +ENABLE_LURE = $(ENABLED) +ENABLE_M4 = $(ENABLED) +ENABLE_MADE = $(ENABLED) +ENABLE_MOHAWK = $(ENABLED) +ENABLE_PARALLACTION = $(ENABLED) +ENABLE_QUEEN = $(ENABLED) +ENABLE_SAGA = $(ENABLED) +ENABLE_IHNM = $(ENABLED) +ENABLE_SAGA2 = $(ENABLED) +ENABLE_SCI = $(ENABLED) +ENABLE_SCI32 = $(ENABLED) +ENABLE_SKY = $(ENABLED) +ENABLE_SWORD1 = $(ENABLED) +ENABLE_SWORD2 = $(ENABLED) +ENABLE_TEENAGENT = $(ENABLED) +ENABLE_TINSEL = $(ENABLED) +ENABLE_TOUCHE = $(ENABLED) +ENABLE_TUCKER = $(ENABLED) + +INCLUDES += -I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include -I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include -I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include -I/opt/open2x/gcc-4.1.1-glibc-2.3.6/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT +OBJS += +DEFINES += -DNDEBUG -DUNIX -DUNIX -DGP2XWIZ -DNDEBUG -DENABLE_VKEYBD -DDATA_PATH=\"${datarootdir}/scummvm\" -DPLUGIN_DIRECTORY=\"${exec_prefix}/lib/scummvm\" +LDFLAGS += -L/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib -lm + +port_mk = ports.mk + +ifeq "$(HAVE_GCC)" "1" + CXXFLAGS:= -Wall $(CXXFLAGS) + # Turn off some annoying and not-so-useful warnings + CXXFLAGS+= -Wno-long-long -Wno-multichar -Wno-unknown-pragmas -Wno-reorder + # Enable even more warnings... + CXXFLAGS+= -Wpointer-arith -Wcast-qual -Wcast-align + CXXFLAGS+= -Wshadow -Wimplicit -Wnon-virtual-dtor -Wwrite-strings + + # Currently we disable this gcc flag, since it will also warn in cases, + # where using GCC_PRINTF (means: __attribute__((format(printf, x, y)))) + # is not possible, thus it would fail compiliation with -Werror without + # being helpful. + #CXXFLAGS+= -Wmissing-format-attribute + + # Disable RTTI and exceptions, and enable checking of pointers returned by "new" + CXXFLAGS+= -fno-rtti -fno-exceptions -fcheck-new +endif + +ifeq "$(HAVE_CLANG)" "1" + CXXFLAGS+= -Wno-conversion -Wno-shorten-64-to-32 -Wno-sign-compare -Wno-four-char-constants +endif + +####################################################################### +# Default commands - put the necessary replacements in config.mk # +####################################################################### + +CAT ?= cat +CP ?= cp +ECHO ?= printf +INSTALL ?= install +MKDIR ?= mkdir -p +RM ?= rm -f +RM_REC ?= $(RM) -r +ZIP ?= zip -q + +####################################################################### +# Misc stuff - you should never have to edit this # +####################################################################### + +EXECUTABLE := scummvm$(EXEEXT) + +include $(srcdir)/Makefile.common + +# check if configure has been run or has been changed since last run +config.h config.mk: $(srcdir)/configure +ifeq "$(findstring config.mk,$(MAKEFILE_LIST))" "config.mk" + @echo "Running $(srcdir)/configure with the last specified parameters" + @sleep 2 + LDFLAGS="$(SAVED_LDFLAGS)" CXX="$(SAVED_CXX)" \ + CXXFLAGS="$(SAVED_CXXFLAGS)" CPPFLAGS="$(SAVED_CPPFLAGS)" \ + ASFLAGS="$(SAVED_ASFLAGS)" WINDRESFLAGS="$(SAVED_WINDRESFLAGS)" \ + $(srcdir)/configure $(SAVED_CONFIGFLAGS) +else + $(error You need to run $(srcdir)/configure before you can run make. Check $(srcdir)/configure --help for a list of parameters) +endif + +ifneq ($(origin port_mk), undefined) +include $(srcdir)/$(port_mk) +endif diff --git a/backends/fs/psp/psp-stream.h b/backends/fs/psp/psp-stream.h index 9fd1ad0470..673630b685 100644 --- a/backends/fs/psp/psp-stream.h +++ b/backends/fs/psp/psp-stream.h @@ -35,39 +35,25 @@ */ class PSPIoStream : public StdioStream, public Suspendable { protected: - Common::String _path; - int _fileSize; - bool _writeMode; // for resuming in the right mode - int _physicalPos; // position in the real file - int _pos; // position. Sometimes virtual - bool _inCache; // whether we're in cache (virtual) mode - bool _eos; // EOS flag - + Common::String _path; /* Need to maintain for reopening after suspend */ + bool _writeMode; /* "" */ + int _pos; /* "" */ + mutable int _ferror; /* Save file ferror */ + mutable bool _feof; /* and eof */ + enum { SuspendError = 2, ResumeError = 3 }; - enum { - CACHE_SIZE = 1024, - MIN_READ_SIZE = 1024 // reading less than 1024 takes exactly the same time as 1024 - }; - - // For caching - char *_cache; - int _cacheStartOffset; // starting offset of the cache. -1 when cache is invalid - - mutable int _ferror; // file error state - int _errorSuspend; // for debugging + int _errorSuspend; mutable int _errorSource; + + // Error checking int _errorPos; void * _errorHandle; int _suspendCount; - bool synchronizePhysicalPos(); // synchronize the physical and virtual positions - bool isOffsetInCache(uint32 pos); // check if an offset is found in cache - bool isCacheValid() { return _cacheStartOffset != -1; } - public: /** diff --git a/backends/module.mk b/backends/module.mk index e0a3f4c683..c0457d8062 100644 --- a/backends/module.mk +++ b/backends/module.mk @@ -22,6 +22,12 @@ MODULE_OBJS := \ midi/timidity.o \ midi/dmedia.o \ midi/windows.o \ + plugins/elf-loader.o \ + plugins/mips-loader.o \ + plugins/shorts-segment-manager.o \ + plugins/arm-loader.o \ + plugins/elf-provider.o \ + plugins/dc/dc-provider.o \ plugins/posix/posix-provider.o \ plugins/sdl/sdl-provider.o \ plugins/win32/win32-provider.o \ @@ -43,7 +49,8 @@ endif ifeq ($(BACKEND),ds) MODULE_OBJS += \ fs/ds/ds-fs-factory.o \ - fs/ds/ds-fs.o + fs/ds/ds-fs.o \ + plugins/ds/ds-provider.o endif ifeq ($(BACKEND),n64) @@ -54,7 +61,8 @@ endif ifeq ($(BACKEND),ps2) MODULE_OBJS += \ - fs/ps2/ps2-fs-factory.o + fs/ps2/ps2-fs-factory.o \ + plugins/ps2/ps2-provider.o endif ifeq ($(BACKEND),psp) diff --git a/backends/platform/ds/arm7/source/main.cpp b/backends/platform/ds/arm7/source/main.cpp index bcaaf8e936..75f7012ed0 100644 --- a/backends/platform/ds/arm7/source/main.cpp +++ b/backends/platform/ds/arm7/source/main.cpp @@ -40,7 +40,7 @@ #include <system.h> #include <stdlib.h> #include <string.h> -#include <registers_alt.h> // Needed for SOUND_CR +#include <registers_alt.h> // Needed for SOUND_CR #include <NDS/scummvm_ipc.h> ////////////////////////////////////////////////////////////////////// #ifdef USE_DEBUGGER @@ -592,7 +592,7 @@ int main(int argc, char ** argv) { IPC->reset = false; - //fifoInit(); + //fifoInit(); for (int r = 0; r < 8; r++) { IPC->adpcm.arm7Buffer[r] = (u8 *) malloc(512); diff --git a/backends/platform/ds/arm9/makefile b/backends/platform/ds/arm9/makefile index eedf75c256..c9a665c442 100644 --- a/backends/platform/ds/arm9/makefile +++ b/backends/platform/ds/arm9/makefile @@ -1,10 +1,13 @@ srcdir ?= . DEPDIR := .deps -#DYNAMIC_MODULES = 1 +VERBOSE_BUILD = 1 +DYNAMIC_MODULES = 1 libndsdir = $(DEVKITPRO)/libnds #libndsdir = /home/neil/devkitpror21/libnds +ENABLED = DYNAMIC_PLUGIN + # Select the build by setting SCUMM_BUILD to a,b,c,d,e,f or g. # Anything else gets build a. @@ -12,7 +15,7 @@ ifeq ($(SCUMM_BUILD),k) DS_BUILD_K = 1 else ifeq ($(SCUMM_BUILD),j) - DS_BUILD_J = 1 + DS_BUILD_J = 1 else ifeq ($(SCUMM_BUILD),i) DS_BUILD_I = 1 @@ -64,9 +67,7 @@ ifdef DS_BUILD_F else ifdef DS_BUILD_E # TODO: Inherit the earth uses so much RAM that I have removed libmad in order to - # claw some back. - - + # claw some back. else ifdef DS_BUILD_I @@ -75,7 +76,7 @@ else ifdef DS_BUILD_K else - # USE_MAD = 1 + #USE_MAD = 1 endif endif endif @@ -112,7 +113,7 @@ USE_ARM_COSTUME_ASM = 1 ifdef DS_BUILD_A DEFINES = -DDS_BUILD_A -DUSE_ARM_GFX_ASM -DUSE_ARM_COSTUME_ASM LOGO = logoa.bmp - ENABLE_SCUMM = STATIC_PLUGIN + ENABLE_SCUMM = $(ENABLED) USE_ARM_GFX_ASM = 1 BUILD=scummvm-A endif @@ -120,66 +121,66 @@ endif ifdef DS_BUILD_B DEFINES = -DDS_BUILD_B LOGO = logob.bmp - ENABLE_SKY = STATIC_PLUGIN - ENABLE_QUEEN = STATIC_PLUGIN + ENABLE_SKY = $(ENABLED) + ENABLE_QUEEN = $(ENABLED) BUILD=scummvm-B endif ifdef DS_BUILD_C DEFINES = -DDS_BUILD_C LOGO = logoc.bmp - ENABLE_AGOS = STATIC_PLUGIN + ENABLE_AGOS = $(ENABLED) BUILD=scummvm-C endif ifdef DS_BUILD_D DEFINES = -DDS_BUILD_D LOGO = logod.bmp - ENABLE_GOB = STATIC_PLUGIN - ENABLE_CINE = STATIC_PLUGIN - ENABLE_AGI = STATIC_PLUGIN + ENABLE_GOB = $(ENABLED) + ENABLE_CINE = $(ENABLED) + ENABLE_AGI = $(ENABLED) BUILD=scummvm-D endif ifdef DS_BUILD_E DEFINES = -DDS_BUILD_E LOGO = logoe.bmp - ENABLE_SAGA = STATIC_PLUGIN + ENABLE_SAGA = $(ENABLED) BUILD=scummvm-E endif ifdef DS_BUILD_F DEFINES = -DDS_BUILD_F LOGO = logof.bmp - ENABLE_KYRA = STATIC_PLUGIN + ENABLE_KYRA = $(ENABLED) BUILD=scummvm-F endif ifdef DS_BUILD_G DEFINES = -DDS_BUILD_G LOGO = logog.bmp - ENABLE_LURE = STATIC_PLUGIN + ENABLE_LURE = $(ENABLED) BUILD=scummvm-G endif ifdef DS_BUILD_H DEFINES = -DDS_BUILD_H LOGO = logoh.bmp - ENABLE_PARALLACTION = STATIC_PLUGIN + ENABLE_PARALLACTION = $(ENABLED) BUILD=scummvm-H endif ifdef DS_BUILD_I DEFINES = -DDS_BUILD_I LOGO = logoi.bmp - ENABLE_MADE = STATIC_PLUGIN + ENABLE_MADE = $(ENABLED) BUILD=scummvm-I endif ifdef DS_BUILD_K DEFINES = -DDS_BUILD_K LOGO = logok.bmp - ENABLE_CRUISE = STATIC_PLUGIN + ENABLE_CRUISE = $(ENABLED) BUILD=scummvm-K endif @@ -187,18 +188,18 @@ endif #ifdef DS_BUILD_L # DEFINES = -DDS_BUILD_L # LOGO = logog.bmp -# ENABLE_DRASCULA = STATIC_PLUGIN +# ENABLE_DRASCULA = $(ENABLED) # BUILD=scummvm-K #endif #ifdef DS_BUILD_M # DEFINES = -DDS_BUILD_M # LOGO = logog.bmp -# ENABLE_TUCKER = STATIC_PLUGIN +# ENABLE_TUCKER = $(ENABLED) # BUILD=scummvm-K #endif -ARM7BIN := -7 $(CURDIR)/../../arm7/arm7.bin +ARM7BIN := -7 $(CURDIR)/../arm7/arm7.bin CC = arm-eabi-gcc CXX = arm-eabi-g++ @@ -236,14 +237,14 @@ CXXFLAGS= $(CFLAGS) -Wno-non-virtual-dtor -Wno-unknown-pragmas -Wno-reorder \ ASFLAGS = -mcpu=arm9tdmi -mthumb-interwork -DEFINES += -D__DS__ -DNDS -DARM9 -DNONSTANDARD_PORT -DDISABLE_FANCY_THEMES -DVECTOR_RENDERER_FORMAT=1555 -DDISABLE_DOSBOX_OPL -DDISABLE_DEFAULT_SAVEFILEMANAGER -DARM +DEFINES += -D__DS__ -DNDS -DARM9 -DNONSTANDARD_PORT -DDISABLE_FANCY_THEMES -DVECTOR_RENDERER_FORMAT=1555 -DDISABLE_DOSBOX_OPL -DDISABLE_DEFAULT_SAVEFILEMANAGER -DELF_LOADER_TARGET -DARM -DARM_TARGET#-DNEW_PLUGIN_DESIGN_FIRST_REFINEMENT ifdef USE_MAD DEFINES += -DUSE_MAD endif DEFINES += -DREDUCE_MEMORY_USAGE -LDFLAGS = -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,-Map,map.txt -Wl,--gc-sections +LDFLAGS = -specs=ds_arm9.specs -mthumb-interwork -mno-fpu -Wl,--target1-abs,-Map,map.txt#-Wl,--gc-sections ifdef WRAP_MALLOC LDFLAGS += -Wl,--wrap,malloc @@ -252,12 +253,11 @@ endif BACKEND := ds -INCLUDES= -I$(portdir)/$(BUILD) -I$(srcdir) -I$(srcdir)/engines \ +INCLUDES= -I$(portdir) -I$(srcdir) -I$(srcdir)/engines \ -I$(portdir)/data -I$(portdir)/../commoninclude \ -I$(portdir)/source -I$(portdir)/source/mad \ -I$(libndsdir)/include -include $(srcdir)/common/scummsys.h - LIBS = -lm -L$(libndsdir)/lib -L$(portdir)/lib -lnds9 ifdef USE_MAD LIBS += -lmad @@ -266,7 +266,19 @@ ifdef USE_DEBUGGER LIBS += -ldsdebugger -ldswifi9 endif +ifeq ($(DYNAMIC_MODULES),1) +DEFINES += -DDYNAMIC_MODULES +PRE_OBJS_FLAGS = -Wl,--whole-archive +POST_OBJS_FLAGS = -Wl,--no-whole-archive +endif + EXECUTABLE = scummvm.elf + +PLUGIN_PREFIX = +PLUGIN_SUFFIX = .plg +PLUGIN_EXTRA_DEPS = $(srcdir)/backends/plugins/ds/plugin.ld $(srcdir)/backends/plugins/plugin.syms $(EXECUTABLE) +PLUGIN_LDFLAGS += -nostartfiles -Wl,-q,--target1-abs,--just-symbols,$(EXECUTABLE),-T$(srcdir)/backends/plugins/ds/plugin.ld,--retain-symbols-file,$(srcdir)/backends/plugins/plugin.syms -lstdc++ -lc -mthumb-interwork -mno-fpu#-Wl,--gc-sections -mno-crt0 $(DEVKITPRO)/devkitARM/arm-eabi/lib/ds_arm9_crt0.o + MKDIR = mkdir -p RM = rm -f RM_REC = rm -rf @@ -340,12 +352,13 @@ OPT_SIZE := -Os -mthumb OBJS := $(DATA_OBJS) $(PORT_OBJS) $(FAT_OBJS) - MODULE_DIRS += . -ndsall: - @[ -d $(BUILD) ] || mkdir -p $(BUILD) - make -C ./$(BUILD) -f ../makefile scummvm.nds scummvm.ds.gba +#ndsall: plugins +# make -f makefile scummvm.nds + +ndsall: plugins + make -f makefile scummvm.nds scummvm.ds.gba include $(srcdir)/Makefile.common @@ -354,11 +367,10 @@ semiclean: clean: $(RM) $(OBJS) $(EXECUTABLE) - rm -fr $(BUILD) + rm -rf *.h engines plugins scummvm.nds scummvm.ds.gba dist : SCUMMVM.BIN plugins plugin_dist - #--------------------------------------------------------------------------------- # canned command sequence for binary data #--------------------------------------------------------------------------------- @@ -400,13 +412,14 @@ endef #--------------------------------------------------------------------------------- %.nds: %.bin - ndstool -c $@ -9 $< $(ARM7BIN) -b ../../$(LOGO) "$(@F);ScummVM $(VERSION);DS Port" + ndstool -c $@ -9 $< $(ARM7BIN) -b ../$(LOGO) "$(@F);ScummVM $(VERSION);DS Port" %.ds.gba: %.nds dsbuild $< -o $@ -l $(portdir)/ndsloader.bin padbin 16 $@ #--------------------------------------------------------------------------------- + %.bin: %.elf $(OBJCOPY) -S -O binary $< $@ diff --git a/backends/platform/ds/arm9/source/dsmain.cpp b/backends/platform/ds/arm9/source/dsmain.cpp index 95bfdfe40a..4c7d6b89ae 100644 --- a/backends/platform/ds/arm9/source/dsmain.cpp +++ b/backends/platform/ds/arm9/source/dsmain.cpp @@ -105,6 +105,8 @@ #endif #include "engine.h" +#include "backends/plugins/ds/ds-provider.h" +#include "backends/plugins/elf-provider.h" #include "backends/fs/ds/ds-fs.h" #include "base/version.h" #include "common/util.h" @@ -3213,6 +3215,9 @@ int main(void) { const char *argv[] = {"/scummvmds"}; #endif +#ifdef DYNAMIC_MODULES + PluginManager::instance().addPluginProvider(new DSPluginProvider()); +#endif while (1) { scummvm_main(ARRAYSIZE(argv), (char **) &argv); diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.c b/backends/platform/ds/arm9/source/fat/io_nmmc.c index 6c996f5de1..261096a27b 100644 --- a/backends/platform/ds/arm9/source/fat/io_nmmc.c +++ b/backends/platform/ds/arm9/source/fat/io_nmmc.c @@ -170,7 +170,7 @@ bool NMMC_IsInserted(void) { Neo_EnableMMC( true ); // Open SPI port to MMC card Neo_SendMMCCommand(MMC_SEND_CSD, 0); - if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occurred + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured Neo_EnableMMC( false ); return false; } @@ -227,14 +227,14 @@ bool NMMC_StartUp(void) { // Set block length Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ ); - if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occurred + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured Neo_EnableMMC( false ); return false; } // Check if we can use a higher SPI frequency Neo_SendMMCCommand(MMC_SEND_CSD, 0); - if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occurred + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured Neo_EnableMMC( false ); return false; } @@ -268,7 +268,7 @@ bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer) Neo_EnableMMC( true ); // Open SPI port to MMC card Neo_SendMMCCommand( 25, sector ); - if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occurred + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured Neo_EnableMMC( false ); return false; } @@ -318,7 +318,7 @@ bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer) while (totalSecs--) { Neo_SendMMCCommand(MMC_READ_BLOCK, sector ); - if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occurred + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured Neo_EnableMMC( false ); return false; } diff --git a/backends/platform/gp2x/graphics.cpp b/backends/platform/gp2x/graphics.cpp index 4a3c668c52..1888cbe47c 100644 --- a/backends/platform/gp2x/graphics.cpp +++ b/backends/platform/gp2x/graphics.cpp @@ -1405,6 +1405,7 @@ void OSystem_GP2X::drawMouse() { SDL_Rect zoomdst; SDL_Rect dst; int scale; + int width, height; int hotX, hotY; int tmpScreenWidth, tmpScreenHeight; @@ -1425,12 +1426,16 @@ void OSystem_GP2X::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp b/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp index f6ad226d42..921558131a 100644 --- a/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp +++ b/backends/platform/gp2xwiz/gp2xwiz-graphics.cpp @@ -149,6 +149,7 @@ void OSystem_GP2XWIZ::drawMouse() { SDL_Rect dst; int scale; + int width, height; int hotX, hotY; if (_videoMode.mode == GFX_HALF && !_overlayVisible){ @@ -161,12 +162,16 @@ void OSystem_GP2XWIZ::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/gp2xwiz/gp2xwiz-main.cpp b/backends/platform/gp2xwiz/gp2xwiz-main.cpp index 394c3090c3..8c95a94639 100644 --- a/backends/platform/gp2xwiz/gp2xwiz-main.cpp +++ b/backends/platform/gp2xwiz/gp2xwiz-main.cpp @@ -42,7 +42,6 @@ #include "base/main.h" #include "backends/saves/default/default-saves.h" - #include "backends/timer/default/default-timer.h" #include "sound/mixer_intern.h" @@ -64,7 +63,7 @@ int main(int argc, char *argv[]) { #ifdef DYNAMIC_MODULES PluginManager::instance().addPluginProvider(new POSIXPluginProvider()); -#endif +#endif /* DYNAMIC_MODULES */ // Invoke the actual ScummVM main entry point: int res = scummvm_main(argc, argv); diff --git a/backends/platform/gp2xwiz/module.mk b/backends/platform/gp2xwiz/module.mk index edf2f2a717..c76238f679 100644 --- a/backends/platform/gp2xwiz/module.mk +++ b/backends/platform/gp2xwiz/module.mk @@ -4,7 +4,8 @@ MODULE_OBJS := \ gp2xwiz-events.o \ gp2xwiz-graphics.o \ gp2xwiz-hw.o \ - gp2xwiz-main.o + gp2xwiz-main.o \ + gp2xwiz-loader.o # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) diff --git a/backends/platform/linuxmoto/linuxmoto-graphics.cpp b/backends/platform/linuxmoto/linuxmoto-graphics.cpp index a39416ebc4..d66d41dfab 100644 --- a/backends/platform/linuxmoto/linuxmoto-graphics.cpp +++ b/backends/platform/linuxmoto/linuxmoto-graphics.cpp @@ -168,6 +168,7 @@ void OSystem_LINUXMOTO::drawMouse() { SDL_Rect dst; int scale; + int width, height; int hotX, hotY; if (_videoMode.mode == GFX_HALF && !_overlayVisible) { @@ -180,12 +181,16 @@ void OSystem_LINUXMOTO::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/ps2/Makefile.ps2 b/backends/platform/ps2/Makefile.ps2 index d2a8d210e4..f836bd3eaa 100644 --- a/backends/platform/ps2/Makefile.ps2 +++ b/backends/platform/ps2/Makefile.ps2 @@ -1,22 +1,45 @@ # $Header: Exp $ include $(PS2SDK)/Defs.make -PS2_EXTRA = /works/devel/ps2/sdk-extra +PS2_EXTRA = /home/tony/GSOC/ps2/sdk-extra PS2_EXTRA_INCS = /zlib/include /libmad/ee/include /SjPcm/ee/src /tremor PS2_EXTRA_LIBS = /zlib/lib /libmad/ee/lib /SjPcm/ee/lib /tremor/tremor -ENABLED=STATIC_PLUGIN +# Set to 1 to enable, 0 to disable dynamic modules +DYNAMIC_MODULES = 1 +# Set to 1 to enable, 0 to disable more detailed printing of gcc commands +VERBOSE_BUILD=0 + +# Test for dynamic plugins +ifeq ($(DYNAMIC_MODULES),1) +ENABLED = DYNAMIC_PLUGIN +DEFINES = -DDYNAMIC_MODULES +PRE_OBJS_FLAGS = -Wl,--whole-archive +POST_OBJS_FLAGS = -Wl,--no-whole-archive +else +ENABLED = STATIC_PLUGIN +endif + +#control build +DISABLE_SCALERS = true +DISABLE_HQ_SCALERS = true ENABLE_SCUMM = $(ENABLED) ENABLE_SCUMM_7_8 = $(ENABLED) ENABLE_HE = $(ENABLED) ENABLE_AGI = $(ENABLED) ENABLE_AGOS = $(ENABLED) +ENABLE_AGOS2 = $(ENABLED) ENABLE_CINE = $(ENABLED) ENABLE_CRUISE = $(ENABLED) +ENABLE_DRACI = $(ENABLED) ENABLE_DRASCULA = $(ENABLED) ENABLE_GOB = $(ENABLED) +ENABLE_GROOVIE = $(ENABLED) +# ENABLE_GROOVIE2 = $(ENABLED) +ENABLE_IHNM = $(ENABLED) ENABLE_KYRA = $(ENABLED) +# ENABLE_LOL = $(ENABLED) ENABLE_LURE = $(ENABLED) # ENABLE_M4 = $(ENABLED) ENABLE_MADE = $(ENABLED) @@ -24,16 +47,18 @@ ENABLE_PARALLACTION = $(ENABLED) ENABLE_QUEEN = $(ENABLED) ENABLE_SAGA = $(ENABLED) ENABLE_SAGA2 = $(ENABLED) -ENABLE_IHNM = $(ENABLED) +ENABLE_SCI = $(ENABLED) +# ENABLE_SCI32 = $(ENABLED) ENABLE_SKY = $(ENABLED) ENABLE_SWORD1 = $(ENABLED) ENABLE_SWORD2 = $(ENABLED) -# ENABLE_TINSEL = $(ENABLED) +ENABLE_TEENAGENT = $(ENABLED) +ENABLE_TINSEL = $(ENABLED) ENABLE_TOUCHE = $(ENABLED) +ENABLE_TUCKER = $(ENABLED) HAVE_GCC3 = true - -CC = ee-gcc +CC = ee-gcc CXX = ee-g++ AS = ee-gcc LD = ee-gcc @@ -42,51 +67,62 @@ RANLIB = ee-ranlib STRIP = ee-strip MKDIR = mkdir -p RM = rm -f +RM_REC = rm -rf +MODULE_DIRS = ./ srcdir = ../../.. VPATH = $(srcdir) INCDIR = ../../../ -# DEPDIR = .deps +DEPDIR = .deps -DEFINES = -DUSE_VORBIS -DUSE_TREMOR -DUSE_MAD -DUSE_ZLIB -DFORCE_RTL -D_EE -D__PLAYSTATION2__ -O2 -Wall -Wno-multichar +TARGET = elf/scummvm.elf +DEFINES += -DUSE_VORBIS -DUSE_TREMOR -DUSE_MAD -DUSE_ZLIB -DFORCE_RTL -DDISABLE_SAVEGAME_SORTING -D_EE -D__PLAYSTATION2__ -G2 -O2 -Wall -Wno-multichar -fno-rtti -fno-exceptions -DNO_ADAPTOR +DEFINES += -DELF_LOADER_TARGET -DMIPS_TARGET#-DNEW_PLUGIN_DESIGN_FIRST_REFINEMENT INCLUDES = $(addprefix -I$(PS2_EXTRA),$(PS2_EXTRA_INCS)) INCLUDES += -I $(PS2SDK)/ee/include -I $(PS2SDK)/common/include -I ./common -I . -I $(srcdir) -I $(srcdir)/engines -TARGET = elf/scummvm.elf +CXX_UPDATE_DEP_FLAG = -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP + +# Variables for dynamic plugin building +PLUGIN_PREFIX = +PLUGIN_SUFFIX = .plg +PLUGIN_EXTRA_DEPS = $(srcdir)/backends/plugins/plugin.syms elf/scummvm.elf +PLUGIN_LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o +PLUGIN_LDFLAGS += -nostartfiles -Wl,-q,--just-symbols,elf/scummvm.elf,-T$(srcdir)/backends/plugins/ps2/plugin.ld,--retain-symbols-file,$(srcdir)/backends/plugins/plugin.syms -lstdc++ -lc -OBJS := backends/platform/ps2/DmaPipe.o \ - backends/platform/ps2/Gs2dScreen.o \ - backends/platform/ps2/irxboot.o \ - backends/platform/ps2/ps2input.o \ - backends/platform/ps2/ps2pad.o \ - backends/platform/ps2/savefilemgr.o \ - backends/platform/ps2/fileio.o \ - backends/platform/ps2/asyncfio.o \ - backends/platform/ps2/icon.o \ - backends/platform/ps2/cd.o \ - backends/platform/ps2/eecodyvdfs.o \ - backends/platform/ps2/rpckbd.o \ - backends/platform/ps2/systemps2.o \ - backends/platform/ps2/ps2mutex.o \ - backends/platform/ps2/ps2time.o \ - backends/platform/ps2/ps2debug.o - -MODULE_DIRS += . BACKEND := ps2 include $(srcdir)/Makefile.common -LDFLAGS += -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(PS2SDK)/ee/startup/linkfile +LDFLAGS = -mno-crt0 $(PS2SDK)/ee/startup/crt0.o -T $(srcdir)/backends/plugins/ps2/main_prog.ld LDFLAGS += -L $(PS2SDK)/ee/lib -L . -LDFLAGS += $(addprefix -L$(PS2_EXTRA),$(PS2_EXTRA_LIBS)) +LDFLAGS += $(addprefix -L$(PS2_EXTRA),$(PS2_EXTRA_LIBS)) LDFLAGS += -lmc -lpad -lmouse -lhdd -lpoweroff -lsjpcm -lmad -ltremor -lz -lm -lc -lfileXio -lkernel -lstdc++ -LDFLAGS += -s + +OBJS := $(srcdir)/backends/platform/ps2/DmaPipe.o \ + $(srcdir)/backends/platform/ps2/Gs2dScreen.o \ + $(srcdir)/backends/platform/ps2/irxboot.o \ + $(srcdir)/backends/platform/ps2/ps2input.o \ + $(srcdir)/backends/platform/ps2/ps2pad.o \ + $(srcdir)/backends/platform/ps2/savefilemgr.o \ + $(srcdir)/backends/platform/ps2/fileio.o \ + $(srcdir)/backends/platform/ps2/asyncfio.o \ + $(srcdir)/backends/platform/ps2/icon.o \ + $(srcdir)/backends/platform/ps2/cd.o \ + $(srcdir)/backends/platform/ps2/eecodyvdfs.o \ + $(srcdir)/backends/platform/ps2/rpckbd.o \ + $(srcdir)/backends/platform/ps2/systemps2.o \ + $(srcdir)/backends/platform/ps2/ps2mutex.o \ + $(srcdir)/backends/platform/ps2/ps2time.o \ + $(srcdir)/backends/platform/ps2/ps2debug.o + +include $(srcdir)/Makefile.common all: $(TARGET) $(TARGET): $(OBJS) - $(LD) $^ $(LDFLAGS) -o $@ + $(LD) $(PRE_OBJS_FLAGS) $(OBJS) $(POST_OBJS_FLAGS) $(LDFLAGS) -o $@ diff --git a/backends/platform/ps2/elf32.h b/backends/platform/ps2/elf32.h new file mode 100644 index 0000000000..616cc4b4d2 --- /dev/null +++ b/backends/platform/ps2/elf32.h @@ -0,0 +1,209 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_ELF_H +#define BACKENDS_ELF_H + +/* ELF stuff */ + +typedef unsigned short Elf32_Half, Elf32_Section; +typedef unsigned int Elf32_Word, Elf32_Addr, Elf32_Off; +typedef signed int Elf32_Sword; +typedef Elf32_Half Elf32_Versym; + +#define EI_NIDENT (16) +#define SELFMAG 6 + +/* ELF File format structures. Look up ELF structure for more details */ + +// ELF header (contains info about the file) +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +// Should be in e_ident +#define ELFMAG "\177ELF\1\1" /* ELF Magic number */ + +// e_type values +#define ET_NONE 0 /* no file type */ +#define ET_REL 1 /* relocatable */ +#define ET_EXEC 2 /* executable */ +#define ET_DYN 3 /* shared object */ +#define ET_CORE 4 /* core file */ + +// e_machine values +#define EM_MIPS 8 + + +// Program header (contains info about segment) +typedef struct { + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +// p_type values +#define PT_NULL 0 /* ignored */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking info */ +#define PT_INTERP 3 /* info about interpreter */ +#define PT_NOTE 4 /* note segment */ +#define PT_SHLIB 5 /* reserved */ +#define PT_PHDR 6 /* Program header table */ +#define PT_MIPS_REGINFO 0x70000000 /* register usage info */ + +// p_flags value +#define PF_X 1 /* execute */ +#define PF_W 2 /* write */ +#define PF_R 4 /* read */ + +// Section header (contains info about section) +typedef struct { + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +// sh_type values +#define SHT_NULL 0 /* Inactive section */ +#define SHT_PROGBITS 1 /* Proprietary */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addend */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Info for dynamic linking */ +#define SHT_NOTE 7 /* Note section */ +#define SHT_NOBITS 8 /* Occupies no space */ +#define SHT_REL 9 /* Relocation entries without addend */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Minimal set of dynamic linking symbols */ +#define SHT_MIPS_LIBLSIT 0x70000000 /* Info about dynamic shared object libs */ +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicts btw executables and shared objects */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global pointer table */ + +// sh_flags values +#define SHF_WRITE 0 /* writable section */ +#define SHF_ALLOC 2 /* section occupies memory */ +#define SHF_EXECINSTR 4 /* machine instructions */ +#define SHF_MIPS_GPREL 0x10000000 /* Must be made part of global data area */ + + +// Symbol entry (contain info about a symbol) +typedef struct { + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +// Extract from the st_info +#define SYM_TYPE(x) ((x)&0xF) +#define SYM_BIND(x) ((x)>>4) + + +// Symbol binding values from st_info +#define STB_LOCAL 0 /* Symbol not visible outside object */ +#define STB_GLOBAL 1 /* Symbol visible to all object files */ +#define STB_WEAK 2 /* Similar to STB_GLOBAL */ + +// Symbol type values from st_info +#define STT_NOTYPE 0 /* Not specified */ +#define STT_OBJECT 1 /* Data object e.g. variable */ +#define STT_FUNC 2 /* Function */ +#define STT_SECTION 3 /* Section */ +#define STT_FILE 4 /* Source file associated with object file */ + +// Special section header index values from st_shndex +#define SHN_UNDEF 0 +#define SHN_LOPROC 0xFF00 /* Extended values */ +#define SHN_ABS 0xFFF1 /* Absolute value: don't relocate */ +#define SHN_COMMON 0xFFF2 /* Common block. Not allocated yet */ +#define SHN_HIPROC 0xFF1F +#define SHN_HIRESERVE 0xFFFF + +// Relocation entry (info about how to relocate) +typedef struct { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +// Access macros for the relocation info +#define REL_TYPE(x) ((x)&0xFF) /* Extract relocation type */ +#define REL_INDEX(x) ((x)>>8) /* Extract relocation index into symbol table */ + +// MIPS relocation types +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 +#define R_MIPS_GOTHI16 13 +#define R_MIPS_GOTLO16 14 +#define R_MIPS_CALLHI16 15 +#define R_MIPS_CALLLO16 16 + +// Mock function to get value of global pointer +#define getGP() ({ \ + unsigned int __valgp; \ + __asm__ ("add %0, $gp, $0" : "=r"(__valgp) : ); \ + __valgp; \ +}) + +#endif /* BACKENDS_ELF_H */ diff --git a/backends/platform/ps2/module.mk b/backends/platform/ps2/module.mk index bf95a5501d..a12d10771f 100644 --- a/backends/platform/ps2/module.mk +++ b/backends/platform/ps2/module.mk @@ -16,7 +16,8 @@ MODULE_OBJS := \ systemps2.o \ ps2mutex.o \ ps2time.o \ - ps2debug.o + ps2debug.o \ + ps2loader.o # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS. MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) diff --git a/backends/platform/ps2/systemps2.cpp b/backends/platform/ps2/systemps2.cpp index 7659d5194d..357404c5c4 100644 --- a/backends/platform/ps2/systemps2.cpp +++ b/backends/platform/ps2/systemps2.cpp @@ -59,6 +59,8 @@ #include "backends/platform/ps2/ps2debug.h" #include "backends/fs/ps2/ps2-fs-factory.h" +#include "backends/plugins/ps2/ps2-provider.h" + #include "backends/saves/default/default-saves.h" #include "common/config-manager.h" @@ -105,7 +107,6 @@ extern "C" int scummvm_main(int argc, char *argv[]); extern "C" int main(int argc, char *argv[]) { SifInitRpc(0); - ee_thread_t thisThread; int tid = GetThreadId(); ReferThreadStatus(tid, &thisThread); @@ -130,8 +131,11 @@ extern "C" int main(int argc, char *argv[]) { sioprintf("Creating system\n"); g_system = g_systemPs2 = new OSystem_PS2(argv[0]); - g_systemPs2->init(); +#ifdef DYNAMIC_MODULES + PluginManager::instance().addPluginProvider(new PS2PluginProvider()); +#endif + g_systemPs2->init(); sioprintf("init done. starting ScummVM.\n"); int res = scummvm_main(argc, argv); sioprintf("scummvm_main terminated: %d\n", res); @@ -337,6 +341,7 @@ OSystem_PS2::OSystem_PS2(const char *elfPath) { } void OSystem_PS2::init(void) { + //msgPrintf(FOREVER, "got to init! Now restart your console... %d\n", res); sioprintf("Timer...\n"); _scummTimerManager = new DefaultTimerManager(); _scummMixer = new Audio::MixerImpl(this, 48000); diff --git a/backends/platform/psp/Makefile b/backends/platform/psp/Makefile index 617ef7c8cc..9c011220b8 100644 --- a/backends/platform/psp/Makefile +++ b/backends/platform/psp/Makefile @@ -67,7 +67,7 @@ endif # Variables for common Scummvm makefile CXX = psp-g++ CXXFLAGS = -O3 -Wall -Wno-multichar -fno-exceptions -fno-rtti -DEFINES = -D__PSP__ -DNONSTANDARD_PORT -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DUSE_ZLIB -DDISABLE_DOSBOX_OPL -DUSE_RGB_COLOR +DEFINES = -D__PSP__ -DNONSTANDARD_PORT -DDISABLE_TEXT_CONSOLE -DDISABLE_COMMAND_LINE -DUSE_ZLIB -DDISABLE_DOSBOX_OPL -DUSE_RGB_COLOR -DELF_LOADER_TARGET -DMIPS_TARGET LDFLAGS := INCDIR := $(srcdir) . $(srcdir)/engines/ $(PSPSDK)/include @@ -87,8 +87,8 @@ CXX_UPDATE_DEP_FLAG = -Wp,-MMD,"$(*D)/$(DEPDIR)/$(*F).d",-MQ,"$@",-MP # Variables for dynamic plugin building PLUGIN_PREFIX = PLUGIN_SUFFIX = .plg -PLUGIN_EXTRA_DEPS = plugin.syms scummvm-psp.elf -PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols=scummvm-psp.org.elf,-Tplugin.ld,--retain-symbols-file,plugin.syms -lstdc++ -lc +PLUGIN_EXTRA_DEPS = $(srcdir)/backends/plugins/plugin.syms scummvm-psp.elf +PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols=scummvm-psp.org.elf,-T$(srcdir)/backends/plugins/psp/plugin.ld,--retain-symbols-file,$(srcdir)/backends/plugins/plugin.syms -lstdc++ -lc # PSP-specific variables STRIP = psp-strip @@ -125,8 +125,7 @@ endif # PSP LIBS PSPLIBS = -lpspprof -lpspvfpu -lpspdebug -lpspgu -lpspge -lpspdisplay -lpspctrl -lpspsdk \ - -lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspaudiocodec \ - -lpspkernel + -lpsputility -lpspuser -lpsppower -lpsphprm -lpspsdk -lpsprtc -lpspaudio -lpspkernel # Add in PSPSDK includes and libraries. LIBS += -lpng -lz -lstdc++ -lc -lm $(PSPLIBS) @@ -150,6 +149,7 @@ OBJS := powerman.o \ mp3.o \ tests.o + BACKEND := psp # Include common Scummvm makefile diff --git a/backends/platform/psp/audio.cpp b/backends/platform/psp/audio.cpp index e540733162..14691befee 100644 --- a/backends/platform/psp/audio.cpp +++ b/backends/platform/psp/audio.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/audio.h b/backends/platform/psp/audio.h index eeba598fed..07f70cec7d 100644 --- a/backends/platform/psp/audio.h +++ b/backends/platform/psp/audio.h @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/cursor.cpp b/backends/platform/psp/cursor.cpp index ae3b8f0050..cf879e095a 100644 --- a/backends/platform/psp/cursor.cpp +++ b/backends/platform/psp/cursor.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.h $ - * $Id: osys_psp.h 46120 2009-11-24 10:33:30Z Bluddy $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/default_display_client.h b/backends/platform/psp/default_display_client.h index 716e6fcc35..2e33632eb1 100644 --- a/backends/platform/psp/default_display_client.h +++ b/backends/platform/psp/default_display_client.h @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/trace.h $ - * $Id: trace.h 44276 2009-09-23 16:11:23Z joostp $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/display_client.cpp b/backends/platform/psp/display_client.cpp index 71b505ec7c..6360772c96 100644 --- a/backends/platform/psp/display_client.cpp +++ b/backends/platform/psp/display_client.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 46126 2009-11-24 14:18:46Z fingolfin $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/display_manager.cpp b/backends/platform/psp/display_manager.cpp index 5037543f12..544e5a1b25 100644 --- a/backends/platform/psp/display_manager.cpp +++ b/backends/platform/psp/display_manager.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 47541 2010-01-25 01:39:44Z lordhoto $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/display_manager.h b/backends/platform/psp/display_manager.h index 1f7320902c..72f252faae 100644 --- a/backends/platform/psp/display_manager.h +++ b/backends/platform/psp/display_manager.h @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 47541 2010-01-25 01:39:44Z lordhoto $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/input.cpp b/backends/platform/psp/input.cpp index 4fe7cb3f92..2a91ce455a 100644 --- a/backends/platform/psp/input.cpp +++ b/backends/platform/psp/input.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.cpp $ - * $Id: osys_psp.cpp 43618 2009-08-21 22:44:49Z joostp $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/psploader.cpp b/backends/platform/psp/psploader.cpp deleted file mode 100644 index 464e20770c..0000000000 --- a/backends/platform/psp/psploader.cpp +++ /dev/null @@ -1,732 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * $URL$ - * $Id$ - * - */ - -#if defined(DYNAMIC_MODULES) && defined(__PSP__) - -#include <string.h> -#include <stdarg.h> -#include <stdio.h> -#include <malloc.h> -#include <unistd.h> -#include <sys/_default_fcntl.h> - -#include <psputils.h> - -#include "backends/platform/psp/psploader.h" -#include "backends/platform/psp/powerman.h" - -//#define __PSP_DEBUG_FUNCS__ /* For debugging the stack */ -//#define __PSP_DEBUG_PRINT__ - -#include "backends/platform/psp/trace.h" - -extern char __plugin_hole_start; // Indicates start of hole in program file for shorts -extern char __plugin_hole_end; // Indicates end of hole in program file -extern char _gp[]; // Value of gp register - -DECLARE_SINGLETON(ShortSegmentManager) // For singleton - -// Get rid of symbol table in memory -void DLObject::discard_symtab() { - DEBUG_ENTER_FUNC(); - free(_symtab); - free(_strtab); - _symtab = NULL; - _strtab = NULL; - _symbol_cnt = 0; -} - -// Unload all objects from memory -void DLObject::unload() { - DEBUG_ENTER_FUNC(); - discard_symtab(); - free(_segment); - _segment = NULL; - - if (_shortsSegment) { - ShortsMan.deleteSegment(_shortsSegment); - _shortsSegment = NULL; - } -} - -/** - * Follow the instruction of a relocation section. - * - * @param fd File Descriptor - * @param offset Offset into the File - * @param size Size of relocation section - * @param relSegment Base address of relocated segment in memory (memory offset) - * - */ -bool DLObject::relocate(int fd, unsigned long offset, unsigned long size, void *relSegment) { - DEBUG_ENTER_FUNC(); - Elf32_Rel *rel = NULL; // relocation entry - - // Allocate memory for relocation table - if (!(rel = (Elf32_Rel *)malloc(size))) { - PSP_ERROR("Out of memory."); - return false; - } - - // Read in our relocation table - if (lseek(fd, offset, SEEK_SET) < 0 || - read(fd, rel, size) != (ssize_t)size) { - PSP_ERROR("Relocation table load failed."); - free(rel); - return false; - } - - // Treat each relocation entry. Loop over all of them - int cnt = size / sizeof(*rel); - - PSP_DEBUG_PRINT("Loaded relocation table. %d entries. base address=%p\n", cnt, relSegment); - - bool seenHi16 = false; // For treating HI/LO16 commands - int firstHi16 = -1; // Mark the point of the first hi16 seen - Elf32_Addr ahl = 0; // Calculated addend - int a = 0; // Addend: taken from the target - - unsigned int *lastTarget = 0; // For processing hi16 when lo16 arrives - unsigned int relocation = 0; - int debugRelocs[10] = {0}; // For debugging - int extendedHi16 = 0; // Count extended hi16 treatments - Elf32_Addr lastHiSymVal = 0; - bool hi16InShorts = false; - -#define DEBUG_NUM 2 - - // Loop over relocation entries - for (int i = 0; i < cnt; i++) { - // Get the symbol this relocation entry is referring to - Elf32_Sym *sym = (Elf32_Sym *)(_symtab) + (REL_INDEX(rel[i].r_info)); - - // Get the target instruction in the code - unsigned int *target = (unsigned int *)((char *)relSegment + rel[i].r_offset); - - PSP_DEBUG_DO(unsigned int origTarget = *target); // Save for debugging - - // Act differently based on the type of relocation - switch (REL_TYPE(rel[i].r_info)) { - - case R_MIPS_HI16: // Absolute addressing. - if (sym->st_shndx < SHN_LOPROC && // Only shift for plugin section (ie. has a real section index) - firstHi16 < 0) { // Only process first in block of HI16s - firstHi16 = i; // Keep the first Hi16 we saw - seenHi16 = true; - ahl = (*target & 0xffff) << 16; // Take lower 16 bits shifted up - - lastHiSymVal = sym->st_value; - hi16InShorts = (ShortsMan.inGeneralSegment((char *)sym->st_value)); // Fix for problem with switching btw segments - if (debugRelocs[0]++ < DEBUG_NUM) // Print only a set number - PSP_DEBUG_PRINT("R_MIPS_HI16: i=%d, offset=%x, ahl = %x, target = %x\n", - i, rel[i].r_offset, ahl, *target); - } - break; - - case R_MIPS_LO16: // Absolute addressing. Needs a HI16 to come before it - if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. (ie. has a real section index) - if (!seenHi16) { // We MUST have seen HI16 first - PSP_ERROR("R_MIPS_LO16 w/o preceding R_MIPS_HI16 at relocation %d!\n", i); - free(rel); - return false; - } - - // Fix: bug in gcc makes LO16s connect to wrong HI16s sometimes (shorts and regular segment) - // Note that we can check the entire shorts segment because the executable's shorts don't belong to this plugin section - // and will be screened out above - bool lo16InShorts = ShortsMan.inGeneralSegment((char *)sym->st_value); - - // Correct the bug by getting the proper value in ahl (taken from the current symbol) - if ((hi16InShorts && !lo16InShorts) || (!hi16InShorts && lo16InShorts)) { - ahl -= (lastHiSymVal & 0xffff0000); // We assume gcc meant the same offset - ahl += (sym->st_value & 0xffff0000); - } - - ahl &= 0xffff0000; // Clean lower 16 bits for repeated LO16s - a = *target & 0xffff; // Take lower 16 bits of the target - a = (a << 16) >> 16; // Sign extend them - ahl += a; // Add lower 16 bits. AHL is now complete - - // Fix: we can have LO16 access to the short segment sometimes - if (lo16InShorts) { - relocation = ahl + _shortsSegment->getOffset(); // Add in the short segment offset - } else // It's in the regular segment - relocation = ahl + (Elf32_Addr)_segment; // Add in the new offset for the segment - - if (firstHi16 >= 0) { // We haven't treated the HI16s yet so do it now - for (int j = firstHi16; j < i; j++) { - if (REL_TYPE(rel[j].r_info) != R_MIPS_HI16) continue; // Skip over non-Hi16s - - lastTarget = (unsigned int *)((char *)relSegment + rel[j].r_offset); // get hi16 target - *lastTarget &= 0xffff0000; // Clear the lower 16 bits of the last target - *lastTarget |= (relocation >> 16) & 0xffff; // Take the upper 16 bits of the relocation - if (relocation & 0x8000)(*lastTarget)++; // Subtle: we need to add 1 to the HI16 in this case - } - firstHi16 = -1; // Reset so we'll know we treated it - } else { - extendedHi16++; - } - - *target &= 0xffff0000; // Clear the lower 16 bits of current target - *target |= relocation & 0xffff; // Take the lower 16 bits of the relocation - - if (debugRelocs[1]++ < DEBUG_NUM) - PSP_DEBUG_PRINT("R_MIPS_LO16: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n", - i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target); - if (lo16InShorts && debugRelocs[2]++ < DEBUG_NUM) - PSP_DEBUG_PRINT("R_MIPS_LO16s: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n", - i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target); - } - break; - - case R_MIPS_26: // Absolute addressing (for jumps and branches only) - if (sym->st_shndx < SHN_LOPROC) { // Only relocate for main segment - a = *target & 0x03ffffff; // Get 26 bits' worth of the addend - a = (a << 6) >> 6; // Sign extend a - relocation = ((a << 2) + (Elf32_Addr)_segment) >> 2; // a already points to the target. Subtract our offset - *target &= 0xfc000000; // Clean lower 26 target bits - *target |= (relocation & 0x03ffffff); - - if (debugRelocs[3]++ < DEBUG_NUM) - PSP_DEBUG_PRINT("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n", - i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target); - } else { - if (debugRelocs[4]++ < DEBUG_NUM) - PSP_DEBUG_PRINT("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n", - i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target); - } - break; - - case R_MIPS_GPREL16: // GP Relative addressing - if (_shortsSegment->getOffset() != 0 && // Only relocate if we shift the shorts section - ShortsMan.inGeneralSegment((char *)sym->st_value)) { // Only relocate things in the plugin hole - a = *target & 0xffff; // Get 16 bits' worth of the addend - a = (a << 16) >> 16; // Sign extend it - - relocation = a + _shortsSegment->getOffset(); - - *target &= 0xffff0000; // Clear the lower 16 bits of the target - *target |= relocation & 0xffff; - - if (debugRelocs[5]++ < DEBUG_NUM) - PSP_DEBUG_PRINT("R_MIPS_GPREL16: i=%d, a=%x, gpVal=%x, origTarget=%x, target=%x, offset=%x\n", - i, a, _gpVal, origTarget, *target, _shortsSegment->getOffset()); - } - - break; - - case R_MIPS_32: // Absolute addressing - if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. - a = *target; // Get full 32 bits of addend - - if (ShortsMan.inGeneralSegment((char *)sym->st_value)) // Check if we're in the shorts segment - relocation = a + _shortsSegment->getOffset(); // Shift by shorts offset - else // We're in the main section - relocation = a + (Elf32_Addr)_segment; // Shift by main offset - *target = relocation; - - if (debugRelocs[6]++ < DEBUG_NUM) - PSP_DEBUG_PRINT("R_MIPS_32: i=%d, a=%x, origTarget=%x, target=%x\n", i, a, origTarget, *target); - } - break; - - default: - PSP_ERROR("Unknown relocation type %x at relocation %d.\n", REL_TYPE(rel[i].r_info), i); - free(rel); - return false; - } - } - - PSP_DEBUG_PRINT("Done with relocation. extendedHi16=%d\n\n", extendedHi16); - - free(rel); - return true; -} - -bool DLObject::readElfHeader(int fd, Elf32_Ehdr *ehdr) { - DEBUG_ENTER_FUNC(); - // Start reading the elf header. Check for errors - if (read(fd, ehdr, sizeof(*ehdr)) != sizeof(*ehdr) || - memcmp(ehdr->e_ident, ELFMAG, SELFMAG) || // Check MAGIC - ehdr->e_type != ET_EXEC || // Check for executable - ehdr->e_machine != EM_MIPS || // Check for MIPS machine type - ehdr->e_phentsize < sizeof(Elf32_Phdr) || // Check for size of program header - ehdr->e_shentsize != sizeof(Elf32_Shdr)) { // Check for size of section header - PSP_ERROR("Invalid file type."); - return false; - } - - PSP_DEBUG_PRINT("phoff = %d, phentsz = %d, phnum = %d\n", - ehdr->e_phoff, ehdr->e_phentsize, ehdr->e_phnum); - - return true; -} - -bool DLObject::readProgramHeaders(int fd, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num) { - DEBUG_ENTER_FUNC(); - // Read program header - if (lseek(fd, ehdr->e_phoff + sizeof(*phdr)*num, SEEK_SET) < 0 || - read(fd, phdr, sizeof(*phdr)) != sizeof(*phdr)) { - PSP_ERROR("Program header load failed."); - return false; - } - - // Check program header values - if (phdr->p_type != PT_LOAD || phdr->p_filesz > phdr->p_memsz) { - PSP_ERROR("Invalid program header."); - return false; - } - - PSP_DEBUG_PRINT("offs = %x, filesz = %x, memsz = %x, align = %x\n", - phdr->p_offset, phdr->p_filesz, phdr->p_memsz, phdr->p_align); - - return true; - -} - -bool DLObject::loadSegment(int fd, Elf32_Phdr *phdr) { - DEBUG_ENTER_FUNC(); - - char *baseAddress = 0; - - // We need to take account of non-allocated segment for shorts - if (phdr->p_flags & PF_X) { // This is a relocated segment - - // Attempt to allocate memory for segment - int extra = phdr->p_vaddr % phdr->p_align; // Get extra length TODO: check logic here - PSP_DEBUG_PRINT("extra mem is %x\n", extra); - - if (phdr->p_align < 0x10000) phdr->p_align = 0x10000; // Fix for wrong alignment on e.g. AGI - - if (!(_segment = (char *)memalign(phdr->p_align, phdr->p_memsz + extra))) { - PSP_ERROR("Out of memory.\n"); - return false; - } - PSP_DEBUG_PRINT("allocated segment @ %p\n", _segment); - - // Get offset to load segment into - baseAddress = (char *)_segment + phdr->p_vaddr; - _segmentSize = phdr->p_memsz + extra; - } else { // This is a shorts section. - _shortsSegment = ShortsMan.newSegment(phdr->p_memsz, (char *)phdr->p_vaddr); - - baseAddress = _shortsSegment->getStart(); - PSP_DEBUG_PRINT("shorts segment @ %p to %p. Segment wants to be at %x. Offset=%x\n", - _shortsSegment->getStart(), _shortsSegment->getEnd(), phdr->p_vaddr, _shortsSegment->getOffset()); - - } - - // Set bss segment to 0 if necessary (assumes bss is at the end) - if (phdr->p_memsz > phdr->p_filesz) { - PSP_DEBUG_PRINT("Setting %p to %p to 0 for bss\n", baseAddress + phdr->p_filesz, baseAddress + phdr->p_memsz); - memset(baseAddress + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); - } - // Read the segment into memory - if (lseek(fd, phdr->p_offset, SEEK_SET) < 0 || - read(fd, baseAddress, phdr->p_filesz) != (ssize_t)phdr->p_filesz) { - PSP_ERROR("Segment load failed."); - return false; - } - - return true; -} - - -Elf32_Shdr * DLObject::loadSectionHeaders(int fd, Elf32_Ehdr *ehdr) { - DEBUG_ENTER_FUNC(); - - Elf32_Shdr *shdr = NULL; - - // Allocate memory for section headers - if (!(shdr = (Elf32_Shdr *)malloc(ehdr->e_shnum * sizeof(*shdr)))) { - PSP_ERROR("Out of memory."); - return NULL; - } - - // Read from file into section headers - if (lseek(fd, ehdr->e_shoff, SEEK_SET) < 0 || - read(fd, shdr, ehdr->e_shnum * sizeof(*shdr)) != - (ssize_t)(ehdr->e_shnum * sizeof(*shdr))) { - PSP_ERROR("Section headers load failed."); - return NULL; - } - - return shdr; -} - -int DLObject::loadSymbolTable(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { - DEBUG_ENTER_FUNC(); - - // Loop over sections, looking for symbol table linked to a string table - for (int i = 0; i < ehdr->e_shnum; i++) { - PSP_DEBUG_PRINT("Section %d: type = %x, size = %x, entsize = %x, link = %x\n", - i, shdr[i].sh_type, shdr[i].sh_size, shdr[i].sh_entsize, shdr[i].sh_link); - - if (shdr[i].sh_type == SHT_SYMTAB && - shdr[i].sh_entsize == sizeof(Elf32_Sym) && - shdr[i].sh_link < ehdr->e_shnum && - shdr[shdr[i].sh_link].sh_type == SHT_STRTAB && - _symtab_sect < 0) { - _symtab_sect = i; - } - } - - // Check for no symbol table - if (_symtab_sect < 0) { - PSP_ERROR("No symbol table."); - return -1; - } - - PSP_DEBUG_PRINT("Symbol section at section %d, size %x\n", _symtab_sect, shdr[_symtab_sect].sh_size); - - // Allocate memory for symbol table - if (!(_symtab = malloc(shdr[_symtab_sect].sh_size))) { - PSP_ERROR("Out of memory."); - return -1; - } - - // Read symbol table into memory - if (lseek(fd, shdr[_symtab_sect].sh_offset, SEEK_SET) < 0 || - read(fd, _symtab, shdr[_symtab_sect].sh_size) != - (ssize_t)shdr[_symtab_sect].sh_size) { - PSP_ERROR("Symbol table load failed."); - return -1; - } - - // Set number of symbols - _symbol_cnt = shdr[_symtab_sect].sh_size / sizeof(Elf32_Sym); - PSP_DEBUG_PRINT("Loaded %d symbols.\n", _symbol_cnt); - - return _symtab_sect; - -} - -bool DLObject::loadStringTable(int fd, Elf32_Shdr *shdr) { - DEBUG_ENTER_FUNC(); - - int string_sect = shdr[_symtab_sect].sh_link; - - // Allocate memory for string table - if (!(_strtab = (char *)malloc(shdr[string_sect].sh_size))) { - PSP_ERROR("Out of memory."); - return false; - } - - // Read string table into memory - if (lseek(fd, shdr[string_sect].sh_offset, SEEK_SET) < 0 || - read(fd, _strtab, shdr[string_sect].sh_size) != - (ssize_t)shdr[string_sect].sh_size) { - PSP_ERROR("Symbol table strings load failed."); - return false; - } - return true; -} - -void DLObject::relocateSymbols(Elf32_Addr offset, Elf32_Addr shortsOffset) { - DEBUG_ENTER_FUNC(); - - int shortsCount = 0, othersCount = 0; - PSP_DEBUG_PRINT("Relocating symbols by %x. Shorts offset=%x\n", offset, shortsOffset); - - // Loop over symbols, add relocation offset - Elf32_Sym *s = (Elf32_Sym *)_symtab; - for (int c = _symbol_cnt; c--; s++) { - // Make sure we don't relocate special valued symbols - if (s->st_shndx < SHN_LOPROC) { - if (!ShortsMan.inGeneralSegment((char *)s->st_value)) { - othersCount++; - s->st_value += offset; - if (s->st_value < (Elf32_Addr)_segment || s->st_value > (Elf32_Addr)_segment + _segmentSize) - PSP_ERROR("Symbol out of bounds! st_value = %x\n", s->st_value); - } else { // shorts section - shortsCount++; - s->st_value += shortsOffset; - if (!_shortsSegment->inSegment((char *)s->st_value)) - PSP_ERROR("Symbol out of bounds! st_value = %x\n", s->st_value); - } - - } - - } - - PSP_DEBUG_PRINT("Relocated %d short symbols, %d others.\n", shortsCount, othersCount); -} - -bool DLObject::relocateRels(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { - DEBUG_ENTER_FUNC(); - - // Loop over sections, finding relocation sections - for (int i = 0; i < ehdr->e_shnum; i++) { - - Elf32_Shdr *curShdr = &(shdr[i]); - //Elf32_Shdr *linkShdr = &(shdr[curShdr->sh_info]); - - if (curShdr->sh_type == SHT_REL && // Check for a relocation section - curShdr->sh_entsize == sizeof(Elf32_Rel) && // Check for proper relocation size - (int)curShdr->sh_link == _symtab_sect && // Check that the sh_link connects to our symbol table - curShdr->sh_info < ehdr->e_shnum && // Check that the relocated section exists - (shdr[curShdr->sh_info].sh_flags & SHF_ALLOC)) { // Check if relocated section resides in memory - if (!ShortsMan.inGeneralSegment((char *)shdr[curShdr->sh_info].sh_addr)) { // regular segment - if (!relocate(fd, curShdr->sh_offset, curShdr->sh_size, _segment)) { - return false; - } - } else { // In Shorts segment - if (!relocate(fd, curShdr->sh_offset, curShdr->sh_size, (void *)_shortsSegment->getOffset())) { - return false; - } - } - - } - } - - return true; -} - - -bool DLObject::load(int fd) { - DEBUG_ENTER_FUNC(); - - Elf32_Ehdr ehdr; // ELF header - Elf32_Phdr phdr; // Program header - Elf32_Shdr *shdr; // Section header - bool ret = true; - - if (readElfHeader(fd, &ehdr) == false) { - return false; - } - - for (int i = 0; i < ehdr.e_phnum; i++) { // Load our 2 segments - PSP_DEBUG_PRINT("Loading segment %d\n", i); - - if (readProgramHeaders(fd, &ehdr, &phdr, i) == false) - return false; - - if (!loadSegment(fd, &phdr)) - return false; - } - - if ((shdr = loadSectionHeaders(fd, &ehdr)) == NULL) - ret = false; - - if (ret && ((_symtab_sect = loadSymbolTable(fd, &ehdr, shdr)) < 0)) - ret = false; - - if (ret && (loadStringTable(fd, shdr) == false)) - ret = false; - - if (ret) - relocateSymbols((Elf32_Addr)_segment, _shortsSegment->getOffset()); // Offset by our segment allocated address - - if (ret && (relocateRels(fd, &ehdr, shdr) == false)) - ret = false; - - free(shdr); - - return ret; -} - -bool DLObject::open(const char *path) { - DEBUG_ENTER_FUNC(); - int fd; - void *ctors_start, *ctors_end; - - PSP_DEBUG_PRINT("open(\"%s\")\n", path); - - // Get the address of the global pointer - _gpVal = (unsigned int) & _gp; - PSP_DEBUG_PRINT("_gpVal is %x\n", _gpVal); - - PowerMan.beginCriticalSection(); - - if ((fd = ::open(path, O_RDONLY)) < 0) { - PSP_ERROR("%s not found.", path); - return false; - } - - // Try to load and relocate - if (!load(fd)) { - ::close(fd); - unload(); - return false; - } - - ::close(fd); - - PowerMan.endCriticalSection(); - - // flush data cache - sceKernelDcacheWritebackAll(); - - // Get the symbols for the global constructors and destructors - ctors_start = symbol("___plugin_ctors"); - ctors_end = symbol("___plugin_ctors_end"); - _dtors_start = symbol("___plugin_dtors"); - _dtors_end = symbol("___plugin_dtors_end"); - - if (ctors_start == NULL || ctors_end == NULL || _dtors_start == NULL || - _dtors_end == NULL) { - PSP_ERROR("Missing ctors/dtors."); - _dtors_start = _dtors_end = NULL; - unload(); - return false; - } - - PSP_DEBUG_PRINT("Calling constructors.\n"); - for (void (**f)(void) = (void (**)(void))ctors_start; f != ctors_end; f++) - (**f)(); - - PSP_DEBUG_PRINT("%s opened ok.\n", path); - return true; -} - -bool DLObject::close() { - DEBUG_ENTER_FUNC(); - if (_dtors_start != NULL && _dtors_end != NULL) - for (void (**f)(void) = (void (**)(void))_dtors_start; f != _dtors_end; f++) - (**f)(); - _dtors_start = _dtors_end = NULL; - unload(); - return true; -} - -void *DLObject::symbol(const char *name) { - DEBUG_ENTER_FUNC(); - PSP_DEBUG_PRINT("symbol(\"%s\")\n", name); - - if (_symtab == NULL || _strtab == NULL || _symbol_cnt < 1) { - PSP_ERROR("No symbol table loaded."); - return NULL; - } - - Elf32_Sym *s = (Elf32_Sym *)_symtab; - for (int c = _symbol_cnt; c--; s++) { - - // We can only import symbols that are global or weak in the plugin - if ((SYM_BIND(s->st_info) == STB_GLOBAL || SYM_BIND(s->st_info) == STB_WEAK) && - /*_strtab[s->st_name] == '_' && */ // Try to make this more efficient - !strcmp(name, _strtab + s->st_name)) { - - // We found the symbol - PSP_DEBUG_PRINT("=> %p\n", (void*)s->st_value); - return (void*)s->st_value; - } - } - - PSP_ERROR("Symbol \"%s\" not found.", name); - return NULL; -} - - - -ShortSegmentManager::ShortSegmentManager() { - DEBUG_ENTER_FUNC(); - _shortsStart = &__plugin_hole_start ; - _shortsEnd = &__plugin_hole_end; -} - -ShortSegmentManager::Segment *ShortSegmentManager::newSegment(int size, char *origAddr) { - DEBUG_ENTER_FUNC(); - char *lastAddress = origAddr; - Common::List<Segment *>::iterator i; - - // Find a block that fits, starting from the beginning - for (i = _list.begin(); i != _list.end(); ++i) { - char *currAddress = (*i)->getStart(); - - if ((int)(currAddress - lastAddress) >= size) break; - - lastAddress = (*i)->getEnd(); - } - - if ((Elf32_Addr)lastAddress & 3) - lastAddress += 4 - ((Elf32_Addr)lastAddress & 3); // Round up to multiple of 4 - - if (lastAddress + size > _shortsEnd) { - PSP_ERROR("No space in shorts segment for %x bytes. Last address is %p, max address is %p.\n", - size, lastAddress, _shortsEnd); - return NULL; - } - - Segment *seg = new Segment(lastAddress, size, origAddr); // Create a new segment - - if (lastAddress + size > _highestAddress) _highestAddress = lastAddress + size; // Keep track of maximum - - _list.insert(i, seg); - - PSP_DEBUG_PRINT("Shorts segment size %x allocated. End = %p. Remaining space = %x. Highest so far is %p.\n", - size, lastAddress + size, _shortsEnd - _list.back()->getEnd(), _highestAddress); - - return seg; -} - -void ShortSegmentManager::deleteSegment(ShortSegmentManager::Segment *seg) { - DEBUG_ENTER_FUNC(); - PSP_DEBUG_PRINT("Deleting shorts segment from %p to %p.\n\n", seg->getStart(), seg->getEnd()); - _list.remove(seg); - delete seg; -} - -static char dlerr[MAXDLERRLEN]; - -void *dlopen(const char *filename, int flags) { - DLObject *obj = new DLObject(dlerr); - if (obj->open(filename)) - return (void *)obj; - delete obj; - return NULL; -} - -int dlclose(void *handle) { - DLObject *obj = (DLObject *)handle; - if (obj == NULL) { - strcpy(dlerr, "Handle is NULL."); - return -1; - } - if (obj->close()) { - delete obj; - return 0; - } - return -1; -} - -void *dlsym(void *handle, const char *symbol) { - if (handle == NULL) { - strcpy(dlerr, "Handle is NULL."); - return NULL; - } - return ((DLObject *)handle)->symbol(symbol); -} - -const char *dlerror() { - return dlerr; -} - -void dlforgetsyms(void *handle) { - if (handle != NULL) - ((DLObject *)handle)->discard_symtab(); -} - - -#endif /* DYNAMIC_MODULES && __PSP__ */ diff --git a/backends/platform/psp/thread.cpp b/backends/platform/psp/thread.cpp index 916b1e553b..e757c2f575 100644 --- a/backends/platform/psp/thread.cpp +++ b/backends/platform/psp/thread.cpp @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/osys_psp.h $ - * $Id: osys_psp.h 49173 2010-05-24 03:05:17Z bluddy $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/psp/thread.h b/backends/platform/psp/thread.h index de1c10a2aa..44394af40a 100644 --- a/backends/platform/psp/thread.h +++ b/backends/platform/psp/thread.h @@ -18,8 +18,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/backends/platform/psp/portdefs.h $ - * $Id: portdefs.h 38687 2009-02-21 12:08:52Z joostp $ + * $URL$ + * $Id$ * */ diff --git a/backends/platform/samsungtv/main.cpp b/backends/platform/samsungtv/main.cpp index 2c025b750c..a6b8376912 100644 --- a/backends/platform/samsungtv/main.cpp +++ b/backends/platform/samsungtv/main.cpp @@ -43,7 +43,7 @@ extern "C" int Game_Main(char *path, char *) { // Invoke the actual ScummVM main entry point: int res = scummvm_main(0, 0); - ((OSystem_SDL *)g_system)->deinit(); + g_system->quit(); // TODO: Consider removing / replacing this! return res; } diff --git a/backends/platform/sdl/graphics.cpp b/backends/platform/sdl/graphics.cpp index a97a153f3c..aec90d1352 100644 --- a/backends/platform/sdl/graphics.cpp +++ b/backends/platform/sdl/graphics.cpp @@ -1693,6 +1693,7 @@ void OSystem_SDL::drawMouse() { SDL_Rect dst; int scale; + int width, height; int hotX, hotY; dst.x = _mouseCurState.x; @@ -1700,12 +1701,16 @@ void OSystem_SDL::drawMouse() { if (!_overlayVisible) { scale = _videoMode.scaleFactor; + width = _videoMode.screenWidth; + height = _videoMode.screenHeight; dst.w = _mouseCurState.vW; dst.h = _mouseCurState.vH; hotX = _mouseCurState.vHotX; hotY = _mouseCurState.vHotY; } else { scale = 1; + width = _videoMode.overlayWidth; + height = _videoMode.overlayHeight; dst.w = _mouseCurState.rW; dst.h = _mouseCurState.rH; hotX = _mouseCurState.rHotX; diff --git a/backends/platform/sdl/main.cpp b/backends/platform/sdl/main.cpp index 52bbb59165..ef95a6d256 100644 --- a/backends/platform/sdl/main.cpp +++ b/backends/platform/sdl/main.cpp @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) { // Invoke the actual ScummVM main entry point: int res = scummvm_main(argc, argv); - ((OSystem_SDL *)g_system)->deinit(); + g_system->quit(); // TODO: Consider removing / replacing this! return res; } diff --git a/backends/platform/sdl/sdl.cpp b/backends/platform/sdl/sdl.cpp index 6686249416..5c50a43daf 100644 --- a/backends/platform/sdl/sdl.cpp +++ b/backends/platform/sdl/sdl.cpp @@ -483,7 +483,7 @@ bool OSystem_SDL::getFeatureState(Feature f) { } } -void OSystem_SDL::deinit() { +void OSystem_SDL::quit() { if (_cdrom) { SDL_CDStop(_cdrom); SDL_CDClose(_cdrom); @@ -505,14 +505,10 @@ void OSystem_SDL::deinit() { SDL_Quit(); - // Event Manager requires save manager for storing + // Even Manager requires save manager for storing // recorded events delete getEventManager(); delete _savefile; -} - -void OSystem_SDL::quit() { - deinit(); #if !defined(SAMSUNGTV) exit(0); diff --git a/backends/platform/sdl/sdl.h b/backends/platform/sdl/sdl.h index 5c901ba711..ace07f349e 100644 --- a/backends/platform/sdl/sdl.h +++ b/backends/platform/sdl/sdl.h @@ -202,8 +202,6 @@ public: // Quit virtual void quit(); // overloaded by CE backend - void deinit(); - virtual void getTimeAndDate(TimeDate &t) const; virtual Common::TimerManager *getTimerManager(); diff --git a/backends/plugins/arm-loader.cpp b/backends/plugins/arm-loader.cpp new file mode 100644 index 0000000000..7e8269220b --- /dev/null +++ b/backends/plugins/arm-loader.cpp @@ -0,0 +1,169 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(ARM_TARGET) + +#include "backends/fs/ds/ds-fs.h" +#include "elf-loader.h" +#include "dsmain.h" +#include "arm-loader.h" + +#define __DEBUG_PLUGINS__ + +#ifdef __DEBUG_PLUGINS__ +#define DBG(x,...) consolePrintf(x, ## __VA_ARGS__) +#else +#define DBG(x,...) +#endif + +#define seterror(x,...) consolePrintf(x, ## __VA_ARGS__) + +/** + * Follow the instruction of a relocation section. + * + * @param DLFile SeekableReadStream of File + * @param offset Offset into the File + * @param size Size of relocation section + * + */ +bool ARMDLObject::relocate(Common::SeekableReadStream* DLFile, unsigned long offset, unsigned long size, void *relSegment) { + Elf32_Rel *rel = NULL; //relocation entry + + // Allocate memory for relocation table + if (!(rel = (Elf32_Rel *)malloc(size))) { + seterror("Out of memory."); + return false; + } + + // Read in our relocation table + if (DLFile->seek(offset, SEEK_SET) < 0 || + DLFile->read(rel, size) != (ssize_t)size) { + seterror("Relocation table load failed."); + free(rel); + return false; + } + + // Treat each relocation entry. Loop over all of them + int cnt = size / sizeof(*rel); + + DBG("Loaded relocation table. %d entries. base address=%p\n", cnt, relSegment); + + int a = 0; + unsigned int relocation = 0; + + // Loop over relocation entries + for (int i = 0; i < cnt; i++) { + + // Get the symbol this relocation entry is referring to + Elf32_Sym *sym = (Elf32_Sym *)(_symtab) + (REL_INDEX(rel[i].r_info)); + + // Get the target instruction in the code + unsigned int *target = (unsigned int *)((char *)relSegment + rel[i].r_offset); + + unsigned int origTarget = *target; //Save for debugging + + // Act differently based on the type of relocation + switch (REL_TYPE(rel[i].r_info)) { + + case R_ARM_ABS32: + if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. + a = *target; // Get full 32 bits of addend + relocation = a + (Elf32_Addr)_segment; // Shift by main offset + + *target = relocation; + + DBG("R_ARM_ABS32: i=%d, a=%x, origTarget=%x, target=%x\n", i, a, origTarget, *target); + } + break; + + case R_ARM_THM_CALL: + DBG("R_ARM_THM_CALL: PC-relative jump, ld takes care of necessary relocation work for us.\n"); + break; + + case R_ARM_CALL: + DBG("R_ARM_CALL: PC-relative jump, ld takes care of necessary relocation work for us.\n"); + break; + + case R_ARM_JUMP24: + DBG("R_ARM_JUMP24: PC-relative jump, ld takes care of all relocation work for us.\n"); + break; + + case R_ARM_TARGET1: + if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. + a = *target; // Get full 32 bits of addend + relocation = a + (Elf32_Addr)_segment; // Shift by main offset + + *target = relocation; + + DBG("R_ARM_TARGET1: i=%d, a=%x, origTarget=%x, target=%x\n", i, a, origTarget, *target); + DBG("Make sure --target1-abs is a flag to LD!\n"); + } + break; + + case R_ARM_V4BX: + DBG("R_ARM_V4BX: No relocation calculation necessary.\n"); + break; + + default: + seterror("Unknown relocation type %d.", REL_TYPE(rel[i].r_info)); + free(rel); + return false; + } + + } + + free(rel); + return true; +} + +bool ARMDLObject::relocateRels(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { + + // Loop over sections, finding relocation sections + for (int i = 0; i < ehdr->e_shnum; i++) { + + Elf32_Shdr *curShdr = &(shdr[i]); + + if ((curShdr->sh_type == SHT_REL || curShdr->sh_type == SHT_RELA) && // Check for a relocation section + curShdr->sh_entsize == sizeof(Elf32_Rel) && // Check for proper relocation size + (int)curShdr->sh_link == _symtab_sect && // Check that the sh_link connects to our symbol table + curShdr->sh_info < ehdr->e_shnum && // Check that the relocated section exists + (shdr[curShdr->sh_info].sh_flags & SHF_ALLOC)) { // Check if relocated section resides in memory + + if (curShdr->sh_type == SHT_RELA) { + seterror("RELA entries not supported yet!\n"); + return false; + } + + if (!relocate(DLFile, curShdr->sh_offset, curShdr->sh_size, _segment)) { + return false; + } + + } + } + + return true; +} + +#endif /* defined(DYNAMIC_MODULES) && defined(ARM_TARGET) */ diff --git a/backends/plugins/arm-loader.h b/backends/plugins/arm-loader.h new file mode 100644 index 0000000000..7f7a1f7f4b --- /dev/null +++ b/backends/plugins/arm-loader.h @@ -0,0 +1,37 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/fs/ds/ds-fs.h" +#include "elf-loader.h" +#include "dsmain.h" + +class ARMDLObject : public DLObject { +protected: + bool relocate(Common::SeekableReadStream* DLFile, unsigned long offset, unsigned long size, void *relSegment); + bool relocateRels(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); + +public: + ARMDLObject() : DLObject() {} +}; diff --git a/backends/plugins/ds/ds-provider.cpp b/backends/plugins/ds/ds-provider.cpp new file mode 100644 index 0000000000..7365a2e6e9 --- /dev/null +++ b/backends/plugins/ds/ds-provider.cpp @@ -0,0 +1,45 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(__DS__) + +#include "backends/plugins/arm-loader.h" +#include "backends/plugins/elf-provider.h" +#include "backends/plugins/ds/ds-provider.h" + + +class DSPlugin : public ELFPlugin { +public: + DSPlugin(const Common::String &filename) : ELFPlugin(filename) {} + + DLObject *makeDLObject() { return new ARMDLObject(); } +}; + +Plugin* DSPluginProvider::createPlugin(const Common::FSNode &node) const { + return new DSPlugin(node.getPath()); +} + +#endif // defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) + diff --git a/backends/plugins/ds/ds-provider.h b/backends/plugins/ds/ds-provider.h new file mode 100644 index 0000000000..462c3e3b63 --- /dev/null +++ b/backends/plugins/ds/ds-provider.h @@ -0,0 +1,32 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/plugins/elf-provider.h" + +class DSPluginProvider : public ELFPluginProvider { + Plugin* createPlugin(const Common::FSNode &node) const; +}; + + diff --git a/backends/plugins/ds/plugin.ld b/backends/plugins/ds/plugin.ld new file mode 100644 index 0000000000..2b2bca9745 --- /dev/null +++ b/backends/plugins/ds/plugin.ld @@ -0,0 +1,218 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", + "elf32-littlearm") +OUTPUT_ARCH(arm) + +/* PHDRS specifies ELF Program Headers (or segments) to the plugin linker */ +PHDRS { + plugin PT_LOAD ; /* Specifies that the plugin segment should be loaded from file */ +} + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = 0; + .interp : { *(.interp) } : plugin + .note.gnu.build-id : { *(.note.gnu.build-id) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + .rel.dyn : + { + *(.rel.init) + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + *(.rel.fini) + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) + *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) + *(.rel.ctors) + *(.rel.dtors) + *(.rel.got) + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rel_iplt_start = .); + *(.rel.iplt) + PROVIDE_HIDDEN (__rel_iplt_end = .); + PROVIDE_HIDDEN (__rela_iplt_start = .); + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .rela.dyn : + { + *(.rela.init) + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + *(.rela.fini) + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) + *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) + *(.rela.ctors) + *(.rela.dtors) + *(.rela.got) + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + PROVIDE_HIDDEN (__rel_iplt_start = .); + PROVIDE_HIDDEN (__rel_iplt_end = .); + PROVIDE_HIDDEN (__rela_iplt_start = .); + *(.rela.iplt) + PROVIDE_HIDDEN (__rela_iplt_end = .); + } + .rel.plt : + { + *(.rel.plt) + } + .rela.plt : + { + *(.rela.plt) + } + .init : + { + KEEP (*(.init)) + } =0 + .plt : { *(.plt) } + .iplt : { *(.iplt) } + .text : + { + *(.text.unlikely .text.*_unlikely) + *(.text .stub .text.* .gnu.linkonce.t.*) + /* .gnu.warning sections are handled specially by elf32.em. */ + *(.gnu.warning) + *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx) + } =0 + .fini : + { + KEEP (*(.fini)) + } =0 + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } + .rodata1 : { *(.rodata1) } + .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } + __exidx_start = .; + .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } + __exidx_end = .; + .eh_frame_hdr : { *(.eh_frame_hdr) } + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) } + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)); + /* Exception handling */ + .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) } + .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) } + /* Thread Local Storage sections */ + .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } + .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } + .preinit_array : + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } + .init_array : + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + } + .fini_array : + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + PROVIDE_HIDDEN (__fini_array_end = .); + } + .ctors : + { + ___plugin_ctors = .; + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + ___plugin_ctors_end = .; + } + .dtors : + { + ___plugin_dtors = .; + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + ___plugin_dtors_end = .; + } + .jcr : { KEEP (*(.jcr)) } + .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) } + .dynamic : { *(.dynamic) } + .got : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) } + .data : + { + __data_start = . ; + *(.data .data.* .gnu.linkonce.d.*) + SORT(CONSTRUCTORS) + } + .data1 : { *(.data1) } + _edata = .; PROVIDE (edata = .); + __bss_start = .; + __bss_start__ = .; + .bss : + { + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + /* Align here to ensure that the .bss section occupies space up to + _end. Align after .bss to ensure correct alignment even if the + .bss section disappears because there are no input sections. + FIXME: Why do we need it? When there is no .bss section, we don't + pad the .data section. */ + . = ALIGN(. != 0 ? 32 / 8 : 1); + } + _bss_end__ = . ; __bss_end__ = . ; + . = ALIGN(32 / 8); + . = ALIGN(32 / 8); + __end__ = . ; + _end = .; PROVIDE (end = .); + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /* DWARF 3 */ + .debug_pubtypes 0 : { *(.debug_pubtypes) } + .debug_ranges 0 : { *(.debug_ranges) } + .stack 0x80000 : + { + _stack = .; + *(.stack) + } + .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) } + .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) } + /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) } +} diff --git a/backends/plugins/elf-loader.cpp b/backends/plugins/elf-loader.cpp new file mode 100644 index 0000000000..9787c880ae --- /dev/null +++ b/backends/plugins/elf-loader.cpp @@ -0,0 +1,413 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) + +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <malloc.h> // for memalign() (Linux specific) +#include <unistd.h> +#include <sys/fcntl.h> +#include <sys/_default_fcntl.h> // FIXME: Why do we need this DevKitPro specific header? + +#include "common/file.h" +#include "common/fs.h" +#include "elf-loader.h" + +#ifdef __PSP__ +#include "backends/platform/psp/powerman.h" +#include "psputils.h" +#endif + +#ifdef __DS__ +#include <nds.h> +#endif + +#define __DEBUG_PLUGINS__ + +#ifdef __DEBUG_PLUGINS__ +#define DBG(x,...) printf(x, ## __VA_ARGS__) +#else +#define DBG(x,...) +#endif + +#define seterror(x,...) printf(x, ## __VA_ARGS__) + +/** + * Flushes the data cache (Platform Specific). + */ +static void flushDataCache() { +#ifdef __DS__ + DC_FlushAll(); +#endif +#ifdef __PLAYSTATION2__ + FlushCache(0); + FlushCache(2); +#endif +#ifdef __PSP__ + sceKernelDcacheWritebackAll(); +#endif +} + +// Expel the symbol table from memory +void DLObject::discard_symtab() { + free(_symtab); + free(_strtab); + _symtab = NULL; + _strtab = NULL; + _symbol_cnt = 0; +} + +// Unload all objects from memory +void DLObject::unload() { + discard_symtab(); + free(_segment); + _segment = NULL; +} + +bool DLObject::readElfHeader(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr) { + + // Start reading the elf header. Check for errors + if (DLFile->read(ehdr, sizeof(*ehdr)) != sizeof(*ehdr) || + memcmp(ehdr->e_ident, ELFMAG, SELFMAG) || // Check MAGIC + ehdr->e_type != ET_EXEC || // Check for executable +#ifdef ARM_TARGET + ehdr->e_machine != EM_ARM || // Check for ARM machine type +#endif +#ifdef MIPS_TARGET + ehdr->e_machine != EM_MIPS || // Check for MIPS machine type +#endif + ehdr->e_phentsize < sizeof(Elf32_Phdr) || // Check for size of program header + ehdr->e_shentsize != sizeof(Elf32_Shdr)) { // Check for size of section header + seterror("Invalid file type."); + return false; + } + + DBG("phoff = %d, phentsz = %d, phnum = %d\n", + ehdr->e_phoff, ehdr->e_phentsize, ehdr->e_phnum); + + return true; +} + +bool DLObject::readProgramHeaders(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num) { + + // Read program header + if (DLFile->seek(ehdr->e_phoff + sizeof(*phdr)*num, SEEK_SET) < 0 || + DLFile->read(phdr, sizeof(*phdr)) != sizeof(*phdr)) { + seterror("Program header load failed."); + return false; + } + + // Check program header values + if (phdr->p_type != PT_LOAD || phdr->p_filesz > phdr->p_memsz) { + seterror("Invalid program header."); + return false; + } + + DBG("offs = %x, filesz = %x, memsz = %x, align = %x\n", + phdr->p_offset, phdr->p_filesz, phdr->p_memsz, phdr->p_align); + + return true; + +} + +bool DLObject::loadSegment(Common::SeekableReadStream* DLFile, Elf32_Phdr *phdr) { + + char *baseAddress = 0; + + // Attempt to allocate memory for segment + int extra = phdr->p_vaddr % phdr->p_align; // Get extra length TODO: check logic here + DBG("extra mem is %x\n", extra); + + if (!(_segment = (char *)memalign(phdr->p_align, phdr->p_memsz + extra))) { + seterror("Out of memory.\n"); + return false; + } + + DBG("allocated segment @ %p\n", _segment); + + // Get offset to load segment into + baseAddress = (char *)_segment + phdr->p_vaddr; + _segmentSize = phdr->p_memsz + extra; + + // Set bss segment to 0 if necessary (assumes bss is at the end) + if (phdr->p_memsz > phdr->p_filesz) { + DBG("Setting %p to %p to 0 for bss\n", baseAddress + phdr->p_filesz, baseAddress + phdr->p_memsz); + memset(baseAddress + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); + } + + DBG("Reading the segment into memory\n"); + + // Read the segment into memory + if (DLFile->seek(phdr->p_offset, SEEK_SET) < 0 || + DLFile->read(baseAddress, phdr->p_filesz) != (ssize_t)phdr->p_filesz) { + seterror("Segment load failed."); + return false; + } + + DBG("Segment has been read into memory\n"); + + return true; +} + +Elf32_Shdr * DLObject::loadSectionHeaders(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr) { + + Elf32_Shdr *shdr = NULL; + + // Allocate memory for section headers + if (!(shdr = (Elf32_Shdr *)malloc(ehdr->e_shnum * sizeof(*shdr)))) { + seterror("Out of memory."); + return NULL; + } + + // Read from file into section headers + if (DLFile->seek(ehdr->e_shoff, SEEK_SET) < 0 || + DLFile->read(shdr, ehdr->e_shnum * sizeof(*shdr)) != + (ssize_t)(ehdr->e_shnum * sizeof(*shdr))) { + seterror("Section headers load failed."); + return NULL; + } + + return shdr; +} + +int DLObject::loadSymbolTable(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { + + // Loop over sections, looking for symbol table linked to a string table + for (int i = 0; i < ehdr->e_shnum; i++) { + if (shdr[i].sh_type == SHT_SYMTAB && + shdr[i].sh_entsize == sizeof(Elf32_Sym) && + shdr[i].sh_link < ehdr->e_shnum && + shdr[shdr[i].sh_link].sh_type == SHT_STRTAB && + _symtab_sect < 0) { + _symtab_sect = i; + } + } + + // Check for no symbol table + if (_symtab_sect < 0) { + seterror("No symbol table."); + return -1; + } + + DBG("Symbol section at section %d, size %x\n", _symtab_sect, shdr[_symtab_sect].sh_size); + + // Allocate memory for symbol table + if (!(_symtab = malloc(shdr[_symtab_sect].sh_size))) { + seterror("Out of memory."); + return -1; + } + + // Read symbol table into memory + if (DLFile->seek(shdr[_symtab_sect].sh_offset, SEEK_SET) < 0 || + DLFile->read(_symtab, shdr[_symtab_sect].sh_size) != + (ssize_t)shdr[_symtab_sect].sh_size) { + seterror("Symbol table load failed."); + return -1; + } + + // Set number of symbols + _symbol_cnt = shdr[_symtab_sect].sh_size / sizeof(Elf32_Sym); + DBG("Loaded %d symbols.\n", _symbol_cnt); + + return _symtab_sect; + +} + +bool DLObject::loadStringTable(Common::SeekableReadStream* DLFile, Elf32_Shdr *shdr) { + + int string_sect = shdr[_symtab_sect].sh_link; + + // Allocate memory for string table + if (!(_strtab = (char *)malloc(shdr[string_sect].sh_size))) { + seterror("Out of memory."); + return false; + } + + // Read string table into memory + if (DLFile->seek(shdr[string_sect].sh_offset, SEEK_SET) < 0 || + DLFile->read(_strtab, shdr[string_sect].sh_size) != + (ssize_t)shdr[string_sect].sh_size) { + seterror("Symbol table strings load failed."); + return false; + } + + return true; +} + +void DLObject::relocateSymbols(Elf32_Addr offset) { + + int mainCount = 0; + int shortsCount= 0; + + // Loop over symbols, add relocation offset + Elf32_Sym *s = (Elf32_Sym *)_symtab; + for (int c = _symbol_cnt; c--; s++) { + + // Make sure we don't relocate special valued symbols + if (s->st_shndx < SHN_LOPROC) { + mainCount++; + s->st_value += offset; + if (s->st_value < (Elf32_Addr)_segment || s->st_value > (Elf32_Addr)_segment + _segmentSize) + seterror("Symbol out of bounds! st_value = %x\n", s->st_value); + } + } +} + +bool DLObject::load(Common::SeekableReadStream* DLFile) { + Elf32_Ehdr ehdr; + Elf32_Phdr phdr; + Elf32_Shdr *shdr; + bool ret = true; + + if (readElfHeader(DLFile, &ehdr) == false) { + return false; + } + + for (int i = 0; i < ehdr.e_phnum; i++) { //Load our segments + + DBG("Loading segment %d\n", i); + + if (readProgramHeaders(DLFile, &ehdr, &phdr, i) == false) + return false; + + if (!loadSegment(DLFile, &phdr)) + return false; + } + + if ((shdr = loadSectionHeaders(DLFile, &ehdr)) == NULL) + ret = false; + + if (ret && ((_symtab_sect = loadSymbolTable(DLFile, &ehdr, shdr)) < 0)) + ret = false; + + if (ret && (loadStringTable(DLFile, shdr) == false)) + ret = false; + + if (ret) + relocateSymbols((Elf32_Addr)_segment); // Offset by our segment allocated address + + if (ret && (relocateRels(DLFile, &ehdr, shdr) == false)) + ret = false; + + free(shdr); + + return ret; + +} + +bool DLObject::open(const char *path) { + + Common::SeekableReadStream* DLFile; + void *ctors_start, *ctors_end; + +#ifdef __PSP__ + PowerMan.beginCriticalSection(); +#endif + + DBG("open(\"%s\")\n", path); + + Common::FSNode file(path); + + if (!(DLFile = file.createReadStream())) { + seterror("%s not found.", path); + return false; + } + + DBG("%s found!\n", path); + + /*Try to load and relocate*/ + if (!load(DLFile)) { + unload(); + return false; + } + + DBG("loaded!/n"); + +#ifdef __PSP__ + PowerMan.endCriticalSection(); +#endif + + flushDataCache(); + + ctors_start = symbol("___plugin_ctors"); + ctors_end = symbol("___plugin_ctors_end"); + _dtors_start = symbol("___plugin_dtors"); + _dtors_end = symbol("___plugin_dtors_end"); + + if (ctors_start == NULL || ctors_end == NULL || _dtors_start == NULL || + _dtors_end == NULL) { + seterror("Missing ctors/dtors."); + _dtors_start = _dtors_end = NULL; + unload(); + return false; + } + + DBG(("Calling constructors.\n")); + for (void (**f)(void) = (void (**)(void))ctors_start; f != ctors_end; f++) + (**f)(); + + DBG(("%s opened ok.\n", path)); + + return true; +} + +bool DLObject::close() { + if (_dtors_start != NULL && _dtors_end != NULL) + for (void (**f)(void) = (void (**)(void))_dtors_start; f != _dtors_end; f++) + (**f)(); + _dtors_start = _dtors_end = NULL; + unload(); + return true; +} + +void *DLObject::symbol(const char *name) { + DBG(("symbol(\"%s\")\n", name)); + + if (_symtab == NULL || _strtab == NULL || _symbol_cnt < 1) { + seterror("No symbol table loaded."); + return NULL; + } + + Elf32_Sym *s = (Elf32_Sym *)_symtab; + for (int c = _symbol_cnt; c--; s++) + + // We can only import symbols that are global or weak in the plugin + if ((SYM_BIND(s->st_info) == STB_GLOBAL || SYM_BIND(s->st_info) == STB_WEAK) && + !strcmp(name, _strtab + s->st_name)) { + + // We found the symbol + DBG("=> %p\n", (void*)s->st_value); + return (void*)s->st_value; + } + + // We didn't find the symbol + seterror("Symbol \"%s\" not found.", name); + return NULL; +} + +#endif /* defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) */ + diff --git a/backends/plugins/elf-loader.h b/backends/plugins/elf-loader.h new file mode 100644 index 0000000000..1d30aa0c3b --- /dev/null +++ b/backends/plugins/elf-loader.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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef ELF_LOADER_H +#define ELF_LOADER_H + +#include "elf32.h" +#include "common/stream.h" +#include "backends/plugins/dynamic-plugin.h" + +class DLObject { +protected: + void *_segment, *_symtab; + char *_strtab; + int _symbol_cnt; + int _symtab_sect; + void *_dtors_start, *_dtors_end; + + int _segmentSize; + + //void seterror(const char *fmt, ...); + virtual void unload(); + virtual bool relocate(Common::SeekableReadStream* DLFile, unsigned long offset, unsigned long size, void *relSegment) = 0; + bool load(Common::SeekableReadStream* DLFile); + + bool readElfHeader(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr); + bool readProgramHeaders(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num); + virtual bool loadSegment(Common::SeekableReadStream* DLFile, Elf32_Phdr *phdr); + Elf32_Shdr *loadSectionHeaders(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr); + int loadSymbolTable(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); + bool loadStringTable(Common::SeekableReadStream* DLFile, Elf32_Shdr *shdr); + virtual void relocateSymbols(Elf32_Addr offset); + virtual bool relocateRels(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) = 0; + +public: + bool open(const char *path); + bool close(); + void *symbol(const char *name); + void discard_symtab(); + + DLObject() : _segment(NULL), _symtab(NULL), _strtab(NULL), _symbol_cnt(0), + _symtab_sect(-1), _dtors_start(NULL), _dtors_end(NULL), _segmentSize(0) {} +}; + +#endif /* ELF_LOADER_H */ diff --git a/backends/plugins/elf-provider.cpp b/backends/plugins/elf-provider.cpp new file mode 100644 index 0000000000..e6edd4c578 --- /dev/null +++ b/backends/plugins/elf-provider.cpp @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) + +#include "backends/plugins/elf-provider.h" +#include "backends/plugins/dynamic-plugin.h" +#include "common/fs.h" + +#include "backends/plugins/elf-loader.h" + +DynamicPlugin::VoidFunc ELFPlugin::findSymbol(const char *symbol) { + void *func; + bool handleNull; + if (_dlHandle == NULL) { + func = NULL; + handleNull = true; + } else { + func = _dlHandle->symbol(symbol); + } + if (!func) { + if (handleNull) { + warning("Failed loading symbol '%s' from plugin '%s' (Handle is NULL)", symbol, _filename.c_str()); + } else { + warning("Failed loading symbol '%s' from plugin '%s'", symbol, _filename.c_str()); + } + } + + // FIXME HACK: This is a HACK to circumvent a clash between the ISO C++ + // standard and POSIX: ISO C++ disallows casting between function pointers + // and data pointers, but dlsym always returns a void pointer. For details, + // see e.g. <http://www.trilithium.com/johan/2004/12/problem-with-dlsym/>. + assert(sizeof(VoidFunc) == sizeof(func)); + VoidFunc tmp; + memcpy(&tmp, &func, sizeof(VoidFunc)); + return tmp; +} + +bool ELFPlugin::loadPlugin() { + assert(!_dlHandle); + DLObject *obj = makeDLObject(); + if (obj->open(_filename.c_str())) { + _dlHandle = obj; + } else { + delete obj; + _dlHandle = NULL; + } + + if (!_dlHandle) { + warning("Failed loading plugin '%s'", _filename.c_str()); + return false; + } + + bool ret = DynamicPlugin::loadPlugin(); + + if (ret && _dlHandle) { + _dlHandle->discard_symtab(); + } + + return ret; +}; + +void ELFPlugin::unloadPlugin() { + DynamicPlugin::unloadPlugin(); + if (_dlHandle) { + if (!_dlHandle->close()) { + warning("Failed unloading plugin '%s'", _filename.c_str()); + } + delete _dlHandle; + _dlHandle = 0; + } +} + +bool ELFPluginProvider::isPluginFilename(const Common::FSNode &node) const { + // Check the plugin suffix + Common::String filename = node.getName(); + if (!filename.hasSuffix(".PLG") && !filename.hasSuffix(".plg") && !filename.hasSuffix(".PLUGIN") && !filename.hasSuffix(".plugin")) { + return false; + } + return true; +} + +#endif // defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) diff --git a/backends/plugins/elf-provider.h b/backends/plugins/elf-provider.h new file mode 100644 index 0000000000..e0382c3e45 --- /dev/null +++ b/backends/plugins/elf-provider.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_PLUGINS_ELF_PROVIDER_H +#define BACKENDS_PLUGINS_ELF_PROVIDER_H + +#include "base/plugins.h" +#include "backends/plugins/dynamic-plugin.h" +#include "common/fs.h" + +#include "backends/plugins/elf-loader.h" + +#if defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) + +class ELFPlugin : public DynamicPlugin { +protected: + DLObject *_dlHandle; + Common::String _filename; + + virtual VoidFunc findSymbol(const char *symbol); + +public: + ELFPlugin(const Common::String &filename) + : _dlHandle(0), _filename(filename) {} + + ~ELFPlugin() { + if (_dlHandle) + unloadPlugin(); + } + + virtual DLObject *makeDLObject() = 0; + + bool loadPlugin(); + void unloadPlugin(); + +}; + +class ELFPluginProvider : public FilePluginProvider { +protected: + virtual Plugin* createPlugin(const Common::FSNode &node) const = 0; + + bool isPluginFilename(const Common::FSNode &node) const; + +}; + +#endif // defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) + +#endif /* BACKENDS_PLUGINS_ELF_PROVIDER_H */ diff --git a/backends/plugins/elf32.h b/backends/plugins/elf32.h new file mode 100644 index 0000000000..5dec1d2e58 --- /dev/null +++ b/backends/plugins/elf32.h @@ -0,0 +1,229 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef BACKENDS_ELF_H +#define BACKENDS_ELF_H + +/* ELF stuff */ + +typedef unsigned short Elf32_Half, Elf32_Section; +typedef unsigned int Elf32_Word, Elf32_Addr, Elf32_Off; +typedef signed int Elf32_Sword; +typedef Elf32_Half Elf32_Versym; + +#define EI_NIDENT (16) +#define SELFMAG 6 + +/* ELF File format structures. Look up ELF structure for more details */ + +// ELF header (contains info about the file) +typedef struct { + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +// Should be in e_ident +#define ELFMAG "\177ELF\1\1" /* ELF Magic number */ + +// e_type values +#define ET_NONE 0 /* no file type */ +#define ET_REL 1 /* relocatable */ +#define ET_EXEC 2 /* executable */ +#define ET_DYN 3 /* shared object */ +#define ET_CORE 4 /* core file */ + +// e_machine values +#define EM_MIPS 8 +#define EM_ARM 40 + +// Program header (contains info about segment) +typedef struct { + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +// p_type values +#define PT_NULL 0 /* ignored */ +#define PT_LOAD 1 /* loadable segment */ +#define PT_DYNAMIC 2 /* dynamic linking info */ +#define PT_INTERP 3 /* info about interpreter */ +#define PT_NOTE 4 /* note segment */ +#define PT_SHLIB 5 /* reserved */ +#define PT_PHDR 6 /* Program header table */ +#define PT_MIPS_REGINFO 0x70000000 /* Register usage info for MIPS */ +#define PT_ARM_ARCHEXT 0x70000000 /* Platform architecture compatibility info for ARM */ +#define PT_ARM_EXIDX 0x70000001 /* Exception unwind tables for ARM */ + +// p_flags value +#define PF_X 1 /* execute */ +#define PF_W 2 /* write */ +#define PF_R 4 /* read */ + +// Section header (contains info about section) +typedef struct { + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +// sh_type values +#define SHT_NULL 0 /* Inactive section */ +#define SHT_PROGBITS 1 /* Proprietary */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addend */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Info for dynamic linking */ +#define SHT_NOTE 7 /* Note section */ +#define SHT_NOBITS 8 /* Occupies no space */ +#define SHT_REL 9 /* Relocation entries without addend */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Minimal set of dynamic linking symbols */ +#define SHT_MIPS_LIBLSIT 0x70000000 /* Info about dynamic shared object libs for MIPS*/ +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicts btw executables and shared objects for MIPS */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global pointer table for MIPS*/ +#define SHT_ARM_EXIDX 0x70000001 /* Exception Index table for ARM*/ +#define SHT_ARM_PREEMPTMAP 0x70000002 /* BPABI DLL dynamic linking pre-emption map for ARM */ +#define SHT_ARM_ATTRIBUTES 0x70000003 /* Object file compatibility attributes for ARM*/ + +// sh_flags values +#define SHF_WRITE 0 /* writable section */ +#define SHF_ALLOC 2 /* section occupies memory */ +#define SHF_EXECINSTR 4 /* machine instructions */ +#define SHF_MIPS_GPREL 0x10000000 /* Must be made part of global data area for MIPS */ + +// Symbol entry (contain info about a symbol) +typedef struct { + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +// Extract from the st_info +#define SYM_TYPE(x) ((x)&0xF) +#define SYM_BIND(x) ((x)>>4) + +// Symbol binding values from st_info +#define STB_LOCAL 0 /* Symbol not visible outside object */ +#define STB_GLOBAL 1 /* Symbol visible to all object files */ +#define STB_WEAK 2 /* Similar to STB_GLOBAL */ + +// Symbol type values from st_info +#define STT_NOTYPE 0 /* Not specified */ +#define STT_OBJECT 1 /* Data object e.g. variable */ +#define STT_FUNC 2 /* Function */ +#define STT_SECTION 3 /* Section */ +#define STT_FILE 4 /* Source file associated with object file */ + +// Special section header index values from st_shndex +#define SHN_UNDEF 0 +#define SHN_LOPROC 0xFF00 /* Extended values */ +#define SHN_ABS 0xFFF1 /* Absolute value: don't relocate */ +#define SHN_COMMON 0xFFF2 /* Common block. Not allocated yet */ +#define SHN_HIPROC 0xFF1F +#define SHN_HIRESERVE 0xFFFF + +// Relocation entry with implicit addend (info about how to relocate) +typedef struct { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +// Relocation entry with explicit addend (info about how to relocate) +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +// Access macros for the relocation info +#define REL_TYPE(x) ((unsigned char) (x)) /* Extract relocation type */ +#define REL_INDEX(x) ((x)>>8) /* Extract relocation index into symbol table */ + +//MIPS relocation types +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 +#define R_MIPS_GOTHI16 13 +#define R_MIPS_GOTLO16 14 +#define R_MIPS_CALLHI16 15 +#define R_MIPS_CALLLO16 16 + +// ARM relocation types +#define R_ARM_NONE 0 +#define R_ARM_ABS32 2 +#define R_ARM_THM_CALL 10 +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_TARGET1 38 +#define R_ARM_V4BX 40 + +// Mock function to get value of global pointer for MIPS +#define getGP() ({ \ + unsigned int __valgp; \ + __asm__ ("add %0, $gp, $0" : "=r"(__valgp) : ); \ + __valgp; \ +}) + +#endif /* BACKENDS_ELF_H */ diff --git a/backends/plugins/mips-loader.cpp b/backends/plugins/mips-loader.cpp new file mode 100644 index 0000000000..7b59a2cffa --- /dev/null +++ b/backends/plugins/mips-loader.cpp @@ -0,0 +1,351 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(MIPS_TARGET) + +#include "mips-loader.h" + +#define __DEBUG_PLUGINS__ + +#ifdef __DEBUG_PLUGINS__ +#define DBG(x,...) printf(x, ## __VA_ARGS__) +#else +#define DBG(x,...) +#endif + +#define seterror(x,...) printf(x, ## __VA_ARGS__) + +/** + * Follow the instruction of a relocation section. + * + * @param DLFile SeekableReadStream of File + * @param offset Offset into the File + * @param size Size of relocation section + * @param relSegment Base address of relocated segment in memory (memory offset) + * + */ +bool MIPSDLObject::relocate(Common::SeekableReadStream* DLFile, unsigned long offset, unsigned long size, void *relSegment) { + Elf32_Rel *rel = NULL; // relocation entry + + // Allocate memory for relocation table + if (!(rel = (Elf32_Rel *)malloc(size))) { + seterror("Out of memory."); + return false; + } + + // Read in our relocation table + if (DLFile->seek(offset, SEEK_SET) < 0 || + DLFile->read(rel, size) != (ssize_t)size) { + seterror("Relocation table load failed."); + free(rel); + return false; + } + + // Treat each relocation entry. Loop over all of them + int cnt = size / sizeof(*rel); + + DBG("Loaded relocation table. %d entries. base address=%p\n", cnt, relSegment); + + bool seenHi16 = false; // For treating HI/LO16 commands + int firstHi16 = -1; // Mark the point of the first hi16 seen + Elf32_Addr ahl = 0; // Calculated addend + int a = 0; // Addend: taken from the target + + unsigned int *lastTarget = 0; // For processing hi16 when lo16 arrives + unsigned int relocation = 0; + int debugRelocs[10] = {0}; // For debugging + int extendedHi16 = 0; // Count extended hi16 treatments + Elf32_Addr lastHiSymVal = 0; + bool hi16InShorts = false; + +#define DEBUG_NUM 2 + + // Loop over relocation entries + for (int i = 0; i < cnt; i++) { + // Get the symbol this relocation entry is referring to + Elf32_Sym *sym = (Elf32_Sym *)(_symtab) + (REL_INDEX(rel[i].r_info)); + + // Get the target instruction in the code + unsigned int *target = (unsigned int *)((char *)relSegment + rel[i].r_offset); + + unsigned int origTarget = *target; // Save for debugging + + // Act differently based on the type of relocation + switch (REL_TYPE(rel[i].r_info)) { + + case R_MIPS_HI16: // Absolute addressing. + if (sym->st_shndx < SHN_LOPROC && // Only shift for plugin section (ie. has a real section index) + firstHi16 < 0) { // Only process first in block of HI16s + firstHi16 = i; // Keep the first Hi16 we saw + seenHi16 = true; + ahl = (*target & 0xffff) << 16; // Take lower 16 bits shifted up + + lastHiSymVal = sym->st_value; + hi16InShorts = (ShortsMan.inGeneralSegment((char *)sym->st_value)); // Fix for problem with switching btw segments + if (debugRelocs[0]++ < DEBUG_NUM) // Print only a set number + DBG("R_MIPS_HI16: i=%d, offset=%x, ahl = %x, target = %x\n", + i, rel[i].r_offset, ahl, *target); + } + break; + + case R_MIPS_LO16: // Absolute addressing. Needs a HI16 to come before it + if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. (ie. has a real section index) + if (!seenHi16) { // We MUST have seen HI16 first + seterror("R_MIPS_LO16 w/o preceding R_MIPS_HI16 at relocation %d!\n", i); + free(rel); + return false; + } + + // Fix: bug in gcc makes LO16s connect to wrong HI16s sometimes (shorts and regular segment) + // Note that we can check the entire shorts segment because the executable's shorts don't belong to this plugin section + // and will be screened out above + bool lo16InShorts = ShortsMan.inGeneralSegment((char *)sym->st_value); + + // Correct the bug by getting the proper value in ahl (taken from the current symbol) + if ((hi16InShorts && !lo16InShorts) || (!hi16InShorts && lo16InShorts)) { + ahl -= (lastHiSymVal & 0xffff0000); // We assume gcc meant the same offset + ahl += (sym->st_value & 0xffff0000); + } + + ahl &= 0xffff0000; // Clean lower 16 bits for repeated LO16s + a = *target & 0xffff; // Take lower 16 bits of the target + a = (a << 16) >> 16; // Sign extend them + ahl += a; // Add lower 16 bits. AHL is now complete + + // Fix: we can have LO16 access to the short segment sometimes + if (lo16InShorts) { + relocation = ahl + _shortsSegment->getOffset(); // Add in the short segment offset + } else // It's in the regular segment + relocation = ahl + (Elf32_Addr)_segment; // Add in the new offset for the segment + + if (firstHi16 >= 0) { // We haven't treated the HI16s yet so do it now + for (int j = firstHi16; j < i; j++) { + if (REL_TYPE(rel[j].r_info) != R_MIPS_HI16) continue; // Skip over non-Hi16s + + lastTarget = (unsigned int *)((char *)relSegment + rel[j].r_offset); // get hi16 target + *lastTarget &= 0xffff0000; // Clear the lower 16 bits of the last target + *lastTarget |= (relocation >> 16) & 0xffff; // Take the upper 16 bits of the relocation + if (relocation & 0x8000)(*lastTarget)++; // Subtle: we need to add 1 to the HI16 in this case + } + firstHi16 = -1; // Reset so we'll know we treated it + } else { + extendedHi16++; + } + + *target &= 0xffff0000; // Clear the lower 16 bits of current target + *target |= relocation & 0xffff; // Take the lower 16 bits of the relocation + + if (debugRelocs[1]++ < DEBUG_NUM) + DBG("R_MIPS_LO16: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n", + i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target); + if (lo16InShorts && debugRelocs[2]++ < DEBUG_NUM) + DBG("R_MIPS_LO16s: i=%d, offset=%x, a=%x, ahl = %x, lastTarget = %x, origt = %x, target = %x\n", + i, rel[i].r_offset, a, ahl, *lastTarget, origTarget, *target); + } + break; + + case R_MIPS_26: // Absolute addressing (for jumps and branches only) + if (sym->st_shndx < SHN_LOPROC) { // Only relocate for main segment + a = *target & 0x03ffffff; // Get 26 bits' worth of the addend + a = (a << 6) >> 6; // Sign extend a + relocation = ((a << 2) + (Elf32_Addr)_segment) >> 2; // a already points to the target. Subtract our offset + *target &= 0xfc000000; // Clean lower 26 target bits + *target |= (relocation & 0x03ffffff); + + if (debugRelocs[3]++ < DEBUG_NUM) + DBG("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n", + i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target); + } else { + if (debugRelocs[4]++ < DEBUG_NUM) + DBG("R_MIPS_26: i=%d, offset=%x, symbol=%d, stinfo=%x, a=%x, origTarget=%x, target=%x\n", + i, rel[i].r_offset, REL_INDEX(rel[i].r_info), sym->st_info, a, origTarget, *target); + } + break; + + case R_MIPS_GPREL16: // GP Relative addressing + if (_shortsSegment->getOffset() != 0 && // Only relocate if we shift the shorts section + ShortsMan.inGeneralSegment((char *)sym->st_value)) { // Only relocate things in the plugin hole + a = *target & 0xffff; // Get 16 bits' worth of the addend + a = (a << 16) >> 16; // Sign extend it + + relocation = a + _shortsSegment->getOffset(); + + *target &= 0xffff0000; // Clear the lower 16 bits of the target + *target |= relocation & 0xffff; + + if (debugRelocs[5]++ < DEBUG_NUM) + DBG("R_MIPS_GPREL16: i=%d, a=%x, gpVal=%x, origTarget=%x, target=%x, offset=%x\n", + i, a, _gpVal, origTarget, *target, _shortsSegment->getOffset()); + } + + break; + + case R_MIPS_32: // Absolute addressing + if (sym->st_shndx < SHN_LOPROC) { // Only shift for plugin section. + a = *target; // Get full 32 bits of addend + + if (ShortsMan.inGeneralSegment((char *)sym->st_value)) // Check if we're in the shorts segment + relocation = a + _shortsSegment->getOffset(); // Shift by shorts offset + else // We're in the main section + relocation = a + (Elf32_Addr)_segment; // Shift by main offset + *target = relocation; + + if (debugRelocs[6]++ < DEBUG_NUM) + DBG("R_MIPS_32: i=%d, a=%x, origTarget=%x, target=%x\n", i, a, origTarget, *target); + } + break; + + default: + seterror("Unknown relocation type %x at relocation %d.\n", REL_TYPE(rel[i].r_info), i); + free(rel); + return false; + } + } + + DBG("Done with relocation. extendedHi16=%d\n\n", extendedHi16); + + free(rel); + return true; +} + +bool MIPSDLObject::relocateRels(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr) { + + // Loop over sections, finding relocation sections + for (int i = 0; i < ehdr->e_shnum; i++) { + + Elf32_Shdr *curShdr = &(shdr[i]); + //Elf32_Shdr *linkShdr = &(shdr[curShdr->sh_info]); + + if (curShdr->sh_type == SHT_REL && // Check for a relocation section + curShdr->sh_entsize == sizeof(Elf32_Rel) && // Check for proper relocation size + (int)curShdr->sh_link == _symtab_sect && // Check that the sh_link connects to our symbol table + curShdr->sh_info < ehdr->e_shnum && // Check that the relocated section exists + (shdr[curShdr->sh_info].sh_flags & SHF_ALLOC)) { // Check if relocated section resides in memory + if (!ShortsMan.inGeneralSegment((char *)shdr[curShdr->sh_info].sh_addr)) { // regular segment + if (!relocate(DLFile, curShdr->sh_offset, curShdr->sh_size, _segment)) { + return false; + } + } else { // In Shorts segment + if (!relocate(DLFile, curShdr->sh_offset, curShdr->sh_size, (void *)_shortsSegment->getOffset())) { + return false; + } + } + + } + } + + return true; +} + +void MIPSDLObject::relocateSymbols(Elf32_Addr offset) { + + int mainCount = 0; + int shortsCount= 0; + + // Loop over symbols, add relocation offset + Elf32_Sym *s = (Elf32_Sym *)_symtab; + for (int c = _symbol_cnt; c--; s++) { + + // Make sure we don't relocate special valued symbols + if (s->st_shndx < SHN_LOPROC) { + if (!ShortsMan.inGeneralSegment((char *)s->st_value)) { + mainCount++; + s->st_value += offset; + if (s->st_value < (Elf32_Addr)_segment || s->st_value > (Elf32_Addr)_segment + _segmentSize) + seterror("Symbol out of bounds! st_value = %x\n", s->st_value); + } else { // shorts section + shortsCount++; + s->st_value += _shortsSegment->getOffset(); + if (!_shortsSegment->inSegment((char *)s->st_value)) + seterror("Symbol out of bounds! st_value = %x\n", s->st_value); + } + + } + } +} + +bool MIPSDLObject::loadSegment(Common::SeekableReadStream* DLFile, Elf32_Phdr *phdr) { + + char *baseAddress = 0; + + // We need to take account of non-allocated segment for shorts + if (phdr->p_flags & PF_X) { // This is a relocated segment + + // Attempt to allocate memory for segment + int extra = phdr->p_vaddr % phdr->p_align; // Get extra length TODO: check logic here + DBG("extra mem is %x\n", extra); + + if (phdr->p_align < 0x10000) phdr->p_align = 0x10000; // Fix for wrong alignment on e.g. AGI + + if (!(_segment = (char *)memalign(phdr->p_align, phdr->p_memsz + extra))) { + seterror("Out of memory.\n"); + return false; + } + DBG("allocated segment @ %p\n", _segment); + + // Get offset to load segment into + baseAddress = (char *)_segment + phdr->p_vaddr; + _segmentSize = phdr->p_memsz + extra; + } else { // This is a shorts section. + _shortsSegment = ShortsMan.newSegment(phdr->p_memsz, (char *)phdr->p_vaddr); + + baseAddress = _shortsSegment->getStart(); + DBG("shorts segment @ %p to %p. Segment wants to be at %x. Offset=%x\n", + _shortsSegment->getStart(), _shortsSegment->getEnd(), phdr->p_vaddr, _shortsSegment->getOffset()); + } + + // Set bss segment to 0 if necessary (assumes bss is at the end) + if (phdr->p_memsz > phdr->p_filesz) { + DBG("Setting %p to %p to 0 for bss\n", baseAddress + phdr->p_filesz, baseAddress + phdr->p_memsz); + memset(baseAddress + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); + } + + DBG("Reading the segment into memory\n"); + + // Read the segment into memory + if (DLFile->seek(phdr->p_offset, SEEK_SET) < 0 || + DLFile->read(baseAddress, phdr->p_filesz) != (ssize_t)phdr->p_filesz) { + seterror("Segment load failed."); + return false; + } + + DBG("Segment has been read into memory\n"); + + return true; +} + +// Unload all objects from memory +void MIPSDLObject::unload() { + discard_symtab(); + free(_segment); + _segment = NULL; + + if (_shortsSegment) { + ShortsMan.deleteSegment(_shortsSegment); + _shortsSegment = NULL; + } +} + +#endif /* defined(DYNAMIC_MODULES) && defined(MIPS_TARGET) */ diff --git a/backends/plugins/mips-loader.h b/backends/plugins/mips-loader.h new file mode 100644 index 0000000000..eb22e368f4 --- /dev/null +++ b/backends/plugins/mips-loader.h @@ -0,0 +1,46 @@ + +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "elf-loader.h" +#include "shorts-segment-manager.h" + +class MIPSDLObject : public DLObject { +protected: + ShortSegmentManager::Segment *_shortsSegment; // For assigning shorts ranges + unsigned int _gpVal; // Value of Global Pointer + + bool relocate(Common::SeekableReadStream* DLFile, unsigned long offset, unsigned long size, void *relSegment); + bool relocateRels(Common::SeekableReadStream* DLFile, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); + void relocateSymbols(Elf32_Addr offset); + bool loadSegment(Common::SeekableReadStream* DLFile, Elf32_Phdr *phdr); + void unload(); + +public: + MIPSDLObject() : DLObject() { + _shortsSegment = NULL; + _gpVal = 0; + } +}; diff --git a/backends/platform/psp/plugin.syms b/backends/plugins/plugin.syms index 24ee1a19dc..24ee1a19dc 100644 --- a/backends/platform/psp/plugin.syms +++ b/backends/plugins/plugin.syms diff --git a/backends/plugins/ps2/main_prog.ld b/backends/plugins/ps2/main_prog.ld new file mode 100644 index 0000000000..9dba69c50e --- /dev/null +++ b/backends/plugins/ps2/main_prog.ld @@ -0,0 +1,99 @@ +ENTRY(_start); + +SECTIONS { + .text 0x00100000: { + _ftext = . ; + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + KEEP(*(.init)) + KEEP(*(.fini)) + QUAD(0) + } + + PROVIDE(_etext = .); + PROVIDE(etext = .); + + .reginfo : { *(.reginfo) } + + /* Global/static constructors and deconstructors. */ + .ctors ALIGN(16): { + KEEP(*crtbegin*.o(.ctors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + } + .dtors ALIGN(16): { + KEEP(*crtbegin*.o(.dtors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + } + + /* Static data. */ + .rodata ALIGN(128): { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r*) + } + + .data ALIGN(128): { + _fdata = . ; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + SORT(CONSTRUCTORS) + } + + .rdata ALIGN(128): { *(.rdata) } + .gcc_except_table ALIGN(128): { *(.gcc_except_table) } + + _gp = ALIGN(128) + 0x7ff0; + .lit4 ALIGN(128): { *(.lit4) } + .lit8 ALIGN(128): { *(.lit8) } + + .sdata ALIGN(128): { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s*) + } + + _edata = .; + PROVIDE(edata = .); + + /* Uninitialized data. */ + .sbss ALIGN(128) : { + _fbss = . ; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb*) + *(.scommon) + } + + /*This "plugin hole" is so the plugins can all have global small data + in the same place.*/ + __plugin_hole_start = .; + . = _gp + 0x7ff0; + __plugin_hole_end = .; + + COMMON : + { + *(COMMON) + } + . = ALIGN(128); + + .bss ALIGN(128) : { + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b*) + } + _end_bss = .; + + _end = . ; + PROVIDE(end = .); + + /* Symbols needed by crt0.s. */ + PROVIDE(_heap_size = -1); + PROVIDE(_stack = -1); + PROVIDE(_stack_size = 128 * 1024); +} diff --git a/backends/plugins/ps2/plugin.ld b/backends/plugins/ps2/plugin.ld new file mode 100644 index 0000000000..9879413b98 --- /dev/null +++ b/backends/plugins/ps2/plugin.ld @@ -0,0 +1,94 @@ +/* PHDRS specifies ELF Program Headers (or segments) to the plugin linker */ +PHDRS { + plugin PT_LOAD ; /* Specifies that the plugin segment should be loaded from file */ + shorts PT_LOAD ; /* Specifies that the shorts segment should be loaded from file */ +} +SECTIONS { + .text 0: { + _ftext = . ; + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + KEEP(*(.init)) + KEEP(*(.fini)) + QUAD(0) + } : plugin /*The ": plugin" tells the linker to assign this and + the following sections to the "plugin" segment*/ + PROVIDE(_etext = .); + PROVIDE(etext = .); + + .reginfo : { *(.reginfo) } + + /* Global/static constructors and deconstructors. */ + .ctors ALIGN(16): { + ___plugin_ctors = .; + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + ___plugin_ctors_end = .; + } + .dtors ALIGN(16): { + ___plugin_dtors = .; + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + ___plugin_dtors_end = .; + } + + /* Static data. */ + .rodata ALIGN(128): { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r*) + } + + .data ALIGN(128): { + _fdata = . ; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + SORT(CONSTRUCTORS) + } + + .rdata ALIGN(128): { *(.rdata) } + .gcc_except_table ALIGN(128): { *(.gcc_except_table) } + + .bss ALIGN(128) : { + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b*) + *(COMMON) + } + _end_bss = .; + + _end = . ; + PROVIDE(end = .); + + /* Symbols needed by crt0.s. */ + PROVIDE(_heap_size = -1); + PROVIDE(_stack = -1); + PROVIDE(_stack_size = 128 * 1024); + + /*We assign the output location counter to the plugin hole made + in main_prog.ld, then assign the small data sections to the shorts segment*/ + . = __plugin_hole_start; + .lit4 ALIGN(128): { *(.lit4) } : shorts + .lit8 ALIGN(128): { *(.lit8) } + + .sdata ALIGN(128): { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s*) + } + + _edata = .; + PROVIDE(edata = .); + + /* Uninitialized data. */ + .sbss ALIGN(128) : { + _fbss = . ; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb*) + *(.scommon) + } + +} diff --git a/backends/plugins/ps2/ps2-provider.cpp b/backends/plugins/ps2/ps2-provider.cpp new file mode 100644 index 0000000000..096c6d4050 --- /dev/null +++ b/backends/plugins/ps2/ps2-provider.cpp @@ -0,0 +1,44 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(__PLAYSTATION2__) + +#include "backends/plugins/mips-loader.h" +#include "backends/plugins/elf-provider.h" +#include "backends/plugins/ps2/ps2-provider.h" + + +class PS2Plugin : public ELFPlugin { +public: + PS2Plugin(const Common::String &filename) : ELFPlugin(filename) {} + + DLObject *makeDLObject() { return new MIPSDLObject(); } +}; + +Plugin* PS2PluginProvider::createPlugin(const Common::FSNode &node) const { + return new PS2Plugin(node.getPath()); +} + +#endif // defined(DYNAMIC_MODULES) && defined(ELF_LOADER_TARGET) diff --git a/backends/plugins/ps2/ps2-provider.h b/backends/plugins/ps2/ps2-provider.h new file mode 100644 index 0000000000..28a2321c29 --- /dev/null +++ b/backends/plugins/ps2/ps2-provider.h @@ -0,0 +1,31 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "backends/plugins/elf-provider.h" + +class PS2PluginProvider : public ELFPluginProvider { + Plugin* createPlugin(const Common::FSNode &node) const; +}; + diff --git a/backends/platform/psp/plugin.ld b/backends/plugins/psp/plugin.ld index db4df45264..db4df45264 100644 --- a/backends/platform/psp/plugin.ld +++ b/backends/plugins/psp/plugin.ld diff --git a/backends/plugins/psp/psp-provider.cpp b/backends/plugins/psp/psp-provider.cpp index 5760424cbf..99aa33c123 100644 --- a/backends/plugins/psp/psp-provider.cpp +++ b/backends/plugins/psp/psp-provider.cpp @@ -25,86 +25,20 @@ #if defined(DYNAMIC_MODULES) && defined(__PSP__) +#include "backends/plugins/mips-loader.h" +#include "backends/plugins/elf-provider.h" #include "backends/plugins/psp/psp-provider.h" -#include "backends/plugins/dynamic-plugin.h" -#include "common/fs.h" -#include "backends/platform/psp/psploader.h" - -#include "backends/platform/psp/trace.h" - - -class PSPPlugin : public DynamicPlugin { -protected: - void *_dlHandle; - Common::String _filename; - - virtual VoidFunc findSymbol(const char *symbol) { - void *func = dlsym(_dlHandle, symbol); - if (!func) - warning("Failed loading symbol '%s' from plugin '%s' (%s)", symbol, _filename.c_str(), dlerror()); - - // FIXME HACK: This is a HACK to circumvent a clash between the ISO C++ - // standard and POSIX: ISO C++ disallows casting between function pointers - // and data pointers, but dlsym always returns a void pointer. For details, - // see e.g. <http://www.trilithium.com/johan/2004/12/problem-with-dlsym/>. - assert(sizeof(VoidFunc) == sizeof(func)); - VoidFunc tmp; - memcpy(&tmp, &func, sizeof(VoidFunc)); - return tmp; - } +class PSPPlugin : public ELFPlugin { public: - PSPPlugin(const Common::String &filename) - : _dlHandle(0), _filename(filename) {} - - ~PSPPlugin() { - if (_dlHandle) unloadPlugin(); - } - - bool loadPlugin() { - assert(!_dlHandle); - _dlHandle = dlopen(_filename.c_str(), RTLD_LAZY); - - if (!_dlHandle) { - warning("Failed loading plugin '%s' (%s)", _filename.c_str(), dlerror()); - return false; - } + PSPPlugin(const Common::String &filename) : ELFPlugin(filename) {} - bool ret = DynamicPlugin::loadPlugin(); - - if (ret) - dlforgetsyms(_dlHandle); - - return ret; - } - - void unloadPlugin() { - DynamicPlugin::unloadPlugin(); - if (_dlHandle) { - if (dlclose(_dlHandle) != 0) - warning("Failed unloading plugin '%s' (%s)", _filename.c_str(), dlerror()); - _dlHandle = 0; - } - } + DLObject *makeDLObject() { return new MIPSDLObject(); } }; - Plugin* PSPPluginProvider::createPlugin(const Common::FSNode &node) const { return new PSPPlugin(node.getPath()); } -bool PSPPluginProvider::isPluginFilename(const Common::FSNode &node) const { - // Check the plugin suffix - Common::String filename = node.getName(); - PSP_DEBUG_PRINT("Testing name %s", filename.c_str()); - if (!filename.hasSuffix(".PLG") && !filename.hasSuffix(".plg")) { - PSP_DEBUG_PRINT(" fail.\n"); - return false; - } - - PSP_DEBUG_PRINT(" success!\n"); - return true; -} - #endif // defined(DYNAMIC_MODULES) && defined(__PSP__) diff --git a/backends/plugins/psp/psp-provider.h b/backends/plugins/psp/psp-provider.h index d6c44c5a85..c4debc7997 100644 --- a/backends/plugins/psp/psp-provider.h +++ b/backends/plugins/psp/psp-provider.h @@ -26,16 +26,12 @@ #ifndef BACKENDS_PLUGINS_PSP_PSP_PROVIDER_H #define BACKENDS_PLUGINS_PSP_PSP_PROVIDER_H -#include "base/plugins.h" +#include "backends/plugins/elf-provider.h" #if defined(DYNAMIC_MODULES) && defined(__PSP__) -class PSPPluginProvider : public FilePluginProvider { -protected: +class PSPPluginProvider : public ELFPluginProvider { Plugin* createPlugin(const Common::FSNode &node) const; - - bool isPluginFilename(const Common::FSNode &node) const; - }; #endif // defined(DYNAMIC_MODULES) && defined(__PSP__) diff --git a/backends/plugins/shorts-segment-manager.cpp b/backends/plugins/shorts-segment-manager.cpp new file mode 100644 index 0000000000..2376759919 --- /dev/null +++ b/backends/plugins/shorts-segment-manager.cpp @@ -0,0 +1,89 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#if defined(DYNAMIC_MODULES) && defined(MIPS_TARGET) + +#include "shorts-segment-manager.h" + +extern char __plugin_hole_start; // Indicates start of hole in program file for shorts +extern char __plugin_hole_end; // Indicates end of hole in program file +extern char _gp[]; // Value of gp register + +#ifdef DEBUG_PLUGINS +#define DBG(x,...) printf(x, ## __VA_ARGS__) +#else +#define DBG(x,...) +#endif + +#define seterror(x,...) printf(x, ## __VA_ARGS__) + +DECLARE_SINGLETON(ShortSegmentManager); // For singleton + +ShortSegmentManager::ShortSegmentManager() { + _shortsStart = &__plugin_hole_start ; //shorts segment begins at the plugin hole we made when linking + _shortsEnd = &__plugin_hole_end; //and ends at the end of that hole. +} + +ShortSegmentManager::Segment *ShortSegmentManager::newSegment(int size, char *origAddr) { + char *lastAddress = origAddr; + Common::List<Segment *>::iterator i; + + // Find a block that fits, starting from the beginning + for (i = _list.begin(); i != _list.end(); ++i) { + char *currAddress = (*i)->getStart(); + + if ((int)(currAddress - lastAddress) >= size) break; + + lastAddress = (*i)->getEnd(); + } + + if ((Elf32_Addr)lastAddress & 3) + lastAddress += 4 - ((Elf32_Addr)lastAddress & 3); // Round up to multiple of 4 + + if (lastAddress + size > _shortsEnd) { + seterror("Error. No space in shorts segment for %x bytes. Last address is %p, max address is %p.\n", + size, lastAddress, _shortsEnd); + return NULL; + } + + Segment *seg = new Segment(lastAddress, size, origAddr); // Create a new segment + + if (lastAddress + size > _highestAddress) _highestAddress = lastAddress + size; // Keep track of maximum + + _list.insert(i, seg); + + DBG("Shorts segment size %x allocated. End = %p. Remaining space = %x. Highest so far is %p.\n", + size, lastAddress + size, _shortsEnd - _list.back()->getEnd(), _highestAddress); + + return seg; +} + +void ShortSegmentManager::deleteSegment(ShortSegmentManager::Segment *seg) { + DBG("Deleting shorts segment from %p to %p.\n\n", seg->getStart(), seg->getEnd()); + _list.remove(seg); + delete seg; +} + +#endif /* DYNAMIC_MODULES && MIPS_TARGET */ diff --git a/backends/platform/psp/psploader.h b/backends/plugins/shorts-segment-manager.h index 13dcf6ef98..54a13d88e1 100644 --- a/backends/platform/psp/psploader.h +++ b/backends/plugins/shorts-segment-manager.h @@ -23,17 +23,21 @@ * */ -#ifndef PSPLOADER_H -#define PSPLOADER_H +#ifndef SHORTS_SEGMENT_MANAGER_H +#define SHORTS_SEGMENT_MANAGER_H -#include "elf32.h" -#include "common/list.h" #include "common/singleton.h" +#include "common/list.h" +#include "elf32.h" -#define MAXDLERRLEN 80 - -#define ShortsMan ShortSegmentManager::instance() +#define ShortsMan ShortSegmentManager::instance() +/** + * Manages the segments of small data put in the gp-relative area for MIPS processors, + * and lets these segments be handled differently in the ELF loader. + * Since there's no true dynamic linker to change the GP register between plugins and the main engine, + * custom linker scripts ensure the GP-area is in the same place for both. + */ class ShortSegmentManager : public Common::Singleton<ShortSegmentManager> { private: char *_shortsStart; @@ -43,6 +47,8 @@ public: char *getShortsStart() { return _shortsStart; } + + // Returns whether or not an absolute address is in the GP-relative section. bool inGeneralSegment(char *addr) { return ((char *)addr >= _shortsStart && (char *)addr < _shortsEnd); } @@ -80,58 +86,4 @@ private: char *_highestAddress; }; - - - -class DLObject { -protected: - char *_errbuf; /* For error messages, at least MAXDLERRLEN in size */ - - ShortSegmentManager::Segment *_shortsSegment; // For assigning shorts ranges - void *_segment, *_symtab; - char *_strtab; - int _symbol_cnt; - int _symtab_sect; - void *_dtors_start, *_dtors_end; - - unsigned int _gpVal; // Value of Global Pointer - int _segmentSize; - - void seterror(const char *fmt, ...); - void unload(); - bool relocate(int fd, unsigned long offset, unsigned long size, void *); - bool load(int fd); - - bool readElfHeader(int fd, Elf32_Ehdr *ehdr); - bool readProgramHeaders(int fd, Elf32_Ehdr *ehdr, Elf32_Phdr *phdr, int num); - bool loadSegment(int fd, Elf32_Phdr *phdr); - Elf32_Shdr *loadSectionHeaders(int fd, Elf32_Ehdr *ehdr); - int loadSymbolTable(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); - bool loadStringTable(int fd, Elf32_Shdr *shdr); - void relocateSymbols(Elf32_Addr offset, Elf32_Addr shortsOffset); - bool relocateRels(int fd, Elf32_Ehdr *ehdr, Elf32_Shdr *shdr); - -public: - bool open(const char *path); - bool close(); - void *symbol(const char *name); - void discard_symtab(); - - DLObject(char *errbuf = NULL) : _errbuf(_errbuf), _shortsSegment(NULL), _segment(NULL), _symtab(NULL), - _strtab(NULL), _symbol_cnt(0), _symtab_sect(-1), _dtors_start(NULL), _dtors_end(NULL), _gpVal(0) , - _segmentSize(0) {} -}; - - - -#define RTLD_LAZY 0 - -extern "C" { - void *dlopen(const char *filename, int flags); - int dlclose(void *handle); - void *dlsym(void *handle, const char *symbol); - const char *dlerror(); - void dlforgetsyms(void *handle); -} - -#endif /* PSPLOADER_H */ +#endif /* SHORTS_SEGMENT_MANAGER_H */ diff --git a/backends/saves/default/default-saves.cpp b/backends/saves/default/default-saves.cpp index 1ab898d2d6..55aa469309 100644 --- a/backends/saves/default/default-saves.cpp +++ b/backends/saves/default/default-saves.cpp @@ -131,11 +131,11 @@ bool DefaultSaveFileManager::removeSavefile(const Common::String &filename) { // There is a nicely portable workaround, too: Make this method overloadable. if (remove(file.getPath().c_str()) != 0) { #ifndef _WIN32_WCE - if (errno == EACCES) + if (errno == EACCES) setError(Common::kWritePermissionDenied, "Search or write permission denied: "+file.getName()); if (errno == ENOENT) - setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid"); + setError(Common::kPathDoesNotExist, "removeSavefile: '"+file.getName()+"' does not exist or path is invalid"); #endif return false; } else { diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 2f4e78fd80..e36ddfca54 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -598,9 +598,7 @@ static void listTargets() { } /** List all saves states for the given target. */ -static Common::Error listSaves(const char *target) { - Common::Error result = Common::kNoError; - +static void listSaves(const char *target) { // FIXME HACK g_system->initBackend(); @@ -625,14 +623,13 @@ static Common::Error listSaves(const char *target) { GameDescriptor game = EngineMan.findGame(gameid, &plugin); if (!plugin) { - warning("Could not find any plugin to handle target '%s' (gameid '%s')", target, gameid.c_str()); - return Common::kPluginNotFound; + error("Could not find any plugin to handle target '%s' (gameid '%s')", target, gameid.c_str()); + return; } if (!(*plugin)->hasFeature(MetaEngine::kSupportsListSaves)) { // TODO: Include more info about the target (desc, engine name, ...) ??? printf("ScummVM does not support listing save states for target '%s' (gameid '%s') .\n", target, gameid.c_str()); - result = Common::kPluginNotSupportSaves; } else { // Query the plugin for a list of savegames SaveStateList saveList = (*plugin)->listSaves(target); @@ -642,9 +639,6 @@ static Common::Error listSaves(const char *target) { printf(" Slot Description \n" " ---- ------------------------------------------------------\n"); - if (saveList.size() == 0) - result = Common::kNoSavesError; - for (SaveStateList::const_iterator x = saveList.begin(); x != saveList.end(); ++x) { printf(" %-4s %s\n", x->save_slot().c_str(), x->description().c_str()); // TODO: Could also iterate over the full hashmap, printing all key-value pairs @@ -653,8 +647,6 @@ static Common::Error listSaves(const char *target) { // Revert to the old active domain ConfMan.setActiveDomain(oldDomain); - - return result; } /** Lists all usable themes */ @@ -867,7 +859,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha #endif // DISABLE_COMMAND_LINE -Common::Error processSettings(Common::String &command, Common::StringMap &settings) { +bool processSettings(Common::String &command, Common::StringMap &settings) { #ifndef DISABLE_COMMAND_LINE @@ -876,33 +868,34 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin // have been loaded. if (command == "list-targets") { listTargets(); - return Common::kNoError; + return false; } else if (command == "list-games") { listGames(); - return Common::kNoError; + return false; } else if (command == "list-saves") { - return listSaves(settings["list-saves"].c_str()); + listSaves(settings["list-saves"].c_str()); + return false; } else if (command == "list-themes") { listThemes(); - return Common::kNoError; + return false; } else if (command == "version") { printf("%s\n", gScummVMFullVersion); printf("Features compiled in: %s\n", gScummVMFeatures); - return Common::kNoError; + return false; } else if (command == "help") { printf(HELP_STRING, s_appName); - return Common::kNoError; + return false; } #ifdef DETECTOR_TESTING_HACK else if (command == "test-detector") { runDetectorTest(); - return Common::kNoError; + return false; } #endif #ifdef UPGRADE_ALL_TARGETS_HACK else if (command == "upgrade-targets") { upgradeTargets(); - return Common::kNoError; + return false; } #endif @@ -974,7 +967,7 @@ Common::Error processSettings(Common::String &command, Common::StringMap &settin ConfMan.set(key, value, Common::ConfigManager::kTransientDomain); } - return Common::kArgumentNotProcessed; + return true; } } // End of namespace Base diff --git a/base/commandLine.h b/base/commandLine.h index c7e8d8b0d0..8801ed17d8 100644 --- a/base/commandLine.h +++ b/base/commandLine.h @@ -33,7 +33,7 @@ namespace Base { void registerDefaults(); Common::String parseCommandLine(Common::StringMap &settings, int argc, const char * const *argv); -Common::Error processSettings(Common::String &command, Common::StringMap &settings); +bool processSettings(Common::String &command, Common::StringMap &settings); } // End of namespace Base diff --git a/base/main.cpp b/base/main.cpp index e651456ace..08d59aafab 100644 --- a/base/main.cpp +++ b/base/main.cpp @@ -105,8 +105,13 @@ static const EnginePlugin *detectPlugin() { // Query the plugins and find one that will handle the specified gameid printf("User picked target '%s' (gameid '%s')...\n", ConfMan.getActiveDomainName().c_str(), gameid.c_str()); printf("%s", " Looking for a plugin supporting this gameid... "); - GameDescriptor game = EngineMan.findGame(gameid, &plugin); +#if defined(NEW_PLUGIN_DESIGN_FIRST_REFINEMENT) && defined(DYNAMIC_MODULES) + GameDescriptor game = EngineMan.findGameOnePlugAtATime(gameid, &plugin); +#else + GameDescriptor game = EngineMan.findGame(gameid, &plugin); +#endif + if (plugin == 0) { printf("failed\n"); warning("%s is an invalid gameid. Use the --list-games option to list supported gameid", gameid.c_str()); @@ -339,8 +344,12 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { settings.erase("debugflags"); } +#if defined(NEW_PLUGIN_DESIGN_FIRST_REFINEMENT) && defined(DYNAMIC_MODULES) //note: I'm going to refactor this name later :P + // Don't load the plugins initially in this case. +#else // Load the plugins. PluginManager::instance().loadPlugins(); +#endif // If we received an invalid music parameter via command line we check this here. // We can't check this before loading the music plugins. @@ -354,10 +363,8 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // Process the remaining command line settings. Must be done after the // config file and the plugins have been loaded. - Common::Error res; - - if ((res = Base::processSettings(command, settings)) != Common::kArgumentNotProcessed) - return res; + if (!Base::processSettings(command, settings)) + return 0; // Init the backend. Must take place after all config data (including // the command line params) was read. @@ -383,7 +390,7 @@ extern "C" int scummvm_main(int argc, const char * const argv[]) { // Unless a game was specified, show the launcher dialog if (0 == ConfMan.getActiveDomain()) launcherDialog(); - + // FIXME: We're now looping the launcher. This, of course, doesn't // work as well as it should. In theory everything should be destroyed // cleanly, so this is now enabled to encourage people to fix bits :) diff --git a/base/plugins.cpp b/base/plugins.cpp index 5d0be11065..61cc747a41 100644 --- a/base/plugins.cpp +++ b/base/plugins.cpp @@ -302,6 +302,31 @@ void PluginManager::addPluginProvider(PluginProvider *pp) { _providers.push_back(pp); } +bool PluginManager::loadFirstPlugin() { //TODO: only deal with engine plugins here, and have a separate "loadNonEnginePlugins" function. + unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL); + PluginList plugs; + for (ProviderList::iterator pp = _providers.begin(); + pp != _providers.end(); + ++pp) { + PluginList pl((*pp)->getPlugins()); + for (PluginList::iterator p = pl.begin(); p != pl.end(); ++p) { + plugs.push_back(*p); + } + } + _pluginsEnd = plugs.end(); + _currentPlugin = plugs.begin(); + if (plugs.empty()) return false; //return false if there are no plugins to load. + return tryLoadPlugin(*_currentPlugin); +} + +bool PluginManager::loadNextPlugin() { + // To ensure only one engine plugin is loaded at a time, we unload all engine plugins before loading a new one. + unloadPluginsExcept(PLUGIN_TYPE_ENGINE, NULL); + ++_currentPlugin; + if (_currentPlugin == _pluginsEnd) return false; //return false if already reached the end of list of plugins. + return tryLoadPlugin(*_currentPlugin); +} + void PluginManager::loadPlugins() { for (ProviderList::iterator pp = _providers.begin(); pp != _providers.end(); @@ -309,7 +334,6 @@ void PluginManager::loadPlugins() { PluginList pl((*pp)->getPlugins()); Common::for_each(pl.begin(), pl.end(), Common::bind1st(Common::mem_fun(&PluginManager::tryLoadPlugin), this)); } - } void PluginManager::unloadPlugins() { @@ -340,7 +364,7 @@ bool PluginManager::tryLoadPlugin(Plugin *plugin) { // The plugin is valid, see if it provides the same module as an // already loaded one and should replace it. bool found = false; - + printf("Plugin loaded is %s\n", plugin->getName()); PluginList::iterator pl = _plugins[plugin->getType()].begin(); while (!found && pl != _plugins[plugin->getType()].end()) { if (!strcmp(plugin->getName(), (*pl)->getName())) { @@ -366,13 +390,24 @@ bool PluginManager::tryLoadPlugin(Plugin *plugin) { } } - // Engine plugins #include "engines/metaengine.h" DECLARE_SINGLETON(EngineManager) +GameDescriptor EngineManager::findGameOnePlugAtATime(const Common::String &gameName, const EnginePlugin **plugin) const { + GameDescriptor result; + PluginManager::instance().loadFirstPlugin(); + do { + result = findGame(gameName, plugin); + if (!result.gameid().empty()) { + break; + } + } while (PluginManager::instance().loadNextPlugin()); + return result; +} + GameDescriptor EngineManager::findGame(const Common::String &gameName, const EnginePlugin **plugin) const { // Find the GameDescriptor for this target const EnginePlugin::List &plugins = getPlugins(); @@ -381,30 +416,36 @@ GameDescriptor EngineManager::findGame(const Common::String &gameName, const Eng if (plugin) *plugin = 0; - EnginePlugin::List::const_iterator iter = plugins.begin(); - for (iter = plugins.begin(); iter != plugins.end(); ++iter) { - result = (**iter)->findGame(gameName.c_str()); - if (!result.gameid().empty()) { - if (plugin) - *plugin = *iter; - break; + EnginePlugin::List::const_iterator iter; + + for (iter = plugins.begin(); iter != plugins.end(); ++iter) { + result = (**iter)->findGame(gameName.c_str()); + if (!result.gameid().empty()) { + if (plugin) + *plugin = *iter; + return result; + } } - } return result; } GameList EngineManager::detectGames(const Common::FSList &fslist) const { GameList candidates; - - const EnginePlugin::List &plugins = getPlugins(); - - // Iterate over all known games and for each check if it might be - // the game in the presented directory. + EnginePlugin::List plugins; EnginePlugin::List::const_iterator iter; - for (iter = plugins.begin(); iter != plugins.end(); ++iter) { - candidates.push_back((**iter)->detectGames(fslist)); - } - +#if defined(NEW_PLUGIN_DESIGN_FIRST_REFINEMENT) && defined(DYNAMIC_MODULES) + PluginManager::instance().loadFirstPlugin(); + do { +#endif + plugins = getPlugins(); + // Iterate over all known games and for each check if it might be + // the game in the presented directory. + for (iter = plugins.begin(); iter != plugins.end(); ++iter) { + candidates.push_back((**iter)->detectGames(fslist)); + } +#if defined(NEW_PLUGIN_DESIGN_FIRST_REFINEMENT) && defined(DYNAMIC_MODULES) + } while (PluginManager::instance().loadNextPlugin()); +#endif return candidates; } diff --git a/base/plugins.h b/base/plugins.h index a4c7f114f9..975c815783 100644 --- a/base/plugins.h +++ b/base/plugins.h @@ -275,9 +275,11 @@ class PluginManager : public Common::Singleton<PluginManager> { private: PluginList _plugins[PLUGIN_TYPE_MAX]; ProviderList _providers; - + PluginList::iterator _currentPlugin; + PluginList::iterator _pluginsEnd; + bool tryLoadPlugin(Plugin *plugin); - + friend class Common::Singleton<SingletonBaseType>; PluginManager(); @@ -286,6 +288,9 @@ public: void addPluginProvider(PluginProvider *pp); + bool loadFirstPlugin(); + bool loadNextPlugin(); + void loadPlugins(); void unloadPlugins(); void unloadPluginsExcept(PluginType type, const Plugin *plugin); diff --git a/common/error.h b/common/error.h index 58343114a2..e79b8d0e91 100644 --- a/common/error.h +++ b/common/error.h @@ -59,16 +59,10 @@ enum Error { kPathNotDirectory, ///< The specified path does not point to a directory kPathNotFile, ///< The specified path does not point to a file - kCreatingFileFailed, ///< Failed creating a (savestate) file - kReadingFailed, ///< Failed to read a file (permission denied?) + kCreatingFileFailed, + kReadingFailed, ///< Failed creating a (savestate) file kWritingFailed, ///< Failure to write data -- disk full? - // The following are used by --list-saves - kPluginNotFound, ///< Failed to find plugin to handle tager - kPluginNotSupportSaves, ///< Failed if plugin does not support saves - kNoSavesError, ///< There are no saves to show - - kArgumentNotProcessed, ///< Used in command line parsing kUnknownError ///< Catch-all error, used if no other error code matches }; diff --git a/common/macresman.cpp b/common/macresman.cpp index df7351d55a..4b726a183d 100644 --- a/common/macresman.cpp +++ b/common/macresman.cpp @@ -439,11 +439,6 @@ Common::SeekableReadStream *MacResManager::getResource(uint32 typeID, uint16 res _stream->seek(_dataOffset + _resLists[typeNum][resNum].dataOffset); uint32 len = _stream->readUint32BE(); - - // Ignore resources with 0 length - if (!len) - return 0; - return _stream->readStream(len); } @@ -453,11 +448,6 @@ Common::SeekableReadStream *MacResManager::getResource(const Common::String &fil if (_resLists[i][j].nameOffset != -1 && filename.equalsIgnoreCase(_resLists[i][j].name)) { _stream->seek(_dataOffset + _resLists[i][j].dataOffset); uint32 len = _stream->readUint32BE(); - - // Ignore resources with 0 length - if (!len) - return 0; - return _stream->readStream(len); } } diff --git a/common/str.h b/common/str.h index e3dec6cdc2..46e721a746 100644 --- a/common/str.h +++ b/common/str.h @@ -225,12 +225,6 @@ public: typedef const char * const_iterator; iterator begin() { - // Since the user could potentially - // change the string via the returned - // iterator we have to assure we are - // pointing to a unique storage. - makeUnique(); - return _str; } diff --git a/common/stream.h b/common/stream.h index 5e0d7149b0..5c81063a7e 100644 --- a/common/stream.h +++ b/common/stream.h @@ -156,7 +156,7 @@ public: class ReadStream : virtual public Stream { public: /** - * Returns true if a read failed because the stream end has been reached. + * Returns true if a read failed because the stream has been reached. * This flag is cleared by clearErr(). * For a SeekableReadStream, it is also cleared by a successful seek. */ diff --git a/common/unzip.cpp b/common/unzip.cpp index a29518a796..eda42fd107 100644 --- a/common/unzip.cpp +++ b/common/unzip.cpp @@ -1472,11 +1472,11 @@ Common::SeekableReadStream *ZipArchive::createReadStreamForMember(const Common:: unz_file_info fileInfo; unzOpenCurrentFile(_zipFile); unzGetCurrentFileInfo(_zipFile, &fileInfo, NULL, 0, NULL, 0, NULL, 0); - byte *buffer = (byte *)malloc(fileInfo.uncompressed_size); + byte *buffer = (byte *)calloc(fileInfo.uncompressed_size+1, 1); assert(buffer); unzReadCurrentFile(_zipFile, buffer, fileInfo.uncompressed_size); unzCloseCurrentFile(_zipFile); - return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size, DisposeAfterUse::YES); + return new Common::MemoryReadStream(buffer, fileInfo.uncompressed_size+1, DisposeAfterUse::YES); // FIXME: instead of reading all into a memory stream, we could // instead create a new ZipStream class. But then we have to be @@ -1628,10 +1628,6 @@ if test -n "$_host"; then _ar="$_host-ar cru" _ranlib=$_host-ranlib ;; - mips-sgi*) - _endian=big - _need_memalign=yes - ;; motoezx) DEFINES="$DEFINES -DMOTOEZX" ASFLAGS="$ASFLAGS -mfpu=vfp" @@ -1996,6 +1992,23 @@ PRE_OBJS_FLAGS := -Wl,--whole-archive POST_OBJS_FLAGS := -Wl,--export-all-symbols -Wl,--no-whole-archive -Wl,--out-implib,./libscummvm.a ' ;; + ps2) +_def_plugin=' +#define PLUGIN_PREFIX "" +#define PLUGIN_SUFFIX ".plg" +' +_mak_plugins=' +DYNAMIC_MODULES := 1 +PLUGIN_PREFIX := +PLUGIN_SUFFIX := .plg +PLUGIN_EXTRA_DEPS = $(EXECUTABLE) +CXXFLAGS += -DDYNAMIC_MODULES +LDFLAGS += -Wl,-T$(srcdir)/backends/platform/ps2/main_prog.ld +PLUGIN_LDFLAGS = -nostartfiles -Wl,-q,--just-symbols,$(EXECUTABLE),--retain-symbols-file,$(srcdir)/backends/platform/ps2/plugin.syms,-T$(srcdir)/backends/platform/ps2/plugin.ld -lstdc++ -lc -lm -Wl,-Map,mapfile.txt +PRE_OBJS_FLAGS := -Wl,--whole-archive +POST_OBJS_FLAGS := -Wl,--no-whole-archive +' + ;; psp) _def_plugin=' #define PLUGIN_PREFIX "" diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp index d6a3bafd9f..1d6f77b8eb 100644 --- a/engines/drascula/animation.cpp +++ b/engines/drascula/animation.cpp @@ -1695,7 +1695,7 @@ void DrasculaEngine::animation_9_6() { // We set the room number to -1 for the same purpose. // Also check animation_2_1(), where the same hack was used // by the original - roomNumber = -2; + roomNumber = -1; loadPic("nota2.alg", bgSurface, HALF_PAL); black(); trackProtagonist = 1; diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 7e9f68a355..b1f16fa505 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -88,7 +88,6 @@ DrasculaEngine::DrasculaEngine(OSystem *syst, const DrasculaGameDescription *gam _textverbs = 0; _textmisc = 0; _textd1 = 0; - _talkSequences = 0; _color = 0; blinking = 0; diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp index 73aea7b7f2..08c1a68a55 100644 --- a/engines/drascula/objects.cpp +++ b/engines/drascula/objects.cpp @@ -92,8 +92,7 @@ void DrasculaEngine::gotoObject(int pointX, int pointY) { updateRoom(); updateScreen(); - // roomNumber -2 is end credits. Do not show cursor there - if (cursorVisible && roomNumber != -2) + if (cursorVisible) showCursor(); } diff --git a/engines/m4/compression.h b/engines/m4/compression.h index 00e3d1f927..74fed357ff 100644 --- a/engines/m4/compression.h +++ b/engines/m4/compression.h @@ -66,8 +66,8 @@ public: class FabDecompressor { private: - int _bitsLeft; - uint32 _bitBuffer; + int _bitsLeft; + uint32 _bitBuffer; const byte *_srcData, *_srcP; int _srcSize; diff --git a/engines/m4/console.cpp b/engines/m4/console.cpp index 71c70e3e1b..9a92cb04d1 100644 --- a/engines/m4/console.cpp +++ b/engines/m4/console.cpp @@ -47,6 +47,7 @@ Console::Console(MadsM4Engine *vm) : GUI::Debugger() { DCmd_Register("start_conv", WRAP_METHOD(Console, cmdStartConversation)); DCmd_Register("textview", WRAP_METHOD(Console, cmdShowTextview)); DCmd_Register("animview", WRAP_METHOD(Console, cmdShowAnimview)); + DCmd_Register("anim", WRAP_METHOD(Console, cmdPlayAnimation)); } Console::~Console() { @@ -246,6 +247,33 @@ bool Console::cmdShowAnimview(int argc, const char **argv) { return false; } +bool Console::cmdPlayAnimation(int argc, const char **argv) { + View *view = _vm->_viewManager->getView(VIEWID_SCENE); + if (view == NULL) { + DebugPrintf("The scene view isn't currently active\n"); + } else if (argc != 2 && argc != 3) { + DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]); + DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n"); + } else { + char resourceName[20]; + strncpy(resourceName, argv[1], 15); + resourceName[15] = '\0'; + if (!strchr(resourceName, '.')) + strcat(resourceName, ".AA"); + + _vm->_viewManager->moveToFront(view); + if (argc == 3 && atoi(argv[2]) == 1) + _vm->_animation->loadFullScreen(resourceName); + else + _vm->_animation->load(resourceName); + _vm->_animation->start(); + view->restore(0, 0, view->width(), view->height()); + return false; + } + + return true; +} + /*--------------------------------------------------------------------------*/ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) { @@ -254,7 +282,6 @@ MadsConsole::MadsConsole(MadsEngine *vm): Console(vm) { DCmd_Register("object", WRAP_METHOD(MadsConsole, cmdObject)); DCmd_Register("message", WRAP_METHOD(MadsConsole, cmdMessage)); DCmd_Register("scene_info", WRAP_METHOD(MadsConsole, cmdSceneInfo)); - DCmd_Register("anim", WRAP_METHOD(MadsConsole, cmdPlayAnimation)); } bool MadsConsole::cmdObject(int argc, const char **argv) { @@ -359,33 +386,6 @@ bool MadsConsole::cmdSceneInfo(int argc, const char **argv) { return true; } -bool MadsConsole::cmdPlayAnimation(int argc, const char **argv) { - View *view = _vm->_viewManager->getView(VIEWID_SCENE); - if (view == NULL) { - DebugPrintf("The scene view isn't currently active\n"); - } else if (argc != 2 && argc != 3) { - DebugPrintf("Usage: %s <anim resource (*.aa)> <fullscreen>\n", argv[0]); - DebugPrintf("If fullscreen is 1, the screen palette is replaced with the palette of the animation\n"); - } else { - char resourceName[20]; - strncpy(resourceName, argv[1], 15); - resourceName[15] = '\0'; - if (!strchr(resourceName, '.')) - strcat(resourceName, ".AA"); - - _vm->_viewManager->moveToFront(view); - if (argc == 3 && atoi(argv[2]) == 1) - _madsVm->_palette->deleteAllRanges(); - - _madsVm->scene()->_sceneAnimation->load(resourceName, 0); - - view->restore(0, 0, view->width(), view->height()); - return false; - } - - return true; -} - /*--------------------------------------------------------------------------*/ M4Console::M4Console(M4Engine *vm): Console(vm) { diff --git a/engines/m4/console.h b/engines/m4/console.h index 53a47dada9..b592f041cf 100644 --- a/engines/m4/console.h +++ b/engines/m4/console.h @@ -50,6 +50,7 @@ private: bool cmdStartConversation(int argc, const char **argv); bool cmdShowTextview(int argc, const char **argv); bool cmdShowAnimview(int argc, const char **argv); + bool cmdPlayAnimation(int argc, const char **argv); public: Console(MadsM4Engine *vm); @@ -63,8 +64,6 @@ private: bool cmdObject(int argc, const char **argv); bool cmdMessage(int argc, const char **argv); bool cmdSceneInfo(int argc, const char **argv); - bool cmdPlayAnimation(int argc, const char **argv); - public: MadsConsole(MadsEngine *vm); virtual ~MadsConsole() {} diff --git a/engines/m4/converse.cpp b/engines/m4/converse.cpp index af26a86313..aab2fc95ce 100644 --- a/engines/m4/converse.cpp +++ b/engines/m4/converse.cpp @@ -96,7 +96,7 @@ void ConversationView::setNode(int32 nodeIndex) { _vm->_font->setFont(FONT_CONVERSATION); // TODO: Conversation styles and colors - _vm->_font->current()->setColours(2, 1, 3); + _vm->_font->setColors(2, 1, 3); _currentNodeIndex = nodeIndex; @@ -124,7 +124,7 @@ void ConversationView::setNode(int32 nodeIndex) { } // Figure out the longest string to determine where option highlighting ends - int tempX = _vm->_font->current()->getWidth(node->entries[i]->text, 0) + + int tempX = _vm->_font->getWidth(node->entries[i]->text, 0) + CONV_ENTRIES_X_OFFSET + 10; _xEnd = MAX(_xEnd, tempX); } @@ -163,10 +163,10 @@ void ConversationView::onRefresh(RectList *rects, M4Surface *destSurface) { if (i > CONV_MAX_SHOWN_ENTRIES - 1) break; - _vm->_font->current()->setColour((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED : + _vm->_font->setColor((_highlightedIndex == i) ? CONVERSATION_ENTRY_HIGHLIGHTED : CONVERSATION_ENTRY_NORMAL); - _vm->_font->current()->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET, + _vm->_font->writeString(this, _activeItems[i]->text, CONV_ENTRIES_X_OFFSET, CONV_ENTRIES_Y_OFFSET + CONV_ENTRIES_HEIGHT * i, 0, 0); } } diff --git a/engines/m4/dialogs.cpp b/engines/m4/dialogs.cpp index a7104537f5..3af94af262 100644 --- a/engines/m4/dialogs.cpp +++ b/engines/m4/dialogs.cpp @@ -127,7 +127,7 @@ void Dialog::writeChars(const char *srcLine) { strcat(line, wordStr); lineLen = strlen(line); - lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING); + lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING); if (((_lineX + lineLen) > _widthChars) || ((_widthX + lineWidth) > _dialogWidth)) { incLine(); @@ -146,7 +146,7 @@ void Dialog::writeChars(const char *srcLine) { */ void Dialog::appendText(const char *line) { _lineX += strlen(line); - _widthX += _vm->_font->current()->getWidth(line, DIALOG_SPACING); + _widthX += _vm->_font->getWidth(line, DIALOG_SPACING); strcat(_lines[_lines.size() - 1].data, line); } @@ -158,7 +158,7 @@ void Dialog::addLine(const char *line, bool underlineP) { if ((_widthX > 0) || (_lineX > 0)) incLine(); - int lineWidth = _vm->_font->current()->getWidth(line, DIALOG_SPACING); + int lineWidth = _vm->_font->getWidth(line, DIALOG_SPACING); int lineLen = strlen(line); if ((lineWidth > _dialogWidth) || (lineLen >= _widthChars)) @@ -383,7 +383,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v if (id > 0) { // Suffix provided - specifies the dialog width in number of chars _widthChars = id * 2; - _dialogWidth = id * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10; + _dialogWidth = id * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10; } } else if (matchCommand(cmdText, "UNDER")) { @@ -416,7 +416,7 @@ Dialog::Dialog(MadsM4Engine *vm, const char *msgData, const char *title): View(v Dialog::Dialog(MadsM4Engine *vm, int widthChars): View(vm, Common::Rect(0, 0, 0, 0)) { _vm->_font->setFont(FONT_INTERFACE_MADS); _widthChars = widthChars * 2; - _dialogWidth = widthChars * (_vm->_font->current()->getMaxWidth() + DIALOG_SPACING) + 10; + _dialogWidth = widthChars * (_vm->_font->getMaxWidth() + DIALOG_SPACING) + 10; _screenType = LAYER_DIALOG; _lineX = 0; _widthX = 0; @@ -439,7 +439,7 @@ void Dialog::draw() { // Calculate bounds int dlgWidth = _dialogWidth; - int dlgHeight = _lines.size() * (_vm->_font->current()->getHeight() + 1) + 10; + int dlgHeight = _lines.size() * (_vm->_font->getHeight() + 1) + 10; int dialogX = (_vm->_screen->width() - dlgWidth) / 2; int dialogY = (_vm->_screen->height() - dlgHeight) / 2; @@ -480,26 +480,26 @@ void Dialog::draw() { } // Handle drawing the text contents - _vm->_font->current()->setColours(7, 7, 7); + _vm->_font->setColours(7, 7, 7); setColour(7); - for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->current()->getHeight() + 1) { + for (uint lineCtr = 0, yp = 5; lineCtr < _lines.size(); ++lineCtr, yp += _vm->_font->getHeight() + 1) { if (_lines[lineCtr].barLine) { // Bar separation line - hLine(5, width() - 6, ((_vm->_font->current()->getHeight() + 1) >> 1) + yp); + hLine(5, width() - 6, ((_vm->_font->getHeight() + 1) >> 1) + yp); } else { // Standard line Common::Point pt(_lines[lineCtr].xp + 5, yp); if (_lines[lineCtr].xp & 0x40) ++pt.y; - _vm->_font->current()->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING); + _vm->_font->writeString(this, _lines[lineCtr].data, pt.x, pt.y, 0, DIALOG_SPACING); if (_lines[lineCtr].underline) // Underline needed - hLine(pt.x, pt.x + _vm->_font->current()->getWidth(_lines[lineCtr].data, DIALOG_SPACING), - pt.y + _vm->_font->current()->getHeight()); + hLine(pt.x, pt.x + _vm->_font->getWidth(_lines[lineCtr].data, DIALOG_SPACING), + pt.y + _vm->_font->getHeight()); } } @@ -528,7 +528,7 @@ void Dialog::display(MadsM4Engine *vm, int widthChars, const char **descEntries) dlg->incLine(); dlg->writeChars(*descEntries); - int lineWidth = vm->_font->current()->getWidth(*descEntries, DIALOG_SPACING); + int lineWidth = vm->_font->getWidth(*descEntries, DIALOG_SPACING); dlg->_lines[dlg->_lines.size() - 1].xp = (dlg->_dialogWidth - 10 - lineWidth) / 2; ++descEntries; } diff --git a/engines/m4/globals.h b/engines/m4/globals.h index 3fc31b4ec2..2bfedf1449 100644 --- a/engines/m4/globals.h +++ b/engines/m4/globals.h @@ -276,7 +276,6 @@ public: // DEPRECATED: ScummVM re-implementation keeps all the quotes loaded, so the methods below are stubs void clearQuotes() {} void loadQuoteRange(int startNum, int endNum) {} - void loadQuoteSet(...) {} void loadQuote(int quoteNum) {} void loadMadsMessagesInfo(); diff --git a/engines/m4/gui.cpp b/engines/m4/gui.cpp index 8665b4e767..8f949de9c5 100644 --- a/engines/m4/gui.cpp +++ b/engines/m4/gui.cpp @@ -290,26 +290,26 @@ void MenuButton::onRefresh() { case OBJTYPE_SL_TEXT: switch (_objectState) { case OS_MOUSEOVER: - _vm->_font->current()->setColours(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_MOUSEOVER_SHADOW, TEXT_COLOR_MOUSEOVER_FOREGROUND, TEXT_COLOR_MOUSEOVER_HILIGHT); sprite = sprites[SL_LINE_MOUSEOVER]; break; case OS_PRESSED: - _vm->_font->current()->setColours(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_PRESSED_SHADOW, TEXT_COLOR_PRESSED_FOREGROUND, TEXT_COLOR_PRESSED_HILIGHT); sprite = sprites[SL_LINE_PRESSED]; break; case OS_GREYED: - _vm->_font->current()->setColours(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_GREYED_SHADOW, TEXT_COLOR_GREYED_FOREGROUND, TEXT_COLOR_GREYED_HILIGHT); sprite = sprites[SL_LINE_NORMAL]; break; default: case OS_NORMAL: - _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, TEXT_COLOR_NORMAL_HILIGHT); sprite = sprites[SL_LINE_NORMAL]; break; @@ -849,11 +849,11 @@ void MenuSaveLoadText::onRefresh() { if (_displayValue != 0) { char tempBuffer[5]; sprintf(tempBuffer, "%02d", _displayValue); - _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); xp = _bounds.left + 26; } - _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); } } @@ -955,18 +955,18 @@ void MenuTextField::onRefresh() { // Draw the text _vm->_font->setFont(FONT_MENU); - _vm->_font->current()->setColours(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, + _vm->_font->setColors(TEXT_COLOR_NORMAL_SHADOW, TEXT_COLOR_NORMAL_FOREGROUND, TEXT_COLOR_NORMAL_HILIGHT); int xp = _bounds.left + 4; if (_displayValue != 0) { char tempBuffer[5]; sprintf(tempBuffer, "%02d", _displayValue); - _vm->_font->current()->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, tempBuffer, xp, _bounds.top + 1, 0, -1); xp = _bounds.left + 26; } - _vm->_font->current()->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); + _vm->_font->writeString(_parent, _displayText, xp, _bounds.top + 1, 0, -1); if (focused) { // Draw in the cursor @@ -975,7 +975,7 @@ void MenuTextField::onRefresh() { // Get the width of the string up to the cursor position char tempCh = *_cursor; *_cursor = '\0'; - int stringWidth = _vm->_font->current()->getWidth(_displayText); + int stringWidth = _vm->_font->getWidth(_displayText); *_cursor = tempCh; parent()->setColor(TEXT_COLOR_MOUSEOVER_FOREGROUND); @@ -1015,10 +1015,10 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb tempP = &tempStr[tempLen]; _vm->_font->setFont(FONT_MENU); - tempLen = _vm->_font->current()->getWidth(tempStr); + tempLen = _vm->_font->getWidth(tempStr); while ((tempP != &tempStr[0]) && (tempLen > x - _bounds.left - 26)) { *--tempP = '\0'; - tempLen = _vm->_font->current()->getWidth(tempStr); + tempLen = _vm->_font->getWidth(tempStr); } _cursor = &_displayText[tempP - &tempStr[0]]; @@ -1098,7 +1098,7 @@ bool MenuTextField::onEvent(M4EventType event, int32 param, int x, int y, MenuOb parent()->_deleteSaveDesc = false; _vm->_font->setFont(FONT_MENU); - tempLen = _vm->_font->current()->getWidth(_displayText); + tempLen = _vm->_font->getWidth(_displayText); if ((strlen(_displayText) < MAX_SAVEGAME_NAME - 1) && (tempLen < _pixelWidth - 12) && (param >= 32) && (param <= 127)) { @@ -1140,9 +1140,9 @@ GUITextField::GUITextField(View *owner, const Common::Rect &bounds): GUIRect(own void GUITextField::onRefresh() { _parent->fillRect(_bounds, _vm->_palette->BLACK); - _vm->_font->current()->setColours(3, 3, 3); + _vm->_font->setColors(3, 3, 3); _vm->_font->setFont(FONT_INTERFACE); - _vm->_font->current()->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1); + _vm->_font->writeString(_parent, _text.c_str(), _bounds.left, _bounds.top, 0, 1); } //-------------------------------------------------------------------------- diff --git a/engines/m4/m4.cpp b/engines/m4/m4.cpp index a999a6bd5a..824896ad33 100644 --- a/engines/m4/m4.cpp +++ b/engines/m4/m4.cpp @@ -145,6 +145,7 @@ MadsM4Engine::~MadsM4Engine() { delete _script; delete _ws; delete _random; + delete _animation; delete _palette; delete _globals; delete _sound; @@ -173,7 +174,7 @@ Common::Error MadsM4Engine::run() { _events = new Events(this); _kernel = new Kernel(this); _player = new Player(this); - _font = new FontManager(this); + _font = new Font(this); if (getGameType() == GType_Burger) { _actor = new Actor(this); _conversationView = new ConversationView(this); @@ -187,6 +188,7 @@ Common::Error MadsM4Engine::run() { _sound = new Sound(this, _mixer, 255); _script = new ScriptInterpreter(this); _ws = new WoodScript(this); + _animation = new Animation(this); //_callbacks = new Callbacks(this); _random = new Common::RandomSource(); g_eventRec.registerRandomSource(*_random, "m4"); @@ -555,9 +557,9 @@ Common::Error MadsEngine::run() { _scene->show(); _font->setFont(FONT_MAIN_MADS); - _font->current()->setColours(2, 1, 3); - _font->current()->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2); - _font->current()->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2); + _font->setColors(2, 1, 3); + _font->writeString(_scene->getBackgroundSurface(), "Testing the M4/MADS ScummVM engine", 5, 160, 310, 2); + _font->writeString(_scene->getBackgroundSurface(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 5, 180, 310, 2); if (getGameType() == GType_DragonSphere) { //_scene->showMADSV2TextBox("Test", 10, 10, NULL); @@ -573,6 +575,8 @@ Common::Error MadsEngine::run() { while (!_events->quitFlag) { eventHandler(); + _animation->updateAnim(); + if (g_system->getMillis() >= nextFrame) { nextFrame = g_system->getMillis() + GAME_FRAME_DELAY; ++_currentTimer; diff --git a/engines/m4/m4.h b/engines/m4/m4.h index 3174c886d5..9e3035957a 100644 --- a/engines/m4/m4.h +++ b/engines/m4/m4.h @@ -192,7 +192,7 @@ public: Player *_player; Mouse *_mouse; Events *_events; - FontManager *_font; + Font *_font; Actor *_actor; Scene *_scene; Dialogs *_dialogs; @@ -203,6 +203,7 @@ public: Rails *_rails; ScriptInterpreter *_script; WoodScript *_ws; + Animation *_animation; Common::RandomSource *_random; Scene *scene() { return _scene; } diff --git a/engines/m4/m4_views.cpp b/engines/m4/m4_views.cpp index f4345787df..3d633cef0d 100644 --- a/engines/m4/m4_views.cpp +++ b/engines/m4/m4_views.cpp @@ -34,7 +34,7 @@ namespace M4 { GUIInventory::GUIInventory(View *owner, MadsM4Engine *vm, const Common::Rect &bounds, int horizCells, int vertCells, int cellWidth, int cellHeight, int tag): GUIRect(owner, bounds, tag) { - _vm = vm; + _vm = vm; _cellCount.x = horizCells; _cellCount.y = vertCells; _cellSize.x = cellWidth; diff --git a/engines/m4/mads_anim.cpp b/engines/m4/mads_anim.cpp index ca53bdca75..5e19e90533 100644 --- a/engines/m4/mads_anim.cpp +++ b/engines/m4/mads_anim.cpp @@ -37,7 +37,7 @@ namespace M4 { TextviewView::TextviewView(MadsM4Engine *vm): View(vm, Common::Rect(0, 0, vm->_screen->width(), vm->_screen->height())), _bgSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT), - _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->current()->getHeight() + + _textSurface(vm->_screen->width(), MADS_SURFACE_HEIGHT + vm->_font->getHeight() + TEXTVIEW_LINE_SPACING) { _screenType = VIEWID_TEXTVIEW; @@ -60,7 +60,7 @@ TextviewView::TextviewView(MadsM4Engine *vm): _vm->_palette->setPalette(&palData[0], 4, 3); _vm->_palette->blockRange(4, 3); - _vm->_font->current()->setColours(5, 6, 4); + _vm->_font->setColors(5, 6, 4); clear(); _bgSurface.clear(); @@ -222,7 +222,7 @@ void TextviewView::updateState() { } } else { // Handling a text row - if (++_lineY == (_vm->_font->current()->getHeight() + TEXTVIEW_LINE_SPACING)) + if (++_lineY == (_vm->_font->getHeight() + TEXTVIEW_LINE_SPACING)) processLines(); } @@ -404,7 +404,7 @@ void TextviewView::processText() { if (!strcmp(_currentLine, "***")) { // Special signifier for end of script - _scrollCount = _vm->_font->current()->getHeight() * 13; + _scrollCount = _vm->_font->getHeight() * 13; _lineY = -1; return; } @@ -416,7 +416,7 @@ void TextviewView::processText() { char *centerP = strchr(_currentLine, '@'); if (centerP) { *centerP = '\0'; - xStart = (width() / 2) - _vm->_font->current()->getWidth(_currentLine); + xStart = (width() / 2) - _vm->_font->getWidth(_currentLine); // Delete the @ character and shift back the remainder of the string char *p = centerP + 1; @@ -424,16 +424,16 @@ void TextviewView::processText() { strcpy(centerP, p); } else { - lineWidth = _vm->_font->current()->getWidth(_currentLine); + lineWidth = _vm->_font->getWidth(_currentLine); xStart = (width() - lineWidth) / 2; } // Copy the text line onto the bottom of the textSurface surface, which will allow it // to gradually scroll onto the screen - int yp = _textSurface.height() - _vm->_font->current()->getHeight() - TEXTVIEW_LINE_SPACING; + int yp = _textSurface.height() - _vm->_font->getHeight() - TEXTVIEW_LINE_SPACING; _textSurface.fillRect(Common::Rect(0, yp, _textSurface.width(), _textSurface.height()), _vm->_palette->BLACK); - _vm->_font->current()->writeString(&_textSurface, _currentLine, xStart, yp); + _vm->_font->writeString(&_textSurface, _currentLine, xStart, yp); } diff --git a/engines/m4/mads_menus.cpp b/engines/m4/mads_menus.cpp index 810acb04fb..1ee6b3d265 100644 --- a/engines/m4/mads_menus.cpp +++ b/engines/m4/mads_menus.cpp @@ -617,10 +617,10 @@ void RexDialogView::initialiseLines() { } _totalTextEntries = 0; - // Set up a default sprite slot entry for a full screen refresh + // Set up a default sprite slot entry _spriteSlots.startIndex = 1; - _spriteSlots[0].spriteType = FULL_SCREEN_REFRESH; - _spriteSlots[0].seqIndex = -1; + _spriteSlots[0].spriteId = -2; + _spriteSlots[0].timerIndex = -1; } void RexDialogView::initialiseGraphics() { @@ -796,8 +796,8 @@ bool RexDialogView::onEvent(M4EventType eventType, int32 param1, int x, int y, b void RexDialogView::setFrame(int frameNumber, int depth) { int slotIndex = _spriteSlots.getIndex(); - _spriteSlots[slotIndex].spriteType = FOREGROUND_SPRITE; - _spriteSlots[slotIndex].seqIndex = 1; + _spriteSlots[slotIndex].spriteId = 1; + _spriteSlots[slotIndex].timerIndex = 1; _spriteSlots[slotIndex].spriteListIndex = 0; //_menuSpritesIndex; _spriteSlots[slotIndex].frameNumber = frameNumber; @@ -986,15 +986,15 @@ RexGameMenuDialog::RexGameMenuDialog(): RexDialogView() { void RexGameMenuDialog::addLines() { // Add the title - int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 2) * 6) >> 1) - 78); + int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 2) * 6) >> 1) - 78); - addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 10); + addQuote(_vm->_font, ALIGN_CENTER, 0, top, 10); // Loop for adding the option lines of the dialog top += 6; for (int idx = 0; idx < 5; ++idx) { - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 11 + idx); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CENTER, 0, top, 11 + idx); } } @@ -1070,42 +1070,42 @@ void RexOptionsDialog::reload() { void RexOptionsDialog::addLines() { // Add the title - int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->current()->getHeight() + 1) * 9 + 12) >> 1) - 78); + int top = MADS_Y_OFFSET - 2 - ((((_vm->_font->getHeight() + 1) * 9 + 12) >> 1) - 78); - addQuote(_vm->_font->current(), ALIGN_CENTER, 0, top, 16); + addQuote(_vm->_font, ALIGN_CENTER, 0, top, 16); // Music state line - top += _vm->_font->current()->getHeight() + 1 + 6; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25); + top += _vm->_font->getHeight() + 1 + 6; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 17, _tempConfig.musicFlag ? 24 : 25); // Sound state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 18, _tempConfig.soundFlag ? 26 : 27); // Interface easy state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 19, _tempConfig.easyMouse ? 29 : 28); // Inventory sppinng state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 20, _tempConfig.invObjectsStill ? 31 : 30); // Text window state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 21, _tempConfig.textWindowStill ? 33 : 32); // Screen fade state line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 22, _tempConfig.screenFades + 34); // Storyline mode line - top += _vm->_font->current()->getHeight() + 1; - addQuote(_vm->_font->current(), ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38); + top += _vm->_font->getHeight() + 1; + addQuote(_vm->_font, ALIGN_CHAR_CENTER, 0, top, 23, (_tempConfig.storyMode == 1) ? 37 : 38); // Add Done and Cancel button texts - top += _vm->_font->current()->getHeight() + 1 + 6; - addQuote(_vm->_font->current(), ALIGN_CENTER, -54, top, 1, 0); - addQuote(_vm->_font->current(), ALIGN_CENTER, 54, top, 2, 0); + top += _vm->_font->getHeight() + 1 + 6; + addQuote(_vm->_font, ALIGN_CENTER, -54, top, 1, 0); + addQuote(_vm->_font, ALIGN_CENTER, 54, top, 2, 0); } bool RexOptionsDialog::onEvent(M4EventType eventType, int32 param1, int x, int y, bool &captureEvents) { diff --git a/engines/metaengine.h b/engines/metaengine.h index 7519feaaa4..964eee071c 100644 --- a/engines/metaengine.h +++ b/engines/metaengine.h @@ -231,6 +231,7 @@ private: friend class Common::Singleton<SingletonBaseType>; public: + GameDescriptor findGameOnePlugAtATime(const Common::String &gameName, const EnginePlugin **plugin = NULL) const; GameDescriptor findGame(const Common::String &gameName, const EnginePlugin **plugin = NULL) const; GameList detectGames(const Common::FSList &fslist) const; const EnginePlugin::List &getPlugins() const; diff --git a/engines/mohawk/video/cinepak.cpp b/engines/mohawk/video/cinepak.cpp new file mode 100644 index 0000000000..2ffe6869ae --- /dev/null +++ b/engines/mohawk/video/cinepak.cpp @@ -0,0 +1,286 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "mohawk/video/cinepak.h" + +#include "common/system.h" +#include "graphics/conversion.h" // For YUV2RGB + +// Code here partially based off of ffmpeg ;) + +namespace Mohawk { + +#define PUT_PIXEL(offset, lum, u, v) \ + Graphics::CPYUV2RGB(lum, u, v, r, g, b); \ + if (_pixelFormat.bytesPerPixel == 2) \ + *((uint16 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b); \ + else \ + *((uint32 *)_curFrame.surface->pixels + offset) = _pixelFormat.RGBToColor(r, g, b) + +CinepakDecoder::CinepakDecoder() : Graphics::Codec() { + _curFrame.surface = NULL; + _curFrame.strips = NULL; + _y = 0; + _pixelFormat = g_system->getScreenFormat(); + + // We're going to have to dither if we're running in 8bpp. + // We'll take RGBA8888 for best color performance in this case. + if (_pixelFormat.bytesPerPixel == 1) + _pixelFormat = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); +} + +CinepakDecoder::~CinepakDecoder() { + if (_curFrame.surface) + _curFrame.surface->free(); + delete[] _curFrame.strips; +} + +Graphics::Surface *CinepakDecoder::decodeImage(Common::SeekableReadStream *stream) { + _curFrame.flags = stream->readByte(); + _curFrame.length = (stream->readByte() << 16) + stream->readUint16BE(); + _curFrame.width = stream->readUint16BE(); + _curFrame.height = stream->readUint16BE(); + _curFrame.stripCount = stream->readUint16BE(); + + if (_curFrame.strips == NULL) + _curFrame.strips = new CinepakStrip[_curFrame.stripCount]; + + debug (4, "Cinepak Frame: Width = %d, Height = %d, Strip Count = %d", _curFrame.width, _curFrame.height, _curFrame.stripCount); + +#if 0 + // Borrowed from FFMPEG. This should cut out the extra data Cinepak for Sega has (which is useless). + // The theory behind this is that this is here to confuse standard Cinepak decoders. But, we won't let that happen! ;) + if (_curFrame.length != (uint32)stream->size()) { + if (stream->readUint16BE() == 0xFE00) + stream->readUint32BE(); + } +#endif + + if (!_curFrame.surface) { + _curFrame.surface = new Graphics::Surface(); + _curFrame.surface->create(_curFrame.width, _curFrame.height, _pixelFormat.bytesPerPixel); + } + + // Reset the y variable. + _y = 0; + + for (uint16 i = 0; i < _curFrame.stripCount; i++) { + if (i > 0 && !(_curFrame.flags & 1)) { // Use codebooks from last strip + for (uint16 j = 0; j < 256; j++) { + _curFrame.strips[i].v1_codebook[j] = _curFrame.strips[i - 1].v1_codebook[j]; + _curFrame.strips[i].v4_codebook[j] = _curFrame.strips[i - 1].v4_codebook[j]; + } + } + + _curFrame.strips[i].id = stream->readUint16BE(); + _curFrame.strips[i].length = stream->readUint16BE() - 12; // Subtract the 12 byte header + _curFrame.strips[i].rect.top = _y; stream->readUint16BE(); // Ignore, substitute with our own. + _curFrame.strips[i].rect.left = 0; stream->readUint16BE(); // Ignore, substitute with our own + _curFrame.strips[i].rect.bottom = _y + stream->readUint16BE(); + _curFrame.strips[i].rect.right = _curFrame.width; stream->readUint16BE(); // Ignore, substitute with our own + + //printf ("Left = %d, Top = %d, Right = %d, Bottom = %d\n", _curFrame.strips[i].rect.left, _curFrame.strips[i].rect.top, _curFrame.strips[i].rect.right, _curFrame.strips[i].rect.bottom); + + // Sanity check. Because Cinepak is based on 4x4 blocks, the width and height of each strip needs to be divisible by 4. + assert(!(_curFrame.strips[i].rect.width() % 4) && !(_curFrame.strips[i].rect.height() % 4)); + + uint32 pos = stream->pos(); + + while ((uint32)stream->pos() < (pos + _curFrame.strips[i].length) && !stream->eos()) { + byte chunkID = stream->readByte(); + + if (stream->eos()) + break; + + // Chunk Size is 24-bit, ignore the first 4 bytes + uint32 chunkSize = stream->readByte() << 16; + chunkSize += stream->readUint16BE() - 4; + + int32 startPos = stream->pos(); + + switch (chunkID) { + case 0x20: + case 0x21: + case 0x24: + case 0x25: + loadCodebook(stream, i, 4, chunkID, chunkSize); + break; + case 0x22: + case 0x23: + case 0x26: + case 0x27: + loadCodebook(stream, i, 1, chunkID, chunkSize); + break; + case 0x30: + case 0x31: + case 0x32: + decodeVectors(stream, i, chunkID, chunkSize); + break; + default: + warning("Unknown Cinepak chunk ID %02x", chunkID); + return _curFrame.surface; + } + + if (stream->pos() != startPos + (int32)chunkSize) + stream->seek(startPos + chunkSize); + } + + _y = _curFrame.strips[i].rect.bottom; + } + + return _curFrame.surface; +} + +void CinepakDecoder::loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize) { + CinepakCodebook *codebook = (codebookType == 1) ? _curFrame.strips[strip].v1_codebook : _curFrame.strips[strip].v4_codebook; + + int32 startPos = stream->pos(); + uint32 flag = 0, mask = 0; + + for (uint16 i = 0; i < 256; i++) { + if ((chunkID & 0x01) && !(mask >>= 1)) { + if ((stream->pos() - startPos + 4) > (int32)chunkSize) + break; + + flag = stream->readUint32BE(); + mask = 0x80000000; + } + + if (!(chunkID & 0x01) || (flag & mask)) { + byte n = (chunkID & 0x04) ? 4 : 6; + if ((stream->pos() - startPos + n) > (int32)chunkSize) + break; + + for (byte j = 0; j < 4; j++) + codebook[i].y[j] = stream->readByte(); + + if (n == 6) { + codebook[i].u = stream->readByte() + 128; + codebook[i].v = stream->readByte() + 128; + } else { + /* this codebook type indicates either greyscale or + * palettized video; if palettized, U & V components will + * not be used so it is safe to set them to 128 for the + * benefit of greyscale rendering in YUV420P */ + codebook[i].u = 128; + codebook[i].v = 128; + } + } + } +} + +void CinepakDecoder::decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize) { + uint32 flag = 0, mask = 0; + uint32 iy[4]; + int32 startPos = stream->pos(); + byte r = 0, g = 0, b = 0; + + for (uint16 y = _curFrame.strips[strip].rect.top; y < _curFrame.strips[strip].rect.bottom; y += 4) { + iy[0] = _curFrame.strips[strip].rect.left + y * _curFrame.width; + iy[1] = iy[0] + _curFrame.width; + iy[2] = iy[1] + _curFrame.width; + iy[3] = iy[2] + _curFrame.width; + + for (uint16 x = _curFrame.strips[strip].rect.left; x < _curFrame.strips[strip].rect.right; x += 4) { + if ((chunkID & 0x01) && !(mask >>= 1)) { + if ((stream->pos() - startPos + 4) > (int32)chunkSize) + return; + + flag = stream->readUint32BE(); + mask = 0x80000000; + } + + if (!(chunkID & 0x01) || (flag & mask)) { + if (!(chunkID & 0x02) && !(mask >>= 1)) { + if ((stream->pos() - startPos + 4) > (int32)chunkSize) + return; + + flag = stream->readUint32BE(); + mask = 0x80000000; + } + + if ((chunkID & 0x02) || (~flag & mask)) { + if ((stream->pos() - startPos + 1) > (int32)chunkSize) + return; + + // Get the codebook + CinepakCodebook codebook = _curFrame.strips[strip].v1_codebook[stream->readByte()]; + + PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[0] + 1, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 0, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 1, codebook.y[0], codebook.u, codebook.v); + + PUT_PIXEL(iy[0] + 2, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 2, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 3, codebook.y[1], codebook.u, codebook.v); + + PUT_PIXEL(iy[2] + 0, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[2] + 1, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 1, codebook.y[2], codebook.u, codebook.v); + + PUT_PIXEL(iy[2] + 2, codebook.y[3], codebook.u, codebook.v); + PUT_PIXEL(iy[2] + 3, codebook.y[3], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 2, codebook.y[3], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v); + } else if (flag & mask) { + if ((stream->pos() - startPos + 4) > (int32)chunkSize) + return; + + CinepakCodebook codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; + PUT_PIXEL(iy[0] + 0, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[0] + 1, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 0, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 1, codebook.y[3], codebook.u, codebook.v); + + codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; + PUT_PIXEL(iy[0] + 2, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[0] + 3, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 2, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[1] + 3, codebook.y[3], codebook.u, codebook.v); + + codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; + PUT_PIXEL(iy[2] + 0, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[2] + 1, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 0, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 1, codebook.y[3], codebook.u, codebook.v); + + codebook = _curFrame.strips[strip].v4_codebook[stream->readByte()]; + PUT_PIXEL(iy[2] + 2, codebook.y[0], codebook.u, codebook.v); + PUT_PIXEL(iy[2] + 3, codebook.y[1], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 2, codebook.y[2], codebook.u, codebook.v); + PUT_PIXEL(iy[3] + 3, codebook.y[3], codebook.u, codebook.v); + } + } + + for (byte i = 0; i < 4; i++) + iy[i] += 4; + } + } +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/cinepak.h b/engines/mohawk/video/cinepak.h new file mode 100644 index 0000000000..3f4cbba17c --- /dev/null +++ b/engines/mohawk/video/cinepak.h @@ -0,0 +1,81 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef CINEPAK_H +#define CINEPAK_H + +#include "common/scummsys.h" +#include "common/stream.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "graphics/pixelformat.h" + +#include "graphics/video/codecs/codec.h" + +namespace Mohawk { + +struct CinepakCodebook { + byte y[4]; + byte u, v; +}; + +struct CinepakStrip { + uint16 id; + uint16 length; + Common::Rect rect; + CinepakCodebook v1_codebook[256], v4_codebook[256]; +}; + +struct CinepakFrame { + byte flags; + uint32 length; + uint16 width; + uint16 height; + uint16 stripCount; + CinepakStrip *strips; + + Graphics::Surface *surface; +}; + +class CinepakDecoder : public Graphics::Codec { +public: + CinepakDecoder(); + ~CinepakDecoder(); + + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } + +private: + CinepakFrame _curFrame; + int32 _y; + Graphics::PixelFormat _pixelFormat; + + void loadCodebook(Common::SeekableReadStream *stream, uint16 strip, byte codebookType, byte chunkID, uint32 chunkSize); + void decodeVectors(Common::SeekableReadStream *stream, uint16 strip, byte chunkID, uint32 chunkSize); +}; + +} + +#endif diff --git a/engines/mohawk/video/qdm2.cpp b/engines/mohawk/video/qdm2.cpp new file mode 100644 index 0000000000..b91440f00d --- /dev/null +++ b/engines/mohawk/video/qdm2.cpp @@ -0,0 +1,3063 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// Based off ffmpeg's QDM2 decoder + +#include "mohawk/video/qdm2.h" +#include "mohawk/video/qdm2data.h" + +#include "common/system.h" + +namespace Mohawk { + +// Fix compilation for non C99-compliant compilers, like MSVC +#ifndef int64_t +typedef signed long long int int64_t; +#endif + +// Integer log2 function. This is much faster than invoking +// double precision C99 log2 math functions or equivalent, since +// this is only used to determine maximum number of bits needed +// i.e. only non-fractional part is needed. Also, the double +// version is incorrect for exact cases due to floating point +// rounding errors. +static inline int scummvm_log2(int n) { + int ret = -1; + while(n != 0) { + n /= 2; + ret++; + } + return ret; +} + +#define QDM2_LIST_ADD(list, size, packet) \ + do { \ + if (size > 0) \ + list[size - 1].next = &list[size]; \ + list[size].packet = packet; \ + list[size].next = NULL; \ + size++; \ + } while(0) + +// Result is 8, 16 or 30 +#define QDM2_SB_USED(subSampling) (((subSampling) >= 2) ? 30 : 8 << (subSampling)) + +#define FIX_NOISE_IDX(noiseIdx) \ + if ((noiseIdx) >= 3840) \ + (noiseIdx) -= 3840 \ + +#define SB_DITHERING_NOISE(sb, noiseIdx) (_noiseTable[(noiseIdx)++] * sb_noise_attenuation[(sb)]) + +static inline void initGetBits(GetBitContext *s, const uint8 *buffer, int bitSize) { + int bufferSize = (bitSize + 7) >> 3; + + debug(1, "void initGetBits(GetBitContext *s, const uint8 *buffer, int bitSize)"); + + if (bufferSize < 0 || bitSize < 0) { + bufferSize = bitSize = 0; + buffer = NULL; + } + + s->buffer = buffer; + s->sizeInBits = bitSize; + s->bufferEnd = buffer + bufferSize; + s->index = 0; +} + +static inline int getBitsCount(GetBitContext *s) { + debug(1, "int getBitsCount(GetBitContext *s)"); + return s->index; +} + +static inline unsigned int getBits1(GetBitContext *s) { + int index; + uint8 result; + + debug(1, "unsigned int getBits1(GetBitContext *s)"); + + index = s->index; + result = s->buffer[index >> 3]; + + debug(1, "index : %d", index); + + result >>= (index & 0x07); + result &= 1; + index++; + s->index = index; + + return result; +} + +static inline unsigned int getBits(GetBitContext *s, int n) { + int tmp, reCache, reIndex; + + debug(1, "unsigned int getBits(GetBitContext *s, int n)"); + + reIndex = s->index; + + debug(1, "reIndex : %d", reIndex); + + reCache = READ_LE_UINT32((const uint8 *)s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); + + tmp = (reCache) & ((uint32)0xffffffff >> (32 - n)); + + s->index = reIndex + n; + + return tmp; +} + +static inline void skipBits(GetBitContext *s, int n) { + int reIndex, reCache; + + debug(1, "void skipBits(GetBitContext *s, int n)"); + + reIndex = s->index; + reCache = 0; + + debug(1, "reIndex : %d", reIndex); + + reCache = READ_LE_UINT32((const uint8 *)s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); + s->index = reIndex + n; +} + +#define BITS_LEFT(length, gb) ((length) - getBitsCount((gb))) + +static int splitRadixPermutation(int i, int n, int inverse) { + if (n <= 2) + return i & 1; + + int m = n >> 1; + + if(!(i & m)) + return splitRadixPermutation(i, m, inverse) * 2; + + m >>= 1; + + if (inverse == !(i & m)) + return splitRadixPermutation(i, m, inverse) * 4 + 1; + + return splitRadixPermutation(i, m, inverse) * 4 - 1; +} + +// sin(2*pi*x/n) for 0<=x<n/4, followed by n/2<=x<3n/4 +float ff_sin_16[8]; +float ff_sin_32[16]; +float ff_sin_64[32]; +float ff_sin_128[64]; +float ff_sin_256[128]; +float ff_sin_512[256]; +float ff_sin_1024[512]; +float ff_sin_2048[1024]; +float ff_sin_4096[2048]; +float ff_sin_8192[4096]; +float ff_sin_16384[8192]; +float ff_sin_32768[16384]; +float ff_sin_65536[32768]; + +float *ff_sin_tabs[] = { + NULL, NULL, NULL, NULL, + ff_sin_16, ff_sin_32, ff_sin_64, ff_sin_128, ff_sin_256, ff_sin_512, ff_sin_1024, + ff_sin_2048, ff_sin_4096, ff_sin_8192, ff_sin_16384, ff_sin_32768, ff_sin_65536, +}; + +// cos(2*pi*x/n) for 0<=x<=n/4, followed by its reverse +float ff_cos_16[8]; +float ff_cos_32[16]; +float ff_cos_64[32]; +float ff_cos_128[64]; +float ff_cos_256[128]; +float ff_cos_512[256]; +float ff_cos_1024[512]; +float ff_cos_2048[1024]; +float ff_cos_4096[2048]; +float ff_cos_8192[4096]; +float ff_cos_16384[8192]; +float ff_cos_32768[16384]; +float ff_cos_65536[32768]; + +float *ff_cos_tabs[] = { + NULL, NULL, NULL, NULL, + ff_cos_16, ff_cos_32, ff_cos_64, ff_cos_128, ff_cos_256, ff_cos_512, ff_cos_1024, + ff_cos_2048, ff_cos_4096, ff_cos_8192, ff_cos_16384, ff_cos_32768, ff_cos_65536, +}; + +void initCosineTables(int index) { + int m = 1 << index; + double freq = 2 * PI / m; + float *tab = ff_cos_tabs[index]; + + for (int i = 0; i <= m / 4; i++) + tab[i] = cos(i * freq); + + for (int i = 1; i < m / 4; i++) + tab[m / 2 - i] = tab[i]; +} + +void fftPermute(FFTContext *s, FFTComplex *z) { + const uint16 *revtab = s->revtab; + int np = 1 << s->nbits; + + if (s->tmpBuf) { + // TODO: handle split-radix permute in a more optimal way, probably in-place + for (int j = 0; j < np; j++) + s->tmpBuf[revtab[j]] = z[j]; + memcpy(z, s->tmpBuf, np * sizeof(FFTComplex)); + return; + } + + // reverse + for (int j = 0; j < np; j++) { + int k = revtab[j]; + if (k < j) { + FFTComplex tmp = z[k]; + z[k] = z[j]; + z[j] = tmp; + } + } +} + +#define DECL_FFT(n,n2,n4) \ +static void fft##n(FFTComplex *z) { \ + fft##n2(z); \ + fft##n4(z + n4 * 2); \ + fft##n4(z + n4 * 3); \ + pass(z, ff_cos_##n, n4 / 2); \ +} + +#ifndef M_SQRT1_2 +#define M_SQRT1_2 7.0710678118654752440E-1 +#endif + +#define sqrthalf (float)M_SQRT1_2 + +#define BF(x,y,a,b) { \ + x = a - b; \ + y = a + b; \ +} + +#define BUTTERFLIES(a0, a1, a2, a3) { \ + BF(t3, t5, t5, t1); \ + BF(a2.re, a0.re, a0.re, t5); \ + BF(a3.im, a1.im, a1.im, t3); \ + BF(t4, t6, t2, t6); \ + BF(a3.re, a1.re, a1.re, t4); \ + BF(a2.im, a0.im, a0.im, t6); \ +} + +// force loading all the inputs before storing any. +// this is slightly slower for small data, but avoids store->load aliasing +// for addresses separated by large powers of 2. +#define BUTTERFLIES_BIG(a0, a1, a2, a3) { \ + float r0 = a0.re, i0 = a0.im, r1 = a1.re, i1 = a1.im; \ + BF(t3, t5, t5, t1); \ + BF(a2.re, a0.re, r0, t5); \ + BF(a3.im, a1.im, i1, t3); \ + BF(t4, t6, t2, t6); \ + BF(a3.re, a1.re, r1, t4); \ + BF(a2.im, a0.im, i0, t6); \ +} + +#define TRANSFORM(a0, a1, a2, a3, wre, wim) { \ + t1 = a2.re * wre + a2.im * wim; \ + t2 = a2.im * wre - a2.re * wim; \ + t5 = a3.re * wre - a3.im * wim; \ + t6 = a3.im * wre + a3.re * wim; \ + BUTTERFLIES(a0, a1, a2, a3) \ +} + +#define TRANSFORM_ZERO(a0, a1, a2, a3) { \ + t1 = a2.re; \ + t2 = a2.im; \ + t5 = a3.re; \ + t6 = a3.im; \ + BUTTERFLIES(a0, a1, a2, a3) \ +} + +// z[0...8n-1], w[1...2n-1] +#define PASS(name) \ +static void name(FFTComplex *z, const float *wre, unsigned int n) { \ + float t1, t2, t3, t4, t5, t6; \ + int o1 = 2 * n; \ + int o2 = 4 * n; \ + int o3 = 6 * n; \ + const float *wim = wre + o1; \ + n--; \ + \ + TRANSFORM_ZERO(z[0], z[o1], z[o2], z[o3]); \ + TRANSFORM(z[1], z[o1 + 1], z[o2 + 1], z[o3 + 1], wre[1], wim[-1]); \ + \ + do { \ + z += 2; \ + wre += 2; \ + wim -= 2; \ + TRANSFORM(z[0], z[o1], z[o2], z[o3], wre[0], wim[0]); \ + TRANSFORM(z[1], z[o1 + 1],z[o2 + 1], z[o3 + 1], wre[1], wim[-1]); \ + } while(--n); \ +} + +PASS(pass) +#undef BUTTERFLIES +#define BUTTERFLIES BUTTERFLIES_BIG +PASS(pass_big) + +static void fft4(FFTComplex *z) { + float t1, t2, t3, t4, t5, t6, t7, t8; + + BF(t3, t1, z[0].re, z[1].re); + BF(t8, t6, z[3].re, z[2].re); + BF(z[2].re, z[0].re, t1, t6); + BF(t4, t2, z[0].im, z[1].im); + BF(t7, t5, z[2].im, z[3].im); + BF(z[3].im, z[1].im, t4, t8); + BF(z[3].re, z[1].re, t3, t7); + BF(z[2].im, z[0].im, t2, t5); +} + +static void fft8(FFTComplex *z) { + float t1, t2, t3, t4, t5, t6, t7, t8; + + fft4(z); + + BF(t1, z[5].re, z[4].re, -z[5].re); + BF(t2, z[5].im, z[4].im, -z[5].im); + BF(t3, z[7].re, z[6].re, -z[7].re); + BF(t4, z[7].im, z[6].im, -z[7].im); + BF(t8, t1, t3, t1); + BF(t7, t2, t2, t4); + BF(z[4].re, z[0].re, z[0].re, t1); + BF(z[4].im, z[0].im, z[0].im, t2); + BF(z[6].re, z[2].re, z[2].re, t7); + BF(z[6].im, z[2].im, z[2].im, t8); + + TRANSFORM(z[1], z[3], z[5], z[7], sqrthalf, sqrthalf); +} + +#undef BF + +DECL_FFT(16,8,4) +DECL_FFT(32,16,8) +DECL_FFT(64,32,16) +DECL_FFT(128,64,32) +DECL_FFT(256,128,64) +DECL_FFT(512,256,128) +#define pass pass_big +DECL_FFT(1024,512,256) +DECL_FFT(2048,1024,512) +DECL_FFT(4096,2048,1024) +DECL_FFT(8192,4096,2048) +DECL_FFT(16384,8192,4096) +DECL_FFT(32768,16384,8192) +DECL_FFT(65536,32768,16384) + +void fftCalc(FFTContext *s, FFTComplex *z) { + static void (* const fftDispatch[])(FFTComplex*) = { + fft4, fft8, fft16, fft32, fft64, fft128, fft256, fft512, fft1024, + fft2048, fft4096, fft8192, fft16384, fft32768, fft65536, + }; + + fftDispatch[s->nbits - 2](z); +} + +// complex multiplication: p = a * b +#define CMUL(pre, pim, are, aim, bre, bim) \ +{\ + float _are = (are); \ + float _aim = (aim); \ + float _bre = (bre); \ + float _bim = (bim); \ + (pre) = _are * _bre - _aim * _bim; \ + (pim) = _are * _bim + _aim * _bre; \ +} + +/** + * Compute the middle half of the inverse MDCT of size N = 2^nbits, + * thus excluding the parts that can be derived by symmetry + * @param output N/2 samples + * @param input N/2 samples + */ +void imdctHalfC(FFTContext *s, float *output, const float *input) { + const uint16 *revtab = s->revtab; + const float *tcos = s->tcos; + const float *tsin = s->tsin; + FFTComplex *z = (FFTComplex *)output; + + int n = 1 << s->mdctBits; + int n2 = n >> 1; + int n4 = n >> 2; + int n8 = n >> 3; + + // pre rotation + const float *in1 = input; + const float *in2 = input + n2 - 1; + for (int k = 0; k < n4; k++) { + int j = revtab[k]; + CMUL(z[j].re, z[j].im, *in2, *in1, tcos[k], tsin[k]); + in1 += 2; + in2 -= 2; + } + + fftCalc(s, z); + + // post rotation + reordering + for (int k = 0; k < n8; k++) { + float r0, i0, r1, i1; + CMUL(r0, i1, z[n8 - k - 1].im, z[n8 - k - 1].re, tsin[n8 - k - 1], tcos[n8 - k - 1]); + CMUL(r1, i0, z[n8 + k].im, z[n8 + k].re, tsin[n8 + k], tcos[n8 + k]); + z[n8 - k - 1].re = r0; + z[n8 - k - 1].im = i0; + z[n8 + k].re = r1; + z[n8 + k].im = i1; + } +} + +/** + * Compute inverse MDCT of size N = 2^nbits + * @param output N samples + * @param input N/2 samples + */ +void imdctCalcC(FFTContext *s, float *output, const float *input) { + int n = 1 << s->mdctBits; + int n2 = n >> 1; + int n4 = n >> 2; + + imdctHalfC(s, output + n4, input); + + for (int k = 0; k < n4; k++) { + output[k] = -output[n2 - k - 1]; + output[n - k - 1] = output[n2 + k]; + } +} + +/** + * Compute MDCT of size N = 2^nbits + * @param input N samples + * @param out N/2 samples + */ +void mdctCalcC(FFTContext *s, float *out, const float *input) { + const uint16 *revtab = s->revtab; + const float *tcos = s->tcos; + const float *tsin = s->tsin; + FFTComplex *x = (FFTComplex *)out; + + int n = 1 << s->mdctBits; + int n2 = n >> 1; + int n4 = n >> 2; + int n8 = n >> 3; + int n3 = 3 * n4; + + // pre rotation + for (int i = 0; i < n8; i++) { + float re = -input[2 * i + 3 * n4] - input[n3 - 1 - 2 * i]; + float im = -input[n4 + 2 * i] + input[n4 - 1 - 2 * i]; + int j = revtab[i]; + CMUL(x[j].re, x[j].im, re, im, -tcos[i], tsin[i]); + + re = input[2 * i] - input[n2 - 1 - 2 * i]; + im = -(input[n2 + 2 * i] + input[n - 1 - 2 * i]); + j = revtab[n8 + i]; + CMUL(x[j].re, x[j].im, re, im, -tcos[n8 + i], tsin[n8 + i]); + } + + fftCalc(s, x); + + // post rotation + for (int i = 0; i < n8; i++) { + float r0, i0, r1, i1; + CMUL(i1, r0, x[n8 - i - 1].re, x[n8 - i - 1].im, -tsin[n8 - i - 1], -tcos[n8 - i - 1]); + CMUL(i0, r1, x[n8 + i].re, x[n8 + i].im, -tsin[n8 + i], -tcos[n8 + i]); + x[n8 - i - 1].re = r0; + x[n8 - i - 1].im = i0; + x[n8 + i].re = r1; + x[n8 + i].im = i1; + } +} + +int fftInit(FFTContext *s, int nbits, int inverse) { + int i, j, m, n; + float alpha, c1, s1, s2; + + if (nbits < 2 || nbits > 16) + goto fail; + + s->nbits = nbits; + n = 1 << nbits; + s->tmpBuf = NULL; + + s->exptab = (FFTComplex *)malloc((n / 2) * sizeof(FFTComplex)); + if (!s->exptab) + goto fail; + + s->revtab = (uint16 *)malloc(n * sizeof(uint16)); + if (!s->revtab) + goto fail; + s->inverse = inverse; + + s2 = inverse ? 1.0 : -1.0; + + s->fftPermute = fftPermute; + s->fftCalc = fftCalc; + s->imdctCalc = imdctCalcC; + s->imdctHalf = imdctHalfC; + s->mdctCalc = mdctCalcC; + s->splitRadix = 1; + + if (s->splitRadix) { + for (j = 4; j <= nbits; j++) + initCosineTables(j); + + for (i = 0; i < n; i++) + s->revtab[-splitRadixPermutation(i, n, s->inverse) & (n - 1)] = i; + + s->tmpBuf = (FFTComplex *)malloc(n * sizeof(FFTComplex)); + } else { + for (i = 0; i < n / 2; i++) { + alpha = 2 * PI * (float)i / (float)n; + c1 = cos(alpha); + s1 = sin(alpha) * s2; + s->exptab[i].re = c1; + s->exptab[i].im = s1; + } + + //int np = 1 << nbits; + //int nblocks = np >> 3; + //int np2 = np >> 1; + + // compute bit reverse table + for (i = 0; i < n; i++) { + m = 0; + + for (j = 0; j < nbits; j++) + m |= ((i >> j) & 1) << (nbits - j - 1); + + s->revtab[i] = m; + } + } + + return 0; + + fail: + free(&s->revtab); + free(&s->exptab); + free(&s->tmpBuf); + return -1; +} + +/** + * Sets up a real FFT. + * @param nbits log2 of the length of the input array + * @param trans the type of transform + */ +int rdftInit(RDFTContext *s, int nbits, RDFTransformType trans) { + int n = 1 << nbits; + const double theta = (trans == RDFT || trans == IRIDFT ? -1 : 1) * 2 * PI / n; + + s->nbits = nbits; + s->inverse = trans == IRDFT || trans == IRIDFT; + s->signConvention = trans == RIDFT || trans == IRIDFT ? 1 : -1; + + if (nbits < 4 || nbits > 16) + return -1; + + if (fftInit(&s->fft, nbits - 1, trans == IRDFT || trans == RIDFT) < 0) + return -1; + + initCosineTables(nbits); + s->tcos = ff_cos_tabs[nbits]; + s->tsin = ff_sin_tabs[nbits] + (trans == RDFT || trans == IRIDFT) * (n >> 2); + + for (int i = 0; i < n >> 2; i++) + s->tsin[i] = sin(i*theta); + + return 0; +} + +/** Map one real FFT into two parallel real even and odd FFTs. Then interleave + * the two real FFTs into one complex FFT. Unmangle the results. + * ref: http://www.engineeringproductivitytools.com/stuff/T0001/PT10.HTM + */ +void rdftCalc(RDFTContext *s, float *data) { + FFTComplex ev, od; + + const int n = 1 << s->nbits; + const float k1 = 0.5; + const float k2 = 0.5 - s->inverse; + const float *tcos = s->tcos; + const float *tsin = s->tsin; + + if (!s->inverse) { + fftPermute(&s->fft, (FFTComplex *)data); + fftCalc(&s->fft, (FFTComplex *)data); + } + + // i=0 is a special case because of packing, the DC term is real, so we + // are going to throw the N/2 term (also real) in with it. + ev.re = data[0]; + data[0] = ev.re + data[1]; + data[1] = ev.re - data[1]; + + int i; + + for (i = 1; i < n >> 2; i++) { + int i1 = i * 2; + int i2 = n - i1; + + // Separate even and odd FFTs + ev.re = k1 * (data[i1] + data[i2]); + od.im = -k2 * (data[i1] - data[i2]); + ev.im = k1 * (data[i1 + 1] - data[i2 + 1]); + od.re = k2 * (data[i1 + 1] + data[i2 + 1]); + + // Apply twiddle factors to the odd FFT and add to the even FFT + data[i1] = ev.re + od.re * tcos[i] - od.im * tsin[i]; + data[i1 + 1] = ev.im + od.im * tcos[i] + od.re * tsin[i]; + data[i2] = ev.re - od.re * tcos[i] + od.im * tsin[i]; + data[i2 + 1] = -ev.im + od.im * tcos[i] + od.re * tsin[i]; + } + + data[i * 2 + 1] = s->signConvention * data[i * 2 + 1]; + if (s->inverse) { + data[0] *= k1; + data[1] *= k1; + fftPermute(&s->fft, (FFTComplex*)data); + fftCalc(&s->fft, (FFTComplex*)data); + } +} + +// half mpeg encoding window (full precision) +const int32 ff_mpa_enwindow[257] = { + 0, -1, -1, -1, -1, -1, -1, -2, + -2, -2, -2, -3, -3, -4, -4, -5, + -5, -6, -7, -7, -8, -9, -10, -11, + -13, -14, -16, -17, -19, -21, -24, -26, + -29, -31, -35, -38, -41, -45, -49, -53, + -58, -63, -68, -73, -79, -85, -91, -97, + -104, -111, -117, -125, -132, -139, -147, -154, + -161, -169, -176, -183, -190, -196, -202, -208, + 213, 218, 222, 225, 227, 228, 228, 227, + 224, 221, 215, 208, 200, 189, 177, 163, + 146, 127, 106, 83, 57, 29, -2, -36, + -72, -111, -153, -197, -244, -294, -347, -401, + -459, -519, -581, -645, -711, -779, -848, -919, + -991, -1064, -1137, -1210, -1283, -1356, -1428, -1498, + -1567, -1634, -1698, -1759, -1817, -1870, -1919, -1962, + -2001, -2032, -2057, -2075, -2085, -2087, -2080, -2063, + 2037, 2000, 1952, 1893, 1822, 1739, 1644, 1535, + 1414, 1280, 1131, 970, 794, 605, 402, 185, + -45, -288, -545, -814, -1095, -1388, -1692, -2006, + -2330, -2663, -3004, -3351, -3705, -4063, -4425, -4788, + -5153, -5517, -5879, -6237, -6589, -6935, -7271, -7597, + -7910, -8209, -8491, -8755, -8998, -9219, -9416, -9585, + -9727, -9838, -9916, -9959, -9966, -9935, -9863, -9750, + -9592, -9389, -9139, -8840, -8492, -8092, -7640, -7134, + 6574, 5959, 5288, 4561, 3776, 2935, 2037, 1082, + 70, -998, -2122, -3300, -4533, -5818, -7154, -8540, + -9975,-11455,-12980,-14548,-16155,-17799,-19478,-21189, +-22929,-24694,-26482,-28289,-30112,-31947,-33791,-35640, +-37489,-39336,-41176,-43006,-44821,-46617,-48390,-50137, +-51853,-53534,-55178,-56778,-58333,-59838,-61289,-62684, +-64019,-65290,-66494,-67629,-68692,-69679,-70590,-71420, +-72169,-72835,-73415,-73908,-74313,-74630,-74856,-74992, + 75038 +}; + +void ff_mpa_synth_init(int16 *window) { + int i; + int32 v; + + // max = 18760, max sum over all 16 coefs : 44736 + for(i = 0; i < 257; i++) { + v = ff_mpa_enwindow[i]; + v = (v + 2) >> 2; + window[i] = v; + + if ((i & 63) != 0) + v = -v; + + if (i != 0) + window[512 - i] = v; + } +} + +static inline uint16 round_sample(int *sum) { + int sum1; + sum1 = (*sum) >> 14; + *sum &= (1 << 14)-1; + if (sum1 < (-0x7fff - 1)) + sum1 = (-0x7fff - 1); + if (sum1 > 0x7fff) + sum1 = 0x7fff; + return sum1; +} + +static inline int MULH(int a, int b) { + return ((int64_t)(a) * (int64_t)(b))>>32; +} + +// signed 16x16 -> 32 multiply add accumulate +#define MACS(rt, ra, rb) rt += (ra) * (rb) + +#define MLSS(rt, ra, rb) ((rt) -= (ra) * (rb)) + +#define SUM8(op, sum, w, p)\ +{\ + op(sum, (w)[0 * 64], (p)[0 * 64]);\ + op(sum, (w)[1 * 64], (p)[1 * 64]);\ + op(sum, (w)[2 * 64], (p)[2 * 64]);\ + op(sum, (w)[3 * 64], (p)[3 * 64]);\ + op(sum, (w)[4 * 64], (p)[4 * 64]);\ + op(sum, (w)[5 * 64], (p)[5 * 64]);\ + op(sum, (w)[6 * 64], (p)[6 * 64]);\ + op(sum, (w)[7 * 64], (p)[7 * 64]);\ +} + +#define SUM8P2(sum1, op1, sum2, op2, w1, w2, p) \ +{\ + tmp_s = p[0 * 64];\ + op1(sum1, (w1)[0 * 64], tmp_s);\ + op2(sum2, (w2)[0 * 64], tmp_s);\ + tmp_s = p[1 * 64];\ + op1(sum1, (w1)[1 * 64], tmp_s);\ + op2(sum2, (w2)[1 * 64], tmp_s);\ + tmp_s = p[2 * 64];\ + op1(sum1, (w1)[2 * 64], tmp_s);\ + op2(sum2, (w2)[2 * 64], tmp_s);\ + tmp_s = p[3 * 64];\ + op1(sum1, (w1)[3 * 64], tmp_s);\ + op2(sum2, (w2)[3 * 64], tmp_s);\ + tmp_s = p[4 * 64];\ + op1(sum1, (w1)[4 * 64], tmp_s);\ + op2(sum2, (w2)[4 * 64], tmp_s);\ + tmp_s = p[5 * 64];\ + op1(sum1, (w1)[5 * 64], tmp_s);\ + op2(sum2, (w2)[5 * 64], tmp_s);\ + tmp_s = p[6 * 64];\ + op1(sum1, (w1)[6 * 64], tmp_s);\ + op2(sum2, (w2)[6 * 64], tmp_s);\ + tmp_s = p[7 * 64];\ + op1(sum1, (w1)[7 * 64], tmp_s);\ + op2(sum2, (w2)[7 * 64], tmp_s);\ +} + +#define FIXHR(a) ((int)((a) * (1LL<<32) + 0.5)) + +// tab[i][j] = 1.0 / (2.0 * cos(pi*(2*k+1) / 2^(6 - j))) + +// cos(i*pi/64) + +#define COS0_0 FIXHR(0.50060299823519630134/2) +#define COS0_1 FIXHR(0.50547095989754365998/2) +#define COS0_2 FIXHR(0.51544730992262454697/2) +#define COS0_3 FIXHR(0.53104259108978417447/2) +#define COS0_4 FIXHR(0.55310389603444452782/2) +#define COS0_5 FIXHR(0.58293496820613387367/2) +#define COS0_6 FIXHR(0.62250412303566481615/2) +#define COS0_7 FIXHR(0.67480834145500574602/2) +#define COS0_8 FIXHR(0.74453627100229844977/2) +#define COS0_9 FIXHR(0.83934964541552703873/2) +#define COS0_10 FIXHR(0.97256823786196069369/2) +#define COS0_11 FIXHR(1.16943993343288495515/4) +#define COS0_12 FIXHR(1.48416461631416627724/4) +#define COS0_13 FIXHR(2.05778100995341155085/8) +#define COS0_14 FIXHR(3.40760841846871878570/8) +#define COS0_15 FIXHR(10.19000812354805681150/32) + +#define COS1_0 FIXHR(0.50241928618815570551/2) +#define COS1_1 FIXHR(0.52249861493968888062/2) +#define COS1_2 FIXHR(0.56694403481635770368/2) +#define COS1_3 FIXHR(0.64682178335999012954/2) +#define COS1_4 FIXHR(0.78815462345125022473/2) +#define COS1_5 FIXHR(1.06067768599034747134/4) +#define COS1_6 FIXHR(1.72244709823833392782/4) +#define COS1_7 FIXHR(5.10114861868916385802/16) + +#define COS2_0 FIXHR(0.50979557910415916894/2) +#define COS2_1 FIXHR(0.60134488693504528054/2) +#define COS2_2 FIXHR(0.89997622313641570463/2) +#define COS2_3 FIXHR(2.56291544774150617881/8) + +#define COS3_0 FIXHR(0.54119610014619698439/2) +#define COS3_1 FIXHR(1.30656296487637652785/4) + +#define COS4_0 FIXHR(0.70710678118654752439/2) + +/* butterfly operator */ +#define BF(a, b, c, s)\ +{\ + tmp0 = tab[a] + tab[b];\ + tmp1 = tab[a] - tab[b];\ + tab[a] = tmp0;\ + tab[b] = MULH(tmp1<<(s), c);\ +} + +#define BF1(a, b, c, d)\ +{\ + BF(a, b, COS4_0, 1);\ + BF(c, d,-COS4_0, 1);\ + tab[c] += tab[d];\ +} + +#define BF2(a, b, c, d)\ +{\ + BF(a, b, COS4_0, 1);\ + BF(c, d,-COS4_0, 1);\ + tab[c] += tab[d];\ + tab[a] += tab[c];\ + tab[c] += tab[b];\ + tab[b] += tab[d];\ +} + +#define ADD(a, b) tab[a] += tab[b] + +// DCT32 without 1/sqrt(2) coef zero scaling. +static void dct32(int32 *out, int32 *tab) { + int tmp0, tmp1; + + // pass 1 + BF( 0, 31, COS0_0 , 1); + BF(15, 16, COS0_15, 5); + // pass 2 + BF( 0, 15, COS1_0 , 1); + BF(16, 31,-COS1_0 , 1); + // pass 1 + BF( 7, 24, COS0_7 , 1); + BF( 8, 23, COS0_8 , 1); + // pass 2 + BF( 7, 8, COS1_7 , 4); + BF(23, 24,-COS1_7 , 4); + // pass 3 + BF( 0, 7, COS2_0 , 1); + BF( 8, 15,-COS2_0 , 1); + BF(16, 23, COS2_0 , 1); + BF(24, 31,-COS2_0 , 1); + // pass 1 + BF( 3, 28, COS0_3 , 1); + BF(12, 19, COS0_12, 2); + // pass 2 + BF( 3, 12, COS1_3 , 1); + BF(19, 28,-COS1_3 , 1); + // pass 1 + BF( 4, 27, COS0_4 , 1); + BF(11, 20, COS0_11, 2); + // pass 2 + BF( 4, 11, COS1_4 , 1); + BF(20, 27,-COS1_4 , 1); + // pass 3 + BF( 3, 4, COS2_3 , 3); + BF(11, 12,-COS2_3 , 3); + BF(19, 20, COS2_3 , 3); + BF(27, 28,-COS2_3 , 3); + // pass 4 + BF( 0, 3, COS3_0 , 1); + BF( 4, 7,-COS3_0 , 1); + BF( 8, 11, COS3_0 , 1); + BF(12, 15,-COS3_0 , 1); + BF(16, 19, COS3_0 , 1); + BF(20, 23,-COS3_0 , 1); + BF(24, 27, COS3_0 , 1); + BF(28, 31,-COS3_0 , 1); + + // pass 1 + BF( 1, 30, COS0_1 , 1); + BF(14, 17, COS0_14, 3); + // pass 2 + BF( 1, 14, COS1_1 , 1); + BF(17, 30,-COS1_1 , 1); + // pass 1 + BF( 6, 25, COS0_6 , 1); + BF( 9, 22, COS0_9 , 1); + // pass 2 + BF( 6, 9, COS1_6 , 2); + BF(22, 25,-COS1_6 , 2); + // pass 3 + BF( 1, 6, COS2_1 , 1); + BF( 9, 14,-COS2_1 , 1); + BF(17, 22, COS2_1 , 1); + BF(25, 30,-COS2_1 , 1); + + // pass 1 + BF( 2, 29, COS0_2 , 1); + BF(13, 18, COS0_13, 3); + // pass 2 + BF( 2, 13, COS1_2 , 1); + BF(18, 29,-COS1_2 , 1); + // pass 1 + BF( 5, 26, COS0_5 , 1); + BF(10, 21, COS0_10, 1); + // pass 2 + BF( 5, 10, COS1_5 , 2); + BF(21, 26,-COS1_5 , 2); + // pass 3 + BF( 2, 5, COS2_2 , 1); + BF(10, 13,-COS2_2 , 1); + BF(18, 21, COS2_2 , 1); + BF(26, 29,-COS2_2 , 1); + // pass 4 + BF( 1, 2, COS3_1 , 2); + BF( 5, 6,-COS3_1 , 2); + BF( 9, 10, COS3_1 , 2); + BF(13, 14,-COS3_1 , 2); + BF(17, 18, COS3_1 , 2); + BF(21, 22,-COS3_1 , 2); + BF(25, 26, COS3_1 , 2); + BF(29, 30,-COS3_1 , 2); + + // pass 5 + BF1( 0, 1, 2, 3); + BF2( 4, 5, 6, 7); + BF1( 8, 9, 10, 11); + BF2(12, 13, 14, 15); + BF1(16, 17, 18, 19); + BF2(20, 21, 22, 23); + BF1(24, 25, 26, 27); + BF2(28, 29, 30, 31); + + // pass 6 + ADD( 8, 12); + ADD(12, 10); + ADD(10, 14); + ADD(14, 9); + ADD( 9, 13); + ADD(13, 11); + ADD(11, 15); + + out[ 0] = tab[0]; + out[16] = tab[1]; + out[ 8] = tab[2]; + out[24] = tab[3]; + out[ 4] = tab[4]; + out[20] = tab[5]; + out[12] = tab[6]; + out[28] = tab[7]; + out[ 2] = tab[8]; + out[18] = tab[9]; + out[10] = tab[10]; + out[26] = tab[11]; + out[ 6] = tab[12]; + out[22] = tab[13]; + out[14] = tab[14]; + out[30] = tab[15]; + + ADD(24, 28); + ADD(28, 26); + ADD(26, 30); + ADD(30, 25); + ADD(25, 29); + ADD(29, 27); + ADD(27, 31); + + out[ 1] = tab[16] + tab[24]; + out[17] = tab[17] + tab[25]; + out[ 9] = tab[18] + tab[26]; + out[25] = tab[19] + tab[27]; + out[ 5] = tab[20] + tab[28]; + out[21] = tab[21] + tab[29]; + out[13] = tab[22] + tab[30]; + out[29] = tab[23] + tab[31]; + out[ 3] = tab[24] + tab[20]; + out[19] = tab[25] + tab[21]; + out[11] = tab[26] + tab[22]; + out[27] = tab[27] + tab[23]; + out[ 7] = tab[28] + tab[18]; + out[23] = tab[29] + tab[19]; + out[15] = tab[30] + tab[17]; + out[31] = tab[31]; +} + +// 32 sub band synthesis filter. Input: 32 sub band samples, Output: +// 32 samples. +// XXX: optimize by avoiding ring buffer usage +void ff_mpa_synth_filter(int16 *synth_buf_ptr, int *synth_buf_offset, + int16 *window, int *dither_state, + int16 *samples, int incr, + int32 sb_samples[32]) +{ + int16 *synth_buf; + const int16 *w, *w2, *p; + int j, offset; + int16 *samples2; + int32 tmp[32]; + int sum, sum2; + int tmp_s; + + offset = *synth_buf_offset; + synth_buf = synth_buf_ptr + offset; + + dct32(tmp, sb_samples); + for(j = 0; j < 32; j++) { + // NOTE: can cause a loss in precision if very high amplitude sound + if (tmp[j] < (-0x7fff - 1)) + synth_buf[j] = (-0x7fff - 1); + else if (tmp[j] > 0x7fff) + synth_buf[j] = 0x7fff; + else + synth_buf[j] = tmp[j]; + } + + // copy to avoid wrap + memcpy(synth_buf + 512, synth_buf, 32 * sizeof(int16)); + + samples2 = samples + 31 * incr; + w = window; + w2 = window + 31; + + sum = *dither_state; + p = synth_buf + 16; + SUM8(MACS, sum, w, p); + p = synth_buf + 48; + SUM8(MLSS, sum, w + 32, p); + *samples = round_sample(&sum); + samples += incr; + w++; + + // we calculate two samples at the same time to avoid one memory + // access per two sample + for(j = 1; j < 16; j++) { + sum2 = 0; + p = synth_buf + 16 + j; + SUM8P2(sum, MACS, sum2, MLSS, w, w2, p); + p = synth_buf + 48 - j; + SUM8P2(sum, MLSS, sum2, MLSS, w + 32, w2 + 32, p); + + *samples = round_sample(&sum); + samples += incr; + sum += sum2; + *samples2 = round_sample(&sum); + samples2 -= incr; + w++; + w2--; + } + + p = synth_buf + 32; + SUM8(MLSS, sum, w + 32, p); + *samples = round_sample(&sum); + *dither_state= sum; + + offset = (offset - 32) & 511; + *synth_buf_offset = offset; +} + +/** + * parses a vlc code, faster then get_vlc() + * @param bits is the number of bits which will be read at once, must be + * identical to nb_bits in init_vlc() + * @param max_depth is the number of times bits bits must be read to completely + * read the longest vlc code + * = (max_vlc_length + bits - 1) / bits + */ +static int getVlc2(GetBitContext *s, int16 (*table)[2], int bits, int maxDepth) { + int reIndex; + int reCache; + int index; + int code; + int n; + + debug(1, "int getVlc2(GetBitContext *s, int16 (*table)[2], int bits, int maxDepth)"); + + reIndex = s->index; + reCache = READ_LE_UINT32(s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); + index = reCache & (0xffffffff >> (32 - bits)); + code = table[index][0]; + n = table[index][1]; + + debug(1, "reIndex : %d", reIndex); + debug(1, "reCache : %d", reCache); + debug(1, "index : %d", index); + debug(1, "code : %d", code); + debug(1, "n : %d", n); + + if (maxDepth > 1 && n < 0){ + reIndex += bits; + reCache = READ_LE_UINT32(s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); + + int nbBits = -n; + + index = (reCache & (0xffffffff >> (32 - nbBits))) + code; + code = table[index][0]; + n = table[index][1]; + + if(maxDepth > 2 && n < 0) { + reIndex += nbBits; + reCache = READ_LE_UINT32(s->buffer + (reIndex >> 3)) >> (reIndex & 0x07); + + nbBits = -n; + + index = (reCache & (0xffffffff >> (32 - nbBits))) + code; + code = table[index][0]; + n = table[index][1]; + } + } + + reCache >>= n; + s->index = reIndex + n; + return code; +} + +static int allocTable(VLC *vlc, int size, int use_static) { + int index; + index = vlc->table_size; + vlc->table_size += size; + if (vlc->table_size > vlc->table_allocated) { + if(use_static) + error("QDM2 cant do anything, init_vlc() is used with too little memory"); + vlc->table_allocated += (1 << vlc->bits); + vlc->table = (int16 (*)[2])realloc(vlc->table, sizeof(int16 *) * 2 * vlc->table_allocated); + if (!vlc->table) + return -1; + } + return index; +} + +#define GET_DATA(v, table, i, wrap, size)\ +{\ + const uint8 *ptr = (const uint8 *)table + i * wrap;\ + switch(size) {\ + case 1:\ + v = *(const uint8 *)ptr;\ + break;\ + case 2:\ + v = *(const uint16 *)ptr;\ + break;\ + default:\ + v = *(const uint32 *)ptr;\ + break;\ + }\ +} + +static int build_table(VLC *vlc, int table_nb_bits, + int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, + int code_prefix, int n_prefix, int flags) +{ + int i, j, k, n, table_size, table_index, nb, n1, index, code_prefix2, symbol; + uint32 code; + int16 (*table)[2]; + + table_size = 1 << table_nb_bits; + table_index = allocTable(vlc, table_size, flags & 4); + debug(2, "QDM2 new table index=%d size=%d code_prefix=%x n=%d", table_index, table_size, code_prefix, n_prefix); + if (table_index < 0) + return -1; + table = &vlc->table[table_index]; + + for(i = 0; i < table_size; i++) { + table[i][1] = 0; //bits + table[i][0] = -1; //codes + } + + // first pass: map codes and compute auxillary table sizes + for(i = 0; i < nb_codes; i++) { + GET_DATA(n, bits, i, bits_wrap, bits_size); + GET_DATA(code, codes, i, codes_wrap, codes_size); + // we accept tables with holes + if (n <= 0) + continue; + if (!symbols) + symbol = i; + else + GET_DATA(symbol, symbols, i, symbols_wrap, symbols_size); + debug(2, "QDM2 i=%d n=%d code=0x%x", i, n, code); + // if code matches the prefix, it is in the table + n -= n_prefix; + if(flags & 2) + code_prefix2= code & (n_prefix>=32 ? 0xffffffff : (1 << n_prefix)-1); + else + code_prefix2= code >> n; + if (n > 0 && code_prefix2 == code_prefix) { + if (n <= table_nb_bits) { + // no need to add another table + j = (code << (table_nb_bits - n)) & (table_size - 1); + nb = 1 << (table_nb_bits - n); + for(k = 0; k < nb; k++) { + if(flags & 2) + j = (code >> n_prefix) + (k<<n); + debug(2, "QDM2 %4x: code=%d n=%d",j, i, n); + if (table[j][1] /*bits*/ != 0) { + error("QDM2 incorrect codes"); + return -1; + } + table[j][1] = n; //bits + table[j][0] = symbol; + j++; + } + } else { + n -= table_nb_bits; + j = (code >> ((flags & 2) ? n_prefix : n)) & ((1 << table_nb_bits) - 1); + debug(2, "QDM2 %4x: n=%d (subtable)", j, n); + // compute table size + n1 = -table[j][1]; //bits + if (n > n1) + n1 = n; + table[j][1] = -n1; //bits + } + } + } + + // second pass : fill auxillary tables recursively + for(i = 0;i < table_size; i++) { + n = table[i][1]; //bits + if (n < 0) { + n = -n; + if (n > table_nb_bits) { + n = table_nb_bits; + table[i][1] = -n; //bits + } + index = build_table(vlc, n, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + symbols, symbols_wrap, symbols_size, + (flags & 2) ? (code_prefix | (i << n_prefix)) : ((code_prefix << table_nb_bits) | i), + n_prefix + table_nb_bits, flags); + if (index < 0) + return -1; + // note: realloc has been done, so reload tables + table = &vlc->table[table_index]; + table[i][0] = index; //code + } + } + return table_index; +} + +/* Build VLC decoding tables suitable for use with get_vlc(). + + 'nb_bits' set thee decoding table size (2^nb_bits) entries. The + bigger it is, the faster is the decoding. But it should not be too + big to save memory and L1 cache. '9' is a good compromise. + + 'nb_codes' : number of vlcs codes + + 'bits' : table which gives the size (in bits) of each vlc code. + + 'codes' : table which gives the bit pattern of of each vlc code. + + 'symbols' : table which gives the values to be returned from get_vlc(). + + 'xxx_wrap' : give the number of bytes between each entry of the + 'bits' or 'codes' tables. + + 'xxx_size' : gives the number of bytes of each entry of the 'bits' + or 'codes' tables. + + 'wrap' and 'size' allows to use any memory configuration and types + (byte/word/long) to store the 'bits', 'codes', and 'symbols' tables. + + 'use_static' should be set to 1 for tables, which should be freed + with av_free_static(), 0 if free_vlc() will be used. +*/ +void initVlcSparse(VLC *vlc, int nb_bits, int nb_codes, + const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size) { + vlc->bits = nb_bits; + + if(vlc->table_size && vlc->table_size == vlc->table_allocated) { + return; + } else if(vlc->table_size) { + error("called on a partially initialized table"); + } + + debug(2, "QDM2 build table nb_codes=%d", nb_codes); + + if (build_table(vlc, nb_bits, nb_codes, + bits, bits_wrap, bits_size, + codes, codes_wrap, codes_size, + symbols, symbols_wrap, symbols_size, + 0, 0, 4 | 2) < 0) { + free(&vlc->table); + return; // Error + } + + if(vlc->table_size != vlc->table_allocated) + error("QDM2 needed %d had %d", vlc->table_size, vlc->table_allocated); +} + +void QDM2Stream::softclipTableInit(void) { + uint16 i; + double dfl = SOFTCLIP_THRESHOLD - 32767; + float delta = 1.0 / -dfl; + + for (i = 0; i < ARRAYSIZE(_softclipTable); i++) + _softclipTable[i] = SOFTCLIP_THRESHOLD - ((int)(sin((float)i * delta) * dfl) & 0x0000FFFF); +} + +// random generated table +void QDM2Stream::rndTableInit(void) { + uint16 i; + uint16 j; + uint32 ldw, hdw; + // TODO: Replace Code with uint64 less version... + int64_t tmp64_1; + int64_t random_seed = 0; + float delta = 1.0 / 16384.0; + + for(i = 0; i < ARRAYSIZE(_noiseTable); i++) { + random_seed = random_seed * 214013 + 2531011; + _noiseTable[i] = (delta * (float)(((int32)random_seed >> 16) & 0x00007FFF)- 1.0) * 1.3; + } + + for (i = 0; i < 256; i++) { + random_seed = 81; + ldw = i; + for (j = 0; j < 5; j++) { + _randomDequantIndex[i][j] = (uint8)((ldw / random_seed) & 0xFF); + ldw = (uint32)ldw % (uint32)random_seed; + tmp64_1 = (random_seed * 0x55555556); + hdw = (uint32)(tmp64_1 >> 32); + random_seed = (int64_t)(hdw + (ldw >> 31)); + } + } + + for (i = 0; i < 128; i++) { + random_seed = 25; + ldw = i; + for (j = 0; j < 3; j++) { + _randomDequantType24[i][j] = (uint8)((ldw / random_seed) & 0xFF); + ldw = (uint32)ldw % (uint32)random_seed; + tmp64_1 = (random_seed * 0x66666667); + hdw = (uint32)(tmp64_1 >> 33); + random_seed = hdw + (ldw >> 31); + } + } +} + +void QDM2Stream::initNoiseSamples(void) { + uint16 i; + uint32 random_seed = 0; + float delta = 1.0 / 16384.0; + + for (i = 0; i < ARRAYSIZE(_noiseSamples); i++) { + random_seed = random_seed * 214013 + 2531011; + _noiseSamples[i] = (delta * (float)((random_seed >> 16) & 0x00007fff) - 1.0); + } +} + +static const uint16 qdm2_vlc_offs[18] = { + 0, 260, 566, 598, 894, 1166, 1230, 1294, 1678, 1950, 2214, 2278, 2310, 2570, 2834, 3124, 3448, 3838 +}; + +void QDM2Stream::initVlc(void) { + static int16 qdm2_table[3838][2]; + + if (!_vlcsInitialized) { + _vlcTabLevel.table = &qdm2_table[qdm2_vlc_offs[0]]; + _vlcTabLevel.table_allocated = qdm2_vlc_offs[1] - qdm2_vlc_offs[0]; + _vlcTabLevel.table_size = 0; + initVlcSparse(&_vlcTabLevel, 8, 24, + vlc_tab_level_huffbits, 1, 1, + vlc_tab_level_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabDiff.table = &qdm2_table[qdm2_vlc_offs[1]]; + _vlcTabDiff.table_allocated = qdm2_vlc_offs[2] - qdm2_vlc_offs[1]; + _vlcTabDiff.table_size = 0; + initVlcSparse(&_vlcTabDiff, 8, 37, + vlc_tab_diff_huffbits, 1, 1, + vlc_tab_diff_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabRun.table = &qdm2_table[qdm2_vlc_offs[2]]; + _vlcTabRun.table_allocated = qdm2_vlc_offs[3] - qdm2_vlc_offs[2]; + _vlcTabRun.table_size = 0; + initVlcSparse(&_vlcTabRun, 5, 6, + vlc_tab_run_huffbits, 1, 1, + vlc_tab_run_huffcodes, 1, 1, NULL, 0, 0); + + _fftLevelExpAltVlc.table = &qdm2_table[qdm2_vlc_offs[3]]; + _fftLevelExpAltVlc.table_allocated = qdm2_vlc_offs[4] - qdm2_vlc_offs[3]; + _fftLevelExpAltVlc.table_size = 0; + initVlcSparse(&_fftLevelExpAltVlc, 8, 28, + fft_level_exp_alt_huffbits, 1, 1, + fft_level_exp_alt_huffcodes, 2, 2, NULL, 0, 0); + + _fftLevelExpVlc.table = &qdm2_table[qdm2_vlc_offs[4]]; + _fftLevelExpVlc.table_allocated = qdm2_vlc_offs[5] - qdm2_vlc_offs[4]; + _fftLevelExpVlc.table_size = 0; + initVlcSparse(&_fftLevelExpVlc, 8, 20, + fft_level_exp_huffbits, 1, 1, + fft_level_exp_huffcodes, 2, 2, NULL, 0, 0); + + _fftStereoExpVlc.table = &qdm2_table[qdm2_vlc_offs[5]]; + _fftStereoExpVlc.table_allocated = qdm2_vlc_offs[6] - qdm2_vlc_offs[5]; + _fftStereoExpVlc.table_size = 0; + initVlcSparse(&_fftStereoExpVlc, 6, 7, + fft_stereo_exp_huffbits, 1, 1, + fft_stereo_exp_huffcodes, 1, 1, NULL, 0, 0); + + _fftStereoPhaseVlc.table = &qdm2_table[qdm2_vlc_offs[6]]; + _fftStereoPhaseVlc.table_allocated = qdm2_vlc_offs[7] - qdm2_vlc_offs[6]; + _fftStereoPhaseVlc.table_size = 0; + initVlcSparse(&_fftStereoPhaseVlc, 6, 9, + fft_stereo_phase_huffbits, 1, 1, + fft_stereo_phase_huffcodes, 1, 1, NULL, 0, 0); + + _vlcTabToneLevelIdxHi1.table = &qdm2_table[qdm2_vlc_offs[7]]; + _vlcTabToneLevelIdxHi1.table_allocated = qdm2_vlc_offs[8] - qdm2_vlc_offs[7]; + _vlcTabToneLevelIdxHi1.table_size = 0; + initVlcSparse(&_vlcTabToneLevelIdxHi1, 8, 20, + vlc_tab_tone_level_idx_hi1_huffbits, 1, 1, + vlc_tab_tone_level_idx_hi1_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabToneLevelIdxMid.table = &qdm2_table[qdm2_vlc_offs[8]]; + _vlcTabToneLevelIdxMid.table_allocated = qdm2_vlc_offs[9] - qdm2_vlc_offs[8]; + _vlcTabToneLevelIdxMid.table_size = 0; + initVlcSparse(&_vlcTabToneLevelIdxMid, 8, 24, + vlc_tab_tone_level_idx_mid_huffbits, 1, 1, + vlc_tab_tone_level_idx_mid_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabToneLevelIdxHi2.table = &qdm2_table[qdm2_vlc_offs[9]]; + _vlcTabToneLevelIdxHi2.table_allocated = qdm2_vlc_offs[10] - qdm2_vlc_offs[9]; + _vlcTabToneLevelIdxHi2.table_size = 0; + initVlcSparse(&_vlcTabToneLevelIdxHi2, 8, 24, + vlc_tab_tone_level_idx_hi2_huffbits, 1, 1, + vlc_tab_tone_level_idx_hi2_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabType30.table = &qdm2_table[qdm2_vlc_offs[10]]; + _vlcTabType30.table_allocated = qdm2_vlc_offs[11] - qdm2_vlc_offs[10]; + _vlcTabType30.table_size = 0; + initVlcSparse(&_vlcTabType30, 6, 9, + vlc_tab_type30_huffbits, 1, 1, + vlc_tab_type30_huffcodes, 1, 1, NULL, 0, 0); + + _vlcTabType34.table = &qdm2_table[qdm2_vlc_offs[11]]; + _vlcTabType34.table_allocated = qdm2_vlc_offs[12] - qdm2_vlc_offs[11]; + _vlcTabType34.table_size = 0; + initVlcSparse(&_vlcTabType34, 5, 10, + vlc_tab_type34_huffbits, 1, 1, + vlc_tab_type34_huffcodes, 1, 1, NULL, 0, 0); + + _vlcTabFftToneOffset[0].table = &qdm2_table[qdm2_vlc_offs[12]]; + _vlcTabFftToneOffset[0].table_allocated = qdm2_vlc_offs[13] - qdm2_vlc_offs[12]; + _vlcTabFftToneOffset[0].table_size = 0; + initVlcSparse(&_vlcTabFftToneOffset[0], 8, 23, + vlc_tab_fft_tone_offset_0_huffbits, 1, 1, + vlc_tab_fft_tone_offset_0_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabFftToneOffset[1].table = &qdm2_table[qdm2_vlc_offs[13]]; + _vlcTabFftToneOffset[1].table_allocated = qdm2_vlc_offs[14] - qdm2_vlc_offs[13]; + _vlcTabFftToneOffset[1].table_size = 0; + initVlcSparse(&_vlcTabFftToneOffset[1], 8, 28, + vlc_tab_fft_tone_offset_1_huffbits, 1, 1, + vlc_tab_fft_tone_offset_1_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabFftToneOffset[2].table = &qdm2_table[qdm2_vlc_offs[14]]; + _vlcTabFftToneOffset[2].table_allocated = qdm2_vlc_offs[15] - qdm2_vlc_offs[14]; + _vlcTabFftToneOffset[2].table_size = 0; + initVlcSparse(&_vlcTabFftToneOffset[2], 8, 32, + vlc_tab_fft_tone_offset_2_huffbits, 1, 1, + vlc_tab_fft_tone_offset_2_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabFftToneOffset[3].table = &qdm2_table[qdm2_vlc_offs[15]]; + _vlcTabFftToneOffset[3].table_allocated = qdm2_vlc_offs[16] - qdm2_vlc_offs[15]; + _vlcTabFftToneOffset[3].table_size = 0; + initVlcSparse(&_vlcTabFftToneOffset[3], 8, 35, + vlc_tab_fft_tone_offset_3_huffbits, 1, 1, + vlc_tab_fft_tone_offset_3_huffcodes, 2, 2, NULL, 0, 0); + + _vlcTabFftToneOffset[4].table = &qdm2_table[qdm2_vlc_offs[16]]; + _vlcTabFftToneOffset[4].table_allocated = qdm2_vlc_offs[17] - qdm2_vlc_offs[16]; + _vlcTabFftToneOffset[4].table_size = 0; + initVlcSparse(&_vlcTabFftToneOffset[4], 8, 38, + vlc_tab_fft_tone_offset_4_huffbits, 1, 1, + vlc_tab_fft_tone_offset_4_huffcodes, 2, 2, NULL, 0, 0); + + _vlcsInitialized = true; + } +} + +QDM2Stream::QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData) { + uint32 tmp; + int32 tmp_s; + int tmp_val; + int i; + + debug(1, "QDM2Stream::QDM2Stream() Call"); + + _stream = stream; + _compressedData = NULL; + _subPacket = 0; + memset(_quantizedCoeffs, 0, sizeof(_quantizedCoeffs)); + memset(_fftLevelExp, 0, sizeof(_fftLevelExp)); + _noiseIdx = 0; + memset(_fftCoefsMinIndex, 0, sizeof(_fftCoefsMinIndex)); + memset(_fftCoefsMaxIndex, 0, sizeof(_fftCoefsMaxIndex)); + _fftToneStart = 0; + _fftToneEnd = 0; + for(i = 0; i < ARRAYSIZE(_subPacketListA); i++) { + _subPacketListA[i].packet = NULL; + _subPacketListA[i].next = NULL; + } + _subPacketsB = 0; + for(i = 0; i < ARRAYSIZE(_subPacketListB); i++) { + _subPacketListB[i].packet = NULL; + _subPacketListB[i].next = NULL; + } + for(i = 0; i < ARRAYSIZE(_subPacketListC); i++) { + _subPacketListC[i].packet = NULL; + _subPacketListC[i].next = NULL; + } + for(i = 0; i < ARRAYSIZE(_subPacketListD); i++) { + _subPacketListD[i].packet = NULL; + _subPacketListD[i].next = NULL; + } + memset(_synthBuf, 0, sizeof(_synthBuf)); + memset(_synthBufOffset, 0, sizeof(_synthBufOffset)); + memset(_sbSamples, 0, sizeof(_sbSamples)); + memset(_outputBuffer, 0, sizeof(_outputBuffer)); + _vlcsInitialized = false; + _superblocktype_2_3 = 0; + _hasErrors = false; + + // Rewind extraData stream from any previous calls... + extraData->seek(0, SEEK_SET); + + tmp_s = extraData->readSint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraSize: %d", tmp_s); + if ((extraData->size() - extraData->pos()) / 4 + 1 != tmp_s) + warning("QDM2Stream::QDM2Stream() extraSize mismatch - Expected %d", (extraData->size() - extraData->pos()) / 4 + 1); + if (tmp_s < 12) + error("QDM2Stream::QDM2Stream() Insufficient extraData"); + + tmp = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraTag: %d", tmp); + if (tmp != MKID_BE('frma')) + warning("QDM2Stream::QDM2Stream() extraTag mismatch"); + + tmp = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraType: %d", tmp); + if (tmp == MKID_BE('QDMC')) + warning("QDM2Stream::QDM2Stream() QDMC stream type not supported."); + else if (tmp != MKID_BE('QDM2')) + error("QDM2Stream::QDM2Stream() Unsupported stream type"); + + tmp_s = extraData->readSint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraSize2: %d", tmp_s); + if ((extraData->size() - extraData->pos()) + 4 != tmp_s) + warning("QDM2Stream::QDM2Stream() extraSize2 mismatch - Expected %d", (extraData->size() - extraData->pos()) + 4); + + tmp = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraTag2: %d", tmp); + if (tmp != MKID_BE('QDCA')) + warning("QDM2Stream::QDM2Stream() extraTag2 mismatch"); + + if (extraData->readUint32BE() != 1) + warning("QDM2Stream::QDM2Stream() u0 field not 1"); + + _channels = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() channels: %d", _channels); + + _sampleRate = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() sampleRate: %d", _sampleRate); + + _bitRate = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() bitRate: %d", _bitRate); + + _blockSize = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() blockSize: %d", _blockSize); + + _frameSize = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() frameSize: %d", _frameSize); + + _packetSize = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() packetSize: %d", _packetSize); + + if (extraData->size() - extraData->pos() != 0) { + tmp_s = extraData->readSint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraSize3: %d", tmp_s); + if (extraData->size() + 4 != tmp_s) + warning("QDM2Stream::QDM2Stream() extraSize3 mismatch - Expected %d", extraData->size() + 4); + + tmp = extraData->readUint32BE(); + debug(1, "QDM2Stream::QDM2Stream() extraTag3: %d", tmp); + if (tmp != MKID_BE('QDCP')) + warning("QDM2Stream::QDM2Stream() extraTag3 mismatch"); + + if ((float)extraData->readUint32BE() != 1.0) + warning("QDM2Stream::QDM2Stream() uf0 field not 1.0"); + + if (extraData->readUint32BE() != 0) + warning("QDM2Stream::QDM2Stream() u1 field not 0"); + + if ((float)extraData->readUint32BE() != 1.0) + warning("QDM2Stream::QDM2Stream() uf1 field not 1.0"); + + if ((float)extraData->readUint32BE() != 1.0) + warning("QDM2Stream::QDM2Stream() uf2 field not 1.0"); + + if (extraData->readUint32BE() != 27) + warning("QDM2Stream::QDM2Stream() u2 field not 27"); + + if (extraData->readUint32BE() != 8) + warning("QDM2Stream::QDM2Stream() u3 field not 8"); + + if (extraData->readUint32BE() != 0) + warning("QDM2Stream::QDM2Stream() u4 field not 0"); + } + + _fftOrder = scummvm_log2(_frameSize) + 1; + _fftFrameSize = 2 * _frameSize; // complex has two floats + + // something like max decodable tones + _groupOrder = scummvm_log2(_blockSize) + 1; + _sFrameSize = _blockSize / 16; // 16 iterations per super block + + _subSampling = _fftOrder - 7; + _frequencyRange = 255 / (1 << (2 - _subSampling)); + + switch ((_subSampling * 2 + _channels - 1)) { + case 0: + tmp = 40; + break; + case 1: + tmp = 48; + break; + case 2: + tmp = 56; + break; + case 3: + tmp = 72; + break; + case 4: + tmp = 80; + break; + case 5: + tmp = 100; + break; + default: + tmp = _subSampling; + break; + } + + tmp_val = 0; + if ((tmp * 1000) < _bitRate) tmp_val = 1; + if ((tmp * 1440) < _bitRate) tmp_val = 2; + if ((tmp * 1760) < _bitRate) tmp_val = 3; + if ((tmp * 2240) < _bitRate) tmp_val = 4; + _cmTableSelect = tmp_val; + + if (_subSampling == 0) + tmp = 7999; + else + tmp = ((-(_subSampling -1)) & 8000) + 20000; + + if (tmp < 8000) + _coeffPerSbSelect = 0; + else if (tmp <= 16000) + _coeffPerSbSelect = 1; + else + _coeffPerSbSelect = 2; + + if (_fftOrder < 7 || _fftOrder > 9) + error("QDM2Stream::QDM2Stream() Unsupported fft_order: %d", _fftOrder); + + rdftInit(&_rdftCtx, _fftOrder, IRDFT); + + initVlc(); + ff_mpa_synth_init(ff_mpa_synth_window); + softclipTableInit(); + rndTableInit(); + initNoiseSamples(); + + _compressedData = new uint8[_packetSize]; +} + +QDM2Stream::~QDM2Stream() { + delete[] _compressedData; + delete _stream; +} + +static int qdm2_get_vlc(GetBitContext *gb, VLC *vlc, int flag, int depth) { + int value = getVlc2(gb, vlc->table, vlc->bits, depth); + + // stage-2, 3 bits exponent escape sequence + if (value-- == 0) + value = getBits(gb, getBits (gb, 3) + 1); + + // stage-3, optional + if (flag) { + int tmp = vlc_stage3_values[value]; + + if ((value & ~3) > 0) + tmp += getBits(gb, (value >> 2)); + value = tmp; + } + + return value; +} + +static int qdm2_get_se_vlc(VLC *vlc, GetBitContext *gb, int depth) +{ + int value = qdm2_get_vlc(gb, vlc, 0, depth); + + return (value & 1) ? ((value + 1) >> 1) : -(value >> 1); +} + +/** + * QDM2 checksum + * + * @param data pointer to data to be checksum'ed + * @param length data length + * @param value checksum value + * + * @return 0 if checksum is OK + */ +static uint16 qdm2_packet_checksum(const uint8 *data, int length, int value) { + int i; + + for (i = 0; i < length; i++) + value -= data[i]; + + return (uint16)(value & 0xffff); +} + +/** + * Fills a QDM2SubPacket structure with packet type, size, and data pointer. + * + * @param gb bitreader context + * @param sub_packet packet under analysis + */ +static void qdm2_decode_sub_packet_header(GetBitContext *gb, QDM2SubPacket *sub_packet) +{ + sub_packet->type = getBits (gb, 8); + + if (sub_packet->type == 0) { + sub_packet->size = 0; + sub_packet->data = NULL; + } else { + sub_packet->size = getBits (gb, 8); + + if (sub_packet->type & 0x80) { + sub_packet->size <<= 8; + sub_packet->size |= getBits (gb, 8); + sub_packet->type &= 0x7f; + } + + if (sub_packet->type == 0x7f) + sub_packet->type |= (getBits (gb, 8) << 8); + + sub_packet->data = &gb->buffer[getBitsCount(gb) / 8]; // FIXME: this depends on bitreader internal data + } + + debug(1, "QDM2 Subpacket: type=%d size=%d start_offs=%x", sub_packet->type, sub_packet->size, getBitsCount(gb) / 8); +} + +/** + * Return node pointer to first packet of requested type in list. + * + * @param list list of subpackets to be scanned + * @param type type of searched subpacket + * @return node pointer for subpacket if found, else NULL + */ +static QDM2SubPNode* qdm2_search_subpacket_type_in_list(QDM2SubPNode *list, int type) +{ + while (list != NULL && list->packet != NULL) { + if (list->packet->type == type) + return list; + list = list->next; + } + return NULL; +} + +/** + * Replaces 8 elements with their average value. + * Called by qdm2_decode_superblock before starting subblock decoding. + */ +void QDM2Stream::average_quantized_coeffs(void) { + int i, j, n, ch, sum; + + n = coeff_per_sb_for_avg[_coeffPerSbSelect][QDM2_SB_USED(_subSampling) - 1] + 1; + + for (ch = 0; ch < _channels; ch++) { + for (i = 0; i < n; i++) { + sum = 0; + + for (j = 0; j < 8; j++) + sum += _quantizedCoeffs[ch][i][j]; + + sum /= 8; + if (sum > 0) + sum--; + + for (j = 0; j < 8; j++) + _quantizedCoeffs[ch][i][j] = sum; + } + } +} + +/** + * Build subband samples with noise weighted by q->tone_level. + * Called by synthfilt_build_sb_samples. + * + * @param sb subband index + */ +void QDM2Stream::build_sb_samples_from_noise(int sb) { + int ch, j; + + FIX_NOISE_IDX(_noiseIdx); + + if (!_channels) + return; + + for (ch = 0; ch < _channels; ch++) { + for (j = 0; j < 64; j++) { + _sbSamples[ch][j * 2][sb] = (int32)(SB_DITHERING_NOISE(sb, _noiseIdx) * _toneLevel[ch][sb][j] + .5); + _sbSamples[ch][j * 2 + 1][sb] = (int32)(SB_DITHERING_NOISE(sb, _noiseIdx) * _toneLevel[ch][sb][j] + .5); + } + } +} + +/** + * Called while processing data from subpackets 11 and 12. + * Used after making changes to coding_method array. + * + * @param sb subband index + * @param channels number of channels + * @param coding_method q->coding_method[0][0][0] + */ +void QDM2Stream::fix_coding_method_array(int sb, int channels, sb_int8_array coding_method) +{ + int j, k; + int ch; + int run, case_val; + int switchtable[23] = {0,5,1,5,5,5,5,5,2,5,5,5,5,5,5,5,3,5,5,5,5,5,4}; + + for (ch = 0; ch < channels; ch++) { + for (j = 0; j < 64; ) { + if((coding_method[ch][sb][j] - 8) > 22) { + run = 1; + case_val = 8; + } else { + switch (switchtable[coding_method[ch][sb][j]-8]) { + case 0: run = 10; case_val = 10; break; + case 1: run = 1; case_val = 16; break; + case 2: run = 5; case_val = 24; break; + case 3: run = 3; case_val = 30; break; + case 4: run = 1; case_val = 30; break; + case 5: run = 1; case_val = 8; break; + default: run = 1; case_val = 8; break; + } + } + for (k = 0; k < run; k++) + if (j + k < 128) + if (coding_method[ch][sb + (j + k) / 64][(j + k) % 64] > coding_method[ch][sb][j]) + if (k > 0) { + warning("QDM2 Untested Code: not debugged, almost never used"); + memset(&coding_method[ch][sb][j + k], case_val, k * sizeof(int8)); + memset(&coding_method[ch][sb][j + k], case_val, 3 * sizeof(int8)); + } + j += run; + } + } +} + +/** + * Related to synthesis filter + * Called by process_subpacket_10 + * + * @param flag 1 if called after getting data from subpacket 10, 0 if no subpacket 10 + */ +void QDM2Stream::fill_tone_level_array(int flag) { + int i, sb, ch, sb_used; + int tmp, tab; + + // This should never happen + if (_channels <= 0) + return; + + for (ch = 0; ch < _channels; ch++) { + for (sb = 0; sb < 30; sb++) { + for (i = 0; i < 8; i++) { + if ((tab=coeff_per_sb_for_dequant[_coeffPerSbSelect][sb]) < (last_coeff[_coeffPerSbSelect] - 1)) + tmp = _quantizedCoeffs[ch][tab + 1][i] * dequant_table[_coeffPerSbSelect][tab + 1][sb]+ + _quantizedCoeffs[ch][tab][i] * dequant_table[_coeffPerSbSelect][tab][sb]; + else + tmp = _quantizedCoeffs[ch][tab][i] * dequant_table[_coeffPerSbSelect][tab][sb]; + if(tmp < 0) + tmp += 0xff; + _toneLevelIdxBase[ch][sb][i] = (tmp / 256) & 0xff; + } + } + } + + sb_used = QDM2_SB_USED(_subSampling); + + if ((_superblocktype_2_3 != 0) && !flag) { + for (sb = 0; sb < sb_used; sb++) { + for (ch = 0; ch < _channels; ch++) { + for (i = 0; i < 64; i++) { + _toneLevelIdx[ch][sb][i] = _toneLevelIdxBase[ch][sb][i / 8]; + if (_toneLevelIdx[ch][sb][i] < 0) + _toneLevel[ch][sb][i] = 0; + else + _toneLevel[ch][sb][i] = fft_tone_level_table[0][_toneLevelIdx[ch][sb][i] & 0x3f]; + } + } + } + } else { + tab = _superblocktype_2_3 ? 0 : 1; + for (sb = 0; sb < sb_used; sb++) { + if ((sb >= 4) && (sb <= 23)) { + for (ch = 0; ch < _channels; ch++) { + for (i = 0; i < 64; i++) { + tmp = _toneLevelIdxBase[ch][sb][i / 8] - + _toneLevelIdxHi1[ch][sb / 8][i / 8][i % 8] - + _toneLevelIdxMid[ch][sb - 4][i / 8] - + _toneLevelIdxHi2[ch][sb - 4]; + _toneLevelIdx[ch][sb][i] = tmp & 0xff; + if ((tmp < 0) || (!_superblocktype_2_3 && !tmp)) + _toneLevel[ch][sb][i] = 0; + else + _toneLevel[ch][sb][i] = fft_tone_level_table[tab][tmp & 0x3f]; + } + } + } else { + if (sb > 4) { + for (ch = 0; ch < _channels; ch++) { + for (i = 0; i < 64; i++) { + tmp = _toneLevelIdxBase[ch][sb][i / 8] - + _toneLevelIdxHi1[ch][2][i / 8][i % 8] - + _toneLevelIdxHi2[ch][sb - 4]; + _toneLevelIdx[ch][sb][i] = tmp & 0xff; + if ((tmp < 0) || (!_superblocktype_2_3 && !tmp)) + _toneLevel[ch][sb][i] = 0; + else + _toneLevel[ch][sb][i] = fft_tone_level_table[tab][tmp & 0x3f]; + } + } + } else { + for (ch = 0; ch < _channels; ch++) { + for (i = 0; i < 64; i++) { + tmp = _toneLevelIdx[ch][sb][i] = _toneLevelIdxBase[ch][sb][i / 8]; + if ((tmp < 0) || (!_superblocktype_2_3 && !tmp)) + _toneLevel[ch][sb][i] = 0; + else + _toneLevel[ch][sb][i] = fft_tone_level_table[tab][tmp & 0x3f]; + } + } + } + } + } + } +} + +/** + * Related to synthesis filter + * Called by process_subpacket_11 + * c is built with data from subpacket 11 + * Most of this function is used only if superblock_type_2_3 == 0, never seen it in samples + * + * @param tone_level_idx + * @param tone_level_idx_temp + * @param coding_method q->coding_method[0][0][0] + * @param nb_channels number of channels + * @param c coming from subpacket 11, passed as 8*c + * @param superblocktype_2_3 flag based on superblock packet type + * @param cm_table_select q->cm_table_select + */ +void QDM2Stream::fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, + sb_int8_array coding_method, int nb_channels, + int c, int superblocktype_2_3, int cm_table_select) { + int ch, sb, j; + int tmp, acc, esp_40, comp; + int add1, add2, add3, add4; + // TODO : Remove multres 64 bit variable necessity... + int64_t multres; + + // This should never happen + if (nb_channels <= 0) + return; + if (!superblocktype_2_3) { + warning("QDM2 This case is untested, no samples available"); + for (ch = 0; ch < nb_channels; ch++) + for (sb = 0; sb < 30; sb++) { + for (j = 1; j < 63; j++) { // The loop only iterates to 63 so the code doesn't overflow the buffer + add1 = tone_level_idx[ch][sb][j] - 10; + if (add1 < 0) + add1 = 0; + add2 = add3 = add4 = 0; + if (sb > 1) { + add2 = tone_level_idx[ch][sb - 2][j] + tone_level_idx_offset_table[sb][0] - 6; + if (add2 < 0) + add2 = 0; + } + if (sb > 0) { + add3 = tone_level_idx[ch][sb - 1][j] + tone_level_idx_offset_table[sb][1] - 6; + if (add3 < 0) + add3 = 0; + } + if (sb < 29) { + add4 = tone_level_idx[ch][sb + 1][j] + tone_level_idx_offset_table[sb][3] - 6; + if (add4 < 0) + add4 = 0; + } + tmp = tone_level_idx[ch][sb][j + 1] * 2 - add4 - add3 - add2 - add1; + if (tmp < 0) + tmp = 0; + tone_level_idx_temp[ch][sb][j + 1] = tmp & 0xff; + } + tone_level_idx_temp[ch][sb][0] = tone_level_idx_temp[ch][sb][1]; + } + acc = 0; + for (ch = 0; ch < nb_channels; ch++) + for (sb = 0; sb < 30; sb++) + for (j = 0; j < 64; j++) + acc += tone_level_idx_temp[ch][sb][j]; + + multres = 0x66666667 * (acc * 10); + esp_40 = (multres >> 32) / 8 + ((multres & 0xffffffff) >> 31); + for (ch = 0; ch < nb_channels; ch++) + for (sb = 0; sb < 30; sb++) + for (j = 0; j < 64; j++) { + comp = tone_level_idx_temp[ch][sb][j]* esp_40 * 10; + if (comp < 0) + comp += 0xff; + comp /= 256; // signed shift + switch(sb) { + case 0: + if (comp < 30) + comp = 30; + comp += 15; + break; + case 1: + if (comp < 24) + comp = 24; + comp += 10; + break; + case 2: + case 3: + case 4: + if (comp < 16) + comp = 16; + } + if (comp <= 5) + tmp = 0; + else if (comp <= 10) + tmp = 10; + else if (comp <= 16) + tmp = 16; + else if (comp <= 24) + tmp = -1; + else + tmp = 0; + coding_method[ch][sb][j] = ((tmp & 0xfffa) + 30 )& 0xff; + } + for (sb = 0; sb < 30; sb++) + fix_coding_method_array(sb, nb_channels, coding_method); + for (ch = 0; ch < nb_channels; ch++) + for (sb = 0; sb < 30; sb++) + for (j = 0; j < 64; j++) + if (sb >= 10) { + if (coding_method[ch][sb][j] < 10) + coding_method[ch][sb][j] = 10; + } else { + if (sb >= 2) { + if (coding_method[ch][sb][j] < 16) + coding_method[ch][sb][j] = 16; + } else { + if (coding_method[ch][sb][j] < 30) + coding_method[ch][sb][j] = 30; + } + } + } else { // superblocktype_2_3 != 0 + for (ch = 0; ch < nb_channels; ch++) + for (sb = 0; sb < 30; sb++) + for (j = 0; j < 64; j++) + coding_method[ch][sb][j] = coding_method_table[cm_table_select][sb]; + } +} + +/** + * + * Called by process_subpacket_11 to process more data from subpacket 11 with sb 0-8 + * Called by process_subpacket_12 to process data from subpacket 12 with sb 8-sb_used + * + * @param gb bitreader context + * @param length packet length in bits + * @param sb_min lower subband processed (sb_min included) + * @param sb_max higher subband processed (sb_max excluded) + */ +void QDM2Stream::synthfilt_build_sb_samples(GetBitContext *gb, int length, int sb_min, int sb_max) { + int sb, j, k, n, ch, run, channels; + int joined_stereo, zero_encoding, chs; + int type34_first; + float type34_div = 0; + float type34_predictor; + float samples[10], sign_bits[16]; + + if (length == 0) { + // If no data use noise + for (sb = sb_min; sb < sb_max; sb++) + build_sb_samples_from_noise(sb); + + return; + } + + for (sb = sb_min; sb < sb_max; sb++) { + FIX_NOISE_IDX(_noiseIdx); + + channels = _channels; + + if (_channels <= 1 || sb < 12) + joined_stereo = 0; + else if (sb >= 24) + joined_stereo = 1; + else + joined_stereo = (BITS_LEFT(length,gb) >= 1) ? getBits1 (gb) : 0; + + if (joined_stereo) { + if (BITS_LEFT(length,gb) >= 16) + for (j = 0; j < 16; j++) + sign_bits[j] = getBits1(gb); + + for (j = 0; j < 64; j++) + if (_codingMethod[1][sb][j] > _codingMethod[0][sb][j]) + _codingMethod[0][sb][j] = _codingMethod[1][sb][j]; + + fix_coding_method_array(sb, _channels, _codingMethod); + channels = 1; + } + + for (ch = 0; ch < channels; ch++) { + zero_encoding = (BITS_LEFT(length,gb) >= 1) ? getBits1(gb) : 0; + type34_predictor = 0.0; + type34_first = 1; + + for (j = 0; j < 128; ) { + switch (_codingMethod[ch][sb][j / 2]) { + case 8: + if (BITS_LEFT(length,gb) >= 10) { + if (zero_encoding) { + for (k = 0; k < 5; k++) { + if ((j + 2 * k) >= 128) + break; + samples[2 * k] = getBits1(gb) ? dequant_1bit[joined_stereo][2 * getBits1(gb)] : 0; + } + } else { + n = getBits(gb, 8); + for (k = 0; k < 5; k++) + samples[2 * k] = dequant_1bit[joined_stereo][_randomDequantIndex[n][k]]; + } + for (k = 0; k < 5; k++) + samples[2 * k + 1] = SB_DITHERING_NOISE(sb, _noiseIdx); + } else { + for (k = 0; k < 10; k++) + samples[k] = SB_DITHERING_NOISE(sb, _noiseIdx); + } + run = 10; + break; + + case 10: + if (BITS_LEFT(length,gb) >= 1) { + double f = 0.81; + + if (getBits1(gb)) + f = -f; + f -= _noiseSamples[((sb + 1) * (j +5 * ch + 1)) & 127] * 9.0 / 40.0; + samples[0] = f; + } else { + samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); + } + run = 1; + break; + + case 16: + if (BITS_LEFT(length,gb) >= 10) { + if (zero_encoding) { + for (k = 0; k < 5; k++) { + if ((j + k) >= 128) + break; + samples[k] = (getBits1(gb) == 0) ? 0 : dequant_1bit[joined_stereo][2 * getBits1(gb)]; + } + } else { + n = getBits (gb, 8); + for (k = 0; k < 5; k++) + samples[k] = dequant_1bit[joined_stereo][_randomDequantIndex[n][k]]; + } + } else { + for (k = 0; k < 5; k++) + samples[k] = SB_DITHERING_NOISE(sb, _noiseIdx); + } + run = 5; + break; + + case 24: + if (BITS_LEFT(length,gb) >= 7) { + n = getBits(gb, 7); + for (k = 0; k < 3; k++) + samples[k] = (_randomDequantType24[n][k] - 2.0) * 0.5; + } else { + for (k = 0; k < 3; k++) + samples[k] = SB_DITHERING_NOISE(sb, _noiseIdx); + } + run = 3; + break; + + case 30: + if (BITS_LEFT(length,gb) >= 4) + samples[0] = type30_dequant[qdm2_get_vlc(gb, &_vlcTabType30, 0, 1)]; + else + samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); + + run = 1; + break; + + case 34: + if (BITS_LEFT(length,gb) >= 7) { + if (type34_first) { + type34_div = (float)(1 << getBits(gb, 2)); + samples[0] = ((float)getBits(gb, 5) - 16.0) / 15.0; + type34_predictor = samples[0]; + type34_first = 0; + } else { + samples[0] = type34_delta[qdm2_get_vlc(gb, &_vlcTabType34, 0, 1)] / type34_div + type34_predictor; + type34_predictor = samples[0]; + } + } else { + samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); + } + run = 1; + break; + + default: + samples[0] = SB_DITHERING_NOISE(sb, _noiseIdx); + run = 1; + break; + } + + if (joined_stereo) { + float tmp[10][MPA_MAX_CHANNELS]; + + for (k = 0; k < run; k++) { + tmp[k][0] = samples[k]; + tmp[k][1] = (sign_bits[(j + k) / 8]) ? -samples[k] : samples[k]; + } + for (chs = 0; chs < _channels; chs++) + for (k = 0; k < run; k++) + if ((j + k) < 128) + _sbSamples[chs][j + k][sb] = (int32)(_toneLevel[chs][sb][((j + k)/2)] * tmp[k][chs] + .5); + } else { + for (k = 0; k < run; k++) + if ((j + k) < 128) + _sbSamples[ch][j + k][sb] = (int32)(_toneLevel[ch][sb][(j + k)/2] * samples[k] + .5); + } + + j += run; + } // j loop + } // channel loop + } // subband loop +} + +/** + * Init the first element of a channel in quantized_coeffs with data from packet 10 (quantized_coeffs[ch][0]). + * This is similar to process_subpacket_9, but for a single channel and for element [0] + * same VLC tables as process_subpacket_9 are used. + * + * @param quantized_coeffs pointer to quantized_coeffs[ch][0] + * @param gb bitreader context + * @param length packet length in bits + */ +void QDM2Stream::init_quantized_coeffs_elem0(int8 *quantized_coeffs, GetBitContext *gb, int length) { + int i, k, run, level, diff; + + if (BITS_LEFT(length,gb) < 16) + return; + level = qdm2_get_vlc(gb, &_vlcTabLevel, 0, 2); + + quantized_coeffs[0] = level; + + for (i = 0; i < 7; ) { + if (BITS_LEFT(length,gb) < 16) + break; + run = qdm2_get_vlc(gb, &_vlcTabRun, 0, 1) + 1; + + if (BITS_LEFT(length,gb) < 16) + break; + diff = qdm2_get_se_vlc(&_vlcTabDiff, gb, 2); + + for (k = 1; k <= run; k++) + quantized_coeffs[i + k] = (level + ((k * diff) / run)); + + level += diff; + i += run; + } +} + +/** + * Related to synthesis filter, process data from packet 10 + * Init part of quantized_coeffs via function init_quantized_coeffs_elem0 + * Init tone_level_idx_hi1, tone_level_idx_hi2, tone_level_idx_mid with data from packet 10 + * + * @param gb bitreader context + * @param length packet length in bits + */ +void QDM2Stream::init_tone_level_dequantization(GetBitContext *gb, int length) { + int sb, j, k, n, ch; + + for (ch = 0; ch < _channels; ch++) { + init_quantized_coeffs_elem0(_quantizedCoeffs[ch][0], gb, length); + + if (BITS_LEFT(length,gb) < 16) { + memset(_quantizedCoeffs[ch][0], 0, 8); + break; + } + } + + n = _subSampling + 1; + + for (sb = 0; sb < n; sb++) + for (ch = 0; ch < _channels; ch++) + for (j = 0; j < 8; j++) { + if (BITS_LEFT(length,gb) < 1) + break; + if (getBits1(gb)) { + for (k=0; k < 8; k++) { + if (BITS_LEFT(length,gb) < 16) + break; + _toneLevelIdxHi1[ch][sb][j][k] = qdm2_get_vlc(gb, &_vlcTabToneLevelIdxHi1, 0, 2); + } + } else { + for (k=0; k < 8; k++) + _toneLevelIdxHi1[ch][sb][j][k] = 0; + } + } + + n = QDM2_SB_USED(_subSampling) - 4; + + for (sb = 0; sb < n; sb++) + for (ch = 0; ch < _channels; ch++) { + if (BITS_LEFT(length,gb) < 16) + break; + _toneLevelIdxHi2[ch][sb] = qdm2_get_vlc(gb, &_vlcTabToneLevelIdxHi2, 0, 2); + if (sb > 19) + _toneLevelIdxHi2[ch][sb] -= 16; + else + for (j = 0; j < 8; j++) + _toneLevelIdxMid[ch][sb][j] = -16; + } + + n = QDM2_SB_USED(_subSampling) - 5; + + for (sb = 0; sb < n; sb++) { + for (ch = 0; ch < _channels; ch++) { + for (j = 0; j < 8; j++) { + if (BITS_LEFT(length,gb) < 16) + break; + _toneLevelIdxMid[ch][sb][j] = qdm2_get_vlc(gb, &_vlcTabToneLevelIdxMid, 0, 2) - 32; + } + } + } +} + +/** + * Process subpacket 9, init quantized_coeffs with data from it + * + * @param node pointer to node with packet + */ +void QDM2Stream::process_subpacket_9(QDM2SubPNode *node) { + GetBitContext gb; + int i, j, k, n, ch, run, level, diff; + + initGetBits(&gb, node->packet->data, node->packet->size*8); + + n = coeff_per_sb_for_avg[_coeffPerSbSelect][QDM2_SB_USED(_subSampling) - 1] + 1; // same as averagesomething function + + for (i = 1; i < n; i++) + for (ch = 0; ch < _channels; ch++) { + level = qdm2_get_vlc(&gb, &_vlcTabLevel, 0, 2); + _quantizedCoeffs[ch][i][0] = level; + + for (j = 0; j < (8 - 1); ) { + run = qdm2_get_vlc(&gb, &_vlcTabRun, 0, 1) + 1; + diff = qdm2_get_se_vlc(&_vlcTabDiff, &gb, 2); + + for (k = 1; k <= run; k++) + _quantizedCoeffs[ch][i][j + k] = (level + ((k*diff) / run)); + + level += diff; + j += run; + } + } + + for (ch = 0; ch < _channels; ch++) + for (i = 0; i < 8; i++) + _quantizedCoeffs[ch][0][i] = 0; +} + +/** + * Process subpacket 10 if not null, else + * + * @param node pointer to node with packet + * @param length packet length in bits + */ +void QDM2Stream::process_subpacket_10(QDM2SubPNode *node, int length) { + GetBitContext gb; + + initGetBits(&gb, ((node == NULL) ? _emptyBuffer : node->packet->data), ((node == NULL) ? 0 : node->packet->size*8)); + + if (length != 0) { + init_tone_level_dequantization(&gb, length); + fill_tone_level_array(1); + } else { + fill_tone_level_array(0); + } +} + +/** + * Process subpacket 11 + * + * @param node pointer to node with packet + * @param length packet length in bit + */ +void QDM2Stream::process_subpacket_11(QDM2SubPNode *node, int length) { + GetBitContext gb; + + initGetBits(&gb, ((node == NULL) ? _emptyBuffer : node->packet->data), ((node == NULL) ? 0 : node->packet->size*8)); + if (length >= 32) { + int c = getBits (&gb, 13); + + if (c > 3) + fill_coding_method_array(_toneLevelIdx, _toneLevelIdxTemp, _codingMethod, + _channels, 8*c, _superblocktype_2_3, _cmTableSelect); + } + + synthfilt_build_sb_samples(&gb, length, 0, 8); +} + +/** + * Process subpacket 12 + * + * @param node pointer to node with packet + * @param length packet length in bits + */ +void QDM2Stream::process_subpacket_12(QDM2SubPNode *node, int length) { + GetBitContext gb; + + initGetBits(&gb, ((node == NULL) ? _emptyBuffer : node->packet->data), ((node == NULL) ? 0 : node->packet->size*8)); + synthfilt_build_sb_samples(&gb, length, 8, QDM2_SB_USED(_subSampling)); +} + +/* + * Process new subpackets for synthesis filter + * + * @param list list with synthesis filter packets (list D) + */ +void QDM2Stream::process_synthesis_subpackets(QDM2SubPNode *list) { + struct QDM2SubPNode *nodes[4]; + + nodes[0] = qdm2_search_subpacket_type_in_list(list, 9); + if (nodes[0] != NULL) + process_subpacket_9(nodes[0]); + + nodes[1] = qdm2_search_subpacket_type_in_list(list, 10); + if (nodes[1] != NULL) + process_subpacket_10(nodes[1], nodes[1]->packet->size << 3); + else + process_subpacket_10(NULL, 0); + + nodes[2] = qdm2_search_subpacket_type_in_list(list, 11); + if (nodes[0] != NULL && nodes[1] != NULL && nodes[2] != NULL) + process_subpacket_11(nodes[2], (nodes[2]->packet->size << 3)); + else + process_subpacket_11(NULL, 0); + + nodes[3] = qdm2_search_subpacket_type_in_list(list, 12); + if (nodes[0] != NULL && nodes[1] != NULL && nodes[3] != NULL) + process_subpacket_12(nodes[3], (nodes[3]->packet->size << 3)); + else + process_subpacket_12(NULL, 0); +} + +/* + * Decode superblock, fill packet lists. + * + */ +void QDM2Stream::qdm2_decode_super_block(void) { + GetBitContext gb; + struct QDM2SubPacket header, *packet; + int i, packet_bytes, sub_packet_size, subPacketsD; + unsigned int next_index = 0; + + memset(_toneLevelIdxHi1, 0, sizeof(_toneLevelIdxHi1)); + memset(_toneLevelIdxMid, 0, sizeof(_toneLevelIdxMid)); + memset(_toneLevelIdxHi2, 0, sizeof(_toneLevelIdxHi2)); + + _subPacketsB = 0; + subPacketsD = 0; + + average_quantized_coeffs(); // average elements in quantized_coeffs[max_ch][10][8] + + initGetBits(&gb, _compressedData, _packetSize*8); + qdm2_decode_sub_packet_header(&gb, &header); + + if (header.type < 2 || header.type >= 8) { + _hasErrors = true; + error("QDM2 : bad superblock type"); + return; + } + + _superblocktype_2_3 = (header.type == 2 || header.type == 3); + packet_bytes = (_packetSize - getBitsCount(&gb) / 8); + + initGetBits(&gb, header.data, header.size*8); + + if (header.type == 2 || header.type == 4 || header.type == 5) { + int csum = 257 * getBits(&gb, 8) + 2 * getBits(&gb, 8); + + csum = qdm2_packet_checksum(_compressedData, _packetSize, csum); + + if (csum != 0) { + _hasErrors = true; + error("QDM2 : bad packet checksum"); + return; + } + } + + _subPacketListB[0].packet = NULL; + _subPacketListD[0].packet = NULL; + + for (i = 0; i < 6; i++) + if (--_fftLevelExp[i] < 0) + _fftLevelExp[i] = 0; + + for (i = 0; packet_bytes > 0; i++) { + int j; + + _subPacketListA[i].next = NULL; + + if (i > 0) { + _subPacketListA[i - 1].next = &_subPacketListA[i]; + + // seek to next block + initGetBits(&gb, header.data, header.size*8); + skipBits(&gb, next_index*8); + + if (next_index >= header.size) + break; + } + + // decode subpacket + packet = &_subPackets[i]; + qdm2_decode_sub_packet_header(&gb, packet); + next_index = packet->size + getBitsCount(&gb) / 8; + sub_packet_size = ((packet->size > 0xff) ? 1 : 0) + packet->size + 2; + + if (packet->type == 0) + break; + + if (sub_packet_size > packet_bytes) { + if (packet->type != 10 && packet->type != 11 && packet->type != 12) + break; + packet->size += packet_bytes - sub_packet_size; + } + + packet_bytes -= sub_packet_size; + + // add subpacket to 'all subpackets' list + _subPacketListA[i].packet = packet; + + // add subpacket to related list + if (packet->type == 8) { + error("Unsupported packet type 8"); + return; + } else if (packet->type >= 9 && packet->type <= 12) { + // packets for MPEG Audio like Synthesis Filter + QDM2_LIST_ADD(_subPacketListD, subPacketsD, packet); + } else if (packet->type == 13) { + for (j = 0; j < 6; j++) + _fftLevelExp[j] = getBits(&gb, 6); + } else if (packet->type == 14) { + for (j = 0; j < 6; j++) + _fftLevelExp[j] = qdm2_get_vlc(&gb, &_fftLevelExpVlc, 0, 2); + } else if (packet->type == 15) { + error("Unsupported packet type 15"); + return; + } else if (packet->type >= 16 && packet->type < 48 && !fft_subpackets[packet->type - 16]) { + // packets for FFT + QDM2_LIST_ADD(_subPacketListB, _subPacketsB, packet); + } + } // Packet bytes loop + +// **************************************************************** + if (_subPacketListD[0].packet != NULL) { + process_synthesis_subpackets(_subPacketListD); + _doSynthFilter = 1; + } else if (_doSynthFilter) { + process_subpacket_10(NULL, 0); + process_subpacket_11(NULL, 0); + process_subpacket_12(NULL, 0); + } +// **************************************************************** +} + +void QDM2Stream::qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, + int channel, int exp, int phase) { + if (_fftCoefsMinIndex[duration] < 0) + _fftCoefsMinIndex[duration] = _fftCoefsIndex; + + _fftCoefs[_fftCoefsIndex].sub_packet = ((sub_packet >= 16) ? (sub_packet - 16) : sub_packet); + _fftCoefs[_fftCoefsIndex].channel = channel; + _fftCoefs[_fftCoefsIndex].offset = offset; + _fftCoefs[_fftCoefsIndex].exp = exp; + _fftCoefs[_fftCoefsIndex].phase = phase; + _fftCoefsIndex++; +} + +void QDM2Stream::qdm2_fft_decode_tones(int duration, GetBitContext *gb, int b) { + debug(1, "QDM2Stream::qdm2_fft_decode_tones() duration: %d b:%d", duration, b); + int channel, stereo, phase, exp; + int local_int_4, local_int_8, stereo_phase, local_int_10; + int local_int_14, stereo_exp, local_int_20, local_int_28; + int n, offset; + + local_int_4 = 0; + local_int_28 = 0; + local_int_20 = 2; + local_int_8 = (4 - duration); + local_int_10 = 1 << (_groupOrder - duration - 1); + offset = 1; + + while (1) { + if (_superblocktype_2_3) { + debug(1, "QDM2Stream::qdm2_fft_decode_tones() local_int_8: %d", local_int_8); + while ((n = qdm2_get_vlc(gb, &_vlcTabFftToneOffset[local_int_8], 1, 2)) < 2) { + debug(1, "QDM2Stream::qdm2_fft_decode_tones() local_int_8: %d", local_int_8); + offset = 1; + if (n == 0) { + local_int_4 += local_int_10; + local_int_28 += (1 << local_int_8); + } else { + local_int_4 += 8*local_int_10; + local_int_28 += (8 << local_int_8); + } + } + offset += (n - 2); + } else { + offset += qdm2_get_vlc(gb, &_vlcTabFftToneOffset[local_int_8], 1, 2); + while (offset >= (local_int_10 - 1)) { + offset += (1 - (local_int_10 - 1)); + local_int_4 += local_int_10; + local_int_28 += (1 << local_int_8); + } + } + + if (local_int_4 >= _blockSize) + return; + + local_int_14 = (offset >> local_int_8); + + if (_channels > 1) { + channel = getBits1(gb); + stereo = getBits1(gb); + } else { + channel = 0; + stereo = 0; + } + + exp = qdm2_get_vlc(gb, (b ? &_fftLevelExpVlc : &_fftLevelExpAltVlc), 0, 2); + exp += _fftLevelExp[fft_level_index_table[local_int_14]]; + exp = (exp < 0) ? 0 : exp; + + phase = getBits(gb, 3); + stereo_exp = 0; + stereo_phase = 0; + + if (stereo) { + stereo_exp = (exp - qdm2_get_vlc(gb, &_fftStereoExpVlc, 0, 1)); + stereo_phase = (phase - qdm2_get_vlc(gb, &_fftStereoPhaseVlc, 0, 1)); + if (stereo_phase < 0) + stereo_phase += 8; + } + + if (_frequencyRange > (local_int_14 + 1)) { + int sub_packet = (local_int_20 + local_int_28); + + qdm2_fft_init_coefficient(sub_packet, offset, duration, channel, exp, phase); + if (stereo) + qdm2_fft_init_coefficient(sub_packet, offset, duration, (1 - channel), stereo_exp, stereo_phase); + } + + offset++; + } +} + +void QDM2Stream::qdm2_decode_fft_packets(void) { + debug(1, "QDM2Stream::qdm2_decode_fft_packets()"); + int i, j, min, max, value, type, unknown_flag; + GetBitContext gb; + + if (_subPacketListB[0].packet == NULL) + return; + + // reset minimum indexes for FFT coefficients + _fftCoefsIndex = 0; + for (i=0; i < 5; i++) + _fftCoefsMinIndex[i] = -1; + + // process subpackets ordered by type, largest type first + for (i = 0, max = 256; i < _subPacketsB; i++) { + QDM2SubPacket *packet= NULL; + + // find subpacket with largest type less than max + for (j = 0, min = 0; j < _subPacketsB; j++) { + value = _subPacketListB[j].packet->type; + if (value > min && value < max) { + min = value; + packet = _subPacketListB[j].packet; + } + } + + max = min; + + // check for errors (?) + if (!packet) + return; + + if (i == 0 && (packet->type < 16 || packet->type >= 48 || fft_subpackets[packet->type - 16])) + return; + + // decode FFT tones + debug(1, "QDM2Stream::qdm2_decode_fft_packets initGetBits() packet->size*8: %d", packet->size*8); + initGetBits(&gb, packet->data, packet->size*8); + + if (packet->type >= 32 && packet->type < 48 && !fft_subpackets[packet->type - 16]) + unknown_flag = 1; + else + unknown_flag = 0; + + type = packet->type; + + if ((type >= 17 && type < 24) || (type >= 33 && type < 40)) { + int duration = _subSampling + 5 - (type & 15); + + if (duration >= 0 && duration < 4) { // TODO: Should be <= 4? + debug(1, "QDM2Stream::qdm2_decode_fft_packets qdm2_fft_decode_tones() #1"); + qdm2_fft_decode_tones(duration, &gb, unknown_flag); + } + } else if (type == 31) { + for (j=0; j < 4; j++) { + debug(1, "QDM2Stream::qdm2_decode_fft_packets qdm2_fft_decode_tones() #2"); + qdm2_fft_decode_tones(j, &gb, unknown_flag); + } + } else if (type == 46) { + for (j=0; j < 6; j++) + _fftLevelExp[j] = getBits(&gb, 6); + for (j=0; j < 4; j++) { + debug(1, "QDM2Stream::qdm2_decode_fft_packets qdm2_fft_decode_tones() #3"); + qdm2_fft_decode_tones(j, &gb, unknown_flag); + } + } + } // Loop on B packets + + // calculate maximum indexes for FFT coefficients + for (i = 0, j = -1; i < 5; i++) + if (_fftCoefsMinIndex[i] >= 0) { + if (j >= 0) + _fftCoefsMaxIndex[j] = _fftCoefsMinIndex[i]; + j = i; + } + if (j >= 0) + _fftCoefsMaxIndex[j] = _fftCoefsIndex; +} + +void QDM2Stream::qdm2_fft_generate_tone(FFTTone *tone) +{ + float level, f[6]; + int i; + QDM2Complex c; + const double iscale = 2.0 * PI / 512.0; + + tone->phase += tone->phase_shift; + + // calculate current level (maximum amplitude) of tone + level = fft_tone_envelope_table[tone->duration][tone->time_index] * tone->level; + c.im = level * sin(tone->phase*iscale); + c.re = level * cos(tone->phase*iscale); + + // generate FFT coefficients for tone + if (tone->duration >= 3 || tone->cutoff >= 3) { + tone->complex[0].im += c.im; + tone->complex[0].re += c.re; + tone->complex[1].im -= c.im; + tone->complex[1].re -= c.re; + } else { + f[1] = -tone->table[4]; + f[0] = tone->table[3] - tone->table[0]; + f[2] = 1.0 - tone->table[2] - tone->table[3]; + f[3] = tone->table[1] + tone->table[4] - 1.0; + f[4] = tone->table[0] - tone->table[1]; + f[5] = tone->table[2]; + for (i = 0; i < 2; i++) { + tone->complex[fft_cutoff_index_table[tone->cutoff][i]].re += c.re * f[i]; + tone->complex[fft_cutoff_index_table[tone->cutoff][i]].im += c.im *((tone->cutoff <= i) ? -f[i] : f[i]); + } + for (i = 0; i < 4; i++) { + tone->complex[i].re += c.re * f[i+2]; + tone->complex[i].im += c.im * f[i+2]; + } + } + + // copy the tone if it has not yet died out + if (++tone->time_index < ((1 << (5 - tone->duration)) - 1)) { + memcpy(&_fftTones[_fftToneEnd], tone, sizeof(FFTTone)); + _fftToneEnd = (_fftToneEnd + 1) % 1000; + } +} + +void QDM2Stream::qdm2_fft_tone_synthesizer(uint8 sub_packet) { + int i, j, ch; + const double iscale = 0.25 * PI; + + for (ch = 0; ch < _channels; ch++) { + memset(_fft.complex[ch], 0, _frameSize * sizeof(QDM2Complex)); + } + + // apply FFT tones with duration 4 (1 FFT period) + if (_fftCoefsMinIndex[4] >= 0) + for (i = _fftCoefsMinIndex[4]; i < _fftCoefsMaxIndex[4]; i++) { + float level; + QDM2Complex c; + + if (_fftCoefs[i].sub_packet != sub_packet) + break; + + ch = (_channels == 1) ? 0 : _fftCoefs[i].channel; + level = (_fftCoefs[i].exp < 0) ? 0.0 : fft_tone_level_table[_superblocktype_2_3 ? 0 : 1][_fftCoefs[i].exp & 63]; + + c.re = level * cos(_fftCoefs[i].phase * iscale); + c.im = level * sin(_fftCoefs[i].phase * iscale); + _fft.complex[ch][_fftCoefs[i].offset + 0].re += c.re; + _fft.complex[ch][_fftCoefs[i].offset + 0].im += c.im; + _fft.complex[ch][_fftCoefs[i].offset + 1].re -= c.re; + _fft.complex[ch][_fftCoefs[i].offset + 1].im -= c.im; + } + + // generate existing FFT tones + for (i = _fftToneEnd; i != _fftToneStart; ) { + qdm2_fft_generate_tone(&_fftTones[_fftToneStart]); + _fftToneStart = (_fftToneStart + 1) % 1000; + } + + // create and generate new FFT tones with duration 0 (long) to 3 (short) + for (i = 0; i < 4; i++) + if (_fftCoefsMinIndex[i] >= 0) { + for (j = _fftCoefsMinIndex[i]; j < _fftCoefsMaxIndex[i]; j++) { + int offset, four_i; + FFTTone tone; + + if (_fftCoefs[j].sub_packet != sub_packet) + break; + + four_i = (4 - i); + offset = _fftCoefs[j].offset >> four_i; + ch = (_channels == 1) ? 0 : _fftCoefs[j].channel; + + if (offset < _frequencyRange) { + if (offset < 2) + tone.cutoff = offset; + else + tone.cutoff = (offset >= 60) ? 3 : 2; + + tone.level = (_fftCoefs[j].exp < 0) ? 0.0 : fft_tone_level_table[_superblocktype_2_3 ? 0 : 1][_fftCoefs[j].exp & 63]; + tone.complex = &_fft.complex[ch][offset]; + tone.table = fft_tone_sample_table[i][_fftCoefs[j].offset - (offset << four_i)]; + tone.phase = 64 * _fftCoefs[j].phase - (offset << 8) - 128; + tone.phase_shift = (2 * _fftCoefs[j].offset + 1) << (7 - four_i); + tone.duration = i; + tone.time_index = 0; + + qdm2_fft_generate_tone(&tone); + } + } + _fftCoefsMinIndex[i] = j; + } +} + +void QDM2Stream::qdm2_calculate_fft(int channel) { + debug(1, "QDM2Stream::qdm2_calculate_fft channel: %d", channel); + const float gain = (_channels == 1 && _channels == 2) ? 0.5f : 1.0f; + int i; + + _fft.complex[channel][0].re *= 2.0f; + _fft.complex[channel][0].im = 0.0f; + + //debug(1, "QDM2Stream::qdm2_calculate_fft _fft.complex[channel][0].re: %lf", _fft.complex[channel][0].re); + //debug(1, "QDM2Stream::qdm2_calculate_fft _fft.complex[channel][0].im: %lf", _fft.complex[channel][0].im); + + rdftCalc(&_rdftCtx, (float *)_fft.complex[channel]); + + // add samples to output buffer + for (i = 0; i < ((_fftFrameSize + 15) & ~15); i++) + _outputBuffer[_channels * i + channel] += ((float *) _fft.complex[channel])[i] * gain; +} + +/** + * @param index subpacket number + */ +void QDM2Stream::qdm2_synthesis_filter(uint8 index) +{ + int16 samples[MPA_MAX_CHANNELS * MPA_FRAME_SIZE]; + int i, k, ch, sb_used, sub_sampling, dither_state = 0; + + // copy sb_samples + sb_used = QDM2_SB_USED(_subSampling); + + for (ch = 0; ch < _channels; ch++) + for (i = 0; i < 8; i++) + for (k = sb_used; k < 32; k++) + _sbSamples[ch][(8 * index) + i][k] = 0; + + for (ch = 0; ch < _channels; ch++) { + int16 *samples_ptr = samples + ch; + + for (i = 0; i < 8; i++) { + ff_mpa_synth_filter(_synthBuf[ch], &(_synthBufOffset[ch]), + ff_mpa_synth_window, &dither_state, + samples_ptr, _channels, + _sbSamples[ch][(8 * index) + i]); + samples_ptr += 32 * _channels; + } + } + + // add samples to output buffer + sub_sampling = (4 >> _subSampling); + + for (ch = 0; ch < _channels; ch++) + for (i = 0; i < _sFrameSize; i++) + _outputBuffer[_channels * i + ch] += (float)(samples[_channels * sub_sampling * i + ch] >> (sizeof(int16)*8-16)); +} + +int QDM2Stream::qdm2_decodeFrame(Common::SeekableReadStream *in) { + debug(1, "QDM2Stream::qdm2_decodeFrame in->pos(): %d in->size(): %d", in->pos(), in->size()); + int ch, i; + const int frame_size = (_sFrameSize * _channels); + + // select input buffer + if(in->eos() || in->size() == in->pos()) { + debug(1, "QDM2Stream::qdm2_decodeFrame End of Input Stream"); + return 0; + } + if((in->size() - in->pos()) < _packetSize) { + debug(1, "QDM2Stream::qdm2_decodeFrame Insufficient Packet Data in Input Stream Found: %d Need: %d", in->size() - in->pos(), _packetSize); + return 0; + } + + in->read(_compressedData, _packetSize); + debug(1, "QDM2Stream::qdm2_decodeFrame constructed input data"); + + // copy old block, clear new block of output samples + memmove(_outputBuffer, &_outputBuffer[frame_size], frame_size * sizeof(float)); + memset(&_outputBuffer[frame_size], 0, frame_size * sizeof(float)); + debug(1, "QDM2Stream::qdm2_decodeFrame cleared outputBuffer"); + + // decode block of QDM2 compressed data + debug(1, "QDM2Stream::qdm2_decodeFrame decode block of QDM2 compressed data"); + if (_subPacket == 0) { + _hasErrors = false; // reset it for a new super block + debug(1, "QDM2 : Superblock follows"); + qdm2_decode_super_block(); + } + + // parse subpackets + debug(1, "QDM2Stream::qdm2_decodeFrame parse subpackets"); + if (!_hasErrors) { + if (_subPacket == 2) { + debug(1, "QDM2Stream::qdm2_decodeFrame qdm2_decode_fft_packets()"); + qdm2_decode_fft_packets(); + } + + debug(1, "QDM2Stream::qdm2_decodeFrame qdm2_fft_tone_synthesizer(%d)", _subPacket); + qdm2_fft_tone_synthesizer(_subPacket); + } + + // sound synthesis stage 1 (FFT) + debug(1, "QDM2Stream::qdm2_decodeFrame sound synthesis stage 1 (FFT)"); + for (ch = 0; ch < _channels; ch++) { + qdm2_calculate_fft(ch); + + if (!_hasErrors && _subPacketListC[0].packet != NULL) { + error("QDM2 : has errors, and C list is not empty"); + return 0; + } + } + + // sound synthesis stage 2 (MPEG audio like synthesis filter) + debug(1, "QDM2Stream::qdm2_decodeFrame sound synthesis stage 2 (MPEG audio like synthesis filter)"); + if (!_hasErrors && _doSynthFilter) + qdm2_synthesis_filter(_subPacket); + + _subPacket = (_subPacket + 1) % 16; + + if(_hasErrors) + warning("QDM2 Packet error..."); + + // clip and convert output float[] to 16bit signed samples + debug(1, "QDM2Stream::qdm2_decodeFrame clip and convert output float[] to 16bit signed samples"); + +/* + debugN(1, "Input Data Packet:"); + for(i = 0; i < _packetSize; i++) { + debugN(1, " %d", _compressedData[i]); + } + debugN(1, " Output Data Packet:"); + for(i = 0; i < frame_size; i++) { + debugN(1, " %d", (int)_outputBuffer[i]); + } + debug(1, ""); +*/ + + for (i = 0; i < frame_size; i++) { + //debug(1, "QDM2Stream::qdm2_decodeFrame i: %d", i); + int value = (int)_outputBuffer[i]; + + if (value > SOFTCLIP_THRESHOLD) + value = (value > HARDCLIP_THRESHOLD) ? 32767 : _softclipTable[ value - SOFTCLIP_THRESHOLD]; + else if (value < -SOFTCLIP_THRESHOLD) + value = (value < -HARDCLIP_THRESHOLD) ? -32767 : -_softclipTable[-value - SOFTCLIP_THRESHOLD]; + + _outputSamples.push_back(value); + } + return frame_size; +} + +int QDM2Stream::readBuffer(int16 *buffer, const int numSamples) { + debug(1, "QDM2Stream::readBuffer numSamples: %d", numSamples); + int32 decodedSamples = _outputSamples.size(); + int32 i; + + //while((int)_outputSamples.size() < numSamples) { + while(!_stream->eos() && _stream->pos() != _stream->size()) { + i = qdm2_decodeFrame(_stream); + if(i == 0) + break; // Out Of Decode Frames... + decodedSamples += i; + } + if(decodedSamples > numSamples) + decodedSamples = numSamples; + + for(i = 0; i < decodedSamples; i++) + buffer[i] = _outputSamples.remove_at(0); + + return decodedSamples; +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/qdm2.h b/engines/mohawk/video/qdm2.h new file mode 100644 index 0000000000..ffa5f77030 --- /dev/null +++ b/engines/mohawk/video/qdm2.h @@ -0,0 +1,289 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MOHAWK_VIDEO_QDM2_H +#define MOHAWK_VIDEO_QDM2_H + +#include "sound/audiostream.h" +#include "common/array.h" +#include "common/stream.h" + +namespace Mohawk { + +enum { + SOFTCLIP_THRESHOLD = 27600, + HARDCLIP_THRESHOLD = 35716, + MPA_MAX_CHANNELS = 2, + MPA_FRAME_SIZE = 1152, + FF_INPUT_BUFFER_PADDING_SIZE = 8 +}; + +typedef int8 sb_int8_array[2][30][64]; + +/* bit input */ +/* buffer, buffer_end and size_in_bits must be present and used by every reader */ +struct GetBitContext { + const uint8 *buffer, *bufferEnd; + int index; + int sizeInBits; +}; + +struct QDM2SubPacket { + int type; + unsigned int size; + const uint8 *data; // pointer to subpacket data (points to input data buffer, it's not a private copy) +}; + +struct QDM2SubPNode { + QDM2SubPacket *packet; + struct QDM2SubPNode *next; // pointer to next packet in the list, NULL if leaf node +}; + +struct QDM2Complex { + float re; + float im; +}; + +struct FFTTone { + float level; + QDM2Complex *complex; + const float *table; + int phase; + int phase_shift; + int duration; + short time_index; + short cutoff; +}; + +struct FFTCoefficient { + int16 sub_packet; + uint8 channel; + int16 offset; + int16 exp; + uint8 phase; +}; + +struct VLC { + int32 bits; + int16 (*table)[2]; // code, bits + int32 table_size; + int32 table_allocated; +}; + +#include "common/pack-start.h" +struct QDM2FFT { + QDM2Complex complex[MPA_MAX_CHANNELS][256]; +} PACKED_STRUCT; +#include "common/pack-end.h" + +enum RDFTransformType { + RDFT, + IRDFT, + RIDFT, + IRIDFT +}; + +struct FFTComplex { + float re, im; +}; + +struct FFTContext { + int nbits; + int inverse; + uint16 *revtab; + FFTComplex *exptab; + FFTComplex *tmpBuf; + int mdctSize; // size of MDCT (i.e. number of input data * 2) + int mdctBits; // n = 2^nbits + // pre/post rotation tables + float *tcos; + float *tsin; + void (*fftPermute)(struct FFTContext *s, FFTComplex *z); + void (*fftCalc)(struct FFTContext *s, FFTComplex *z); + void (*imdctCalc)(struct FFTContext *s, float *output, const float *input); + void (*imdctHalf)(struct FFTContext *s, float *output, const float *input); + void (*mdctCalc)(struct FFTContext *s, float *output, const float *input); + int splitRadix; + int permutation; +}; + +enum { + FF_MDCT_PERM_NONE = 0, + FF_MDCT_PERM_INTERLEAVE = 1 +}; + +struct RDFTContext { + int nbits; + int inverse; + int signConvention; + + // pre/post rotation tables + float *tcos; + float *tsin; + FFTContext fft; +}; + +class QDM2Stream : public Audio::AudioStream { +public: + QDM2Stream(Common::SeekableReadStream *stream, Common::SeekableReadStream *extraData); + ~QDM2Stream(); + + bool isStereo() const { return _channels == 2; } + bool endOfData() const { return ((_stream->pos() == _stream->size()) && (_outputSamples.size() == 0)); } + int getRate() const { return _sampleRate; } + int readBuffer(int16 *buffer, const int numSamples); + +private: + Common::SeekableReadStream *_stream; + + // Parameters from codec header, do not change during playback + uint8 _channels; + uint16 _sampleRate; + uint16 _bitRate; + uint16 _blockSize; // Group + uint16 _frameSize; // FFT + uint16 _packetSize; // Checksum + + // Parameters built from header parameters, do not change during playback + int _groupOrder; // order of frame group + int _fftOrder; // order of FFT (actually fft order+1) + int _fftFrameSize; // size of fft frame, in components (1 comples = re + im) + int _sFrameSize; // size of data frame + int _frequencyRange; + int _subSampling; // subsampling: 0=25%, 1=50%, 2=100% */ + int _coeffPerSbSelect; // selector for "num. of coeffs. per subband" tables. Can be 0, 1, 2 + int _cmTableSelect; // selector for "coding method" tables. Can be 0, 1 (from init: 0-4) + + // Packets and packet lists + QDM2SubPacket _subPackets[16]; // the packets themselves + QDM2SubPNode _subPacketListA[16]; // list of all packets + QDM2SubPNode _subPacketListB[16]; // FFT packets B are on list + int _subPacketsB; // number of packets on 'B' list + QDM2SubPNode _subPacketListC[16]; // packets with errors? + QDM2SubPNode _subPacketListD[16]; // DCT packets + + // FFT and tones + FFTTone _fftTones[1000]; + int _fftToneStart; + int _fftToneEnd; + FFTCoefficient _fftCoefs[1000]; + int _fftCoefsIndex; + int _fftCoefsMinIndex[5]; + int _fftCoefsMaxIndex[5]; + int _fftLevelExp[6]; + //RDFTContext _rdftCtx; + QDM2FFT _fft; + + // I/O data + uint8 *_compressedData; + float _outputBuffer[1024]; + Common::Array<int16> _outputSamples; + + // Synthesis filter + int16 ff_mpa_synth_window[512]; + int16 _synthBuf[MPA_MAX_CHANNELS][512*2]; + int _synthBufOffset[MPA_MAX_CHANNELS]; + int32 _sbSamples[MPA_MAX_CHANNELS][128][32]; + + // Mixed temporary data used in decoding + float _toneLevel[MPA_MAX_CHANNELS][30][64]; + int8 _codingMethod[MPA_MAX_CHANNELS][30][64]; + int8 _quantizedCoeffs[MPA_MAX_CHANNELS][10][8]; + int8 _toneLevelIdxBase[MPA_MAX_CHANNELS][30][8]; + int8 _toneLevelIdxHi1[MPA_MAX_CHANNELS][3][8][8]; + int8 _toneLevelIdxMid[MPA_MAX_CHANNELS][26][8]; + int8 _toneLevelIdxHi2[MPA_MAX_CHANNELS][26]; + int8 _toneLevelIdx[MPA_MAX_CHANNELS][30][64]; + int8 _toneLevelIdxTemp[MPA_MAX_CHANNELS][30][64]; + + // Flags + bool _hasErrors; // packet has errors + int _superblocktype_2_3; // select fft tables and some algorithm based on superblock type + int _doSynthFilter; // used to perform or skip synthesis filter + + uint8 _subPacket; // 0 to 15 + int _noiseIdx; // index for dithering noise table + + byte _emptyBuffer[FF_INPUT_BUFFER_PADDING_SIZE]; + + VLC _vlcTabLevel; + VLC _vlcTabDiff; + VLC _vlcTabRun; + VLC _fftLevelExpAltVlc; + VLC _fftLevelExpVlc; + VLC _fftStereoExpVlc; + VLC _fftStereoPhaseVlc; + VLC _vlcTabToneLevelIdxHi1; + VLC _vlcTabToneLevelIdxMid; + VLC _vlcTabToneLevelIdxHi2; + VLC _vlcTabType30; + VLC _vlcTabType34; + VLC _vlcTabFftToneOffset[5]; + bool _vlcsInitialized; + void initVlc(void); + + uint16 _softclipTable[HARDCLIP_THRESHOLD - SOFTCLIP_THRESHOLD + 1]; + void softclipTableInit(void); + + float _noiseTable[4096]; + byte _randomDequantIndex[256][5]; + byte _randomDequantType24[128][3]; + void rndTableInit(void); + + float _noiseSamples[128]; + void initNoiseSamples(void); + + RDFTContext _rdftCtx; + + void average_quantized_coeffs(void); + void build_sb_samples_from_noise(int sb); + void fix_coding_method_array(int sb, int channels, sb_int8_array coding_method); + void fill_tone_level_array(int flag); + void fill_coding_method_array(sb_int8_array tone_level_idx, sb_int8_array tone_level_idx_temp, + sb_int8_array coding_method, int nb_channels, + int c, int superblocktype_2_3, int cm_table_select); + void synthfilt_build_sb_samples(GetBitContext *gb, int length, int sb_min, int sb_max); + void init_quantized_coeffs_elem0(int8 *quantized_coeffs, GetBitContext *gb, int length); + void init_tone_level_dequantization(GetBitContext *gb, int length); + void process_subpacket_9(QDM2SubPNode *node); + void process_subpacket_10(QDM2SubPNode *node, int length); + void process_subpacket_11(QDM2SubPNode *node, int length); + void process_subpacket_12(QDM2SubPNode *node, int length); + void process_synthesis_subpackets(QDM2SubPNode *list); + void qdm2_decode_super_block(void); + void qdm2_fft_init_coefficient(int sub_packet, int offset, int duration, + int channel, int exp, int phase); + void qdm2_fft_decode_tones(int duration, GetBitContext *gb, int b); + void qdm2_decode_fft_packets(void); + void qdm2_fft_generate_tone(FFTTone *tone); + void qdm2_fft_tone_synthesizer(uint8 sub_packet); + void qdm2_calculate_fft(int channel); + void qdm2_synthesis_filter(uint8 index); + int qdm2_decodeFrame(Common::SeekableReadStream *in); +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/video/qdm2data.h b/engines/mohawk/video/qdm2data.h new file mode 100644 index 0000000000..f1c18db41c --- /dev/null +++ b/engines/mohawk/video/qdm2data.h @@ -0,0 +1,531 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MOHAWK_VIDEO_QDM2DATA_H +#define MOHAWK_VIDEO_QDM2DATA_H + +#include "common/scummsys.h" + +namespace Mohawk { + +/// VLC TABLES + +// values in this table range from -1..23; adjust retrieved value by -1 +static const uint16 vlc_tab_level_huffcodes[24] = { + 0x037c, 0x0004, 0x003c, 0x004c, 0x003a, 0x002c, 0x001c, 0x001a, + 0x0024, 0x0014, 0x0001, 0x0002, 0x0000, 0x0003, 0x0007, 0x0005, + 0x0006, 0x0008, 0x0009, 0x000a, 0x000c, 0x00fc, 0x007c, 0x017c +}; + +static const byte vlc_tab_level_huffbits[24] = { + 10, 6, 7, 7, 6, 6, 6, 6, 6, 5, 4, 4, 4, 3, 3, 3, 3, 4, 4, 5, 7, 8, 9, 10 +}; + +// values in this table range from -1..36; adjust retrieved value by -1 +static const uint16 vlc_tab_diff_huffcodes[37] = { + 0x1c57, 0x0004, 0x0000, 0x0001, 0x0003, 0x0002, 0x000f, 0x000e, + 0x0007, 0x0016, 0x0037, 0x0027, 0x0026, 0x0066, 0x0006, 0x0097, + 0x0046, 0x01c6, 0x0017, 0x0786, 0x0086, 0x0257, 0x00d7, 0x0357, + 0x00c6, 0x0386, 0x0186, 0x0000, 0x0157, 0x0c57, 0x0057, 0x0000, + 0x0b86, 0x0000, 0x1457, 0x0000, 0x0457 +}; + +static const byte vlc_tab_diff_huffbits[37] = { + 13, 3, 3, 2, 3, 3, 4, 4, 6, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 8, 11, 9, 10, 8, 10, 9, 12, 10, 0, 10, 13, 11, 0, + 12, 0, 13, 0, 13 +}; + +// values in this table range from -1..5; adjust retrieved value by -1 +static const byte vlc_tab_run_huffcodes[6] = { + 0x1f, 0x00, 0x01, 0x03, 0x07, 0x0f +}; + +static const byte vlc_tab_run_huffbits[6] = { + 5, 1, 2, 3, 4, 5 +}; + +// values in this table range from -1..19; adjust retrieved value by -1 +static const uint16 vlc_tab_tone_level_idx_hi1_huffcodes[20] = { + 0x5714, 0x000c, 0x0002, 0x0001, 0x0000, 0x0004, 0x0034, 0x0054, + 0x0094, 0x0014, 0x0114, 0x0214, 0x0314, 0x0614, 0x0e14, 0x0f14, + 0x2714, 0x0714, 0x1714, 0x3714 +}; + +static const byte vlc_tab_tone_level_idx_hi1_huffbits[20] = { + 15, 4, 2, 1, 3, 5, 6, 7, 8, 10, 10, 11, 11, 12, 12, 12, 14, 14, 15, 14 +}; + +// values in this table range from -1..23; adjust retrieved value by -1 +static const uint16 vlc_tab_tone_level_idx_mid_huffcodes[24] = { + 0x0fea, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x03ea, 0x00ea, 0x002a, 0x001a, + 0x0006, 0x0001, 0x0000, 0x0002, 0x000a, 0x006a, 0x01ea, 0x07ea +}; + +static const byte vlc_tab_tone_level_idx_mid_huffbits[24] = { + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12 +}; + +// values in this table range from -1..23; adjust retrieved value by -1 +static const uint16 vlc_tab_tone_level_idx_hi2_huffcodes[24] = { + 0x0664, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0064, 0x00e4, + 0x00a4, 0x0068, 0x0004, 0x0008, 0x0014, 0x0018, 0x0000, 0x0001, + 0x0002, 0x0003, 0x000c, 0x0028, 0x0024, 0x0164, 0x0000, 0x0264 +}; + +static const byte vlc_tab_tone_level_idx_hi2_huffbits[24] = { + 11, 0, 0, 0, 0, 0, 10, 8, 8, 7, 6, 6, 5, 5, 4, 2, 2, 2, 4, 7, 8, 9, 0, 11 +}; + +// values in this table range from -1..8; adjust retrieved value by -1 +static const byte vlc_tab_type30_huffcodes[9] = { + 0x3c, 0x06, 0x00, 0x01, 0x03, 0x02, 0x04, 0x0c, 0x1c +}; + +static const byte vlc_tab_type30_huffbits[9] = { + 6, 3, 3, 2, 2, 3, 4, 5, 6 +}; + +// values in this table range from -1..9; adjust retrieved value by -1 +static const byte vlc_tab_type34_huffcodes[10] = { + 0x18, 0x00, 0x01, 0x04, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08 +}; + +static const byte vlc_tab_type34_huffbits[10] = { + 5, 4, 3, 3, 3, 3, 3, 3, 3, 5 +}; + +// values in this table range from -1..22; adjust retrieved value by -1 +static const uint16 vlc_tab_fft_tone_offset_0_huffcodes[23] = { + 0x038e, 0x0001, 0x0000, 0x0022, 0x000a, 0x0006, 0x0012, 0x0002, + 0x001e, 0x003e, 0x0056, 0x0016, 0x000e, 0x0032, 0x0072, 0x0042, + 0x008e, 0x004e, 0x00f2, 0x002e, 0x0036, 0x00c2, 0x018e +}; + +static const byte vlc_tab_fft_tone_offset_0_huffbits[23] = { + 10, 1, 2, 6, 4, 5, 6, 7, 6, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 6, 6, 8, 10 +}; + +// values in this table range from -1..27; adjust retrieved value by -1 +static const uint16 vlc_tab_fft_tone_offset_1_huffcodes[28] = { + 0x07a4, 0x0001, 0x0020, 0x0012, 0x001c, 0x0008, 0x0006, 0x0010, + 0x0000, 0x0014, 0x0004, 0x0032, 0x0070, 0x000c, 0x0002, 0x003a, + 0x001a, 0x002c, 0x002a, 0x0022, 0x0024, 0x000a, 0x0064, 0x0030, + 0x0062, 0x00a4, 0x01a4, 0x03a4 +}; + +static const byte vlc_tab_fft_tone_offset_1_huffbits[28] = { + 11, 1, 6, 6, 5, 4, 3, 6, 6, 5, 6, 6, 7, 6, 6, 6, + 6, 6, 6, 7, 8, 6, 7, 7, 7, 9, 10, 11 +}; + +// values in this table range from -1..31; adjust retrieved value by -1 +static const uint16 vlc_tab_fft_tone_offset_2_huffcodes[32] = { + 0x1760, 0x0001, 0x0000, 0x0082, 0x000c, 0x0006, 0x0003, 0x0007, + 0x0008, 0x0004, 0x0010, 0x0012, 0x0022, 0x001a, 0x0000, 0x0020, + 0x000a, 0x0040, 0x004a, 0x006a, 0x002a, 0x0042, 0x0002, 0x0060, + 0x00aa, 0x00e0, 0x00c2, 0x01c2, 0x0160, 0x0360, 0x0760, 0x0f60 +}; + +static const byte vlc_tab_fft_tone_offset_2_huffbits[32] = { + 13, 2, 0, 8, 4, 3, 3, 3, 4, 4, 5, 5, 6, 5, 7, 7, + 7, 7, 7, 7, 8, 8, 8, 9, 8, 8, 9, 9, 10, 11, 13, 12 +}; + +// values in this table range from -1..34; adjust retrieved value by -1 +static const uint16 vlc_tab_fft_tone_offset_3_huffcodes[35] = { + 0x33ea, 0x0005, 0x0000, 0x000c, 0x0000, 0x0006, 0x0003, 0x0008, + 0x0002, 0x0001, 0x0004, 0x0007, 0x001a, 0x000f, 0x001c, 0x002c, + 0x000a, 0x001d, 0x002d, 0x002a, 0x000d, 0x004c, 0x008c, 0x006a, + 0x00cd, 0x004d, 0x00ea, 0x020c, 0x030c, 0x010c, 0x01ea, 0x07ea, + 0x0bea, 0x03ea, 0x13ea +}; + +static const byte vlc_tab_fft_tone_offset_3_huffbits[35] = { + 14, 4, 0, 10, 4, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6, + 6, 5, 6, 7, 7, 7, 8, 8, 8, 8, 9, 10, 10, 10, 10, 11, + 12, 13, 14 +}; + +// values in this table range from -1..37; adjust retrieved value by -1 +static const uint16 vlc_tab_fft_tone_offset_4_huffcodes[38] = { + 0x5282, 0x0016, 0x0000, 0x0136, 0x0004, 0x0000, 0x0007, 0x000a, + 0x000e, 0x0003, 0x0001, 0x000d, 0x0006, 0x0009, 0x0012, 0x0005, + 0x0025, 0x0022, 0x0015, 0x0002, 0x0076, 0x0035, 0x0042, 0x00c2, + 0x0182, 0x00b6, 0x0036, 0x03c2, 0x0482, 0x01c2, 0x0682, 0x0882, + 0x0a82, 0x0082, 0x0282, 0x1282, 0x3282, 0x2282 +}; + +static const byte vlc_tab_fft_tone_offset_4_huffbits[38] = { + 15, 6, 0, 9, 3, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6, + 6, 6, 6, 8, 7, 6, 8, 9, 9, 8, 9, 10, 11, 10, 11, 12, + 12, 12, 14, 15, 14, 14 +}; + +/// FFT TABLES + +// values in this table range from -1..27; adjust retrieved value by -1 +static const uint16 fft_level_exp_alt_huffcodes[28] = { + 0x1ec6, 0x0006, 0x00c2, 0x0142, 0x0242, 0x0246, 0x00c6, 0x0046, + 0x0042, 0x0146, 0x00a2, 0x0062, 0x0026, 0x0016, 0x000e, 0x0005, + 0x0004, 0x0003, 0x0000, 0x0001, 0x000a, 0x0012, 0x0002, 0x0022, + 0x01c6, 0x02c6, 0x06c6, 0x0ec6 +}; + +static const byte fft_level_exp_alt_huffbits[28] = { + 13, 7, 8, 9, 10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3, + 3, 2, 3, 3, 4, 5, 7, 8, 9, 11, 12, 13 +}; + +// values in this table range from -1..19; adjust retrieved value by -1 +static const uint16 fft_level_exp_huffcodes[20] = { + 0x0f24, 0x0001, 0x0002, 0x0000, 0x0006, 0x0005, 0x0007, 0x000c, + 0x000b, 0x0014, 0x0013, 0x0004, 0x0003, 0x0023, 0x0064, 0x00a4, + 0x0024, 0x0124, 0x0324, 0x0724 +}; + +static const byte fft_level_exp_huffbits[20] = { + 12, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 10, 11, 12 +}; + +// values in this table range from -1..6; adjust retrieved value by -1 +static const byte fft_stereo_exp_huffcodes[7] = { + 0x3e, 0x01, 0x00, 0x02, 0x06, 0x0e, 0x1e +}; + +static const byte fft_stereo_exp_huffbits[7] = { + 6, 1, 2, 3, 4, 5, 6 +}; + +// values in this table range from -1..8; adjust retrieved value by -1 +static const byte fft_stereo_phase_huffcodes[9] = { + 0x35, 0x02, 0x00, 0x01, 0x0d, 0x15, 0x05, 0x09, 0x03 +}; + +static const byte fft_stereo_phase_huffbits[9] = { + 6, 2, 2, 4, 4, 6, 5, 4, 2 +}; + +static const int fft_cutoff_index_table[4][2] = { + { 1, 2 }, {-1, 0 }, {-1,-2 }, { 0, 0 } +}; + +static const int16 fft_level_index_table[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static const byte last_coeff[3] = { + 4, 7, 10 +}; + +static const byte coeff_per_sb_for_avg[3][30] = { + { 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9 } +}; + +static const uint32 dequant_table[3][10][30] = { + { { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 256, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 51, 102, 154, 205, 256, 238, 219, 201, 183, 165, 146, 128, 110, 91, 73, 55, 37, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256, 228, 199, 171, 142, 114, 85, 57, 28 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 85, 171, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 85, 171, 256, 219, 183, 146, 110, 73, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 73, 110, 146, 183, 219, 256, 228, 199, 171, 142, 114, 85, 57, 28, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 57, 85, 114, 142, 171, 199, 228, 256, 213, 171, 128, 85, 43 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, + { { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 256, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 85, 171, 256, 192, 128, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 128, 192, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 102, 154, 205, 256, 213, 171, 128, 85, 43, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 85, 128, 171, 213, 256, 213, 171, 128, 85, 43 } } +}; + +static const byte coeff_per_sb_for_dequant[3][30] = { + { 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + { 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 }, + { 0, 1, 2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9 } +}; + +// first index is subband, 2nd index is 0, 1 or 3 (2 is unused) +static const int8 tone_level_idx_offset_table[30][4] = { + { -50, -50, 0, -50 }, + { -50, -50, 0, -50 }, + { -50, -9, 0, -19 }, + { -16, -6, 0, -12 }, + { -11, -4, 0, -8 }, + { -8, -3, 0, -6 }, + { -7, -3, 0, -5 }, + { -6, -2, 0, -4 }, + { -5, -2, 0, -3 }, + { -4, -1, 0, -3 }, + { -4, -1, 0, -2 }, + { -3, -1, 0, -2 }, + { -3, -1, 0, -2 }, + { -3, -1, 0, -2 }, + { -2, -1, 0, -1 }, + { -2, -1, 0, -1 }, + { -2, -1, 0, -1 }, + { -2, 0, 0, -1 }, + { -2, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, -1 }, + { -1, 0, 0, 0 }, + { -1, 0, 0, 0 }, + { -1, 0, 0, 0 }, + { -1, 0, 0, 0 } +}; + +/* all my samples have 1st index 0 or 1 */ +/* second index is subband, only indexes 0-29 seem to be used */ +static const int8 coding_method_table[5][30] = { + { 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 + }, + { 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 + }, + { 34, 30, 30, 30, 24, 24, 16, 16, 16, 16, 16, 16, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 + }, + { 34, 34, 30, 30, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, 10 + }, + { 34, 34, 30, 30, 30, 30, 30, 30, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 + }, +}; + +static const int vlc_stage3_values[60] = { + 0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, + 28, 36, 44, 52, 60, 76, 92, 108, 124, 156, 188, 220, + 252, 316, 380, 444, 508, 636, 764, 892, 1020, 1276, 1532, 1788, + 2044, 2556, 3068, 3580, 4092, 5116, 6140, 7164, 8188, 10236, 12284, 14332, + 16380, 20476, 24572, 28668, 32764, 40956, 49148, 57340, 65532, 81916, 98300,114684 +}; + +static const float fft_tone_sample_table[4][16][5] = { + { { .0100000000f,-.0037037037f,-.0020000000f,-.0069444444f,-.0018416207f }, + { .0416666667f, .0000000000f, .0000000000f,-.0208333333f,-.0123456791f }, + { .1250000000f, .0558035709f, .0330687836f,-.0164473690f,-.0097465888f }, + { .1562500000f, .0625000000f, .0370370370f,-.0062500000f,-.0037037037f }, + { .1996007860f, .0781250000f, .0462962948f, .0022727272f, .0013468013f }, + { .2000000000f, .0625000000f, .0370370373f, .0208333333f, .0074074073f }, + { .2127659619f, .0555555556f, .0329218097f, .0208333333f, .0123456791f }, + { .2173913121f, .0473484844f, .0280583613f, .0347222239f, .0205761325f }, + { .2173913121f, .0347222239f, .0205761325f, .0473484844f, .0280583613f }, + { .2127659619f, .0208333333f, .0123456791f, .0555555556f, .0329218097f }, + { .2000000000f, .0208333333f, .0074074073f, .0625000000f, .0370370370f }, + { .1996007860f, .0022727272f, .0013468013f, .0781250000f, .0462962948f }, + { .1562500000f,-.0062500000f,-.0037037037f, .0625000000f, .0370370370f }, + { .1250000000f,-.0164473690f,-.0097465888f, .0558035709f, .0330687836f }, + { .0416666667f,-.0208333333f,-.0123456791f, .0000000000f, .0000000000f }, + { .0100000000f,-.0069444444f,-.0018416207f,-.0037037037f,-.0020000000f } }, + + { { .0050000000f,-.0200000000f, .0125000000f,-.3030303030f, .0020000000f }, + { .1041666642f, .0400000000f,-.0250000000f, .0333333333f,-.0200000000f }, + { .1250000000f, .0100000000f, .0142857144f,-.0500000007f,-.0200000000f }, + { .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f }, + { .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f }, + { .1250000000f,-.0500000000f,-.0200000000f, .0100000000f, .0142857144f }, + { .1041666667f, .0333333333f,-.0200000000f, .0400000000f,-.0250000000f }, + { .0050000000f,-.3030303030f, .0020000001f,-.0200000000f, .0125000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } }, + + { { .1428571492f, .1250000000f,-.0285714287f,-.0357142873f, .0208333333f }, + { .1818181818f, .0588235296f, .0333333333f, .0212765951f, .0100000000f }, + { .1818181818f, .0212765951f, .0100000000f, .0588235296f, .0333333333f }, + { .1428571492f,-.0357142873f, .0208333333f, .1250000000f,-.0285714287f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } }, + + { { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f }, + { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } } +}; + +static const float fft_tone_level_table[2][64] = { { +// pow ~ (i > 46) ? 0 : (((((i & 1) ? 431 : 304) << (i >> 1))) / 1024.0); + 0.17677669f, 0.42677650f, 0.60355347f, 0.85355347f, + 1.20710683f, 1.68359375f, 2.37500000f, 3.36718750f, + 4.75000000f, 6.73437500f, 9.50000000f, 13.4687500f, + 19.0000000f, 26.9375000f, 38.0000000f, 53.8750000f, + 76.0000000f, 107.750000f, 152.000000f, 215.500000f, + 304.000000f, 431.000000f, 608.000000f, 862.000000f, + 1216.00000f, 1724.00000f, 2432.00000f, 3448.00000f, + 4864.00000f, 6896.00000f, 9728.00000f, 13792.0000f, + 19456.0000f, 27584.0000f, 38912.0000f, 55168.0000f, + 77824.0000f, 110336.000f, 155648.000f, 220672.000f, + 311296.000f, 441344.000f, 622592.000f, 882688.000f, + 1245184.00f, 1765376.00f, 2490368.00f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + }, { +// pow = (i > 45) ? 0 : ((((i & 1) ? 431 : 304) << (i >> 1)) / 512.0); + 0.59375000f, 0.84179688f, 1.18750000f, 1.68359375f, + 2.37500000f, 3.36718750f, 4.75000000f, 6.73437500f, + 9.50000000f, 13.4687500f, 19.0000000f, 26.9375000f, + 38.0000000f, 53.8750000f, 76.0000000f, 107.750000f, + 152.000000f, 215.500000f, 304.000000f, 431.000000f, + 608.000000f, 862.000000f, 1216.00000f, 1724.00000f, + 2432.00000f, 3448.00000f, 4864.00000f, 6896.00000f, + 9728.00000f, 13792.0000f, 19456.0000f, 27584.0000f, + 38912.0000f, 55168.0000f, 77824.0000f, 110336.000f, + 155648.000f, 220672.000f, 311296.000f, 441344.000f, + 622592.000f, 882688.000f, 1245184.00f, 1765376.00f, + 2490368.00f, 3530752.00f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f, + 0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f +} }; + +static const float fft_tone_envelope_table[4][31] = { + { .009607375f, .038060248f, .084265202f, .146446645f, .222214907f, .308658302f, + .402454883f, .500000060f, .597545207f, .691341758f, .777785182f, .853553414f, + .915734828f, .961939812f, .990392685f, 1.00000000f, .990392625f, .961939752f, + .915734768f, .853553295f, .777785063f, .691341639f, .597545087f, .500000000f, + .402454853f, .308658272f, .222214878f, .146446615f, .084265172f, .038060218f, + .009607345f }, + { .038060248f, .146446645f, .308658302f, .500000060f, .691341758f, .853553414f, + .961939812f, 1.00000000f, .961939752f, .853553295f, .691341639f, .500000000f, + .308658272f, .146446615f, .038060218f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f }, + { .146446645f, .500000060f, .853553414f, 1.00000000f, .853553295f, .500000000f, + .146446615f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f }, + { .500000060f, 1.00000000f, .500000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f, + .000000000f } +}; + +static const float sb_noise_attenuation[32] = { + 0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 0.7f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, +}; + +static const byte fft_subpackets[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0 +}; + +// first index is joined_stereo, second index is 0 or 2 (1 is unused) +static const float dequant_1bit[2][3] = { + {-0.920000f, 0.000000f, 0.920000f }, + {-0.890000f, 0.000000f, 0.890000f } +}; + +static const float type30_dequant[8] = { + -1.0f,-0.625f,-0.291666656732559f,0.0f, + 0.25f,0.5f,0.75f,1.0f, +}; + +static const float type34_delta[10] = { // FIXME: covers 8 entries.. + -1.0f,-0.60947573184967f,-0.333333343267441f,-0.138071194291115f,0.0f, + 0.138071194291115f,0.333333343267441f,0.60947573184967f,1.0f,0.0f, +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/video/qt_player.cpp b/engines/mohawk/video/qt_player.cpp new file mode 100644 index 0000000000..0ed05bb84d --- /dev/null +++ b/engines/mohawk/video/qt_player.cpp @@ -0,0 +1,1272 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// +// Heavily based on ffmpeg code. +// +// Copyright (c) 2001 Fabrice Bellard. +// First version by Francois Revol revol@free.fr +// Seek function by Gael Chardon gael.dev@4now.net +// + +#include "mohawk/video/qt_player.h" + +#include "common/debug.h" +#include "common/endian.h" +#include "common/util.h" +#include "common/zlib.h" + +// Audio codecs +#include "sound/decoders/adpcm.h" +#include "sound/decoders/raw.h" +#include "mohawk/video/qdm2.h" + +// Video codecs +#include "mohawk/jpeg.h" +#include "mohawk/video/cinepak.h" +#include "mohawk/video/qtrle.h" +#include "mohawk/video/rpza.h" +#include "mohawk/video/smc.h" + +namespace Mohawk { + +//////////////////////////////////////////// +// QTPlayer +//////////////////////////////////////////// + +QTPlayer::QTPlayer() : Graphics::VideoDecoder() { + _audStream = NULL; + _beginOffset = 0; + _videoCodec = NULL; + _curFrame = -1; + _startTime = _nextFrameStartTime = 0; + _audHandle = Audio::SoundHandle(); + _numStreams = 0; + _fd = 0; + _scaledSurface = 0; + _dirtyPalette = false; +} + +QTPlayer::~QTPlayer() { + close(); +} + +uint16 QTPlayer::getWidth() const { + if (_videoStreamIndex < 0) + return 0; + + return _streams[_videoStreamIndex]->width / getScaleMode(); +} + +uint16 QTPlayer::getHeight() const { + if (_videoStreamIndex < 0) + return 0; + + return _streams[_videoStreamIndex]->height / getScaleMode(); +} + +uint32 QTPlayer::getFrameCount() const { + if (_videoStreamIndex < 0) + return 0; + + return _streams[_videoStreamIndex]->nb_frames; +} + +byte QTPlayer::getBitsPerPixel() { + if (_videoStreamIndex < 0) + return 0; + + return _streams[_videoStreamIndex]->bits_per_sample & 0x1F; +} + +uint32 QTPlayer::getCodecTag() { + if (_videoStreamIndex < 0) + return 0; + + return _streams[_videoStreamIndex]->codec_tag; +} + +ScaleMode QTPlayer::getScaleMode() const { + if (_videoStreamIndex < 0) + return kScaleNormal; + + return (ScaleMode)(_scaleMode * _streams[_videoStreamIndex]->scaleMode); +} + +uint32 QTPlayer::getFrameDuration() { + if (_videoStreamIndex < 0) + return 0; + + uint32 curFrameIndex = 0; + for (int32 i = 0; i < _streams[_videoStreamIndex]->stts_count; i++) { + curFrameIndex += _streams[_videoStreamIndex]->stts_data[i].count; + if ((uint32)_curFrame < curFrameIndex) { + // Ok, now we have what duration this frame has. + return _streams[_videoStreamIndex]->stts_data[i].duration; + } + } + + // This should never occur + error ("Cannot find duration for frame %d", _curFrame); + return 0; +} + +Graphics::PixelFormat QTPlayer::getPixelFormat() const { + if (!_videoCodec) + return Graphics::PixelFormat::createFormatCLUT8(); + + return _videoCodec->getPixelFormat(); +} + +void QTPlayer::rewind() { + delete _videoCodec; _videoCodec = NULL; + _curFrame = -1; + _startTime = _nextFrameStartTime = 0; + + // Restart the audio too + stopAudio(); + if (_audioStreamIndex >= 0) { + _curAudioChunk = 0; + _audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2); + } + startAudio(); +} + +Graphics::Codec *QTPlayer::createCodec(uint32 codecTag, byte bitsPerPixel) { + if (codecTag == MKID_BE('cvid')) { + // Cinepak: As used by most Myst and all Riven videos as well as some Myst ME videos. "The Chief" videos also use this. + return new CinepakDecoder(); + } else if (codecTag == MKID_BE('rpza')) { + // Apple Video ("Road Pizza"): Used by some Myst videos. + return new RPZADecoder(getWidth(), getHeight()); + } else if (codecTag == MKID_BE('rle ')) { + // QuickTime RLE: Used by some Myst ME videos. + return new QTRLEDecoder(getWidth(), getHeight(), bitsPerPixel); + } else if (codecTag == MKID_BE('smc ')) { + // Apple SMC: Used by some Myst videos. + return new SMCDecoder(getWidth(), getHeight()); + } else if (codecTag == MKID_BE('SVQ1')) { + // Sorenson Video 1: Used by some Myst ME videos. + warning ("Sorenson Video 1 not yet supported"); + } else if (codecTag == MKID_BE('SVQ3')) { + // Sorenson Video 3: Used by some Myst ME videos. + warning ("Sorenson Video 3 not yet supported"); + } else if (codecTag == MKID_BE('jpeg')) { + // Motion JPEG: Used by some Myst ME 10th Anniversary videos. + return new JPEGDecoder(true); + } else if (codecTag == MKID_BE('QkBk')) { + // CDToons: Used by most of the Broderbund games. This is an unknown format so far. + warning ("CDToons not yet supported"); + } else { + warning ("Unsupported codec \'%s\'", tag2str(codecTag)); + } + + return NULL; +} + +void QTPlayer::startAudio() { + if (_audStream) // No audio/audio not supported + g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &_audHandle, _audStream); +} + +void QTPlayer::stopAudio() { + if (_audStream) { + g_system->getMixer()->stopHandle(_audHandle); + _audStream = NULL; // the mixer automatically frees the stream + } +} + +void QTPlayer::pauseVideoIntern(bool pause) { + if (_audStream) + g_system->getMixer()->pauseHandle(_audHandle, pause); +} + +Graphics::Surface *QTPlayer::decodeNextFrame() { + if (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1) + return NULL; + + if (_startTime == 0) + _startTime = g_system->getMillis(); + + _curFrame++; + _nextFrameStartTime += getFrameDuration(); + + Common::SeekableReadStream *frameData = getNextFramePacket(); + + if (frameData) { + Graphics::Surface *frame = _videoCodec->decodeImage(frameData); + delete frameData; + return scaleSurface(frame); + } + + return NULL; +} + +Graphics::Surface *QTPlayer::scaleSurface(Graphics::Surface *frame) { + if (getScaleMode() == kScaleNormal) + return frame; + + assert(_scaledSurface); + + for (uint32 j = 0; j < _scaledSurface->h; j++) + for (uint32 k = 0; k < _scaledSurface->w; k++) + memcpy(_scaledSurface->getBasePtr(k, j), frame->getBasePtr(k * getScaleMode(), j * getScaleMode()), frame->bytesPerPixel); + + return _scaledSurface; +} + +bool QTPlayer::endOfVideo() const { + return (!_audStream || _audStream->endOfData()) && (!_videoCodec || _curFrame >= (int32)getFrameCount() - 1); +} + +bool QTPlayer::needsUpdate() const { + return !endOfVideo() && getTimeToNextFrame() == 0; +} + +uint32 QTPlayer::getElapsedTime() const { + if (_audStream) + return g_system->getMixer()->getSoundElapsedTime(_audHandle); + + return g_system->getMillis() - _startTime; +} + +uint32 QTPlayer::getTimeToNextFrame() const { + if (endOfVideo() || _curFrame < 0) + return 0; + + // Convert from the Sega FILM base to 1000 + uint32 nextFrameStartTime = _nextFrameStartTime * 1000 / _streams[_videoStreamIndex]->time_scale; + uint32 elapsedTime = getElapsedTime(); + + if (nextFrameStartTime <= elapsedTime) + return 0; + + return nextFrameStartTime - elapsedTime; +} + +bool QTPlayer::load(Common::SeekableReadStream &stream) { + _fd = &stream; + _foundMOOV = _foundMDAT = false; + _numStreams = 0; + _partial = 0; + _videoStreamIndex = _audioStreamIndex = -1; + _startTime = 0; + + initParseTable(); + + MOVatom atom = { 0, 0, 0xffffffff }; + + if (readDefault(atom) < 0 || (!_foundMOOV && !_foundMDAT)) + return false; + + debug(0, "on_parse_exit_offset=%d", _fd->pos()); + + // some cleanup : make sure we are on the mdat atom + if((uint32)_fd->pos() != _mdatOffset) + _fd->seek(_mdatOffset, SEEK_SET); + + _next_chunk_offset = _mdatOffset; // initialise reading + + for (uint32 i = 0; i < _numStreams;) { + if (_streams[i]->codec_type == CODEC_TYPE_MOV_OTHER) {// not audio, not video, delete + delete _streams[i]; + for (uint32 j = i + 1; j < _numStreams; j++) + _streams[j - 1] = _streams[j]; + _numStreams--; + } else + i++; + } + + for (uint32 i = 0; i < _numStreams; i++) { + MOVStreamContext *sc = _streams[i]; + + if(!sc->time_rate) + sc->time_rate = 1; + + if(!sc->time_scale) + sc->time_scale = _timeScale; + + //av_set_pts_info(s->streams[i], 64, sc->time_rate, sc->time_scale); + + sc->duration /= sc->time_rate; + + sc->ffindex = i; + sc->is_ff_stream = 1; + + if (sc->codec_type == CODEC_TYPE_VIDEO && _videoStreamIndex < 0) + _videoStreamIndex = i; + else if (sc->codec_type == CODEC_TYPE_AUDIO && _audioStreamIndex < 0) + _audioStreamIndex = i; + } + + if (_audioStreamIndex >= 0 && checkAudioCodecSupport(_streams[_audioStreamIndex]->codec_tag)) { + _audStream = Audio::makeQueuingAudioStream(_streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels == 2); + _curAudioChunk = 0; + + // Make sure the bits per sample transfers to the sample size + if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('twos')) + _streams[_audioStreamIndex]->sample_size = (_streams[_audioStreamIndex]->bits_per_sample / 8) * _streams[_audioStreamIndex]->channels; + + startAudio(); + } + + if (_videoStreamIndex >= 0) { + _videoCodec = createCodec(getCodecTag(), getBitsPerPixel()); + + if (getScaleMode() != kScaleNormal) { + // We have to initialize the scaled surface + _scaledSurface = new Graphics::Surface(); + _scaledSurface->create(getWidth(), getHeight(), getPixelFormat().bytesPerPixel); + } + } + + return true; +} + +void QTPlayer::initParseTable() { + static const ParseTable p[] = { + { MKID_BE('dinf'), &QTPlayer::readDefault }, + { MKID_BE('dref'), &QTPlayer::readLeaf }, + { MKID_BE('edts'), &QTPlayer::readDefault }, + { MKID_BE('elst'), &QTPlayer::readELST }, + { MKID_BE('hdlr'), &QTPlayer::readHDLR }, + { MKID_BE('mdat'), &QTPlayer::readMDAT }, + { MKID_BE('mdhd'), &QTPlayer::readMDHD }, + { MKID_BE('mdia'), &QTPlayer::readDefault }, + { MKID_BE('minf'), &QTPlayer::readDefault }, + { MKID_BE('moov'), &QTPlayer::readMOOV }, + { MKID_BE('mvhd'), &QTPlayer::readMVHD }, + { MKID_BE('smhd'), &QTPlayer::readLeaf }, + { MKID_BE('stbl'), &QTPlayer::readDefault }, + { MKID_BE('stco'), &QTPlayer::readSTCO }, + { MKID_BE('stsc'), &QTPlayer::readSTSC }, + { MKID_BE('stsd'), &QTPlayer::readSTSD }, + { MKID_BE('stss'), &QTPlayer::readSTSS }, + { MKID_BE('stsz'), &QTPlayer::readSTSZ }, + { MKID_BE('stts'), &QTPlayer::readSTTS }, + { MKID_BE('tkhd'), &QTPlayer::readTKHD }, + { MKID_BE('trak'), &QTPlayer::readTRAK }, + { MKID_BE('udta'), &QTPlayer::readLeaf }, + { MKID_BE('vmhd'), &QTPlayer::readLeaf }, + { MKID_BE('cmov'), &QTPlayer::readCMOV }, + { MKID_BE('wave'), &QTPlayer::readWAVE }, + { 0, 0 } + }; + + _parseTable = p; +} + +int QTPlayer::readDefault(MOVatom atom) { + uint32 total_size = 0; + MOVatom a; + int err = 0; + + a.offset = atom.offset; + + while(((total_size + 8) < atom.size) && !_fd->eos() && !err) { + a.size = atom.size; + a.type = 0; + + if (atom.size >= 8) { + a.size = _fd->readUint32BE(); + a.type = _fd->readUint32BE(); + } + + total_size += 8; + a.offset += 8; + debug(4, "type: %08x %.4s sz: %x %x %x", a.type, tag2str(a.type), a.size, atom.size, total_size); + + if (a.size == 1) { // 64 bit extended size + warning("64 bit extended size is not supported in QuickTime"); + return -1; + } + + if (a.size == 0) { + a.size = atom.size - total_size; + if (a.size <= 8) + break; + } + + uint32 i = 0; + + for (; _parseTable[i].type != 0 && _parseTable[i].type != a.type; i++) + // empty; + + if (a.size < 8) + break; + + a.size -= 8; + + if (_parseTable[i].type == 0) { // skip leaf atoms data + debug(0, ">>> Skipped [%s]", tag2str(a.type)); + + _fd->seek(a.size, SEEK_CUR); + } else { + uint32 start_pos = _fd->pos(); + err = (this->*_parseTable[i].func)(a); + + uint32 left = a.size - _fd->pos() + start_pos; + + if (left > 0) // skip garbage at atom end + _fd->seek(left, SEEK_CUR); + } + + a.offset += a.size; + total_size += a.size; + } + + if (!err && total_size < atom.size) + _fd->seek(atom.size - total_size, SEEK_SET); + + return err; +} + +int QTPlayer::readLeaf(MOVatom atom) { + if (atom.size > 1) + _fd->seek(atom.size, SEEK_SET); + + return 0; +} + +int QTPlayer::readMOOV(MOVatom atom) { + if (readDefault(atom) < 0) + return -1; + + // we parsed the 'moov' atom, we can terminate the parsing as soon as we find the 'mdat' + // so we don't parse the whole file if over a network + _foundMOOV = true; + + if(_foundMDAT) + return 1; // found both, just go + + return 0; // now go for mdat +} + +int QTPlayer::readCMOV(MOVatom atom) { +#ifdef USE_ZLIB + // Read in the dcom atom + _fd->readUint32BE(); + if (_fd->readUint32BE() != MKID_BE('dcom')) + return -1; + if (_fd->readUint32BE() != MKID_BE('zlib')) { + warning("Unknown cmov compression type"); + return -1; + } + + // Read in the cmvd atom + uint32 compressedSize = _fd->readUint32BE() - 12; + if (_fd->readUint32BE() != MKID_BE('cmvd')) + return -1; + uint32 uncompressedSize = _fd->readUint32BE(); + + // Read in data + byte *compressedData = (byte *)malloc(compressedSize); + _fd->read(compressedData, compressedSize); + + // Create uncompressed stream + byte *uncompressedData = (byte *)malloc(uncompressedSize); + + // Uncompress the data + unsigned long dstLen = uncompressedSize; + if (!Common::uncompress(uncompressedData, &dstLen, compressedData, compressedSize)) { + warning ("Could not uncompress cmov chunk"); + return -1; + } + + // Load data into a new MemoryReadStream and assign _fd to be that + Common::SeekableReadStream *oldStream = _fd; + _fd = new Common::MemoryReadStream(uncompressedData, uncompressedSize, DisposeAfterUse::YES); + + // Read the contents of the uncompressed data + MOVatom a = { MKID_BE('moov'), 0, uncompressedSize }; + int err = readDefault(a); + + // Assign the file handle back to the original handle + free(compressedData); + delete _fd; + _fd = oldStream; + + return err; +#else + warning ("zlib not found, cannot read QuickTime cmov atom"); + return -1; +#endif +} + +int QTPlayer::readMVHD(MOVatom atom) { + byte version = _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + if (version == 1) { + warning("QuickTime version 1"); + _fd->readUint32BE(); _fd->readUint32BE(); + _fd->readUint32BE(); _fd->readUint32BE(); + } else { + _fd->readUint32BE(); // creation time + _fd->readUint32BE(); // modification time + } + + _timeScale = _fd->readUint32BE(); // time scale + debug(0, "time scale = %i\n", _timeScale); + + // duration + _duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); + _fd->readUint32BE(); // preferred scale + + _fd->readUint16BE(); // preferred volume + + _fd->seek(10, SEEK_CUR); // reserved + + // We only need two values from the movie display matrix. Most of the values are just + // skipped. xMod and yMod are 16:16 fixed point numbers, the last part of the 3x3 matrix + // is 2:30. + uint32 xMod = _fd->readUint32BE(); + _fd->skip(12); + uint32 yMod = _fd->readUint32BE(); + _fd->skip(16); + + if (xMod != yMod) + error("X and Y resolution modifiers differ"); + + if (xMod == 0x8000) + _scaleMode = kScaleHalf; + else if (xMod == 0x4000) + _scaleMode = kScaleQuarter; + else + _scaleMode = kScaleNormal; + + debug(1, "readMVHD(): scaleMode = %d", (int)_scaleMode); + + _fd->readUint32BE(); // preview time + _fd->readUint32BE(); // preview duration + _fd->readUint32BE(); // poster time + _fd->readUint32BE(); // selection time + _fd->readUint32BE(); // selection duration + _fd->readUint32BE(); // current time + _fd->readUint32BE(); // next track ID + + return 0; +} + +int QTPlayer::readTRAK(MOVatom atom) { + MOVStreamContext *sc = new MOVStreamContext(); + + if (!sc) + return -1; + + sc->sample_to_chunk_index = -1; + sc->codec_type = CODEC_TYPE_MOV_OTHER; + sc->start_time = 0; // XXX: check + _streams[_numStreams++] = sc; + + return readDefault(atom); +} + +// this atom contains actual media data +int QTPlayer::readMDAT(MOVatom atom) { + if (atom.size == 0) // wrong one (MP4) + return 0; + + _foundMDAT = true; + + _mdatOffset = atom.offset; + _mdatSize = atom.size; + + if (_foundMOOV) + return 1; // found both, just go + + _fd->seek(atom.size, SEEK_CUR); + + return 0; // now go for moov +} + +int QTPlayer::readTKHD(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + byte version = _fd->readByte(); + + _fd->readByte(); _fd->readByte(); + _fd->readByte(); // flags + // + //MOV_TRACK_ENABLED 0x0001 + //MOV_TRACK_IN_MOVIE 0x0002 + //MOV_TRACK_IN_PREVIEW 0x0004 + //MOV_TRACK_IN_POSTER 0x0008 + // + + if (version == 1) { + _fd->readUint32BE(); _fd->readUint32BE(); + _fd->readUint32BE(); _fd->readUint32BE(); + } else { + _fd->readUint32BE(); // creation time + _fd->readUint32BE(); // modification time + } + + /* st->id = */_fd->readUint32BE(); // track id (NOT 0 !) + _fd->readUint32BE(); // reserved + //st->start_time = 0; // check + (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // highlevel (considering edits) duration in movie timebase + _fd->readUint32BE(); // reserved + _fd->readUint32BE(); // reserved + + _fd->readUint16BE(); // layer + _fd->readUint16BE(); // alternate group + _fd->readUint16BE(); // volume + _fd->readUint16BE(); // reserved + + // We only need the two values from the displacement matrix for a track. + // See readMVHD() for more information. + uint32 xMod = _fd->readUint32BE(); + _fd->skip(12); + uint32 yMod = _fd->readUint32BE(); + _fd->skip(16); + + if (xMod != yMod) + error("X and Y resolution modifiers differ"); + + if (xMod == 0x8000) + st->scaleMode = kScaleHalf; + else if (xMod == 0x4000) + st->scaleMode = kScaleQuarter; + else + st->scaleMode = kScaleNormal; + + debug(1, "readTKHD(): scaleMode = %d", (int)_scaleMode); + + // these are fixed-point, 16:16 + // uint32 tkWidth = _fd->readUint32BE() >> 16; // track width + // uint32 tkHeight = _fd->readUint32BE() >> 16; // track height + + return 0; +} + +// edit list atom +int QTPlayer::readELST(MOVatom atom) { + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + uint32 editCount = _streams[_numStreams - 1]->edit_count = _fd->readUint32BE(); // entries + + for (uint32 i = 0; i < editCount; i++){ + _fd->readUint32BE(); // Track duration + _fd->readUint32BE(); // Media time + _fd->readUint32BE(); // Media rate + } + + debug(0, "track[%i].edit_count = %i", _numStreams - 1, _streams[_numStreams - 1]->edit_count); + + if (editCount != 1) + warning("Multiple edit list entries. Things may go awry"); + + return 0; +} + +int QTPlayer::readHDLR(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + // component type + uint32 ctype = _fd->readUint32LE(); + uint32 type = _fd->readUint32BE(); // component subtype + + debug(0, "ctype= %s (0x%08lx)", tag2str(ctype), (long)ctype); + debug(0, "stype= %s", tag2str(type)); + + if(ctype == MKID_BE('mhlr')) // MOV + debug(0, "MOV detected"); + else if(ctype == 0) { + warning("MP4 streams are not supported"); + return -1; + } + + if (type == MKID_BE('vide')) + st->codec_type = CODEC_TYPE_VIDEO; + else if (type == MKID_BE('soun')) + st->codec_type = CODEC_TYPE_AUDIO; + + _fd->readUint32BE(); // component manufacture + _fd->readUint32BE(); // component flags + _fd->readUint32BE(); // component flags mask + + if (atom.size <= 24) + return 0; // nothing left to read + + // .mov: PASCAL string + byte len = _fd->readByte(); + _fd->seek(len, SEEK_CUR); + + _fd->seek(atom.size - (_fd->pos() - atom.offset), SEEK_CUR); + + return 0; +} + +int QTPlayer::readMDHD(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + byte version = _fd->readByte(); + + if (version > 1) + return 1; // unsupported + + _fd->readByte(); _fd->readByte(); + _fd->readByte(); // flags + + if (version == 1) { + _fd->readUint32BE(); _fd->readUint32BE(); + _fd->readUint32BE(); _fd->readUint32BE(); + } else { + _fd->readUint32BE(); // creation time + _fd->readUint32BE(); // modification time + } + + st->time_scale = _fd->readUint32BE(); + st->duration = (version == 1) ? (_fd->readUint32BE(), _fd->readUint32BE()) : _fd->readUint32BE(); // duration + + _fd->readUint16BE(); // language + _fd->readUint16BE(); // quality + + return 0; +} + +int QTPlayer::readSTSD(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + uint32 entries = _fd->readUint32BE(); + + while (entries--) { //Parsing Sample description table + MOVatom a = { 0, 0, 0 }; + uint32 start_pos = _fd->pos(); + int size = _fd->readUint32BE(); // size + uint32 format = _fd->readUint32BE(); // data format + + _fd->readUint32BE(); // reserved + _fd->readUint16BE(); // reserved + _fd->readUint16BE(); // index + + debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), st->codec_type); + st->codec_tag = format; + + if (st->codec_type == CODEC_TYPE_VIDEO) { + debug(0, "Video Codec FourCC: \'%s\'", tag2str(format)); + + _fd->readUint16BE(); // version + _fd->readUint16BE(); // revision level + _fd->readUint32BE(); // vendor + _fd->readUint32BE(); // temporal quality + _fd->readUint32BE(); // spacial quality + + st->width = _fd->readUint16BE(); // width + st->height = _fd->readUint16BE(); // height + + _fd->readUint32BE(); // horiz resolution + _fd->readUint32BE(); // vert resolution + _fd->readUint32BE(); // data size, always 0 + uint16 frames_per_sample = _fd->readUint16BE(); // frames per samples + + debug(0, "frames/samples = %d", frames_per_sample); + + byte codec_name[32]; + _fd->read(codec_name, 32); // codec name, pascal string (FIXME: true for mp4?) + if (codec_name[0] <= 31) { + memcpy(st->codec_name, &codec_name[1], codec_name[0]); + st->codec_name[codec_name[0]] = 0; + } + + st->bits_per_sample = _fd->readUint16BE(); // depth + st->color_table_id = _fd->readUint16BE(); // colortable id + +// These are set in mov_read_stts and might already be set! +// st->codec->time_base.den = 25; +// st->codec->time_base.num = 1; + + + // figure out the palette situation + byte colorDepth = st->bits_per_sample & 0x1F; + bool colorGreyscale = (st->bits_per_sample & 0x20) != 0; + + debug(0, "color depth: %d", colorDepth); + + // if the depth is 2, 4, or 8 bpp, file is palettized + if (colorDepth == 2 || colorDepth == 4 || colorDepth == 8) { + _dirtyPalette = true; + + if (colorGreyscale) { + debug(0, "Greyscale palette"); + + // compute the greyscale palette + uint16 colorCount = 1 << colorDepth; + int16 colorIndex = 255; + byte colorDec = 256 / (colorCount - 1); + for (byte j = 0; j < colorCount; j++) { + _palette[j * 3] = _palette[j * 3 + 1] = _palette[j * 3 + 2] = colorIndex; + colorIndex -= colorDec; + if (colorIndex < 0) + colorIndex = 0; + } + } else if (st->color_table_id & 0x08) { + // if flag bit 3 is set, use the default palette + //uint16 colorCount = 1 << colorDepth; + + warning("Predefined palette! %dbpp", colorDepth); +#if 0 + byte *color_table; + byte r, g, b; + + if (colorDepth == 2) + color_table = ff_qt_default_palette_4; + else if (colorDepth == 4) + color_table = ff_qt_default_palette_16; + else + color_table = ff_qt_default_palette_256; + + for (byte j = 0; j < color_count; j++) { + r = color_table[j * 4 + 0]; + g = color_table[j * 4 + 1]; + b = color_table[j * 4 + 2]; + _palette_control.palette[j] = (r << 16) | (g << 8) | (b); + } +#endif + + } else { + debug(0, "Palette from file"); + + // load the palette from the file + uint32 colorStart = _fd->readUint32BE(); + /* uint16 colorCount = */ _fd->readUint16BE(); + uint16 colorEnd = _fd->readUint16BE(); + for (uint32 j = colorStart; j <= colorEnd; j++) { + // each R, G, or B component is 16 bits; + // only use the top 8 bits; skip alpha bytes + // up front + _fd->readByte(); + _fd->readByte(); + _palette[j * 3] = _fd->readByte(); + _fd->readByte(); + _palette[j * 3 + 1] = _fd->readByte(); + _fd->readByte(); + _palette[j * 3 + 2] = _fd->readByte(); + _fd->readByte(); + } + } + st->palettized = true; + } else + st->palettized = false; + } else if (st->codec_type == CODEC_TYPE_AUDIO) { + debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format)); + + st->stsd_version = _fd->readUint16BE(); + _fd->readUint16BE(); // revision level + _fd->readUint32BE(); // vendor + + st->channels = _fd->readUint16BE(); // channel count + st->bits_per_sample = _fd->readUint16BE(); // sample size + // do we need to force to 16 for AMR ? + + // handle specific s8 codec + _fd->readUint16BE(); // compression id = 0 + _fd->readUint16BE(); // packet size = 0 + + st->sample_rate = (_fd->readUint32BE() >> 16); + + debug(0, "stsd version =%d", st->stsd_version); + if (st->stsd_version == 0) { + // Not used, except in special cases. See below. + st->samples_per_frame = st->bytes_per_frame = 0; + } else if (st->stsd_version == 1) { + // Read QT version 1 fields. In version 0 these dont exist. + st->samples_per_frame = _fd->readUint32BE(); + debug(0, "stsd samples_per_frame =%d", st->samples_per_frame); + _fd->readUint32BE(); // bytes per packet + st->bytes_per_frame = _fd->readUint32BE(); + debug(0, "stsd bytes_per_frame =%d", st->bytes_per_frame); + _fd->readUint32BE(); // bytes per sample + } else { + warning("Unsupported QuickTime STSD audio version %d", st->stsd_version); + return 1; + } + + // Version 0 videos (such as the Riven ones) don't have this set, + // but we need it later on. Add it in here. + if (format == MKID_BE('ima4')) { + st->samples_per_frame = 64; + st->bytes_per_frame = 34 * st->channels; + } + } else { + // other codec type, just skip (rtp, mp4s, tmcd ...) + _fd->seek(size - (_fd->pos() - start_pos), SEEK_CUR); + } + + // this will read extra atoms at the end (wave, alac, damr, avcC, SMI ...) + a.size = size - (_fd->pos() - start_pos); + if (a.size > 8) + readDefault(a); + else if (a.size > 0) + _fd->seek(a.size, SEEK_CUR); + } + + if (st->codec_type == CODEC_TYPE_AUDIO && st->sample_rate == 0 && st->time_scale > 1) + st->sample_rate= st->time_scale; + + return 0; +} + +int QTPlayer::readSTSC(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + st->sample_to_chunk_sz = _fd->readUint32BE(); + + debug(0, "track[%i].stsc.entries = %i", _numStreams - 1, st->sample_to_chunk_sz); + + st->sample_to_chunk = new MOVstsc[st->sample_to_chunk_sz]; + + if (!st->sample_to_chunk) + return -1; + + for (uint32 i = 0; i < st->sample_to_chunk_sz; i++) { + st->sample_to_chunk[i].first = _fd->readUint32BE(); + st->sample_to_chunk[i].count = _fd->readUint32BE(); + st->sample_to_chunk[i].id = _fd->readUint32BE(); + //printf ("Sample to Chunk[%d]: First = %d, Count = %d\n", i, st->sample_to_chunk[i].first, st->sample_to_chunk[i].count); + } + + return 0; +} + +int QTPlayer::readSTSS(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + st->keyframe_count = _fd->readUint32BE(); + + debug(0, "keyframe_count = %d", st->keyframe_count); + + st->keyframes = new uint32[st->keyframe_count]; + + if (!st->keyframes) + return -1; + + for (uint32 i = 0; i < st->keyframe_count; i++) { + st->keyframes[i] = _fd->readUint32BE(); + debug(6, "keyframes[%d] = %d", i, st->keyframes[i]); + + } + return 0; +} + +int QTPlayer::readSTSZ(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + st->sample_size = _fd->readUint32BE(); + st->sample_count = _fd->readUint32BE(); + + debug(5, "sample_size = %d sample_count = %d", st->sample_size, st->sample_count); + + if (st->sample_size) + return 0; // there isn't any table following + + st->sample_sizes = new uint32[st->sample_count]; + + if (!st->sample_sizes) + return -1; + + for(uint32 i = 0; i < st->sample_count; i++) { + st->sample_sizes[i] = _fd->readUint32BE(); + debug(6, "sample_sizes[%d] = %d", i, st->sample_sizes[i]); + } + + return 0; +} + +static uint32 ff_gcd(uint32 a, uint32 b) { + if(b) return ff_gcd(b, a%b); + else return a; +} + +int QTPlayer::readSTTS(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + uint32 duration = 0; + uint32 total_sample_count = 0; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + st->stts_count = _fd->readUint32BE(); + st->stts_data = new MOVstts[st->stts_count]; + + debug(0, "track[%i].stts.entries = %i", _numStreams - 1, st->stts_count); + + st->time_rate = 0; + + for (int32 i = 0; i < st->stts_count; i++) { + int sample_duration; + int sample_count; + + sample_count = _fd->readUint32BE(); + sample_duration = _fd->readUint32BE(); + st->stts_data[i].count = sample_count; + st->stts_data[i].duration = sample_duration; + + st->time_rate = ff_gcd(st->time_rate, sample_duration); + + debug(0, "sample_count=%d, sample_duration=%d", sample_count, sample_duration); + + duration += sample_duration * sample_count; + total_sample_count += sample_count; + } + + st->nb_frames = total_sample_count; + + if (duration) + st->duration = duration; + + return 0; +} + +int QTPlayer::readSTCO(MOVatom atom) { + MOVStreamContext *st = _streams[_numStreams - 1]; + + _fd->readByte(); // version + _fd->readByte(); _fd->readByte(); _fd->readByte(); // flags + + st->chunk_count = _fd->readUint32BE(); + st->chunk_offsets = new uint32[st->chunk_count]; + + if (!st->chunk_offsets) + return -1; + + for (uint32 i = 0; i < st->chunk_count; i++) { + // WORKAROUND/HACK: The offsets in Riven videos (ones inside the Mohawk archives themselves) + // have offsets relative to the archive and not the video. This is quite nasty. We subtract + // the initial offset of the stream to get the correct value inside of the stream. + st->chunk_offsets[i] = _fd->readUint32BE() - _beginOffset; + } + + for (uint32 i = 0; i < _numStreams; i++) { + MOVStreamContext *sc2 = _streams[i]; + + if(sc2 && sc2->chunk_offsets){ + uint32 first = sc2->chunk_offsets[0]; + uint32 last = sc2->chunk_offsets[sc2->chunk_count - 1]; + + if(first >= st->chunk_offsets[st->chunk_count - 1] || last <= st->chunk_offsets[0]) + _ni = 1; + } + } + + return 0; +} + +int QTPlayer::readWAVE(MOVatom atom) { + if (_numStreams < 1) + return 0; + + MOVStreamContext *st = _streams[_numStreams - 1]; + + if (atom.size > (1 << 30)) + return -1; + + if (st->codec_tag == MKID_BE('QDM2')) // Read extradata for QDM2 + st->extradata = _fd->readStream(atom.size - 8); + else if (atom.size > 8) + return readDefault(atom); + else + _fd->skip(atom.size); + + return 0; +} + +void QTPlayer::close() { + stopAudio(); + + delete _videoCodec; _videoCodec = 0; + + for (uint32 i = 0; i < _numStreams; i++) + delete _streams[i]; + + delete _fd; + + if (_scaledSurface) { + _scaledSurface->free(); + delete _scaledSurface; + _scaledSurface = 0; + } + + // The audio stream is deleted automatically + _audStream = NULL; + + Graphics::VideoDecoder::reset(); +} + +Common::SeekableReadStream *QTPlayer::getNextFramePacket() { + if (_videoStreamIndex < 0) + return NULL; + + // First, we have to track down which chunk holds the sample and which sample in the chunk contains the frame we are looking for. + int32 totalSampleCount = 0; + int32 sampleInChunk = 0; + int32 actualChunk = -1; + + for (uint32 i = 0; i < _streams[_videoStreamIndex]->chunk_count; i++) { + int32 sampleToChunkIndex = -1; + + for (uint32 j = 0; j < _streams[_videoStreamIndex]->sample_to_chunk_sz; j++) + if (i >= _streams[_videoStreamIndex]->sample_to_chunk[j].first - 1) + sampleToChunkIndex = j; + + if (sampleToChunkIndex < 0) + error("This chunk (%d) is imaginary", sampleToChunkIndex); + + totalSampleCount += _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count; + + if (totalSampleCount > getCurFrame()) { + actualChunk = i; + sampleInChunk = _streams[_videoStreamIndex]->sample_to_chunk[sampleToChunkIndex].count - totalSampleCount + getCurFrame(); + break; + } + } + + if (actualChunk < 0) { + warning ("Could not find data for frame %d", getCurFrame()); + return NULL; + } + + // Next seek to that frame + _fd->seek(_streams[_videoStreamIndex]->chunk_offsets[actualChunk]); + + // Then, if the chunk holds more than one frame, seek to where the frame we want is located + for (int32 i = getCurFrame() - sampleInChunk; i < getCurFrame(); i++) { + if (_streams[_videoStreamIndex]->sample_size != 0) + _fd->skip(_streams[_videoStreamIndex]->sample_size); + else + _fd->skip(_streams[_videoStreamIndex]->sample_sizes[i]); + } + + // Finally, read in the raw data for the frame + //printf ("Frame Data[%d]: Offset = %d, Size = %d\n", getCurFrame(), _fd->pos(), _streams[_videoStreamIndex]->sample_sizes[getCurFrame()]); + + if (_streams[_videoStreamIndex]->sample_size != 0) + return _fd->readStream(_streams[_videoStreamIndex]->sample_size); + + return _fd->readStream(_streams[_videoStreamIndex]->sample_sizes[getCurFrame()]); +} + +bool QTPlayer::checkAudioCodecSupport(uint32 tag) { + // Check if the codec is a supported codec + if (tag == MKID_BE('twos') || tag == MKID_BE('raw ') || tag == MKID_BE('ima4') || tag == MKID_BE('QDM2')) + return true; + + warning("Audio Codec Not Supported: \'%s\'", tag2str(tag)); + + return false; +} + +Audio::AudioStream *QTPlayer::createAudioStream(Common::SeekableReadStream *stream) { + if (!stream || _audioStreamIndex < 0) + return NULL; + + if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('twos') || _streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) { + // Fortunately, most of the audio used in Myst videos is raw... + uint16 flags = 0; + if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('raw ')) + flags |= Audio::FLAG_UNSIGNED; + if (_streams[_audioStreamIndex]->channels == 2) + flags |= Audio::FLAG_STEREO; + if (_streams[_audioStreamIndex]->bits_per_sample == 16) + flags |= Audio::FLAG_16BITS; + uint32 dataSize = stream->size(); + byte *data = (byte *)malloc(dataSize); + stream->read(data, dataSize); + delete stream; + return Audio::makeRawStream(data, dataSize, _streams[_audioStreamIndex]->sample_rate, flags); + } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('ima4')) { + // Riven uses this codec (as do some Myst ME videos) + return Audio::makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), Audio::kADPCMApple, _streams[_audioStreamIndex]->sample_rate, _streams[_audioStreamIndex]->channels, 34); + } else if (_streams[_audioStreamIndex]->codec_tag == MKID_BE('QDM2')) { + // Several Myst ME videos use this codec + return new QDM2Stream(stream, _streams[_audioStreamIndex]->extradata); + } + + error("Unsupported audio codec"); + + return NULL; +} + +void QTPlayer::updateAudioBuffer() { + if (!_audStream) + return; + + // Keep two streams in buffer so that when the first ends, it goes right into the next + for (; _audStream->numQueuedStreams() < 2 && _curAudioChunk < _streams[_audioStreamIndex]->chunk_count; _curAudioChunk++) { + Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(); + + _fd->seek(_streams[_audioStreamIndex]->chunk_offsets[_curAudioChunk]); + + // First, we have to get the sample count + uint32 sampleCount = 0; + for (uint32 j = 0; j < _streams[_audioStreamIndex]->sample_to_chunk_sz; j++) + if (_curAudioChunk >= (_streams[_audioStreamIndex]->sample_to_chunk[j].first - 1)) + sampleCount = _streams[_audioStreamIndex]->sample_to_chunk[j].count; + assert(sampleCount); + + // Then calculate the right sizes + while (sampleCount > 0) { + uint32 samples = 0, size = 0; + + if (_streams[_audioStreamIndex]->samples_per_frame >= 160) { + samples = _streams[_audioStreamIndex]->samples_per_frame; + size = _streams[_audioStreamIndex]->bytes_per_frame; + } else if (_streams[_audioStreamIndex]->samples_per_frame > 1) { + samples = MIN<uint32>((1024 / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->samples_per_frame, sampleCount); + size = (samples / _streams[_audioStreamIndex]->samples_per_frame) * _streams[_audioStreamIndex]->bytes_per_frame; + } else { + samples = MIN<uint32>(1024, sampleCount); + size = samples * _streams[_audioStreamIndex]->sample_size; + } + + // Now, we read in the data for this data and output it + byte *data = (byte *)malloc(size); + _fd->read(data, size); + wStream->write(data, size); + free(data); + sampleCount -= samples; + } + + // Now queue the buffer + _audStream->queueAudioStream(createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES))); + delete wStream; + } +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/qt_player.h b/engines/mohawk/video/qt_player.h new file mode 100644 index 0000000000..6657d3edba --- /dev/null +++ b/engines/mohawk/video/qt_player.h @@ -0,0 +1,282 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// +// Heavily based on ffmpeg code. +// +// Copyright (c) 2001 Fabrice Bellard. +// First version by Francois Revol revol@free.fr +// Seek function by Gael Chardon gael.dev@4now.net +// + +#ifndef MOHAWK_QT_PLAYER_H +#define MOHAWK_QT_PLAYER_H + +#include "common/scummsys.h" +#include "common/queue.h" + +#include "graphics/video/video_decoder.h" +#include "graphics/video/codecs/codec.h" + +#include "sound/audiostream.h" +#include "sound/mixer.h" + +namespace Common { + class File; +} + +namespace Mohawk { + +enum ScaleMode { + kScaleNormal = 1, + kScaleHalf = 2, + kScaleQuarter = 4 +}; + +class QTPlayer : public Graphics::RewindableVideoDecoder { +public: + QTPlayer(); + virtual ~QTPlayer(); + + /** + * Returns the width of the video + * @return the width of the video + */ + uint16 getWidth() const; + + /** + * Returns the height of the video + * @return the height of the video + */ + uint16 getHeight() const; + + /** + * Returns the amount of frames in the video + * @return the amount of frames in the video + */ + uint32 getFrameCount() const; + + /** + * Load a QuickTime video file from a SeekableReadStream + * @param stream the stream to load + */ + bool load(Common::SeekableReadStream &stream); + + /** + * Close a QuickTime encoded video file + */ + void close(); + + /** + * Returns the palette of the video + * @return the palette of the video + */ + byte *getPalette() { _dirtyPalette = false; return _palette; } + bool hasDirtyPalette() const { return _dirtyPalette; } + + /** + * Set the beginning offset of the video so we can modify the offsets in the stco + * atom of videos inside the Mohawk archives + * @param the beginning offset of the video + */ + void setChunkBeginOffset(uint32 offset) { _beginOffset = offset; } + + bool isVideoLoaded() const { return _fd != 0; } + Graphics::Surface *decodeNextFrame(); + bool needsUpdate() const; + bool endOfVideo() const; + uint32 getElapsedTime() const; + uint32 getTimeToNextFrame() const; + Graphics::PixelFormat getPixelFormat() const; + + // RewindableVideoDecoder API + void rewind(); + + // TODO: This audio function need to be removed from the public and/or added to + // the VideoDecoder API directly. I plan on replacing this function with something + // that can figure out how much audio is needed instead of constantly keeping two + // chunks in memory. + void updateAudioBuffer(); + +protected: + // This is the file handle from which data is read from. It can be the actual file handle or a decompressed stream. + Common::SeekableReadStream *_fd; + + struct MOVatom { + uint32 type; + uint32 offset; + uint32 size; + }; + + struct ParseTable { + uint32 type; + int (QTPlayer::*func)(MOVatom atom); + }; + + struct MOVstts { + int count; + int duration; + }; + + struct MOVstsc { + uint32 first; + uint32 count; + uint32 id; + }; + + enum CodecType { + CODEC_TYPE_MOV_OTHER, + CODEC_TYPE_VIDEO, + CODEC_TYPE_AUDIO + }; + + struct MOVStreamContext { + MOVStreamContext() { memset(this, 0, sizeof(MOVStreamContext)); } + ~MOVStreamContext() { + delete[] chunk_offsets; + delete[] stts_data; + delete[] ctts_data; + delete[] sample_to_chunk; + delete[] sample_sizes; + delete[] keyframes; + delete extradata; + } + + int ffindex; /* the ffmpeg stream id */ + int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */ + uint32 next_chunk; + uint32 chunk_count; + uint32 *chunk_offsets; + int stts_count; + MOVstts *stts_data; + int ctts_count; + MOVstts *ctts_data; + int edit_count; /* number of 'edit' (elst atom) */ + uint32 sample_to_chunk_sz; + MOVstsc *sample_to_chunk; + int32 sample_to_chunk_index; + int sample_to_time_index; + uint32 sample_to_time_sample; + uint32 sample_to_time_time; + int sample_to_ctime_index; + int sample_to_ctime_sample; + uint32 sample_size; + uint32 sample_count; + uint32 *sample_sizes; + uint32 keyframe_count; + uint32 *keyframes; + int32 time_scale; + int time_rate; + uint32 current_sample; + uint32 left_in_chunk; /* how many samples before next chunk */ + + uint16 width; + uint16 height; + int codec_type; + uint32 codec_tag; + char codec_name[32]; + uint16 bits_per_sample; + uint16 color_table_id; + bool palettized; + Common::SeekableReadStream *extradata; + + uint16 stsd_version; + uint16 channels; + uint16 sample_rate; + uint32 samples_per_frame; + uint32 bytes_per_frame; + + uint32 nb_frames; + uint32 duration; + uint32 start_time; + ScaleMode scaleMode; + }; + + const ParseTable *_parseTable; + bool _foundMOOV; + bool _foundMDAT; + uint32 _timeScale; + uint32 _duration; + uint32 _mdatOffset; + uint32 _mdatSize; + uint32 _next_chunk_offset; + MOVStreamContext *_partial; + uint32 _numStreams; + int _ni; + ScaleMode _scaleMode; + MOVStreamContext *_streams[20]; + byte _palette[256 * 3]; + bool _dirtyPalette; + uint32 _beginOffset; + + void initParseTable(); + Audio::AudioStream *createAudioStream(Common::SeekableReadStream *stream); + bool checkAudioCodecSupport(uint32 tag); + Common::SeekableReadStream *getNextFramePacket(); + uint32 getFrameDuration(); + uint32 getCodecTag(); + byte getBitsPerPixel(); + + Audio::QueuingAudioStream *_audStream; + void startAudio(); + void stopAudio(); + int8 _audioStreamIndex; + uint _curAudioChunk; + Audio::SoundHandle _audHandle; + + Graphics::Codec *createCodec(uint32 codecTag, byte bitsPerPixel); + Graphics::Codec *_videoCodec; + uint32 _nextFrameStartTime; + int8 _videoStreamIndex; + + Graphics::Surface *_scaledSurface; + Graphics::Surface *scaleSurface(Graphics::Surface *frame); + ScaleMode getScaleMode() const; + + void pauseVideoIntern(bool pause); + + int readDefault(MOVatom atom); + int readLeaf(MOVatom atom); + int readELST(MOVatom atom); + int readHDLR(MOVatom atom); + int readMDAT(MOVatom atom); + int readMDHD(MOVatom atom); + int readMOOV(MOVatom atom); + int readMVHD(MOVatom atom); + int readTKHD(MOVatom atom); + int readTRAK(MOVatom atom); + int readSTCO(MOVatom atom); + int readSTSC(MOVatom atom); + int readSTSD(MOVatom atom); + int readSTSS(MOVatom atom); + int readSTSZ(MOVatom atom); + int readSTTS(MOVatom atom); + int readCMOV(MOVatom atom); + int readWAVE(MOVatom atom); +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/video/qtrle.cpp b/engines/mohawk/video/qtrle.cpp new file mode 100644 index 0000000000..c06dbefcb3 --- /dev/null +++ b/engines/mohawk/video/qtrle.cpp @@ -0,0 +1,420 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// QuickTime RLE Decoder +// Based off ffmpeg's QuickTime RLE decoder (written by Mike Melanson) + +#include "mohawk/video/qtrle.h" + +#include "common/scummsys.h" +#include "common/stream.h" +#include "common/system.h" +#include "graphics/colormasks.h" +#include "graphics/surface.h" + +namespace Mohawk { + +QTRLEDecoder::QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel) : Graphics::Codec() { + _bitsPerPixel = bitsPerPixel; + _pixelFormat = g_system->getScreenFormat(); + + // We need to increase the surface size to a multiple of 4 + uint16 wMod = width % 4; + if(wMod != 0) + width += 4 - wMod; + + debug(2, "QTRLE corrected width: %d", width); + + _surface = new Graphics::Surface(); + _surface->create(width, height, _bitsPerPixel <= 8 ? 1 : _pixelFormat.bytesPerPixel); +} + +#define CHECK_STREAM_PTR(n) \ + if ((stream->pos() + n) > stream->size()) { \ + warning ("Problem: stream out of bounds (%d >= %d)", stream->pos() + n, stream->size()); \ + return; \ + } + +#define CHECK_PIXEL_PTR(n) \ + if ((int32)pixelPtr + n > _surface->w * _surface->h) { \ + warning ("Problem: pixel ptr = %d, pixel limit = %d", pixelPtr + n, _surface->w * _surface->h); \ + return; \ + } \ + +void QTRLEDecoder::decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { + uint32 pixelPtr = 0; + byte *rgb = (byte *)_surface->pixels; + + while (linesToChange) { + CHECK_STREAM_PTR(2); + byte skip = stream->readByte(); + int8 rleCode = stream->readSByte(); + + if (rleCode == 0) + break; + + if (skip & 0x80) { + linesToChange--; + rowPtr += _surface->w; + pixelPtr = rowPtr + 2 * (skip & 0x7f); + } else + pixelPtr += 2 * skip; + + if (rleCode < 0) { + // decode the run length code + rleCode = -rleCode; + // get the next 2 bytes from the stream, treat them as groups of 8 pixels, and output them rleCode times */ + CHECK_STREAM_PTR(2); + byte pi0 = stream->readByte(); + byte pi1 = stream->readByte(); + CHECK_PIXEL_PTR(rleCode * 2); + + while (rleCode--) { + rgb[pixelPtr++] = pi0; + rgb[pixelPtr++] = pi1; + } + } else { + // copy the same pixel directly to output 2 times + rleCode *= 2; + CHECK_STREAM_PTR(rleCode); + CHECK_PIXEL_PTR(rleCode); + + while (rleCode--) + rgb[pixelPtr++] = stream->readByte(); + } + } +} + +void QTRLEDecoder::decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp) { + uint32 pixelPtr = 0; + byte *rgb = (byte *)_surface->pixels; + byte numPixels = (bpp == 4) ? 8 : 16; + + while (linesToChange--) { + CHECK_STREAM_PTR(2); + pixelPtr = rowPtr + (numPixels * (stream->readByte() - 1)); + + for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { + if (rleCode == 0) { + // there's another skip code in the stream + CHECK_STREAM_PTR(1); + pixelPtr += (numPixels * (stream->readByte() - 1)); + } else if (rleCode < 0) { + // decode the run length code + rleCode = -rleCode; + + // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times */ + CHECK_STREAM_PTR(4); + + byte pi[16]; // 16 palette indices + + for (int8 i = numPixels - 1; i >= 0; i--) { + pi[numPixels - 1 - i] = (stream->readByte() >> ((i * bpp) & 0x07)) & ((1 << bpp) - 1); + + // FIXME: Is this right? + //stream_ptr += ((i & ((num_pixels>>2)-1)) == 0); + if ((i & ((numPixels >> 2) - 1)) == 0) + stream->readByte(); + } + + CHECK_PIXEL_PTR(rleCode * numPixels); + + while (rleCode--) + for (byte i = 0; i < numPixels; i++) + rgb[pixelPtr++] = pi[i]; + } else { + // copy the same pixel directly to output 4 times + rleCode *= 4; + CHECK_STREAM_PTR(rleCode); + CHECK_PIXEL_PTR(rleCode * (numPixels >> 2)); + + while (rleCode--) { + byte temp = stream->readByte(); + if (bpp == 4) { + rgb[pixelPtr++] = (temp >> 4) & 0x0f; + rgb[pixelPtr++] = temp & 0x0f; + } else { + rgb[pixelPtr++] = (temp >> 6) & 0x03; + rgb[pixelPtr++] = (temp >> 4) & 0x03; + rgb[pixelPtr++] = (temp >> 2) & 0x03; + rgb[pixelPtr++] = temp & 0x03; + } + } + } + } + + rowPtr += _surface->w; + } +} + +void QTRLEDecoder::decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { + uint32 pixelPtr = 0; + byte *rgb = (byte *)_surface->pixels; + + while (linesToChange--) { + CHECK_STREAM_PTR(2); + pixelPtr = rowPtr + 4 * (stream->readByte() - 1); + + for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { + if (rleCode == 0) { + // there's another skip code in the stream + CHECK_STREAM_PTR(1); + pixelPtr += 4 * (stream->readByte() - 1); + } else if (rleCode < 0) { + // decode the run length code + rleCode = -rleCode; + + // get the next 4 bytes from the stream, treat them as palette indices, and output them rleCode times + CHECK_STREAM_PTR(4); + + byte pi[4]; // 4 palette indexes + + for (byte i = 0; i < 4; i++) + pi[i] = stream->readByte(); + + CHECK_PIXEL_PTR(rleCode * 4); + + while (rleCode--) + for (byte i = 0; i < 4; i++) + rgb[pixelPtr++] = pi[i]; + } else { + // copy the same pixel directly to output 4 times + rleCode *= 4; + CHECK_STREAM_PTR(rleCode); + CHECK_PIXEL_PTR(rleCode); + + while (rleCode--) + rgb[pixelPtr++] = stream->readByte(); + } + } + + rowPtr += _surface->w; + } +} + +void QTRLEDecoder::decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { + uint32 pixelPtr = 0; + OverlayColor *rgb = (OverlayColor *)_surface->pixels; + + while (linesToChange--) { + CHECK_STREAM_PTR(2); + pixelPtr = rowPtr + stream->readByte() - 1; + + for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { + if (rleCode == 0) { + // there's another skip code in the stream + CHECK_STREAM_PTR(1); + pixelPtr += stream->readByte() - 1; + } else if (rleCode < 0) { + // decode the run length code + rleCode = -rleCode; + CHECK_STREAM_PTR(2); + + uint16 rgb16 = stream->readUint16BE(); + + CHECK_PIXEL_PTR(rleCode); + + while (rleCode--) { + // Convert from RGB555 to the format specified by the Overlay + byte r = 0, g = 0, b = 0; + Graphics::colorToRGB<Graphics::ColorMasks<555> >(rgb16, r, g, b); + rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); + } + } else { + CHECK_STREAM_PTR(rleCode * 2); + CHECK_PIXEL_PTR(rleCode); + + // copy pixels directly to output + while (rleCode--) { + uint16 rgb16 = stream->readUint16BE(); + + // Convert from RGB555 to the format specified by the Overlay + byte r = 0, g = 0, b = 0; + Graphics::colorToRGB<Graphics::ColorMasks<555> >(rgb16, r, g, b); + rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); + } + } + } + + rowPtr += _surface->w; + } +} + +void QTRLEDecoder::decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { + uint32 pixelPtr = 0; + OverlayColor *rgb = (OverlayColor *)_surface->pixels; + + while (linesToChange--) { + CHECK_STREAM_PTR(2); + pixelPtr = rowPtr + stream->readByte() - 1; + + for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { + if (rleCode == 0) { + // there's another skip code in the stream + CHECK_STREAM_PTR(1); + pixelPtr += stream->readByte() - 1; + } else if (rleCode < 0) { + // decode the run length code + rleCode = -rleCode; + + CHECK_STREAM_PTR(3); + + byte r = stream->readByte(); + byte g = stream->readByte(); + byte b = stream->readByte(); + + CHECK_PIXEL_PTR(rleCode); + + while (rleCode--) + rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); + } else { + CHECK_STREAM_PTR(rleCode * 3); + CHECK_PIXEL_PTR(rleCode); + + // copy pixels directly to output + while (rleCode--) { + byte r = stream->readByte(); + byte g = stream->readByte(); + byte b = stream->readByte(); + rgb[pixelPtr++] = _pixelFormat.RGBToColor(r, g, b); + } + } + } + + rowPtr += _surface->w; + } +} + +void QTRLEDecoder::decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange) { + uint32 pixelPtr = 0; + OverlayColor *rgb = (OverlayColor *)_surface->pixels; + + while (linesToChange--) { + CHECK_STREAM_PTR(2); + pixelPtr = rowPtr + stream->readByte() - 1; + + for (int8 rleCode = stream->readSByte(); rleCode != -1; rleCode = stream->readSByte()) { + if (rleCode == 0) { + // there's another skip code in the stream + CHECK_STREAM_PTR(1); + pixelPtr += stream->readByte() - 1; + } else if (rleCode < 0) { + // decode the run length code + rleCode = -rleCode; + + CHECK_STREAM_PTR(4); + + byte a = stream->readByte(); + byte r = stream->readByte(); + byte g = stream->readByte(); + byte b = stream->readByte(); + + CHECK_PIXEL_PTR(rleCode); + + while (rleCode--) + rgb[pixelPtr++] = _pixelFormat.ARGBToColor(a, r, g, b); + } else { + CHECK_STREAM_PTR(rleCode * 4); + CHECK_PIXEL_PTR(rleCode); + + // copy pixels directly to output + while (rleCode--) { + byte a = stream->readByte(); + byte r = stream->readByte(); + byte g = stream->readByte(); + byte b = stream->readByte(); + rgb[pixelPtr++] = _pixelFormat.ARGBToColor(a, r, g, b); + } + } + } + + rowPtr += _surface->w; + } +} + +Graphics::Surface *QTRLEDecoder::decodeImage(Common::SeekableReadStream *stream) { + uint16 start_line = 0; + uint16 height = _surface->h; + + // check if this frame is even supposed to change + if (stream->size() < 8) + return _surface; + + // start after the chunk size + stream->readUint32BE(); + + // fetch the header + uint16 header = stream->readUint16BE(); + + // if a header is present, fetch additional decoding parameters + if (header & 8) { + if(stream->size() < 14) + return _surface; + start_line = stream->readUint16BE(); + stream->readUint16BE(); // Unknown + height = stream->readUint16BE(); + stream->readUint16BE(); // Unknown + } + + uint32 row_ptr = _surface->w * start_line; + + switch (_bitsPerPixel) { + case 1: + case 33: + decode1(stream, row_ptr, height); + break; + case 2: + case 34: + decode2_4(stream, row_ptr, height, 2); + break; + case 4: + case 36: + decode2_4(stream, row_ptr, height, 4); + break; + case 8: + case 40: + decode8(stream, row_ptr, height); + break; + case 16: + decode16(stream, row_ptr, height); + break; + case 24: + decode24(stream, row_ptr, height); + break; + case 32: + decode32(stream, row_ptr, height); + break; + default: + error ("Unsupported bits per pixel %d", _bitsPerPixel); + } + + return _surface; +} + +QTRLEDecoder::~QTRLEDecoder() { + _surface->free(); +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/qtrle.h b/engines/mohawk/video/qtrle.h new file mode 100644 index 0000000000..2832bd6b24 --- /dev/null +++ b/engines/mohawk/video/qtrle.h @@ -0,0 +1,58 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MOHAWK_QTRLE_H +#define MOHAWK_QTRLE_H + +#include "graphics/pixelformat.h" +#include "graphics/video/codecs/codec.h" + +namespace Mohawk { + +class QTRLEDecoder : public Graphics::Codec { +public: + QTRLEDecoder(uint16 width, uint16 height, byte bitsPerPixel); + ~QTRLEDecoder(); + + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } + +private: + byte _bitsPerPixel; + + Graphics::Surface *_surface; + Graphics::PixelFormat _pixelFormat; + + void decode1(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); + void decode2_4(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange, byte bpp); + void decode8(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); + void decode16(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); + void decode24(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); + void decode32(Common::SeekableReadStream *stream, uint32 rowPtr, uint32 linesToChange); +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/video/rpza.cpp b/engines/mohawk/video/rpza.cpp new file mode 100644 index 0000000000..f48c055ae2 --- /dev/null +++ b/engines/mohawk/video/rpza.cpp @@ -0,0 +1,208 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + + // Based off ffmpeg's RPZA decoder + +#include "mohawk/video/rpza.h" + +#include "common/system.h" +#include "graphics/colormasks.h" + +namespace Mohawk { + +RPZADecoder::RPZADecoder(uint16 width, uint16 height) : Graphics::Codec() { + _pixelFormat = g_system->getScreenFormat(); + + // We need to increase the surface size to a multiple of 4 + uint16 wMod = width % 4; + if(wMod != 0) + width += 4 - wMod; + + debug(2, "RPZA corrected width: %d", width); + + _surface = new Graphics::Surface(); + _surface->create(width, height, _pixelFormat.bytesPerPixel); +} + +#define ADVANCE_BLOCK() \ + pixelPtr += 4; \ + if (pixelPtr >= _surface->w) { \ + pixelPtr = 0; \ + rowPtr += _surface->w * 4; \ + } \ + totalBlocks--; \ + if (totalBlocks < 0) \ + error("block counter just went negative (this should not happen)") \ + +// Convert from RGB555 to the format specified by the screen +#define PUT_PIXEL(color) \ + if ((int32)blockPtr < _surface->w * _surface->h) { \ + byte r = 0, g = 0, b = 0; \ + Graphics::colorToRGB<Graphics::ColorMasks<555> >(color, r, g, b); \ + if (_pixelFormat.bytesPerPixel == 2) \ + *((uint16 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \ + else \ + *((uint32 *)_surface->pixels + blockPtr) = _pixelFormat.RGBToColor(r, g, b); \ + } \ + blockPtr++ + +Graphics::Surface *RPZADecoder::decodeImage(Common::SeekableReadStream *stream) { + uint16 colorA = 0, colorB = 0; + uint16 color4[4]; + + uint32 rowPtr = 0; + uint32 pixelPtr = 0; + uint32 blockPtr = 0; + uint32 rowInc = _surface->w - 4; + uint16 ta; + uint16 tb; + + // First byte is always 0xe1. Warn if it's different + byte firstByte = stream->readByte(); + if (firstByte != 0xe1) + warning("First RPZA chunk byte is 0x%02x instead of 0xe1", firstByte); + + // Get chunk size, ingnoring first byte + uint32 chunkSize = stream->readUint16BE() << 8; + chunkSize += stream->readByte(); + + // If length mismatch use size from MOV file and try to decode anyway + if (chunkSize != (uint32)stream->size()) { + warning("MOV chunk size != encoded chunk size; using MOV chunk size"); + chunkSize = stream->size(); + } + + // Number of 4x4 blocks in frame + int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4); + + // Process chunk data + while ((uint32)stream->pos() < chunkSize) { + byte opcode = stream->readByte(); // Get opcode + byte numBlocks = (opcode & 0x1f) + 1; // Extract block counter from opcode + + // If opcode MSbit is 0, we need more data to decide what to do + if ((opcode & 0x80) == 0) { + colorA = (opcode << 8) | stream->readByte(); + opcode = 0; + if (stream->readByte() & 0x80) { + // Must behave as opcode 110xxxxx, using colorA computed + // above. Use fake opcode 0x20 to enter switch block at + // the right place + opcode = 0x20; + numBlocks = 1; + } + stream->seek(-1, SEEK_CUR); + } + + switch (opcode & 0xe0) { + case 0x80: // Skip blocks + while (numBlocks--) { + ADVANCE_BLOCK(); + } + break; + case 0xa0: // Fill blocks with one color + colorA = stream->readUint16BE(); + while (numBlocks--) { + blockPtr = rowPtr + pixelPtr; + for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { + for (byte pixel_x = 0; pixel_x < 4; pixel_x++) { + PUT_PIXEL(colorA); + } + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // Fill blocks with 4 colors + case 0xc0: + colorA = stream->readUint16BE(); + case 0x20: + colorB = stream->readUint16BE(); + + // Sort out the colors + color4[0] = colorB; + color4[1] = 0; + color4[2] = 0; + color4[3] = colorA; + + // Red components + ta = (colorA >> 10) & 0x1F; + tb = (colorB >> 10) & 0x1F; + color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; + color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; + + // Green components + ta = (colorA >> 5) & 0x1F; + tb = (colorB >> 5) & 0x1F; + color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; + color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; + + // Blue components + ta = colorA & 0x1F; + tb = colorB & 0x1F; + color4[1] |= ((11 * ta + 21 * tb) >> 5); + color4[2] |= ((21 * ta + 11 * tb) >> 5); + + while (numBlocks--) { + blockPtr = rowPtr + pixelPtr; + for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { + byte index = stream->readByte(); + for (byte pixel_x = 0; pixel_x < 4; pixel_x++){ + byte idx = (index >> (2 * (3 - pixel_x))) & 0x03; + PUT_PIXEL(color4[idx]); + } + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // Fill block with 16 colors + case 0x00: + blockPtr = rowPtr + pixelPtr; + for (byte pixel_y = 0; pixel_y < 4; pixel_y++) { + for (byte pixel_x = 0; pixel_x < 4; pixel_x++){ + // We already have color of upper left pixel + if (pixel_y != 0 || pixel_x != 0) + colorA = stream->readUint16BE(); + + PUT_PIXEL(colorA); + } + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + break; + + // Unknown opcode + default: + error("Unknown opcode %02x in rpza chunk", opcode); + } + } + + return _surface; +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/rpza.h b/engines/mohawk/video/rpza.h new file mode 100644 index 0000000000..c6d0ada6f5 --- /dev/null +++ b/engines/mohawk/video/rpza.h @@ -0,0 +1,49 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MOHAWK_RPZA_H +#define MOHAWK_RPZA_H + +#include "graphics/pixelformat.h" +#include "graphics/video/codecs/codec.h" + +namespace Mohawk { + +class RPZADecoder : public Graphics::Codec { +public: + RPZADecoder(uint16 width, uint16 height); + ~RPZADecoder() { delete _surface; } + + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } + +private: + Graphics::Surface *_surface; + Graphics::PixelFormat _pixelFormat; +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/video/smc.cpp b/engines/mohawk/video/smc.cpp new file mode 100644 index 0000000000..4a0d16dfcc --- /dev/null +++ b/engines/mohawk/video/smc.cpp @@ -0,0 +1,385 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// Based off ffmpeg's SMC decoder + +#include "mohawk/video/smc.h" + +namespace Mohawk { + +#define GET_BLOCK_COUNT() \ + (opcode & 0x10) ? (1 + stream->readByte()) : 1 + (opcode & 0x0F); + +#define ADVANCE_BLOCK() \ +{ \ + pixelPtr += 4; \ + if (pixelPtr >= _surface->w) { \ + pixelPtr = 0; \ + rowPtr += _surface->w * 4; \ + } \ + totalBlocks--; \ + if (totalBlocks < 0) { \ + warning("block counter just went negative (this should not happen)"); \ + return _surface; \ + } \ +} + +SMCDecoder::SMCDecoder(uint16 width, uint16 height) { + _surface = new Graphics::Surface(); + _surface->create(width, height, 1); +} + +Graphics::Surface *SMCDecoder::decodeImage(Common::SeekableReadStream *stream) { + byte *pixels = (byte *)_surface->pixels; + + uint32 numBlocks = 0; + uint32 colorFlags = 0; + uint32 colorFlagsA = 0; + uint32 colorFlagsB = 0; + + const uint16 rowInc = _surface->w - 4; + int32 rowPtr = 0; + int32 pixelPtr = 0; + uint32 blockPtr = 0; + uint32 prevBlockPtr = 0; + uint32 prevBlockPtr1 = 0, prevBlockPtr2 = 0; + byte prevBlockFlag = false; + byte pixel = 0; + + uint32 colorPairIndex = 0; + uint32 colorQuadIndex = 0; + uint32 colorOctetIndex = 0; + uint32 colorTableIndex = 0; // indices to color pair, quad, or octet tables + + int32 chunkSize = stream->readUint32BE() & 0x00FFFFFF; + if (chunkSize != stream->size()) + warning("MOV chunk size != SMC chunk size (%d != %d); ignoring SMC chunk size", chunkSize, stream->size()); + + int32 totalBlocks = ((_surface->w + 3) / 4) * ((_surface->h + 3) / 4); + + // traverse through the blocks + while (totalBlocks != 0) { + // sanity checks + + // make sure stream ptr hasn't gone out of bounds + if (stream->pos() > stream->size()) { + warning("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)", stream->pos(), stream->size()); + return _surface; + } + + // make sure the row pointer hasn't gone wild + if (rowPtr >= _surface->w * _surface->h) { + warning("SMC decoder just went out of bounds (row ptr = %d, size = %d)", rowPtr, _surface->w * _surface->h); + return _surface; + } + + byte opcode = stream->readByte(); + + switch (opcode & 0xF0) { + // skip n blocks + case 0x00: + case 0x10: + numBlocks = GET_BLOCK_COUNT(); + while (numBlocks--) { + ADVANCE_BLOCK(); + } + break; + + // repeat last block n times + case 0x20: + case 0x30: + numBlocks = GET_BLOCK_COUNT(); + + // sanity check + if (rowPtr == 0 && pixelPtr == 0) { + warning("encountered repeat block opcode (%02X) but no blocks rendered yet", opcode & 0xF0); + break; + } + + // figure out where the previous block started + if (pixelPtr == 0) + prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4; + else + prevBlockPtr1 = rowPtr + pixelPtr - 4; + + while (numBlocks--) { + blockPtr = rowPtr + pixelPtr; + prevBlockPtr = prevBlockPtr1; + for (byte y = 0; y < 4; y++) { + for (byte x = 0; x < 4; x++) + pixels[blockPtr++] = pixels[prevBlockPtr++]; + blockPtr += rowInc; + prevBlockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // repeat previous pair of blocks n times + case 0x40: + case 0x50: + numBlocks = GET_BLOCK_COUNT(); + numBlocks *= 2; + + // sanity check + if (rowPtr == 0 && pixelPtr < 2 * 4) { + warning("encountered repeat block opcode (%02X) but not enough blocks rendered yet", opcode & 0xF0); + break; + } + + // figure out where the previous 2 blocks started + if (pixelPtr == 0) + prevBlockPtr1 = (rowPtr - _surface->w * 4) + _surface->w - 4 * 2; + else if (pixelPtr == 4) + prevBlockPtr1 = (rowPtr - _surface->w * 4) + rowInc; + else + prevBlockPtr1 = rowPtr + pixelPtr - 4 * 2; + + if (pixelPtr == 0) + prevBlockPtr2 = (rowPtr - _surface->w * 4) + rowInc; + else + prevBlockPtr2 = rowPtr + pixelPtr - 4; + + prevBlockFlag = 0; + while (numBlocks--) { + blockPtr = rowPtr + pixelPtr; + + if (prevBlockFlag) + prevBlockPtr = prevBlockPtr2; + else + prevBlockPtr = prevBlockPtr1; + + prevBlockFlag = !prevBlockFlag; + + for (byte y = 0; y < 4; y++) { + for (byte x = 0; x < 4; x++) + pixels[blockPtr++] = pixels[prevBlockPtr++]; + + blockPtr += rowInc; + prevBlockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // 1-color block encoding + case 0x60: + case 0x70: + numBlocks = GET_BLOCK_COUNT(); + pixel = stream->readByte(); + + while (numBlocks--) { + blockPtr = rowPtr + pixelPtr; + for (byte y = 0; y < 4; y++) { + for (byte x = 0; x < 4; x++) + pixels[blockPtr++] = pixel; + + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // 2-color block encoding + case 0x80: + case 0x90: + numBlocks = (opcode & 0x0F) + 1; + + // figure out which color pair to use to paint the 2-color block + if ((opcode & 0xF0) == 0x80) { + // fetch the next 2 colors from bytestream and store in next + // available entry in the color pair table + for (byte i = 0; i < CPAIR; i++) { + pixel = stream->readByte(); + colorTableIndex = CPAIR * colorPairIndex + i; + _colorPairs[colorTableIndex] = pixel; + } + + // this is the base index to use for this block + colorTableIndex = CPAIR * colorPairIndex; + colorPairIndex++; + + // wraparound + if (colorPairIndex == COLORS_PER_TABLE) + colorPairIndex = 0; + } else + colorTableIndex = CPAIR * stream->readByte(); + + while (numBlocks--) { + colorFlags = stream->readUint16BE(); + uint16 flagMask = 0x8000; + blockPtr = rowPtr + pixelPtr; + for (byte y = 0; y < 4; y++) { + for (byte x = 0; x < 4; x++) { + if (colorFlags & flagMask) + pixel = colorTableIndex + 1; + else + pixel = colorTableIndex; + + flagMask >>= 1; + pixels[blockPtr++] = _colorPairs[pixel]; + } + + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // 4-color block encoding + case 0xA0: + case 0xB0: + numBlocks = (opcode & 0x0F) + 1; + + // figure out which color quad to use to paint the 4-color block + if ((opcode & 0xF0) == 0xA0) { + // fetch the next 4 colors from bytestream and store in next + // available entry in the color quad table + for (byte i = 0; i < CQUAD; i++) { + pixel = stream->readByte(); + colorTableIndex = CQUAD * colorQuadIndex + i; + _colorQuads[colorTableIndex] = pixel; + } + + // this is the base index to use for this block + colorTableIndex = CQUAD * colorQuadIndex; + colorQuadIndex++; + + // wraparound + if (colorQuadIndex == COLORS_PER_TABLE) + colorQuadIndex = 0; + } else + colorTableIndex = CQUAD * stream->readByte(); + + while (numBlocks--) { + colorFlags = stream->readUint32BE(); + + // flag mask actually acts as a bit shift count here + byte flagMask = 30; + blockPtr = rowPtr + pixelPtr; + + for (byte y = 0; y < 4; y++) { + for (byte x = 0; x < 4; x++) { + pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x03); + flagMask -= 2; + pixels[blockPtr++] = _colorQuads[pixel]; + } + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // 8-color block encoding + case 0xC0: + case 0xD0: + numBlocks = (opcode & 0x0F) + 1; + + // figure out which color octet to use to paint the 8-color block + if ((opcode & 0xF0) == 0xC0) { + // fetch the next 8 colors from bytestream and store in next + // available entry in the color octet table + for (byte i = 0; i < COCTET; i++) { + pixel = stream->readByte(); + colorTableIndex = COCTET * colorOctetIndex + i; + _colorOctets[colorTableIndex] = pixel; + } + + // this is the base index to use for this block + colorTableIndex = COCTET * colorOctetIndex; + colorOctetIndex++; + + // wraparound + if (colorOctetIndex == COLORS_PER_TABLE) + colorOctetIndex = 0; + } else + colorTableIndex = COCTET * stream->readByte(); + + while (numBlocks--) { + /* + For this input of 6 hex bytes: + 01 23 45 67 89 AB + Mangle it to this output: + flags_a = xx012456, flags_b = xx89A37B + */ + + // build the color flags + byte flagData[6]; + stream->read(flagData, 6); + + colorFlagsA = ((READ_BE_UINT16(flagData) & 0xFFF0) << 8) | (READ_BE_UINT16(flagData + 2) >> 4); + colorFlagsB = ((READ_BE_UINT16(flagData + 4) & 0xFFF0) << 8) | ((flagData[1] & 0xF) << 8) | + ((flagData[3] & 0xF) << 4) | (flagData[5] & 0xf); + + colorFlags = colorFlagsA; + + // flag mask actually acts as a bit shift count here + byte flagMask = 21; + blockPtr = rowPtr + pixelPtr; + for (byte y = 0; y < 4; y++) { + // reload flags at third row (iteration y == 2) + if (y == 2) { + colorFlags = colorFlagsB; + flagMask = 21; + } + + for (byte x = 0; x < 4; x++) { + pixel = colorTableIndex + ((colorFlags >> flagMask) & 0x07); + flagMask -= 3; + pixels[blockPtr++] = _colorOctets[pixel]; + } + + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + // 16-color block encoding (every pixel is a different color) + case 0xE0: + numBlocks = (opcode & 0x0F) + 1; + + while (numBlocks--) { + blockPtr = rowPtr + pixelPtr; + for (byte y = 0; y < 4; y++) { + for (byte x = 0; x < 4; x++) + pixels[blockPtr++] = stream->readByte(); + + blockPtr += rowInc; + } + ADVANCE_BLOCK(); + } + break; + + case 0xF0: + warning("0xF0 opcode seen in SMC chunk (contact the developers)"); + break; + } + } + + return _surface; +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/smc.h b/engines/mohawk/video/smc.h new file mode 100644 index 0000000000..c52226100e --- /dev/null +++ b/engines/mohawk/video/smc.h @@ -0,0 +1,59 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MOHAWK_VIDEO_SMC_H +#define MOHAWK_VIDEO_SMC_H + +#include "graphics/video/codecs/codec.h" + +namespace Mohawk { + +enum { + CPAIR = 2, + CQUAD = 4, + COCTET = 8, + COLORS_PER_TABLE = 256 +}; + +class SMCDecoder : public Graphics::Codec { +public: + SMCDecoder(uint16 width, uint16 height); + ~SMCDecoder() { delete _surface; } + + Graphics::Surface *decodeImage(Common::SeekableReadStream *stream); + Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); } + +private: + Graphics::Surface *_surface; + + // SMC color tables + byte _colorPairs[COLORS_PER_TABLE * CPAIR]; + byte _colorQuads[COLORS_PER_TABLE * CQUAD]; + byte _colorOctets[COLORS_PER_TABLE * COCTET]; +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/video/video.cpp b/engines/mohawk/video/video.cpp new file mode 100644 index 0000000000..86ecd4dedf --- /dev/null +++ b/engines/mohawk/video/video.cpp @@ -0,0 +1,377 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "mohawk/resource.h" +#include "mohawk/video/video.h" +#include "mohawk/video/qt_player.h" + +#include "common/events.h" + +namespace Mohawk { + +VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { +} + +VideoManager::~VideoManager() { + _mlstRecords.clear(); + stopVideos(); +} + +void VideoManager::pauseVideos() { + for (uint16 i = 0; i < _videoStreams.size(); i++) + _videoStreams[i]->pauseVideo(true); +} + +void VideoManager::resumeVideos() { + for (uint16 i = 0; i < _videoStreams.size(); i++) + _videoStreams[i]->pauseVideo(false); +} + +void VideoManager::stopVideos() { + for (uint16 i = 0; i < _videoStreams.size(); i++) + delete _videoStreams[i].video; + _videoStreams.clear(); +} + +void VideoManager::playMovie(Common::String filename, uint16 x, uint16 y, bool clearScreen) { + VideoHandle videoHandle = createVideoHandle(filename, x, y, false); + if (videoHandle == NULL_VID_HANDLE) + return; + + // Clear screen if requested + if (clearScreen) { + _vm->_system->fillScreen(_vm->_system->getScreenFormat().RGBToColor(0, 0, 0)); + _vm->_system->updateScreen(); + } + + waitUntilMovieEnds(videoHandle); +} + +void VideoManager::playMovieCentered(Common::String filename, bool clearScreen) { + VideoHandle videoHandle = createVideoHandle(filename, 0, 0, false); + if (videoHandle == NULL_VID_HANDLE) + return; + + // Clear screen if requested + if (clearScreen) { + _vm->_system->fillScreen(_vm->_system->getScreenFormat().RGBToColor(0, 0, 0)); + _vm->_system->updateScreen(); + } + + _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2; + _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2; + + waitUntilMovieEnds(videoHandle); +} + +void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { + bool continuePlaying = true; + + while (!_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) { + if (updateBackgroundMovies()) + _vm->_system->updateScreen(); + + Common::Event event; + while (_vm->_system->getEventManager()->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_RTL: + case Common::EVENT_QUIT: + continuePlaying = false; + break; + case Common::EVENT_KEYDOWN: + switch (event.kbd.keycode) { + case Common::KEYCODE_SPACE: + _vm->pauseGame(); + break; + case Common::KEYCODE_ESCAPE: + continuePlaying = false; + break; + default: + break; + } + default: + break; + } + } + + // Cut down on CPU usage + _vm->_system->delayMillis(10); + } + + _videoStreams[videoHandle]->close(); + _videoStreams.clear(); +} + +void VideoManager::playBackgroundMovie(Common::String filename, int16 x, int16 y, bool loop) { + VideoHandle videoHandle = createVideoHandle(filename, x, y, loop); + if (videoHandle == NULL_VID_HANDLE) + return; + + // Center x if requested + if (x < 0) + _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2; + + // Center y if requested + if (y < 0) + _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2; +} + +bool VideoManager::updateBackgroundMovies() { + bool updateScreen = false; + + for (uint32 i = 0; i < _videoStreams.size() && !_vm->shouldQuit(); i++) { + // Skip deleted videos + if (!_videoStreams[i].video) + continue; + + // Remove any videos that are over + if (_videoStreams[i]->endOfVideo()) { + if (_videoStreams[i].loop) { + _videoStreams[i]->rewind(); + } else { + delete _videoStreams[i].video; + memset(&_videoStreams[i], 0, sizeof(VideoEntry)); + _videoStreams[i].video = NULL; + continue; + } + } + + // Check if we need to draw a frame + if (_videoStreams[i]->needsUpdate()) { + Graphics::Surface *frame = _videoStreams[i]->decodeNextFrame(); + bool deleteFrame = false; + + if (frame && _videoStreams[i].enabled) { + // Convert from 8bpp to the current screen format if necessary + if (frame->bytesPerPixel == 1) { + Graphics::Surface *newFrame = new Graphics::Surface(); + Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); + byte *palette = _videoStreams[i]->getPalette(); + assert(palette); + + newFrame->create(frame->w, frame->h, pixelFormat.bytesPerPixel); + + for (uint16 j = 0; j < frame->h; j++) { + for (uint16 k = 0; k < frame->w; k++) { + byte palIndex = *((byte *)frame->getBasePtr(k, j)); + byte r = palette[palIndex * 3]; + byte g = palette[palIndex * 3 + 1]; + byte b = palette[palIndex * 3 + 2]; + if (pixelFormat.bytesPerPixel == 2) + *((uint16 *)newFrame->getBasePtr(k, j)) = pixelFormat.RGBToColor(r, g, b); + else + *((uint32 *)newFrame->getBasePtr(k, j)) = pixelFormat.RGBToColor(r, g, b); + } + } + + frame = newFrame; + deleteFrame = true; + } + + // Clip the width/height to make sure we stay on the screen (Myst does this a few times) + uint16 width = MIN<int32>(_videoStreams[i]->getWidth(), _vm->_system->getWidth() - _videoStreams[i].x); + uint16 height = MIN<int32>(_videoStreams[i]->getHeight(), _vm->_system->getHeight() - _videoStreams[i].y); + _vm->_system->copyRectToScreen((byte*)frame->pixels, frame->pitch, _videoStreams[i].x, _videoStreams[i].y, width, height); + + // We've drawn something to the screen, make sure we update it + updateScreen = true; + + // Delete the frame if we're using the buffer from the 8bpp conversion + if (deleteFrame) { + frame->free(); + delete frame; + } + } + } + + // Update the audio buffer too + _videoStreams[i]->updateAudioBuffer(); + } + + // Return true if we need to update the screen + return updateScreen; +} + +void VideoManager::activateMLST(uint16 mlstId, uint16 card) { + Common::SeekableReadStream *mlstStream = _vm->getRawData(ID_MLST, card); + uint16 recordCount = mlstStream->readUint16BE(); + + for (uint16 i = 0; i < recordCount; i++) { + MLSTRecord mlstRecord; + mlstRecord.index = mlstStream->readUint16BE(); + mlstRecord.movieID = mlstStream->readUint16BE(); + mlstRecord.code = mlstStream->readUint16BE(); + mlstRecord.left = mlstStream->readUint16BE(); + mlstRecord.top = mlstStream->readUint16BE(); + + for (byte j = 0; j < 2; j++) + if (mlstStream->readUint16BE() != 0) + warning("u0[%d] in MLST non-zero", j); + + if (mlstStream->readUint16BE() != 0xFFFF) + warning("u0[2] in MLST not 0xFFFF"); + + mlstRecord.loop = mlstStream->readUint16BE(); + mlstRecord.volume = mlstStream->readUint16BE(); + mlstRecord.u1 = mlstStream->readUint16BE(); + + if (mlstRecord.u1 != 1) + warning("mlstRecord.u1 not 1"); + + if (mlstRecord.index == mlstId) { + _mlstRecords.push_back(mlstRecord); + break; + } + } + + delete mlstStream; +} + +void VideoManager::playMovie(uint16 id) { + for (uint16 i = 0; i < _mlstRecords.size(); i++) + if (_mlstRecords[i].code == id) { + debug(1, "Play tMOV %d (non-blocking) at (%d, %d) %s", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0 ? "looping" : "non-looping"); + createVideoHandle(_mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0); + return; + } +} + +void VideoManager::playMovieBlocking(uint16 id) { + for (uint16 i = 0; i < _mlstRecords.size(); i++) + if (_mlstRecords[i].code == id) { + debug(1, "Play tMOV %d (blocking) at (%d, %d)", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top); + VideoHandle videoHandle = createVideoHandle(_mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, false); + waitUntilMovieEnds(videoHandle); + return; + } +} + +void VideoManager::stopMovie(uint16 id) { + debug(2, "Stopping movie %d", id); + for (uint16 i = 0; i < _mlstRecords.size(); i++) + if (_mlstRecords[i].code == id) + for (uint16 j = 0; j < _videoStreams.size(); j++) + if (_mlstRecords[i].movieID == _videoStreams[j].id) { + delete _videoStreams[i].video; + memset(&_videoStreams[i].video, 0, sizeof(VideoEntry)); + return; + } +} + +void VideoManager::enableMovie(uint16 id) { + debug(2, "Enabling movie %d", id); + for (uint16 i = 0; i < _mlstRecords.size(); i++) + if (_mlstRecords[i].code == id) + for (uint16 j = 0; j < _videoStreams.size(); j++) + if (_mlstRecords[i].movieID == _videoStreams[j].id) { + _videoStreams[j].enabled = true; + return; + } +} + +void VideoManager::disableMovie(uint16 id) { + debug(2, "Disabling movie %d", id); + for (uint16 i = 0; i < _mlstRecords.size(); i++) + if (_mlstRecords[i].code == id) + for (uint16 j = 0; j < _videoStreams.size(); j++) + if (_mlstRecords[i].movieID == _videoStreams[j].id) { + _videoStreams[j].enabled = false; + return; + } +} + +void VideoManager::disableAllMovies() { + debug(2, "Disabling all movies"); + for (uint16 i = 0; i < _videoStreams.size(); i++) + _videoStreams[i].enabled = false; +} + +VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop) { + // First, check to see if that video is already playing + for (uint32 i = 0; i < _videoStreams.size(); i++) + if (_videoStreams[i].id == id) + return i; + + // Otherwise, create a new entry + VideoEntry entry; + entry.video = new QTPlayer(); + entry.x = x; + entry.y = y; + entry.filename = ""; + entry.id = id; + entry.loop = loop; + entry.enabled = true; + entry->setChunkBeginOffset(_vm->getResourceOffset(ID_TMOV, id)); + entry->load(*_vm->getRawData(ID_TMOV, id)); + + // Search for any deleted videos so we can take a formerly used slot + for (uint32 i = 0; i < _videoStreams.size(); i++) + if (!_videoStreams[i].video) { + _videoStreams[i] = entry; + return i; + } + + // Otherwise, just add it to the list + _videoStreams.push_back(entry); + return _videoStreams.size() - 1; +} + +VideoHandle VideoManager::createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop) { + // First, check to see if that video is already playing + for (uint32 i = 0; i < _videoStreams.size(); i++) + if (_videoStreams[i].filename == filename) + return i; + + // Otherwise, create a new entry + VideoEntry entry; + entry.video = new QTPlayer(); + entry.x = x; + entry.y = y; + entry.filename = filename; + entry.id = 0; + entry.loop = loop; + entry.enabled = true; + + Common::File *file = new Common::File(); + if (!file->open(filename)) { + delete file; + return NULL_VID_HANDLE; + } + + entry->load(*file); + + // Search for any deleted videos so we can take a formerly used slot + for (uint32 i = 0; i < _videoStreams.size(); i++) + if (!_videoStreams[i].video) { + _videoStreams[i] = entry; + return i; + } + + // Otherwise, just add it to the list + _videoStreams.push_back(entry); + return _videoStreams.size() - 1; +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/video/video.h b/engines/mohawk/video/video.h new file mode 100644 index 0000000000..a5d2bde65d --- /dev/null +++ b/engines/mohawk/video/video.h @@ -0,0 +1,107 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef MOHAWK_VIDEO_H +#define MOHAWK_VIDEO_H + +#include "graphics/pixelformat.h" + +namespace Mohawk { + +class MohawkEngine; + +struct MLSTRecord { + uint16 index; + uint16 movieID; + uint16 code; + uint16 left; + uint16 top; + uint16 u0[3]; + uint16 loop; + uint16 volume; + uint16 u1; +}; + +class QTPlayer; + +struct VideoEntry { + QTPlayer *video; + uint16 x; + uint16 y; + bool loop; + Common::String filename; + uint16 id; // Riven only + bool enabled; + + QTPlayer *operator->() const { assert(video); return video; } +}; + +typedef int32 VideoHandle; + +enum { + NULL_VID_HANDLE = -1 +}; + +class VideoManager { +public: + VideoManager(MohawkEngine *vm); + ~VideoManager(); + + // Generic movie functions + void playMovie(Common::String filename, uint16 x = 0, uint16 y = 0, bool clearScreen = false); + void playMovieCentered(Common::String filename, bool clearScreen = true); + void playBackgroundMovie(Common::String filename, int16 x = -1, int16 y = -1, bool loop = false); + bool updateBackgroundMovies(); + void pauseVideos(); + void resumeVideos(); + void stopVideos(); + + // Riven-related functions + void activateMLST(uint16 mlstId, uint16 card); + void enableMovie(uint16 id); + void disableMovie(uint16 id); + void disableAllMovies(); + void playMovie(uint16 id); + void stopMovie(uint16 id); + void playMovieBlocking(uint16 id); + + // Riven-related variables + Common::Array<MLSTRecord> _mlstRecords; + +private: + MohawkEngine *_vm; + + void waitUntilMovieEnds(VideoHandle videoHandle); + + // Keep tabs on any videos playing + Common::Array<VideoEntry> _videoStreams; + + VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop); + VideoHandle createVideoHandle(Common::String filename, uint16 x, uint16 y, bool loop); +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index b5eb82b456..8864c84e2f 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -42,7 +42,6 @@ Debugger::Debugger(Parallaction *vm) DCmd_Register("zones", WRAP_METHOD(Debugger, Cmd_Zones)); DCmd_Register("animations", WRAP_METHOD(Debugger, Cmd_Animations)); DCmd_Register("globalflags",WRAP_METHOD(Debugger, Cmd_GlobalFlags)); - DCmd_Register("toggleglobalflag",WRAP_METHOD(Debugger, Cmd_ToggleGlobalFlag)); DCmd_Register("localflags", WRAP_METHOD(Debugger, Cmd_LocalFlags)); DCmd_Register("locations", WRAP_METHOD(Debugger, Cmd_Locations)); DCmd_Register("gfxobjects", WRAP_METHOD(Debugger, Cmd_GfxObjects)); @@ -118,32 +117,6 @@ bool Debugger::Cmd_GlobalFlags(int argc, const char **argv) { return true; } -bool Debugger::Cmd_ToggleGlobalFlag(int argc, const char **argv) { - - int i; - - switch (argc) { - case 2: - i = _vm->_globalFlagsNames->lookup(argv[1]); - if (i == Table::notFound) { - DebugPrintf("invalid flag '%s'\n", argv[1]); - } else { - i--; - if ((_globalFlags & (1 << i)) == 0) - _globalFlags |= (1 << i); - else - _globalFlags &= ~(1 << i); - } - break; - - default: - DebugPrintf("toggleglobalflag <flag name>\n"); - - } - - return true; -} - bool Debugger::Cmd_LocalFlags(int argc, const char **argv) { uint32 flags = _vm->getLocationFlags(); diff --git a/engines/parallaction/debug.h b/engines/parallaction/debug.h index 5267206d04..54b578e95f 100644 --- a/engines/parallaction/debug.h +++ b/engines/parallaction/debug.h @@ -28,7 +28,6 @@ protected: bool Cmd_Animations(int argc, const char **argv); bool Cmd_LocalFlags(int argc, const char **argv); bool Cmd_GlobalFlags(int argc, const char **argv); - bool Cmd_ToggleGlobalFlag(int argc, const char **argv); bool Cmd_Locations(int argc, const char **argv); bool Cmd_GfxObjects(int argc, const char **argv); bool Cmd_Programs(int argc, const char** argv); diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index 7a28d18f17..6332600226 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -461,10 +461,6 @@ public: void Parallaction::enterDialogueMode(ZonePtr z) { - if (!z->u._speakDialogue) { - return; - } - debugC(1, kDebugDialogue, "Parallaction::enterDialogueMode(%s)", z->u._filename.c_str()); _dialogueMan = createDialogueManager(z); assert(_dialogueMan); diff --git a/engines/parallaction/exec_br.cpp b/engines/parallaction/exec_br.cpp index 1d8724e2d8..13c1318123 100644 --- a/engines/parallaction/exec_br.cpp +++ b/engines/parallaction/exec_br.cpp @@ -337,7 +337,7 @@ DECLARE_COMMAND_OPCODE(speak) { return; } - if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak && ctxt._cmd->_zone->u._speakDialogue) { + if (ACTIONTYPE(ctxt._cmd->_zone) == kZoneSpeak) { _vm->enterDialogueMode(ctxt._cmd->_zone); } else { _vm->_activeZone = ctxt._cmd->_zone; diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 2990d024d2..326ae2c519 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -538,12 +538,12 @@ GfxObj *Gfx::renderFloatingLabel(Font *font, char *text) { setupLabelSurface(*cnv, w, h); - font->setColor((_gameType == GType_BRA) ? 0 : 7); + font->setColor((_vm->getGameType() == GType_BRA) ? 0 : 7); font->drawString((byte*)cnv->pixels + 1, cnv->w, text); font->drawString((byte*)cnv->pixels + 1 + cnv->w * 2, cnv->w, text); font->drawString((byte*)cnv->pixels + cnv->w, cnv->w, text); font->drawString((byte*)cnv->pixels + 2 + cnv->w, cnv->w, text); - font->setColor((_gameType == GType_BRA) ? 11 : 1); + font->setColor((_vm->getGameType() == GType_BRA) ? 11 : 1); font->drawString((byte*)cnv->pixels + 1 + cnv->w, cnv->w, text); } else { w = font->getStringWidth(text); @@ -835,7 +835,7 @@ void Gfx::setBackground(uint type, BackgroundInfo *info) { // The PC version of BRA needs the entries 20-31 of the palette to be constant, but // the background resource files are screwed up. The right colors come from an unused // bitmap (pointer.bmp). Nothing is known about the Amiga version so far. - if ((_gameType == GType_BRA) && (_vm->getPlatform() == Common::kPlatformPC)) { + if ((_vm->getGameType() == GType_BRA) && (_vm->getPlatform() == Common::kPlatformPC)) { int r, g, b; for (uint i = 16; i < 32; i++) { _backupPal.getEntry(i, r, g, b); diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index ca8f358158..7ad1be8681 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -203,13 +203,13 @@ int Input::updateGameInput() { return event; } - if (_gameType == GType_Nippon) { + if (_vm->getGameType() == GType_Nippon) { if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) { if (_keyPressed.keycode == Common::KEYCODE_l) event = kEvLoadGame; if (_keyPressed.keycode == Common::KEYCODE_s) event = kEvSaveGame; } } else - if (_gameType == GType_BRA) { + if (_vm->getGameType() == GType_BRA) { if (_hasKeyPressEvent && (_vm->getFeatures() & GF_DEMO) == 0) { if (_keyPressed.keycode == Common::KEYCODE_F5) event = kEvIngameMenu; } @@ -325,13 +325,8 @@ bool Input::translateGameInput() { if ((_mouseButtons == kMouseLeftUp) && ((_activeItem._id != 0) || (ACTIONTYPE(z) == kZoneCommand))) { - bool noWalk = z->_flags & kFlagsNoWalk; // check the explicit no-walk flag - if (_gameType == GType_BRA) { - // action performed on object marked for self-use do not need walk in BRA - noWalk |= ((z->_flags & kFlagsYourself) != 0); - } - - if (noWalk) { + if (z->_flags & kFlagsNoWalk) { + // character doesn't need to walk to take specified action takeAction(z); } else { // action delayed: if Zone defined a moveto position the character is programmed to move there, @@ -356,7 +351,7 @@ bool Input::translateGameInput() { void Input::enterInventoryMode() { Common::Point mousePos; - getAbsoluteCursorPos(mousePos); + getCursorPos(mousePos); bool hitCharacter = _vm->hitZone(kZoneYou, mousePos.x, mousePos.y); if (hitCharacter) { diff --git a/engines/parallaction/objects.h b/engines/parallaction/objects.h index 36231cfcc5..50a789247f 100644 --- a/engines/parallaction/objects.h +++ b/engines/parallaction/objects.h @@ -88,9 +88,9 @@ enum ZoneFlags { kFlagsNoWalk = 0x800, // Zone: character doesn't need to walk towards object to interact // BRA specific - kFlagsYourself = 0x1000, // BRA: marks zones used by the character on him/herself + kFlagsYourself = 0x1000, kFlagsScaled = 0x2000, - kFlagsSelfuse = 0x4000, // BRA: marks zones to be preserved across location changes (see Parallaction::freeZones) + kFlagsSelfuse = 0x4000, kFlagsIsAnimation = 0x1000000, // BRA: used in walk code (trap check), to tell is a Zone is an Animation kFlagsAnimLinked = 0x2000000 }; diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp index ce7525345a..cb208a17ff 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -102,8 +102,7 @@ Parallaction::~Parallaction() { Common::Error Parallaction::init() { - - _gameType = getGameType(); + _engineFlags = 0; _objectsNames = NULL; _globalFlagsNames = NULL; @@ -409,7 +408,7 @@ void Parallaction::drawAnimation(AnimationPtr anim) { uint16 layer = LAYER_FOREGROUND; uint16 scale = 100; - switch (_gameType) { + switch (getGameType()) { case GType_Nippon: if ((anim->_flags & kFlagsNoMasked) == 0) { // Layer in NS depends on where the animation is on the screen, for each animation. @@ -524,7 +523,7 @@ void Parallaction::enterCommentMode(ZonePtr z) { } // TODO: move this balloons stuff into DialogueManager and BalloonManager - if (_gameType == GType_Nippon) { + if (getGameType() == GType_Nippon) { if (!data->_filename.empty()) { if (data->_gfxobj == 0) { data->_gfxobj = _disk->loadStatic(data->_filename.c_str()); @@ -541,7 +540,7 @@ void Parallaction::enterCommentMode(ZonePtr z) { _gfx->setItem(_char._talk, 190, 80); } } else - if (_gameType == GType_BRA) { + if (getGameType() == GType_BRA) { _balloonMan->setSingleBalloon(data->_examineText.c_str(), 0, 0, 1, BalloonManager::kNormalColor); _gfx->setItem(_char._talk, 10, 80); } @@ -652,21 +651,13 @@ bool Parallaction::pickupItem(ZonePtr z) { return (slot != -1); } +// FIXME: input coordinates must be offseted to handle scrolling! bool Parallaction::checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y) { - // check if really a special zone - if (_gameType == GType_Nippon) { - // so-called special zones in NS have special x coordinates - if ((z->getX() != -2) && (z->getX() != -3)) { - return false; - } - } - if (_gameType == GType_BRA) { - // so far, special zones in BRA are only merge zones - if (ACTIONTYPE(z) != kZoneMerge) { - return false; - } + // not a special zone + if ((z->getX() != -2) && (z->getX() != -3)) { + return false; } - + // WORKAROUND: this huge condition is needed because we made TypeData a collection of structs // instead of an union. So, merge->_obj1 and get->_icon were just aliases in the original engine, // but we need to check it separately here. The same workaround is applied in freeZones. @@ -690,33 +681,7 @@ bool Parallaction::checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y) { return false; } -bool Parallaction::checkZoneType(ZonePtr z, uint32 type) { - if (_gameType == GType_Nippon) { - if ((type == 0) && (ITEMTYPE(z) == 0)) - return true; - } - - if (_gameType == GType_BRA) { - if (type == 0) { - if (ITEMTYPE(z) == 0) { - if (ACTIONTYPE(z) != kZonePath) { - return true; - } - } - if (ACTIONTYPE(z) == kZoneDoor) { - return true; - } - } - } - - if (z->_type == type) - return true; - if (ITEMTYPE(z) == type) - return true; - - return false; -} - +// FIXME: input coordinates must be offseted to handle scrolling! bool Parallaction::checkZoneBox(ZonePtr z, uint32 type, uint x, uint y) { if (z->_flags & kFlagsRemove) return false; @@ -724,30 +689,29 @@ bool Parallaction::checkZoneBox(ZonePtr z, uint32 type, uint x, uint y) { debugC(5, kDebugExec, "checkZoneBox for %s (type = %x, x = %i, y = %i)", z->_name, type, x, y); if (!z->hitRect(x, y)) { + // check for special zones (items defined in common.loc) if (checkSpecialZoneBox(z, type, x, y)) return true; - // check if self-use zone (nothing to do with kFlagsSelfuse) - if (_gameType == GType_Nippon) { - if (z->getX() != -1) { // no explicit self-use flag in NS - return false; - } - } - if (_gameType == GType_BRA) { - if (!(z->_flags & kFlagsYourself)) { - return false; - } - } - if (!_char._ani->hitFrameRect(x, y)) { + if (z->getX() != -1) + return false; + if (!_char._ani->hitFrameRect(x, y)) return false; - } - // we get here only if (x,y) hits the character and the zone is marked as self-use } - return checkZoneType(z, type); + // normal Zone + if ((type == 0) && (ITEMTYPE(z) == 0)) + return true; + if (z->_type == type) + return true; + if (ITEMTYPE(z) == type) + return true; + + return false; } +// FIXME: input coordinates must be offseted to handle scrolling! bool Parallaction::checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y) { if (z->_flags & kFlagsRemove) return false; @@ -763,14 +727,18 @@ bool Parallaction::checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y) { return false; } - return checkZoneType(z, type); -} + // NOTE: the implementation of the following lines is a different in the + // original... it is working so far, though + if ((type == 0) && (ITEMTYPE(z) == 0)) + return true; + if (z->_type == type) + return true; + if (ITEMTYPE(z) == type) + return true; -/* NOTE: hitZone needs to be passed absolute game coordinates to work. + return false; +} - When type is kZoneMerge, then x and y are the identifiers of the objects to merge, - and the above requirement does not apply. -*/ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { uint16 _di = y; uint16 _si = x; @@ -784,20 +752,14 @@ ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) { } } + int16 _a, _b, _c, _d; bool _ef; for (AnimationList::iterator ait = _location._animations.begin(); ait != _location._animations.end(); ++ait) { AnimationPtr a = *ait; - _a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation - - if (!_a) { - if (_gameType == GType_BRA && ACTIONTYPE(a) != kZoneTrap) { - continue; - } - } - + _a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation _ef = a->hitFrameRect(_si, _di); _b = ((type != 0) || (a->_type == kZoneYou)) ? 0 : 1; // _b: (no type specified) AND (Animation is not the character) @@ -989,7 +951,7 @@ bool CharacterName::dummy() const { } void Parallaction::beep() { - if (_gameType == GType_Nippon) { + if (getGameType() == GType_Nippon) { _soundMan->execute(SC_SETSFXCHANNEL, 3); _soundMan->execute(SC_SETSFXVOLUME, 127); _soundMan->execute(SC_SETSFXLOOPING, (int32)0); diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 7bbdf79f1c..3a84aa215e 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -280,7 +280,6 @@ public: int32 _screenWidth; int32 _screenHeight; int32 _screenSize; - int _gameType; // subsystems Gfx *_gfx; @@ -361,7 +360,6 @@ public: uint32 getLocationFlags(); bool checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y); bool checkZoneBox(ZonePtr z, uint32 type, uint x, uint y); - bool checkZoneType(ZonePtr z, uint32 type); bool checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y); ZonePtr hitZone(uint32 type, uint16 x, uint16 y); void runZone(ZonePtr z); diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 470c698a21..ee718189b5 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -196,7 +196,7 @@ void Parallaction_br::runPendingZones() { if (_activeZone) { z = _activeZone; // speak Zone or sound _activeZone.reset(); - if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) { + if (ACTIONTYPE(z) == kZoneSpeak) { enterDialogueMode(z); } else { runZone(z); // FIXME: BRA doesn't handle sound yet @@ -206,7 +206,7 @@ void Parallaction_br::runPendingZones() { if (_activeZone2) { z = _activeZone2; // speak Zone or sound _activeZone2.reset(); - if (ACTIONTYPE(z) == kZoneSpeak && z->u._speakDialogue) { + if (ACTIONTYPE(z) == kZoneSpeak) { enterDialogueMode(z); } else { runZone(z); // FIXME: BRA doesn't handle sound yet diff --git a/engines/parallaction/parser.cpp b/engines/parallaction/parser.cpp index df1e91e8b4..928f3f5b74 100644 --- a/engines/parallaction/parser.cpp +++ b/engines/parallaction/parser.cpp @@ -44,10 +44,8 @@ Script::~Script() { /* * readLineIntern read a text line and prepares it for * parsing, by stripping the leading whitespace and - * changing tabs to spaces. It will stop on a CR, LF, or - * SUB (0x1A), which may all occur at the end of a script - * line. - * Returns an empty string (length = 0) when a line + * changing tabs to spaces. It will stop on a CR or LF, + * and return an empty string (length = 0) when a line * has no printable text in it. */ char *Script::readLineIntern(char *buf, size_t bufSize) { @@ -56,8 +54,7 @@ char *Script::readLineIntern(char *buf, size_t bufSize) { char c = _input->readSByte(); if (_input->eos()) break; - // break if EOL - if (c == '\n' || c == '\r' || c == (char)0x1A) + if (c == '\n' || c == '\r') break; if (c == '\t') c = ' '; diff --git a/engines/parallaction/parser_ns.cpp b/engines/parallaction/parser_ns.cpp index ff24a06ceb..be72cf73a1 100644 --- a/engines/parallaction/parser_ns.cpp +++ b/engines/parallaction/parser_ns.cpp @@ -286,7 +286,6 @@ void LocationParser_ns::parseAnimation(AnimationList &list, char *name) { debugC(5, kDebugParser, "parseAnimation(name: %s)", name); if (_vm->_location.findAnimation(name)) { - _zoneProg++; _script->skip("endanimation"); return; } @@ -1306,7 +1305,6 @@ void LocationParser_ns::parseZone(ZoneList &list, char *name) { debugC(5, kDebugParser, "parseZone(name: %s)", name); if (_vm->_location.findZone(name)) { - _zoneProg++; _script->skip("endzone"); return; } diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp index 407dd86ec3..1c724ddc1c 100644 --- a/engines/parallaction/sound_br.cpp +++ b/engines/parallaction/sound_br.cpp @@ -172,11 +172,11 @@ bool MidiParser_MSC::loadMusic(byte *data, uint32 size) { byte *pos = data; - if (memcmp("MSCt", pos, 4)) { + uint32 signature = read4high(pos); + if (memcmp("tCSM", &signature, 4)) { warning("Expected header not found in music file."); return false; } - pos += 4; _beats = read1(pos); _ppqn = read2low(pos); diff --git a/engines/saga/saga.cpp b/engines/saga/saga.cpp index 1b7fa97f8d..025462c558 100644 --- a/engines/saga/saga.cpp +++ b/engines/saga/saga.cpp @@ -316,6 +316,20 @@ Common::Error SagaEngine::run() { syncSoundSettings(); + +#if 0 + // FIXME: Disabled this code for now. We want to get rid of OSystem::kFeatureAutoComputeDirtyRects + // and this is the last place to make use of it. We need to find out whether doing + // so causes any regressions. If it does, we can reenable it, if not, we can remove + // this code in 0.13.0. + + // FIXME: This is the ugly way of reducing redraw overhead. It works + // well for 320x200 but it's unclear how well it will work for + // 640x480. + if (getGameId() == GID_ITE) + _system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true); +#endif + int msec = 0; _previousTicks = _system->getMillis(); diff --git a/engines/sci/engine/script_patches.cpp b/engines/sci/engine/script_patches.cpp index ef7b7dcb41..e5fcbf72c2 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -52,7 +52,7 @@ struct SciScriptSignature { // - rinse and repeat -// daySixBeignet::changeState (4) is called when the cop goes out and sets cycles to 220. +// daySixBeignet::changeState is called when the cop goes out and sets cycles to 220. // this is not enough time to get to the door, so we patch that to 23 seconds const byte gk1SignatureDay6PoliceBeignet[] = { 4, @@ -78,8 +78,6 @@ const uint16 gk1PatchDay6PoliceBeignet[] = { PATCH_END }; -// sargSleeping::changeState (8) is called when the cop falls asleep and sets cycles to 220. -// this is not enough time to get to the door, so we patch it to 42 seconds const byte gk1SignatureDay6PoliceSleep[] = { 4, 0x35, 0x08, // ldi 08 @@ -99,27 +97,8 @@ const uint16 gk1PatchDay6PoliceSleep[] = { PATCH_END }; -// startOfDay5::changeState (20h) - when gabriel goes to the phone the script will hang -const byte gk1SignatureDay5PhoneFreeze[] = { - 5, - 0x35, 0x03, // ldi 03 - 0x65, 0x1a, // aTop cycles - 0x32, // jmp [end] - +2, 3, // [skip 2 bytes, offset of jmp] - 0x3c, // dup - 0x35, 0x21, // ldi 21 - 0 -}; - -const uint16 gk1PatchDay5PhoneFreeze[] = { - 0x35, 0x06, // ldi 06 - 0x65, 0x20, // aTop ticks - PATCH_END -}; - // script, description, magic DWORD, adjust const SciScriptSignature gk1Signatures[] = { - { 212, "day 5 phone freeze", PATCH_MAGICDWORD(0x35, 0x03, 0x65, 0x1a), 0, gk1SignatureDay5PhoneFreeze, gk1PatchDay5PhoneFreeze }, { 230, "day 6 police beignet timer issue", PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -16, gk1SignatureDay6PoliceBeignet, gk1PatchDay6PoliceBeignet }, { 230, "day 6 police sleep timer issue", PATCH_MAGICDWORD(0x34, 0xdc, 0x00, 0x65), -5, gk1SignatureDay6PoliceSleep, gk1PatchDay6PoliceSleep }, { 0, NULL, 0, 0, NULL, NULL } diff --git a/engines/sci/sound/iterator/core.cpp b/engines/sci/sound/iterator/core.cpp new file mode 100644 index 0000000000..7cd730b3e2 --- /dev/null +++ b/engines/sci/sound/iterator/core.cpp @@ -0,0 +1,1013 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* Sound subsystem core: Event handler, sound player dispatching */ + +#include "sci/sci.h" +#ifdef USE_OLD_MUSIC_FUNCTIONS + +#include "sci/sound/iterator/core.h" +#include "sci/sound/iterator/iterator.h" +#include "sci/sound/drivers/mididriver.h" + +#include "common/system.h" +#include "common/timer.h" + +#include "sound/mixer.h" + +namespace Sci { + +/* Plays a song iterator that found a PCM through a PCM device, if possible +** Parameters: (SongIterator *) it: The iterator to play +** (SongHandle) handle: Debug handle +** Returns : (int) 0 if the effect will not be played, nonzero if it will +** This assumes that the last call to 'it->next()' returned SI_PCM. +*/ +static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle); + + +#pragma mark - + + +class SfxPlayer { +public: + /** Number of voices that can play simultaneously */ + int _polyphony; + +protected: + SciVersion _soundVersion; + MidiPlayer *_mididrv; + + SongIterator *_iterator; + Audio::Timestamp _wakeupTime; + Audio::Timestamp _currentTime; + uint32 _pauseTimeDiff; + + bool _paused; + bool _iteratorIsDone; + uint32 _tempo; + + Common::Mutex _mutex; + int _volume; + + void play_song(SongIterator *it); + static void player_timer_callback(void *refCon); + +public: + SfxPlayer(SciVersion soundVersion); + ~SfxPlayer(); + + /** + * Initializes the player. + * @param resMan a resource manager for driver initialization + * @param expected_latency expected delay in between calls to 'maintenance' (in microseconds) + * @return Common::kNoError on success, Common::kUnknownError on failure + */ + Common::Error init(ResourceManager *resMan, int expected_latency); + + /** + * Adds an iterator to the song player + * @param it The iterator to play + * @param start_time The time to assume as the time the first MIDI command executes at + * @return Common::kNoError on success, Common::kUnknownError on failure + * + * The iterator should not be cloned (to avoid memory leaks) and + * may be modified according to the needs of the player. + * Implementors may use the 'sfx_iterator_combine()' function + * to add iterators onto their already existing iterators. + */ + Common::Error add_iterator(SongIterator *it, uint32 start_time); + + /** + * Stops the currently playing song and deletes the associated iterator. + * @return Common::kNoError on success, Common::kUnknownError on failure + */ + Common::Error stop(); + + /** + * Transmits a song iterator message to the active song. + * @param msg the message to transmit + * @return Common::kNoError on success, Common::kUnknownError on failure + */ + Common::Error iterator_message(const SongIterator::Message &msg); + + /** + * Pauses song playing. + * @return Common::kNoError on success, Common::kUnknownError on failure + */ + Common::Error pause(); + + /** + * Resumes song playing after a pause. + * @return Common::kNoError on success, Common::kUnknownError on failure + */ + Common::Error resume(); + + /** + * Pass a raw MIDI event to the synth. + * @param argc length of buffer holding the midi event + * @param argv the buffer itself + */ + void tell_synth(int buf_nr, byte *buf); + + void setVolume(int vol); + + int getVolume(); +}; + +SfxPlayer::SfxPlayer(SciVersion soundVersion) + : _soundVersion(soundVersion), _wakeupTime(0, SFX_TICKS_PER_SEC), _currentTime(0, 1) { + _polyphony = 0; + + _mididrv = 0; + + _iterator = NULL; + _pauseTimeDiff = 0; + + _paused = false; + _iteratorIsDone = false; + _tempo = 0; + + _volume = 15; +} + +SfxPlayer::~SfxPlayer() { + if (_mididrv) { + _mididrv->close(); + delete _mididrv; + } + delete _iterator; + _iterator = NULL; +} + +void SfxPlayer::play_song(SongIterator *it) { + while (_iterator && _wakeupTime.msecsDiff(_currentTime) <= 0) { + int delay; + byte buf[8]; + int result; + + switch ((delay = songit_next(&(_iterator), + buf, &result, + IT_READER_MASK_ALL + | IT_READER_MAY_FREE + | IT_READER_MAY_CLEAN))) { + + case SI_FINISHED: + delete _iterator; + _iterator = NULL; + _iteratorIsDone = true; + return; + + case SI_IGNORE: + case SI_LOOP: + case SI_RELATIVE_CUE: + case SI_ABSOLUTE_CUE: + break; + + case SI_PCM: + sfx_play_iterator_pcm(_iterator, 0); + break; + + case 0: + static_cast<MidiDriver *>(_mididrv)->send(buf[0], buf[1], buf[2]); + + break; + + default: + _wakeupTime = _wakeupTime.addFrames(delay); + } + } +} + +void SfxPlayer::tell_synth(int buf_nr, byte *buf) { + byte op1 = (buf_nr < 2 ? 0 : buf[1]); + byte op2 = (buf_nr < 3 ? 0 : buf[2]); + + static_cast<MidiDriver *>(_mididrv)->send(buf[0], op1, op2); +} + +void SfxPlayer::player_timer_callback(void *refCon) { + SfxPlayer *thePlayer = (SfxPlayer *)refCon; + assert(refCon); + Common::StackLock lock(thePlayer->_mutex); + + if (thePlayer->_iterator && !thePlayer->_iteratorIsDone && !thePlayer->_paused) { + thePlayer->play_song(thePlayer->_iterator); + } + + thePlayer->_currentTime = thePlayer->_currentTime.addFrames(1); +} + +/* API implementation */ + +Common::Error SfxPlayer::init(ResourceManager *resMan, int expected_latency) { + MidiDriverType musicDriver = MidiDriver::detectMusicDriver(MDT_PCSPK | MDT_ADLIB); + + switch (musicDriver) { + case MD_ADLIB: + // FIXME: There's no Amiga sound option, so we hook it up to AdLib + if (g_sci->getPlatform() == Common::kPlatformAmiga) + _mididrv = MidiPlayer_Amiga_create(_soundVersion); + else + _mididrv = MidiPlayer_AdLib_create(_soundVersion); + break; + case MD_PCJR: + _mididrv = MidiPlayer_PCJr_create(_soundVersion); + break; + case MD_PCSPK: + _mididrv = MidiPlayer_PCSpeaker_create(_soundVersion); + break; + default: + break; + } + + assert(_mididrv); + + _polyphony = _mididrv->getPolyphony(); + + _tempo = _mididrv->getBaseTempo(); + uint32 time = g_system->getMillis(); + _currentTime = Audio::Timestamp(time, 1000000 / _tempo); + _wakeupTime = Audio::Timestamp(time, SFX_TICKS_PER_SEC); + + _mididrv->setTimerCallback(this, player_timer_callback); + _mididrv->open(resMan); + _mididrv->setVolume(_volume); + + return Common::kNoError; +} + +Common::Error SfxPlayer::add_iterator(SongIterator *it, uint32 start_time) { + Common::StackLock lock(_mutex); + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(_mididrv->getPlayId())); + SIMSG_SEND(it, SIMSG_SET_RHYTHM(_mididrv->hasRhythmChannel())); + + if (_iterator == NULL) { + // Resync with clock + _currentTime = Audio::Timestamp(g_system->getMillis(), 1000000 / _tempo); + _wakeupTime = Audio::Timestamp(start_time, SFX_TICKS_PER_SEC); + } + + _iterator = sfx_iterator_combine(_iterator, it); + _iteratorIsDone = false; + + return Common::kNoError; +} + +Common::Error SfxPlayer::stop() { + debug(3, "Player: Stopping song iterator %p", (void *)_iterator); + Common::StackLock lock(_mutex); + delete _iterator; + _iterator = NULL; + for (int i = 0; i < MIDI_CHANNELS; i++) + static_cast<MidiDriver *>(_mididrv)->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0); + + return Common::kNoError; +} + +Common::Error SfxPlayer::iterator_message(const SongIterator::Message &msg) { + Common::StackLock lock(_mutex); + if (!_iterator) { + return Common::kUnknownError; + } + + songit_handle_message(&_iterator, msg); + + return Common::kNoError; +} + +Common::Error SfxPlayer::pause() { + Common::StackLock lock(_mutex); + + _paused = true; + _pauseTimeDiff = _wakeupTime.msecsDiff(_currentTime); + + _mididrv->playSwitch(false); + + return Common::kNoError; +} + +Common::Error SfxPlayer::resume() { + Common::StackLock lock(_mutex); + + _wakeupTime = Audio::Timestamp(_currentTime.msecs() + _pauseTimeDiff, SFX_TICKS_PER_SEC); + _mididrv->playSwitch(true); + _paused = false; + + return Common::kNoError; +} + +void SfxPlayer::setVolume(int vol) { + _mididrv->setVolume(vol); +} + +int SfxPlayer::getVolume() { + return _mididrv->getVolume(); +} + +#pragma mark - + +void SfxState::sfx_reset_player() { + if (_player) + _player->stop(); +} + +void SfxState::sfx_player_tell_synth(int buf_nr, byte *buf) { + if (_player) + _player->tell_synth(buf_nr, buf); +} + +int SfxState::sfx_get_player_polyphony() { + if (_player) + return _player->_polyphony; + else + return 0; +} + +SfxState::SfxState() { + _player = NULL; + _it = NULL; + _flags = 0; + _song = NULL; + _suspended = 0; +} + +SfxState::~SfxState() { +} + + +void SfxState::freezeTime() { + /* Freezes the top song delay time */ + const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); + Song *song = _song; + + while (song) { + song->_delay = song->_wakeupTime.frameDiff(ctime); + if (song->_delay < 0) + song->_delay = 0; + + song = song->_nextPlaying; + } +} + +void SfxState::thawTime() { + /* inverse of freezeTime() */ + const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); + Song *song = _song; + + while (song) { + song->_wakeupTime = ctime.addFrames(song->_delay); + + song = song->_nextPlaying; + } +} + +#if 0 +// Unreferenced - removed +static void _dump_playing_list(SfxState *self, char *msg) { + Song *song = self->_song; + + fprintf(stderr, "[] Song list : [ "); + song = *(self->_songlib.lib); + while (song) { + fprintf(stderr, "%08lx:%d ", song->handle, song->_status); + song = song->_nextPlaying; + } + fprintf(stderr, "]\n"); + + fprintf(stderr, "[] Play list (%s) : [ " , msg); + + while (song) { + fprintf(stderr, "%08lx ", song->handle); + song = song->_nextPlaying; + } + + fprintf(stderr, "]\n"); +} +#endif + +#if 0 +static void _dump_songs(SfxState *self) { + Song *song = self->_song; + + fprintf(stderr, "Cue iterators:\n"); + song = *(self->_songlib.lib); + while (song) { + fprintf(stderr, " **\tHandle %08x (p%d): status %d\n", + song->handle, song->_priority, song->_status); + SIMSG_SEND(song->_it, SIMSG_PRINT(1)); + song = song->_next; + } + + if (self->_player) { + fprintf(stderr, "Audio iterator:\n"); + self->_player->iterator_message(SongIterator::Message(0, SIMSG_PRINT(1))); + } +} +#endif + +bool SfxState::isPlaying(Song *song) { + Song *playing_song = _song; + + /* _dump_playing_list(this, "is-playing");*/ + + while (playing_song) { + if (playing_song == song) + return true; + playing_song = playing_song->_nextPlaying; + } + return false; +} + +void SfxState::setSongStatus(Song *song, int status) { + const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); + + switch (status) { + + case SOUND_STATUS_STOPPED: + // Reset + song->_it->init(); + break; + + case SOUND_STATUS_SUSPENDED: + case SOUND_STATUS_WAITING: + if (song->_status == SOUND_STATUS_PLAYING) { + // Update delay, set wakeup_time + song->_delay += song->_wakeupTime.frameDiff(ctime); + song->_wakeupTime = ctime; + } + if (status == SOUND_STATUS_SUSPENDED) + break; + + /* otherwise... */ + + case SOUND_STATUS_PLAYING: + if (song->_status == SOUND_STATUS_STOPPED) { + // Starting anew + song->_wakeupTime = ctime; + } + + if (isPlaying(song)) + status = SOUND_STATUS_PLAYING; + else + status = SOUND_STATUS_WAITING; + break; + + default: + fprintf(stderr, "%s L%d: Attempt to set invalid song" + " state %d!\n", __FILE__, __LINE__, status); + return; + + } + song->_status = status; +} + +/* Update internal state iff only one song may be played */ +void SfxState::updateSingleSong() { + Song *newsong = _songlib.findFirstActive(); + + if (newsong != _song) { + freezeTime(); /* Store song delay time */ + + if (_player) + _player->stop(); + + if (newsong) { + if (!newsong->_it) + return; /* Restore in progress and not ready for this yet */ + + /* Change song */ + if (newsong->_status == SOUND_STATUS_WAITING) + setSongStatus(newsong, SOUND_STATUS_PLAYING); + + /* Change instrument mappings */ + } else { + /* Turn off sound */ + } + if (_song) { + if (_song->_status == SOUND_STATUS_PLAYING) + setSongStatus(newsong, SOUND_STATUS_WAITING); + } + + Common::String debugMessage = "[SFX] Changing active song:"; + if (!_song) { + debugMessage += " New song:"; + } else { + char tmp[50]; + sprintf(tmp, " pausing %08lx, now playing ", _song->_handle); + debugMessage += tmp; + } + + if (newsong) { + char tmp[20]; + sprintf(tmp, "%08lx\n", newsong->_handle); + debugMessage += tmp; + } else { + debugMessage += " none\n"; + } + + debugC(2, kDebugLevelSound, "%s", debugMessage.c_str()); + + _song = newsong; + thawTime(); /* Recover song delay time */ + + if (newsong && _player) { + SongIterator *clonesong = newsong->_it->clone(newsong->_delay); + + _player->add_iterator(clonesong, newsong->_wakeupTime.msecs()); + } + } +} + + +void SfxState::updateMultiSong() { + Song *oldfirst = _song; + Song *oldseeker; + Song *newsong = _songlib.findFirstActive(); + Song *newseeker; + Song not_playing_anymore; /* Dummy object, referenced by + ** songs which are no longer + ** active. */ + + /* _dump_playing_list(this, "before");*/ + freezeTime(); /* Store song delay time */ + + // WORKAROUND: sometimes, newsong can be NULL (e.g. in SQ4). + // Handle this here, so that we avoid a crash + if (!newsong) { + // Iterators should get freed when there's only one song left playing + if(oldfirst && oldfirst->_status == SOUND_STATUS_STOPPED) { + debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx", oldfirst->_handle); + if (_player && oldfirst->_it) + _player->iterator_message(SongIterator::Message(oldfirst->_it->ID, SIMSG_STOP)); + } + return; + } + + for (newseeker = newsong; newseeker; + newseeker = newseeker->_nextPlaying) { + if (!newseeker || !newseeker->_it) + return; /* Restore in progress and not ready for this yet */ + } + + /* First, put all old songs into the 'stopping' list and + ** mark their 'next-playing' as not_playing_anymore. */ + for (oldseeker = oldfirst; oldseeker; + oldseeker = oldseeker->_nextStopping) { + oldseeker->_nextStopping = oldseeker->_nextPlaying; + oldseeker->_nextPlaying = ¬_playing_anymore; + + if (oldseeker == oldseeker->_nextPlaying) { + error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__); + } + } + + /* Second, re-generate the new song queue. */ + for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) { + newseeker->_nextPlaying = _songlib.findNextActive(newseeker); + + if (newseeker == newseeker->_nextPlaying) { + error("updateMultiSong() failed. Breakpoint in %s, line %d", __FILE__, __LINE__); + } + } + /* We now need to update the currently playing song list, because we're + ** going to use some functions that require this list to be in a sane + ** state (particularly isPlaying(), indirectly */ + _song = newsong; + + /* Third, stop all old songs */ + for (oldseeker = oldfirst; oldseeker; + oldseeker = oldseeker->_nextStopping) + if (oldseeker->_nextPlaying == ¬_playing_anymore) { + setSongStatus(oldseeker, SOUND_STATUS_SUSPENDED); + debugC(2, kDebugLevelSound, "[SFX] Stopping song %lx", oldseeker->_handle); + + if (_player && oldseeker->_it) + _player->iterator_message(SongIterator::Message(oldseeker->_it->ID, SIMSG_STOP)); + oldseeker->_nextPlaying = NULL; /* Clear this pointer; we don't need the tag anymore */ + } + + for (newseeker = newsong; newseeker; newseeker = newseeker->_nextPlaying) { + if (newseeker->_status != SOUND_STATUS_PLAYING && _player) { + debugC(2, kDebugLevelSound, "[SFX] Adding song %lx", newseeker->_it->ID); + + SongIterator *clonesong = newseeker->_it->clone(newseeker->_delay); + _player->add_iterator(clonesong, g_system->getMillis()); + } + setSongStatus(newseeker, SOUND_STATUS_PLAYING); + } + + _song = newsong; + thawTime(); + /* _dump_playing_list(this, "after");*/ +} + +/* Update internal state */ +void SfxState::update() { + if (_flags & SFX_STATE_FLAG_MULTIPLAY) + updateMultiSong(); + else + updateSingleSong(); +} + +static int sfx_play_iterator_pcm(SongIterator *it, SongHandle handle) { +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Playing PCM: %08lx\n", handle); +#endif + if (g_system->getMixer()->isReady()) { + Audio::AudioStream *newfeed = it->getAudioStream(); + if (newfeed) { + g_system->getMixer()->playStream(Audio::Mixer::kSFXSoundType, 0, newfeed); + return 1; + } + } + return 0; +} + +#define DELAY (1000000 / SFX_TICKS_PER_SEC) + +void SfxState::sfx_init(ResourceManager *resMan, int flags, SciVersion soundVersion) { + _songlib._lib = 0; + _song = NULL; + _flags = flags; + + _player = NULL; + + if (flags & SFX_STATE_FLAG_NOSOUND) { + warning("[SFX] Sound disabled"); + return; + } + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Initialising: flags=%x\n", flags); +#endif + + /*-------------------*/ + /* Initialise player */ + /*-------------------*/ + + if (!resMan) { + warning("[SFX] Warning: No resource manager present, cannot initialise player"); + return; + } + + _player = new SfxPlayer(soundVersion); + + if (!_player) { + warning("[SFX] No song player found"); + return; + } + + if (_player->init(resMan, DELAY / 1000)) { + warning("[SFX] Song player reported error, disabled"); + delete _player; + _player = NULL; + } + + _resMan = resMan; +} + +void SfxState::sfx_exit() { +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Uninitialising\n"); +#endif + + delete _player; + _player = 0; + + g_system->getMixer()->stopAll(); + + _songlib.freeSounds(); +} + +void SfxState::sfx_suspend(bool suspend) { +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Suspending? = %d\n", suspend); +#endif + if (suspend && (!_suspended)) { + /* suspend */ + + freezeTime(); + if (_player) + _player->pause(); + /* Suspend song player */ + + } else if (!suspend && (_suspended)) { + /* unsuspend */ + + thawTime(); + if (_player) + _player->resume(); + + /* Unsuspend song player */ + } + + _suspended = suspend; +} + +int SfxState::sfx_poll(SongHandle *handle, int *cue) { + if (!_song) + return 0; /* No milk today */ + + *handle = _song->_handle; + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Polling any (%08lx)\n", *handle); +#endif + return sfx_poll_specific(*handle, cue); +} + +int SfxState::sfx_poll_specific(SongHandle handle, int *cue) { + const Audio::Timestamp ctime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); + Song *song = _song; + + while (song && song->_handle != handle) + song = song->_nextPlaying; + + if (!song) + return 0; /* Song not playing */ + + debugC(2, kDebugLevelSound, "[SFX:CUE] Polled song %08lx ", handle); + + while (1) { + if (song->_wakeupTime.frameDiff(ctime) > 0) + return 0; /* Patience, young hacker! */ + + byte buf[8]; + int result = songit_next(&(song->_it), buf, cue, IT_READER_MASK_ALL); + + switch (result) { + + case SI_FINISHED: + setSongStatus(song, SOUND_STATUS_STOPPED); + update(); + /* ...fall through... */ + case SI_LOOP: + case SI_RELATIVE_CUE: + case SI_ABSOLUTE_CUE: + if (result == SI_FINISHED) + debugC(2, kDebugLevelSound, " => finished"); + else { + if (result == SI_LOOP) + debugC(2, kDebugLevelSound, " => Loop: %d (0x%x)", *cue, *cue); + else + debugC(2, kDebugLevelSound, " => Cue: %d (0x%x)", *cue, *cue); + + } + return result; + + default: + if (result > 0) + song->_wakeupTime = song->_wakeupTime.addFrames(result); + + /* Delay */ + break; + } + } + +} + + +/*****************/ +/* Song basics */ +/*****************/ + +void SfxState::sfx_add_song(SongIterator *it, int priority, SongHandle handle, int number) { + Song *song = _songlib.findSong(handle); + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Adding song: %08lx at %d, it=%p\n", handle, priority, it); +#endif + if (!it) { + error("[SFX] Attempt to add empty song with handle %08lx", handle); + return; + } + + it->init(); + + /* If we're already playing this, stop it */ + /* Tell player to shut up */ +// _dump_songs(this); + + if (_player) + _player->iterator_message(SongIterator::Message(handle, SIMSG_STOP)); + + if (song) { + setSongStatus( song, SOUND_STATUS_STOPPED); + + fprintf(stderr, "Overwriting old song (%08lx) ...\n", handle); + if (song->_status == SOUND_STATUS_PLAYING || song->_status == SOUND_STATUS_SUSPENDED) { + delete it; + error("Unexpected (error): Song %ld still playing/suspended (%d)", + handle, song->_status); + return; + } else { + _songlib.removeSong(handle); /* No duplicates */ + } + + } + + song = new Song(handle, it, priority); + song->_resourceNum = number; + song->_hold = 0; + song->_loops = 0; + song->_wakeupTime = Audio::Timestamp(g_system->getMillis(), SFX_TICKS_PER_SEC); + _songlib.addSong(song); + _song = NULL; /* As above */ + update(); + + return; +} + +void SfxState::sfx_remove_song(SongHandle handle) { +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Removing song: %08lx\n", handle); +#endif + if (_song && _song->_handle == handle) + _song = NULL; + + _songlib.removeSong(handle); + update(); +} + + + +/**********************/ +/* Song modifications */ +/**********************/ + +#define ASSERT_SONG(s) if (!(s)) { warning("Looking up song handle %08lx failed in %s, L%d", handle, __FILE__, __LINE__); return; } + +void SfxState::sfx_song_set_status(SongHandle handle, int status) { + Song *song = _songlib.findSong(handle); + ASSERT_SONG(song); +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting song status to %d" + " (0:stop, 1:play, 2:susp, 3:wait): %08lx\n", status, handle); +#endif + + setSongStatus(song, status); + + update(); +} + +void SfxState::sfx_song_set_fade(SongHandle handle, fade_params_t *params) { +#ifdef DEBUG_SONG_API + static const char *stopmsg[] = {"??? Should not happen", "Do not stop afterwards", "Stop afterwards"}; +#endif + Song *song = _songlib.findSong(handle); + + ASSERT_SONG(song); + +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting fade params of %08lx to " + "final volume %d in steps of %d per %d ticks. %s.", + handle, fade->final_volume, fade->step_size, fade->ticks_per_step, + stopmsg[fade->action]); +#endif + + SIMSG_SEND_FADE(song->_it, params); + + update(); +} + +void SfxState::sfx_song_renice(SongHandle handle, int priority) { + Song *song = _songlib.findSong(handle); + ASSERT_SONG(song); +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Renicing song %08lx to %d\n", + handle, priority); +#endif + + song->_priority = priority; + + update(); +} + +void SfxState::sfx_song_set_loops(SongHandle handle, int loops) { + Song *song = _songlib.findSong(handle); + SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_LOOPS(loops)); + ASSERT_SONG(song); + + song->_loops = loops; +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting loops on %08lx to %d\n", + handle, loops); +#endif + songit_handle_message(&(song->_it), msg); + + if (_player/* && _player->send_iterator_message*/) + /* FIXME: The above should be optional! */ + _player->iterator_message(msg); +} + +void SfxState::sfx_song_set_hold(SongHandle handle, int hold) { + Song *song = _songlib.findSong(handle); + SongIterator::Message msg = SongIterator::Message(handle, SIMSG_SET_HOLD(hold)); + ASSERT_SONG(song); + + song->_hold = hold; +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] Setting hold on %08lx to %d\n", + handle, hold); +#endif + songit_handle_message(&(song->_it), msg); + + if (_player/* && _player->send_iterator_message*/) + /* FIXME: The above should be optional! */ + _player->iterator_message(msg); +} + +/* Different from the one in iterator.c */ +static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 0, 3, 2, 0, 3, 0 + }; + +static const SongHandle midi_send_base = 0xffff0000; + +Common::Error SfxState::sfx_send_midi(SongHandle handle, int channel, + int command, int arg1, int arg2) { + byte buffer[5]; + + /* Yes, in that order. SCI channel mutes are actually done via + a counting semaphore. 0 means to decrement the counter, 1 + to increment it. */ + static const char *channel_state[] = {"ON", "OFF"}; + + if (command == 0xb0 && + arg1 == SCI_MIDI_CHANNEL_MUTE) { + warning("TODO: channel mute (channel %d %s)", channel, channel_state[arg2]); + /* We need to have a GET_PLAYMASK interface to use + here. SET_PLAYMASK we've got. + */ + return Common::kNoError; + } + + buffer[0] = channel | command; /* No channel remapping yet */ + + switch (command) { + case 0x80 : + case 0x90 : + case 0xb0 : + buffer[1] = arg1 & 0xff; + buffer[2] = arg2 & 0xff; + break; + case 0xc0 : + buffer[1] = arg1 & 0xff; + break; + case 0xe0 : + buffer[1] = (arg1 & 0x7f) | 0x80; + buffer[2] = (arg1 & 0xff00) >> 7; + break; + default: + warning("Unexpected explicit MIDI command %02x", command); + return Common::kUnknownError; + } + + if (_player) + _player->tell_synth(MIDI_cmdlen[command >> 4], buffer); + return Common::kNoError; +} + +int SfxState::sfx_getVolume() { + return _player->getVolume(); +} + +void SfxState::sfx_setVolume(int volume) { + _player->setVolume(volume); +} + +void SfxState::sfx_all_stop() { +#ifdef DEBUG_SONG_API + fprintf(stderr, "[sfx-core] All stop\n"); +#endif + + _songlib.freeSounds(); + update(); +} + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS diff --git a/engines/sci/sound/iterator/core.h b/engines/sci/sound/iterator/core.h new file mode 100644 index 0000000000..a44fe2ecae --- /dev/null +++ b/engines/sci/sound/iterator/core.h @@ -0,0 +1,209 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* Sound engine */ +#ifndef SCI_SFX_CORE_H +#define SCI_SFX_CORE_H + +#include "common/error.h" + +#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS + +#ifdef USE_OLD_MUSIC_FUNCTIONS +#include "sci/sound/iterator/songlib.h" +#include "sci/resource.h" + +namespace Sci { + +class SfxPlayer; +class SongIterator; +struct fade_params_t; + +#define SFX_TICKS_PER_SEC 60 /* MIDI ticks per second */ + + +#define SFX_STATE_FLAG_MULTIPLAY (1 << 0) /* More than one song playable +** simultaneously ? */ +#define SFX_STATE_FLAG_NOSOUND (1 << 1) /* Completely disable sound playing */ + +class SfxState { +private: + SfxPlayer *_player; + +public: // FIXME, make private + SongIterator *_it; /**< The song iterator at the heart of things */ + uint _flags; /**< SFX_STATE_FLAG_* */ + SongLibrary _songlib; /**< Song library */ + Song *_song; /**< Active song, or start of active song chain */ + bool _suspended; /**< Whether we are suspended */ + ResourceManager *_resMan; + +public: + SfxState(); + ~SfxState(); + + /***********/ + /* General */ + /***********/ + + /* Initializes the sound engine + ** Parameters: (ResourceManager *) resMan: Resource manager for initialization + ** (int) flags: SFX_STATE_FLAG_* + */ + void sfx_init(ResourceManager *resMan, int flags, SciVersion soundVersion); + + /** Deinitializes the sound subsystem. */ + void sfx_exit(); + + /* Suspends/unsuspends the sound sybsystem + ** Parameters: (int) suspend: Whether to suspend (non-null) or to unsuspend + */ + void sfx_suspend(bool suspend); + + /* Polls the sound server for cues etc. + ** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise + ** (SongHandle) *handle: The affected handle + ** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP) + */ + int sfx_poll(SongHandle *handle, int *cue); + + /* Polls the sound server for cues etc. + ** Parameters: (SongHandle) handle: The handle to poll + ** Returns : (int) 0 if the cue queue is empty, SI_LOOP, SI_CUE, or SI_FINISHED otherwise + ** (int) *cue: The sound cue number (if SI_CUE), or the loop number (if SI_LOOP) + */ + int sfx_poll_specific(SongHandle handle, int *cue); + + /* Determines the current global volume settings + ** Returns : (int) The global volume, between 0 (silent) and 127 (max. volume) + */ + int sfx_getVolume(); + + /* Determines the current global volume settings + ** Parameters: (int) volume: The new global volume, between 0 and 127 (see above) + */ + void sfx_setVolume(int volume); + + /* Stops all songs currently playing, purges song library + */ + void sfx_all_stop(); + + + /*****************/ + /* Song basics */ + /*****************/ + + /* Adds a song to the internal sound library + ** Parameters: (SongIterator *) it: The iterator describing the song + ** (int) priority: Initial song priority (higher <-> more important) + ** (SongHandle) handle: The handle to associate with the song + */ + void sfx_add_song(SongIterator *it, int priority, SongHandle handle, int resnum); + + + /* Deletes a song and its associated song iterator from the song queue + ** Parameters: (SongHandle) handle: The song to remove + */ + void sfx_remove_song(SongHandle handle); + + + /**********************/ + /* Song modifications */ + /**********************/ + + + /* Sets the song status, i.e. whether it is playing, suspended, or stopped. + ** Parameters: (SongHandle) handle: Handle of the song to modify + ** (int) status: The song status the song should assume + ** WAITING and PLAYING are set implicitly and essentially describe the same state + ** as far as this function is concerned. + */ + void sfx_song_set_status(SongHandle handle, int status); + + /* Sets the new song priority + ** Parameters: (SongHandle) handle: The handle to modify + ** (int) priority: The priority to set + */ + void sfx_song_renice(SongHandle handle, int priority); + + /* Sets the number of loops for the specified song + ** Parameters: (SongHandle) handle: The song handle to reference + ** (int) loops: Number of loops to set + */ + void sfx_song_set_loops(SongHandle handle, int loops); + + /* Sets the number of loops for the specified song + ** Parameters: (SongHandle) handle: The song handle to reference + ** (int) hold: Number of loops to setn + */ + void sfx_song_set_hold(SongHandle handle, int hold); + + /* Instructs a song to be faded out + ** Parameters: (SongHandle) handle: The song handle to reference + ** (fade_params_t *) fade_setup: The precise fade-out configuration to use + */ + void sfx_song_set_fade(SongHandle handle, fade_params_t *fade_setup); + + + // Previously undocumented: + Common::Error sfx_send_midi(SongHandle handle, int channel, + int command, int arg1, int arg2); + + // misc + + /** + * Determines the polyphony of the player in use. + * @return Number of voices the active player can emit + */ + int sfx_get_player_polyphony(); + + /** + * Tells the player to stop its internal iterator. + */ + void sfx_reset_player(); + + /** + * Pass a raw MIDI event to the synth of the player. + * @param argc Length of buffer holding the midi event + * @param argv The buffer itself + */ + void sfx_player_tell_synth(int buf_nr, byte *buf); + +protected: + void freezeTime(); + void thawTime(); + + bool isPlaying(Song *song); + void setSongStatus(Song *song, int status); + void updateSingleSong(); + void updateMultiSong(); + void update(); +}; + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS + +#endif // SCI_SFX_CORE_H diff --git a/engines/sci/sound/iterator/iterator.cpp b/engines/sci/sound/iterator/iterator.cpp new file mode 100644 index 0000000000..5d9d63e5af --- /dev/null +++ b/engines/sci/sound/iterator/iterator.cpp @@ -0,0 +1,1686 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* Song iterators */ + +#include "common/util.h" + +#include "sci/sci.h" +#ifdef USE_OLD_MUSIC_FUNCTIONS + +#include "sci/sound/iterator/iterator_internal.h" +#include "sci/engine/state.h" // for sfx_player_tell_synth :/ +#include "sci/sound/iterator/core.h" // for sfx_player_tell_synth + +#include "sound/audiostream.h" +#include "sound/mixer.h" +#include "sound/decoders/raw.h" + +namespace Sci { + + +static const int MIDI_cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 1, 1, 2, 0 + }; + +/*#define DEBUG_DECODING*/ +/*#define DEBUG_VERBOSE*/ + +/** Find first set bit in bits and return its index. Returns 0 if bits is 0. */ +static int sci_ffs(int bits) { + if (!bits) + return 0; + + int retval = 1; + + while (!(bits & 1)) { + retval++; + bits >>= 1; + } + + return retval; +} + +static void print_tabs_id(int nr, songit_id_t id) { + while (nr-- > 0) + fprintf(stderr, "\t"); + + fprintf(stderr, "[%08lx] ", id); +} + +BaseSongIterator::BaseSongIterator(byte *data, uint size, songit_id_t id) + : _data(data, size) { + ID = id; +} + +/************************************/ +/*-- SCI0 iterator implementation --*/ +/************************************/ + +#define SCI0_MIDI_OFFSET 33 +#define SCI0_END_OF_SONG 0xfc /* proprietary MIDI command */ + +#define SCI0_PCM_SAMPLE_RATE_OFFSET 0x0e +#define SCI0_PCM_SIZE_OFFSET 0x20 +#define SCI0_PCM_DATA_OFFSET 0x2c + +#define CHECK_FOR_END_ABSOLUTE(offset) \ + if (offset > _data.size()) { \ + warning("Reached end of song without terminator (%x/%x) at %d", offset, _data.size(), __LINE__); \ + return SI_FINISHED; \ + } + +#define CHECK_FOR_END(offset_augment) \ + if ((channel->offset + (offset_augment)) > channel->end) { \ + channel->state = SI_STATE_FINISHED; \ + warning("Reached end of track %d without terminator (%x+%x/%x) at %d", channel->id, channel->offset, offset_augment, channel->end, __LINE__); \ + return SI_FINISHED; \ + } + + +static int _parse_ticks(byte *data, int *offset_p, int size) { + int ticks = 0; + int tempticks; + int offset = 0; + + do { + tempticks = data[offset++]; + ticks += (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX) ? + SCI_MIDI_TIME_EXPANSION_LENGTH : tempticks; + } while (tempticks == SCI_MIDI_TIME_EXPANSION_PREFIX + && offset < size); + + if (offset_p) + *offset_p = offset; + + return ticks; +} + + +static int _sci0_get_pcm_data(Sci0SongIterator *self, int *rate, int *xoffset, uint *xsize); + + +#define PARSE_FLAG_LOOPS_UNLIMITED (1 << 0) /* Unlimited # of loops? */ +#define PARSE_FLAG_PARAMETRIC_CUE (1 << 1) /* Assume that cues take an additional "cue value" argument */ +/* This implements a difference between SCI0 and SCI1 cues. */ + +void SongIteratorChannel::init(int id_, int offset_, int end_) { + playmask = PLAYMASK_NONE; /* Disable all channels */ + id = id_; + state = SI_STATE_DELTA_TIME; + loop_timepos = 0; + total_timepos = 0; + timepos_increment = 0; + delay = 0; /* Only used for more than one channel */ + last_cmd = 0xfe; + + offset = loop_offset = initial_offset = offset_; + end = end_; +} + +void SongIteratorChannel::resetSynthChannels() { + byte buf[5]; + + // FIXME: Evil hack + SfxState &sound = g_sci->getEngineState()->_sound; + + for (int i = 0; i < MIDI_CHANNELS; i++) { + if (playmask & (1 << i)) { + buf[0] = 0xe0 | i; /* Pitch bend */ + buf[1] = 0x80; /* Wheel center */ + buf[2] = 0x40; + sound.sfx_player_tell_synth(3, buf); + + buf[0] = 0xb0 | i; // Set control + buf[1] = 0x40; // Hold pedal + buf[2] = 0x00; // Off + sound.sfx_player_tell_synth(3, buf); + /* TODO: Reset other controls? */ + } + } +} + +int BaseSongIterator::parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags) { + byte cmd; + int paramsleft; + int midi_op; + int midi_channel; + + channel->state = SI_STATE_DELTA_TIME; + + cmd = _data[channel->offset++]; + + if (!(cmd & 0x80)) { + /* 'Running status' mode */ + channel->offset--; + cmd = channel->last_cmd; + } + + if (cmd == 0xfe) { + warning("song iterator subsystem: Corrupted sound resource detected."); + return SI_FINISHED; + } + + midi_op = cmd >> 4; + midi_channel = cmd & 0xf; + paramsleft = MIDI_cmdlen[midi_op]; + +#if 0 + if (1) { + fprintf(stderr, "[IT]: off=%x, cmd=%02x, takes %d args ", + channel->offset - 1, cmd, paramsleft); + fprintf(stderr, "[%02x %02x <%02x> %02x %02x %02x]\n", + _data[channel->offset-3], + _data[channel->offset-2], + _data[channel->offset-1], + _data[channel->offset], + _data[channel->offset+1], + _data[channel->offset+2]); + } +#endif + + buf[0] = cmd; + + + CHECK_FOR_END(paramsleft); + memcpy(buf + 1, _data.begin() + channel->offset, paramsleft); + *result = 1 + paramsleft; + + channel->offset += paramsleft; + + channel->last_cmd = cmd; + + /* Are we supposed to play this channel? */ + if ( + /* First, exclude "global" properties-- such as cues-- from consideration */ + (midi_op < 0xf + && !(cmd == SCI_MIDI_SET_SIGNAL) + && !(SCI_MIDI_CONTROLLER(cmd) + && buf[1] == SCI_MIDI_CUMULATIVE_CUE)) + + /* Next, check if the channel is allowed */ + && (!((1 << midi_channel) & channel->playmask))) + return /* Execute next command */ + nextCommand(buf, result); + + + if (cmd == SCI_MIDI_EOT) { + /* End of track? */ + channel->resetSynthChannels(); + if (_loops > 1) { + /* If allowed, decrement the number of loops */ + if (!(flags & PARSE_FLAG_LOOPS_UNLIMITED)) + *result = --_loops; + +#ifdef DEBUG_DECODING + fprintf(stderr, "%s L%d: (%p):%d Looping ", __FILE__, __LINE__, this, channel->id); + if (flags & PARSE_FLAG_LOOPS_UNLIMITED) + fprintf(stderr, "(indef.)"); + else + fprintf(stderr, "(%d)", _loops); + fprintf(stderr, " %x -> %x\n", + channel->offset, channel->loop_offset); +#endif + channel->offset = channel->loop_offset; + channel->state = SI_STATE_DELTA_TIME; + channel->total_timepos = channel->loop_timepos; + channel->last_cmd = 0xfe; + debugC(2, kDebugLevelSound, "Looping song iterator %08lx.", ID); + return SI_LOOP; + } else { + channel->state = SI_STATE_FINISHED; + return SI_FINISHED; + } + + } else if (cmd == SCI_MIDI_SET_SIGNAL) { + if (buf[1] == SCI_MIDI_SET_SIGNAL_LOOP) { + channel->loop_offset = channel->offset; + channel->loop_timepos = channel->total_timepos; + + return /* Execute next command */ + nextCommand(buf, result); + } else { + /* Used to be conditional <= 127 */ + *result = buf[1]; /* Absolute cue */ + return SI_ABSOLUTE_CUE; + } + } else if (SCI_MIDI_CONTROLLER(cmd)) { + switch (buf[1]) { + + case SCI_MIDI_CUMULATIVE_CUE: + if (flags & PARSE_FLAG_PARAMETRIC_CUE) + _ccc += buf[2]; + else { /* No parameter to CC */ + _ccc++; + /* channel->offset--; */ + } + *result = _ccc; + return SI_RELATIVE_CUE; + + case SCI_MIDI_RESET_ON_SUSPEND: + _resetflag = buf[2]; + break; + + case SCI_MIDI_SET_POLYPHONY: + _polyphony[midi_channel] = buf[2]; + +#if 0 + { + Sci1SongIterator *self1 = (Sci1SongIterator *)this; + int i; + int voices = 0; + for (i = 0; i < self1->_numChannels; i++) { + voices += _polyphony[i]; + } + + printf("SET_POLYPHONY(%d, %d) for a total of %d voices\n", midi_channel, buf[2], voices); + printf("[iterator] DEBUG: Polyphony = [ "); + for (i = 0; i < self1->_numChannels; i++) + printf("%d ", _polyphony[i]); + printf("]\n"); + printf("[iterator] DEBUG: Importance = [ "); + printf("]\n"); + } +#endif + break; + + case SCI_MIDI_SET_REVERB: + break; + + case SCI_MIDI_CHANNEL_MUTE: + warning("CHANNEL_MUTE(%d, %d)", midi_channel, buf[2]); + break; + + case SCI_MIDI_HOLD: { + // Safe cast: This controller is only used in SCI1 + Sci1SongIterator *self1 = (Sci1SongIterator *)this; + + if (buf[2] == self1->_hold) { + channel->offset = channel->initial_offset; + channel->state = SI_STATE_COMMAND; + channel->total_timepos = 0; + + self1->_numLoopedChannels = self1->_numActiveChannels - 1; + + // FIXME: + // This implementation of hold breaks getting out of the + // limo when visiting the airport near the start of LSL5. + // It seems like all channels should be reset here somehow, + // but not sure how. + // Forcing all channel offsets to 0 seems to fix the hang, + // but somehow slows the exit sequence down to take 20 seconds + // instead of about 3. + + return SI_LOOP; + } + + break; + } + case 0x04: /* UNKNOWN NYI (happens in LSL2 gameshow) */ + case 0x46: /* UNKNOWN NYI (happens in LSL3 binoculars) */ + case 0x61: /* UNKNOWN NYI (special for AdLib? Iceman) */ + case 0x73: /* UNKNOWN NYI (happens in Hoyle) */ + case 0xd1: /* UNKNOWN NYI (happens in KQ4 when riding the unicorn) */ + return /* Execute next command */ + nextCommand(buf, result); + + case 0x01: /* modulation */ + case 0x07: /* volume */ + case 0x0a: /* panpot */ + case 0x0b: /* expression */ + case 0x40: /* hold */ + case 0x79: /* reset all */ + /* No special treatment neccessary */ + break; + + } + return 0; + + } else { +#if 0 + /* Perform remapping, if neccessary */ + if (cmd != SCI_MIDI_SET_SIGNAL + && cmd < 0xf0) { /* Not a generic command */ + int chan = cmd & 0xf; + int op = cmd & 0xf0; + + chan = channel_remap[chan]; + buf[0] = chan | op; + } +#endif + + /* Process as normal MIDI operation */ + return 0; + } +} + +int BaseSongIterator::processMidi(byte *buf, int *result, + SongIteratorChannel *channel, int flags) { + CHECK_FOR_END(0); + + switch (channel->state) { + + case SI_STATE_PCM: { + if (_data[channel->offset] == 0 + && _data[channel->offset + 1] == SCI_MIDI_EOT) + /* Fake one extra tick to trick the interpreter into not killing the song iterator right away */ + channel->state = SI_STATE_PCM_MAGIC_DELTA; + else + channel->state = SI_STATE_DELTA_TIME; + return SI_PCM; + } + + case SI_STATE_PCM_MAGIC_DELTA: { + int rate; + int offset; + uint size; + int delay; + if (_sci0_get_pcm_data((Sci0SongIterator *)this, &rate, &offset, &size)) + return SI_FINISHED; /* 'tis broken */ + channel->state = SI_STATE_FINISHED; + delay = (size * 50 + rate - 1) / rate; /* number of ticks to completion*/ + + debugC(2, kDebugLevelSound, "delaying %d ticks", delay); + return delay; + } + + case SI_STATE_UNINITIALISED: + warning("Attempt to read command from uninitialized iterator"); + init(); + return nextCommand(buf, result); + + case SI_STATE_FINISHED: + return SI_FINISHED; + + case SI_STATE_DELTA_TIME: { + int offset; + int ticks = _parse_ticks(_data.begin() + channel->offset, + &offset, + _data.size() - channel->offset); + + channel->offset += offset; + channel->delay += ticks; + channel->timepos_increment = ticks; + + CHECK_FOR_END(0); + + channel->state = SI_STATE_COMMAND; + + if (ticks) + return ticks; + } + + /* continute otherwise... */ + + case SI_STATE_COMMAND: { + int retval; + channel->total_timepos += channel->timepos_increment; + channel->timepos_increment = 0; + + retval = parseMidiCommand(buf, result, channel, flags); + + if (retval == SI_FINISHED) { + if (_numActiveChannels) + --(_numActiveChannels); +#ifdef DEBUG_DECODING + fprintf(stderr, "%s L%d: (%p):%d Finished channel, %d channels left\n", + __FILE__, __LINE__, this, channel->id, + _numActiveChannels); +#endif + /* If we still have channels left... */ + if (_numActiveChannels) { + return nextCommand(buf, result); + } + + /* Otherwise, we have reached the end */ + _loops = 0; + } + + return retval; + } + + default: + error("Invalid iterator state %d", channel->state); + return SI_FINISHED; + } +} + +int Sci0SongIterator::nextCommand(byte *buf, int *result) { + return processMidi(buf, result, &_channel, PARSE_FLAG_PARAMETRIC_CUE); +} + +static int _sci0_header_magic_p(byte *data, int offset, int size) { + if (offset + 0x10 > size) + return 0; + return (data[offset] == 0x1a) + && (data[offset + 1] == 0x00) + && (data[offset + 2] == 0x01) + && (data[offset + 3] == 0x00); +} + + +static int _sci0_get_pcm_data(Sci0SongIterator *self, + int *rate, int *xoffset, uint *xsize) { + int tries = 2; + bool found_it = false; + byte *pcm_data; + int size; + uint offset = SCI0_MIDI_OFFSET; + + if (self->_data[0] != 2) + return 1; + /* No such luck */ + + while ((tries--) && (offset < self->_data.size()) && (!found_it)) { + // Search through the garbage manually + // FIXME: Replace offset by an iterator + Common::Array<byte>::iterator iter = Common::find(self->_data.begin() + offset, self->_data.end(), SCI0_END_OF_SONG); + + if (iter == self->_data.end()) { + warning("Playing unterminated song"); + return 1; + } + + // add one to move it past the END_OF_SONG marker + iter++; + offset = iter - self->_data.begin(); // FIXME + + + if (_sci0_header_magic_p(self->_data.begin(), offset, self->_data.size())) + found_it = true; + } + + if (!found_it) { + warning("Song indicates presence of PCM, but" + " none found (finally at offset %04x)", offset); + + return 1; + } + + pcm_data = self->_data.begin() + offset; + + size = READ_LE_UINT16(pcm_data + SCI0_PCM_SIZE_OFFSET); + + /* Two of the format parameters are fixed by design: */ + *rate = READ_LE_UINT16(pcm_data + SCI0_PCM_SAMPLE_RATE_OFFSET); + + if (offset + SCI0_PCM_DATA_OFFSET + size != self->_data.size()) { + int d = offset + SCI0_PCM_DATA_OFFSET + size - self->_data.size(); + + warning("PCM advertizes %d bytes of data, but %d" + " bytes are trailing in the resource", + size, self->_data.size() - (offset + SCI0_PCM_DATA_OFFSET)); + + if (d > 0) + size -= d; /* Fix this */ + } + + *xoffset = offset; + *xsize = size; + + return 0; +} + +static Audio::AudioStream *makeStream(byte *data, int size, int rate) { + debugC(2, kDebugLevelSound, "Playing PCM data of size %d, rate %d", size, rate); + + // Duplicate the data + byte *sound = (byte *)malloc(size); + memcpy(sound, data, size); + + // Convert stream format flags + int flags = Audio::FLAG_UNSIGNED; + return Audio::makeRawStream(sound, size, rate, flags); +} + +Audio::AudioStream *Sci0SongIterator::getAudioStream() { + int rate; + int offset; + uint size; + if (_sci0_get_pcm_data(this, &rate, &offset, &size)) + return NULL; + + _channel.state = SI_STATE_FINISHED; /* Don't play both PCM and music */ + + return makeStream(_data.begin() + offset + SCI0_PCM_DATA_OFFSET, size, rate); +} + +SongIterator *Sci0SongIterator::handleMessage(Message msg) { + if (msg._class == _SIMSG_BASE) { + switch (msg._type) { + + case _SIMSG_BASEMSG_PRINT: + print_tabs_id(msg._arg.i, ID); + debugC(2, kDebugLevelSound, "SCI0: dev=%d, active-chan=%d, size=%d, loops=%d", + _deviceId, _numActiveChannels, _data.size(), _loops); + break; + + case _SIMSG_BASEMSG_SET_LOOPS: + _loops = msg._arg.i; + break; + + case _SIMSG_BASEMSG_STOP: { + songit_id_t sought_id = msg.ID; + + if (sought_id == ID) + _channel.state = SI_STATE_FINISHED; + break; + } + + case _SIMSG_BASEMSG_SET_PLAYMASK: { + int i; + _deviceId = msg._arg.i; + + /* Set all but the rhytm channel mask bits */ + _channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL); + + for (i = 0; i < MIDI_CHANNELS; i++) + if (_data[2 + (i << 1)] & _deviceId + && i != MIDI_RHYTHM_CHANNEL) + _channel.playmask |= (1 << i); + } + break; + + case _SIMSG_BASEMSG_SET_RHYTHM: + _channel.playmask &= ~(1 << MIDI_RHYTHM_CHANNEL); + if (msg._arg.i) + _channel.playmask |= (1 << MIDI_RHYTHM_CHANNEL); + break; + + case _SIMSG_BASEMSG_SET_FADE: { + fade_params_t *fp = (fade_params_t *) msg._arg.p; + fade.action = fp->action; + fade.final_volume = fp->final_volume; + fade.ticks_per_step = fp->ticks_per_step; + fade.step_size = fp->step_size; + break; + } + + default: + return NULL; + } + + return this; + } + return NULL; +} + +int Sci0SongIterator::getTimepos() { + return _channel.total_timepos; +} + +Sci0SongIterator::Sci0SongIterator(byte *data, uint size, songit_id_t id) + : BaseSongIterator(data, size, id) { + channel_mask = 0xffff; // Allocate all channels by default + _channel.state = SI_STATE_UNINITIALISED; + + for (int i = 0; i < MIDI_CHANNELS; i++) + _polyphony[i] = data[1 + (i << 1)]; + + init(); +} + +void Sci0SongIterator::init() { + fade.action = FADE_ACTION_NONE; + _resetflag = 0; + _loops = 0; + priority = 0; + + _ccc = 0; /* Reset cumulative cue counter */ + _numActiveChannels = 1; + _channel.init(0, SCI0_MIDI_OFFSET, _data.size()); + _channel.resetSynthChannels(); + + if (_data[0] == 2) /* Do we have an embedded PCM? */ + _channel.state = SI_STATE_PCM; +} + +SongIterator *Sci0SongIterator::clone(int delta) { + Sci0SongIterator *newit = new Sci0SongIterator(*this); + return newit; +} + + +/***************************/ +/*-- SCI1 song iterators --*/ +/***************************/ + +int Sci1SongIterator::initSample(const int offset) { + Sci1Sample sample; + int rate; + int length; + int begin; + int end; + + CHECK_FOR_END_ABSOLUTE((uint)offset + 10); + if (_data[offset + 1] != 0) + warning("[iterator-1] In sample at offset 0x04x: Byte #1 is %02x instead of zero", + _data[offset + 1]); + + rate = (int16)READ_LE_UINT16(_data.begin() + offset + 2); + length = READ_LE_UINT16(_data.begin() + offset + 4); + begin = (int16)READ_LE_UINT16(_data.begin() + offset + 6); + end = (int16)READ_LE_UINT16(_data.begin() + offset + 8); + + CHECK_FOR_END_ABSOLUTE((uint)(offset + 10 + length)); + + sample.delta = begin; + sample.size = length; + sample._data = _data.begin() + offset + 10; + +#ifdef DEBUG_VERBOSE + fprintf(stderr, "[SAMPLE] %x/%x/%x/%x l=%x\n", + offset + 10, begin, end, _data.size(), length); +#endif + + sample.rate = rate; + + sample.announced = false; + + /* Insert into the sample list at the right spot, keeping it sorted by delta */ + Common::List<Sci1Sample>::iterator seeker = _samples.begin(); + while (seeker != _samples.end() && seeker->delta < begin) + ++seeker; + _samples.insert(seeker, sample); + + return 0; /* Everything's fine */ +} + +int Sci1SongIterator::initSong() { + int last_time; + uint offset = 0; + _numChannels = 0; + _samples.clear(); +// _deviceId = 0x0c; + + if (_data[offset] == 0xf0) { + priority = _data[offset + 1]; + + offset += 8; + } + + while (_data[offset] != 0xff + && _data[offset] != _deviceId) { + offset++; + CHECK_FOR_END_ABSOLUTE(offset + 1); + while (_data[offset] != 0xff) { + CHECK_FOR_END_ABSOLUTE(offset + 7); + offset += 6; + } + offset++; + } + + if (_data[offset] == 0xff) { + warning("[iterator] Song does not support hardware 0x%02x", _deviceId); + return 1; + } + + offset++; + + while (_data[offset] != 0xff) { /* End of list? */ + uint track_offset; + int end; + offset += 2; + + CHECK_FOR_END_ABSOLUTE(offset + 4); + + track_offset = READ_LE_UINT16(_data.begin() + offset); + end = READ_LE_UINT16(_data.begin() + offset + 2); + + CHECK_FOR_END_ABSOLUTE(track_offset - 1); + + if (_data[track_offset] == 0xfe) { + if (initSample(track_offset)) + return 1; /* Error */ + } else { + /* Regular MIDI channel */ + if (_numChannels >= MIDI_CHANNELS) { + warning("[iterator] Song has more than %d channels, cutting them off", + MIDI_CHANNELS); + break; /* Scan for remaining samples */ + } else { + int channel_nr = _data[track_offset] & 0xf; + SongIteratorChannel &channel = _channels[_numChannels++]; + + /* + if (_data[track_offset] & 0xf0) + printf("Channel %d has mapping bits %02x\n", + channel_nr, _data[track_offset] & 0xf0); + */ + + // Add 2 to skip over header bytes */ + channel.init(channel_nr, track_offset + 2, track_offset + end); + channel.resetSynthChannels(); + + _polyphony[_numChannels - 1] = _data[channel.offset - 1] & 15; + + channel.playmask = ~0; /* Enable all */ + channel_mask |= (1 << channel_nr); + + CHECK_FOR_END_ABSOLUTE(offset + end); + } + } + offset += 4; + CHECK_FOR_END_ABSOLUTE(offset); + } + + /* Now ensure that sample deltas are relative to the previous sample */ + last_time = 0; + _numActiveChannels = _numChannels; + _numLoopedChannels = 0; + + for (Common::List<Sci1Sample>::iterator seeker = _samples.begin(); + seeker != _samples.end(); ++seeker) { + int prev_last_time = last_time; + //printf("[iterator] Detected sample: %d Hz, %d bytes at time %d\n", + // seeker->format.rate, seeker->size, seeker->delta); + last_time = seeker->delta; + seeker->delta -= prev_last_time; + } + + return 0; /* Success */ +} + +int Sci1SongIterator::getSmallestDelta() const { + int d = -1; + for (int i = 0; i < _numChannels; i++) + if (_channels[i].state == SI_STATE_COMMAND + && (d == -1 || _channels[i].delay < d)) + d = _channels[i].delay; + + if (!_samples.empty() && _samples.begin()->delta < d) + return _samples.begin()->delta; + else + return d; +} + +void Sci1SongIterator::updateDelta(int delta) { + if (!_samples.empty()) + _samples.begin()->delta -= delta; + + for (int i = 0; i < _numChannels; i++) + if (_channels[i].state == SI_STATE_COMMAND) + _channels[i].delay -= delta; +} + +bool Sci1SongIterator::noDeltaTime() const { + for (int i = 0; i < _numChannels; i++) + if (_channels[i].state == SI_STATE_DELTA_TIME) + return false; + return true; +} + +#define COMMAND_INDEX_NONE -1 +#define COMMAND_INDEX_PCM -2 + +int Sci1SongIterator::getCommandIndex() const { + /* Determine the channel # of the next active event, or -1 */ + int i; + int base_delay = 0x7ffffff; + int best_chan = COMMAND_INDEX_NONE; + + for (i = 0; i < _numChannels; i++) + if ((_channels[i].state != SI_STATE_PENDING) + && (_channels[i].state != SI_STATE_FINISHED)) { + + if ((_channels[i].state == SI_STATE_DELTA_TIME) + && (_channels[i].delay == 0)) + return i; + /* First, read all unknown delta times */ + + if (_channels[i].delay < base_delay) { + best_chan = i; + base_delay = _channels[i].delay; + } + } + + if (!_samples.empty() && base_delay >= _samples.begin()->delta) + return COMMAND_INDEX_PCM; + + return best_chan; +} + + +Audio::AudioStream *Sci1SongIterator::getAudioStream() { + Common::List<Sci1Sample>::iterator sample = _samples.begin(); + if (sample != _samples.end() && sample->delta <= 0) { + Audio::AudioStream *feed = makeStream(sample->_data, sample->size, sample->rate); + _samples.erase(sample); + + return feed; + } else + return NULL; +} + +int Sci1SongIterator::nextCommand(byte *buf, int *result) { + + if (!_initialised) { + //printf("[iterator] DEBUG: Initialising for %d\n", _deviceId); + _initialised = true; + if (initSong()) + return SI_FINISHED; + } + + + if (_delayRemaining) { + int delay = _delayRemaining; + _delayRemaining = 0; + return delay; + } + + int retval = 0; + do { /* All delays must be processed separately */ + int chan = getCommandIndex(); + + if (chan == COMMAND_INDEX_NONE) { + return SI_FINISHED; + } + + if (chan == COMMAND_INDEX_PCM) { + + if (_samples.begin()->announced) { + /* Already announced; let's discard it */ + Audio::AudioStream *feed = getAudioStream(); + delete feed; + } else { + int delay = _samples.begin()->delta; + + if (delay) { + updateDelta(delay); + return delay; + } + /* otherwise we're touching a PCM */ + _samples.begin()->announced = true; + return SI_PCM; + } + } else { /* Not a PCM */ + + retval = processMidi(buf, result, + &(_channels[chan]), + PARSE_FLAG_LOOPS_UNLIMITED); + + if (retval == SI_LOOP) { + _numLoopedChannels++; + _channels[chan].state = SI_STATE_PENDING; + _channels[chan].delay = 0; + + if (_numLoopedChannels == _numActiveChannels) { + int i; + + /* Everyone's ready: Let's loop */ + for (i = 0; i < _numChannels; i++) + if (_channels[i].state == SI_STATE_PENDING) + _channels[i].state = SI_STATE_DELTA_TIME; + + _numLoopedChannels = 0; + return SI_LOOP; + } + } else if (retval == SI_FINISHED) { +#ifdef DEBUG + fprintf(stderr, "FINISHED some channel\n"); +#endif + } else if (retval > 0) { + int sd ; + sd = getSmallestDelta(); + + if (noDeltaTime() && sd) { + /* No other channel is ready */ + updateDelta(sd); + + /* Only from here do we return delta times */ + return sd; + } + } + + } /* Not a PCM */ + + } while (retval > 0); + + return retval; +} + +SongIterator *Sci1SongIterator::handleMessage(Message msg) { + if (msg._class == _SIMSG_BASE) { /* May extend this in the future */ + switch (msg._type) { + + case _SIMSG_BASEMSG_PRINT: { + int playmask = 0; + int i; + + for (i = 0; i < _numChannels; i++) + playmask |= _channels[i].playmask; + + print_tabs_id(msg._arg.i, ID); + debugC(2, kDebugLevelSound, "SCI1: chan-nr=%d, playmask=%04x", + _numChannels, playmask); + } + break; + + case _SIMSG_BASEMSG_STOP: { + songit_id_t sought_id = msg.ID; + int i; + + if (sought_id == ID) { + ID = 0; + + for (i = 0; i < _numChannels; i++) + _channels[i].state = SI_STATE_FINISHED; + } + break; + } + + case _SIMSG_BASEMSG_SET_PLAYMASK: + if (msg.ID == ID) { + channel_mask = 0; + + _deviceId = msg._arg.i; + + if (_initialised) { + int i; + int toffset = -1; + + for (i = 0; i < _numChannels; i++) + if (_channels[i].state != SI_STATE_FINISHED + && _channels[i].total_timepos > toffset) { + toffset = _channels[i].total_timepos + + _channels[i].timepos_increment + - _channels[i].delay; + } + + /* Find an active channel so that we can + ** get the correct time offset */ + + initSong(); + + toffset -= _delayRemaining; + _delayRemaining = 0; + + if (toffset > 0) + return new_fast_forward_iterator(this, toffset); + } else { + initSong(); + _initialised = true; + } + + break; + + } + + case _SIMSG_BASEMSG_SET_LOOPS: + if (msg.ID == ID) + _loops = (msg._arg.i > 32767) ? 99 : 0; + /* 99 is arbitrary, but we can't use '1' because of + ** the way we're testing in the decoding section. */ + break; + + case _SIMSG_BASEMSG_SET_HOLD: + _hold = msg._arg.i; + break; + case _SIMSG_BASEMSG_SET_RHYTHM: + /* Ignore */ + break; + + case _SIMSG_BASEMSG_SET_FADE: { + fade_params_t *fp = (fade_params_t *) msg._arg.p; + fade.action = fp->action; + fade.final_volume = fp->final_volume; + fade.ticks_per_step = fp->ticks_per_step; + fade.step_size = fp->step_size; + break; + } + + default: + warning("Unsupported command %d to SCI1 iterator", msg._type); + } + return this; + } + return NULL; +} + +Sci1SongIterator::Sci1SongIterator(byte *data, uint size, songit_id_t id) + : BaseSongIterator(data, size, id) { + channel_mask = 0; // Defer channel allocation + + for (int i = 0; i < MIDI_CHANNELS; i++) + _polyphony[i] = 0; // Unknown + + init(); +} + +void Sci1SongIterator::init() { + fade.action = FADE_ACTION_NONE; + _resetflag = 0; + _loops = 0; + priority = 0; + + _ccc = 0; + _deviceId = 0x00; // Default to Sound Blaster/AdLib for purposes of cue computation + _numChannels = 0; + _initialised = false; + _delayRemaining = 0; + _loops = 0; + _hold = 0; + memset(_polyphony, 0, sizeof(_polyphony)); +} + +Sci1SongIterator::~Sci1SongIterator() { +} + + +SongIterator *Sci1SongIterator::clone(int delta) { + Sci1SongIterator *newit = new Sci1SongIterator(*this); + newit->_delayRemaining = delta; + return newit; +} + +int Sci1SongIterator::getTimepos() { + int max = 0; + int i; + + for (i = 0; i < _numChannels; i++) + if (_channels[i].total_timepos > max) + max = _channels[i].total_timepos; + + return max; +} + +/** + * A song iterator with the purpose of sending notes-off channel commands. + */ +class CleanupSongIterator : public SongIterator { +public: + CleanupSongIterator(uint channels) { + channel_mask = channels; + ID = 17; + } + + int nextCommand(byte *buf, int *result); + Audio::AudioStream *getAudioStream() { return NULL; } + SongIterator *handleMessage(Message msg); + int getTimepos() { return 0; } + SongIterator *clone(int delta) { return new CleanupSongIterator(*this); } +}; + +SongIterator *CleanupSongIterator::handleMessage(Message msg) { + if (msg._class == _SIMSG_BASEMSG_PRINT && msg._type == _SIMSG_BASEMSG_PRINT) { + print_tabs_id(msg._arg.i, ID); + debugC(2, kDebugLevelSound, "CLEANUP"); + } + + return NULL; +} + +int CleanupSongIterator::nextCommand(byte *buf, int *result) { + /* Task: Return channel-notes-off for each channel */ + if (channel_mask) { + int bs = sci_ffs(channel_mask) - 1; + + channel_mask &= ~(1 << bs); + buf[0] = 0xb0 | bs; /* Controller */ + buf[1] = SCI_MIDI_CHANNEL_NOTES_OFF; + buf[2] = 0; /* Hmm... */ + *result = 3; + return 0; + } else + return SI_FINISHED; +} + +/**********************/ +/*-- Timer iterator --*/ +/**********************/ +int TimerSongIterator::nextCommand(byte *buf, int *result) { + if (_delta) { + int d = _delta; + _delta = 0; + return d; + } + return SI_FINISHED; +} + +SongIterator *new_timer_iterator(int delta) { + return new TimerSongIterator(delta); +} + +/**********************************/ +/*-- Fast-forward song iterator --*/ +/**********************************/ + +int FastForwardSongIterator::nextCommand(byte *buf, int *result) { + if (_delta <= 0) + return SI_MORPH; /* Did our duty */ + + while (1) { + int rv = _delegate->nextCommand(buf, result); + + if (rv > 0) { + /* Subtract from the delta we want to wait */ + _delta -= rv; + + /* Done */ + if (_delta < 0) + return -_delta; + } + + if (rv <= 0) + return rv; + } +} + +Audio::AudioStream *FastForwardSongIterator::getAudioStream() { + return _delegate->getAudioStream(); +} + +SongIterator *FastForwardSongIterator::handleMessage(Message msg) { + if (msg._class == _SIMSG_PLASTICWRAP) { + assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH); + + if (_delta <= 0) { + SongIterator *it = _delegate; + delete this; + return it; + } + + warning("[ff-iterator] Morphing without need"); + return this; + } + + if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) { + print_tabs_id(msg._arg.i, ID); + debugC(2, kDebugLevelSound, "FASTFORWARD:"); + msg._arg.i++; + } + + // And continue with the delegate + songit_handle_message(&_delegate, msg); + + return NULL; +} + + +int FastForwardSongIterator::getTimepos() { + return _delegate->getTimepos(); +} + +FastForwardSongIterator::FastForwardSongIterator(SongIterator *capsit, int delta) + : _delegate(capsit), _delta(delta) { + + channel_mask = capsit->channel_mask; +} + +SongIterator *FastForwardSongIterator::clone(int delta) { + FastForwardSongIterator *newit = new FastForwardSongIterator(*this); + newit->_delegate = _delegate->clone(delta); + return newit; +} + +SongIterator *new_fast_forward_iterator(SongIterator *capsit, int delta) { + if (capsit == NULL) + return NULL; + + FastForwardSongIterator *it = new FastForwardSongIterator(capsit, delta); + return it; +} + + +/********************/ +/*-- Tee iterator --*/ +/********************/ + + +static void song_iterator_add_death_listener(SongIterator *it, TeeSongIterator *client) { + for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) { + if (it->_deathListeners[i] == 0) { + it->_deathListeners[i] = client; + return; + } + } + error("FATAL: Too many death listeners for song iterator"); +} + +static void song_iterator_remove_death_listener(SongIterator *it, TeeSongIterator *client) { + for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) { + if (it->_deathListeners[i] == client) { + it->_deathListeners[i] = 0; + return; + } + } +} + +static void song_iterator_transfer_death_listeners(SongIterator *it, SongIterator *it_from) { + for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) { + if (it_from->_deathListeners[i]) + song_iterator_add_death_listener(it, it_from->_deathListeners[i]); + it_from->_deathListeners[i] = 0; + } +} + +static void songit_tee_death_notification(TeeSongIterator *self, SongIterator *corpse) { + if (corpse == self->_children[TEE_LEFT].it) { + self->_status &= ~TEE_LEFT_ACTIVE; + self->_children[TEE_LEFT].it = NULL; + } else if (corpse == self->_children[TEE_RIGHT].it) { + self->_status &= ~TEE_RIGHT_ACTIVE; + self->_children[TEE_RIGHT].it = NULL; + } else { + error("songit_tee_death_notification() failed: Breakpoint in %s, line %d", __FILE__, __LINE__); + } +} + +TeeSongIterator::TeeSongIterator(SongIterator *left, SongIterator *right) { + int i; + int firstfree = 1; /* First free channel */ + int incomplete_map = 0; + + _readyToMorph = false; + _status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE; + + _children[TEE_LEFT].it = left; + _children[TEE_RIGHT].it = right; + + /* Default to lhs channels */ + channel_mask = left->channel_mask; + for (i = 0; i < 16; i++) + if (channel_mask & (1 << i) & right->channel_mask + && (i != MIDI_RHYTHM_CHANNEL) /* Share rhythm */) { /*conflict*/ + while ((firstfree == MIDI_RHYTHM_CHANNEL) + /* Either if it's the rhythm channel or if it's taken */ + || (firstfree < MIDI_CHANNELS + && ((1 << firstfree) & channel_mask))) + ++firstfree; + + if (firstfree == MIDI_CHANNELS) { + incomplete_map = 1; + //warning("[songit-tee <%08lx,%08lx>] Could not remap right channel #%d: Out of channels", + // left->ID, right->ID, i); + } else { + _children[TEE_RIGHT].it->channel_remap[i] = firstfree; + + channel_mask |= (1 << firstfree); + } + } +#ifdef DEBUG_TEE_ITERATOR + if (incomplete_map) { + int c; + fprintf(stderr, "[songit-tee <%08lx,%08lx>] Channels:" + " %04x <- %04x | %04x\n", + left->ID, right->ID, + channel_mask, + left->channel_mask, right->channel_mask); + for (c = 0 ; c < 2; c++) + for (i = 0 ; i < 16; i++) + fprintf(stderr, " map [%d][%d] -> %d\n", + c, i, _children[c].it->channel_remap[i]); + } +#endif + + + song_iterator_add_death_listener(left, this); + song_iterator_add_death_listener(right, this); +} + +TeeSongIterator::~TeeSongIterator() { + // When we die, remove any listeners from our children + if (_children[TEE_LEFT].it) { + song_iterator_remove_death_listener(_children[TEE_LEFT].it, this); + } + + if (_children[TEE_RIGHT].it) { + song_iterator_remove_death_listener(_children[TEE_RIGHT].it, this); + } +} + + +int TeeSongIterator::nextCommand(byte *buf, int *result) { + static const int ready_masks[2] = {TEE_LEFT_READY, TEE_RIGHT_READY}; + static const int active_masks[2] = {TEE_LEFT_ACTIVE, TEE_RIGHT_ACTIVE}; + static const int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM}; + int i; + int retid; + +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "[Tee] %02x\n", _status); +#endif + + if (!(_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) + /* None is active? */ + return SI_FINISHED; + + if (_readyToMorph) + return SI_MORPH; + + if ((_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) + != (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE)) { + /* Not all are is active? */ + int which = 0; +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "\tRequesting transformation...\n"); +#endif + if (_status & TEE_LEFT_ACTIVE) + which = TEE_LEFT; + else if (_status & TEE_RIGHT_ACTIVE) + which = TEE_RIGHT; + memcpy(buf, _children[which].buf, sizeof(buf)); + *result = _children[which].result; + _readyToMorph = true; + return _children[which].retval; + } + + /* First, check for unreported PCMs */ + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if ((_status & (ready_masks[i] | pcm_masks[i])) + == (ready_masks[i] | pcm_masks[i])) { + _status &= ~ready_masks[i]; + return SI_PCM; + } + + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if (!(_status & ready_masks[i])) { + + /* Buffers aren't ready yet */ + _children[i].retval = + songit_next(&(_children[i].it), + _children[i].buf, + &(_children[i].result), + IT_READER_MASK_ALL + | IT_READER_MAY_FREE + | IT_READER_MAY_CLEAN); + + _status |= ready_masks[i]; +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "\t Must check %d: %d\n", i, _children[i].retval); +#endif + + if (_children[i].retval == SI_ABSOLUTE_CUE || + _children[i].retval == SI_RELATIVE_CUE) + return _children[i].retval; + if (_children[i].retval == SI_FINISHED) { + _status &= ~active_masks[i]; + /* Recurse to complete */ +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "\t Child %d signalled completion, recursing w/ status %02x\n", i, _status); +#endif + return nextCommand(buf, result); + } else if (_children[i].retval == SI_PCM) { + _status |= pcm_masks[i]; + _status &= ~ready_masks[i]; + return SI_PCM; + } + } + + + /* We've already handled PCM, MORPH and FINISHED, CUEs & LOOP remain */ + + retid = TEE_LEFT; + if ((_children[TEE_LEFT].retval > 0) + /* Asked to delay */ + && (_children[TEE_RIGHT].retval <= _children[TEE_LEFT].retval)) + /* Is not delaying or not delaying as much */ + retid = TEE_RIGHT; + +#ifdef DEBUG_TEE_ITERATOR + fprintf(stderr, "\tl:%d / r:%d / chose %d\n", + _children[TEE_LEFT].retval, _children[TEE_RIGHT].retval, retid); +#endif + + /* Adjust delta times */ + if (_children[retid].retval > 0 + && _children[1-retid].retval > 0) { + if (_children[1-retid].retval + == _children[retid].retval) + /* If both _children wait the same amount of time, + ** we have to re-fetch commands from both */ + _status &= ~ready_masks[1-retid]; + else + /* If they don't, we can/must re-use the other + ** child's delay time */ + _children[1-retid].retval + -= _children[retid].retval; + } + + _status &= ~ready_masks[retid]; + memcpy(buf, _children[retid].buf, sizeof(buf)); + *result = _children[retid].result; + + return _children[retid].retval; +} + +Audio::AudioStream *TeeSongIterator::getAudioStream() { + static const int pcm_masks[2] = {TEE_LEFT_PCM, TEE_RIGHT_PCM}; + int i; + + for (i = TEE_LEFT; i <= TEE_RIGHT; i++) + if (_status & pcm_masks[i]) { + _status &= ~pcm_masks[i]; + return _children[i].it->getAudioStream(); + } + + return NULL; // No iterator +} + +SongIterator *TeeSongIterator::handleMessage(Message msg) { + if (msg._class == _SIMSG_PLASTICWRAP) { + assert(msg._type == _SIMSG_PLASTICWRAP_ACK_MORPH); + + SongIterator *old_it; + if (!(_status & (TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE))) { + delete this; + return NULL; + } else if (!(_status & TEE_LEFT_ACTIVE)) { + delete _children[TEE_LEFT].it; + _children[TEE_LEFT].it = 0; + old_it = _children[TEE_RIGHT].it; + song_iterator_remove_death_listener(old_it, this); + song_iterator_transfer_death_listeners(old_it, this); + delete this; + return old_it; + } else if (!(_status & TEE_RIGHT_ACTIVE)) { + delete _children[TEE_RIGHT].it; + _children[TEE_RIGHT].it = 0; + old_it = _children[TEE_LEFT].it; + song_iterator_remove_death_listener(old_it, this); + song_iterator_transfer_death_listeners(old_it, this); + delete this; + return old_it; + } + + warning("[tee-iterator] Morphing without need"); + return this; + } + + if (msg._class == _SIMSG_BASE && msg._type == _SIMSG_BASEMSG_PRINT) { + print_tabs_id(msg._arg.i, ID); + debugC(2, kDebugLevelSound, "TEE:"); + msg._arg.i++; + } + + // And continue with the children + if (_children[TEE_LEFT].it) + songit_handle_message(&(_children[TEE_LEFT].it), msg); + if (_children[TEE_RIGHT].it) + songit_handle_message(&(_children[TEE_RIGHT].it), msg); + + return NULL; +} + +void TeeSongIterator::init() { + _status = TEE_LEFT_ACTIVE | TEE_RIGHT_ACTIVE; + _children[TEE_LEFT].it->init(); + _children[TEE_RIGHT].it->init(); +} + +SongIterator *TeeSongIterator::clone(int delta) { + TeeSongIterator *newit = new TeeSongIterator(*this); + + if (_children[TEE_LEFT].it) + newit->_children[TEE_LEFT].it = _children[TEE_LEFT].it->clone(delta); + if (_children[TEE_RIGHT].it) + newit->_children[TEE_RIGHT].it = _children[TEE_RIGHT].it->clone(delta); + + return newit; +} + + +/*************************************/ +/*-- General purpose functionality --*/ +/*************************************/ + +int songit_next(SongIterator **it, byte *buf, int *result, int mask) { + int retval; + + if (!*it) + return SI_FINISHED; + + do { + retval = (*it)->nextCommand(buf, result); + if (retval == SI_MORPH) { + debugC(2, kDebugLevelSound, " Morphing %p (stored at %p)", (void *)*it, (void *)it); + if (!SIMSG_SEND((*it), SIMSG_ACK_MORPH)) { + error("SI_MORPH failed. Breakpoint in %s, line %d", __FILE__, __LINE__); + } else + debugC(2, kDebugLevelSound, "SI_MORPH successful"); + } + + if (retval == SI_FINISHED) + debugC(2, kDebugLevelSound, "[song-iterator] Song finished. mask = %04x, cm=%04x", + mask, (*it)->channel_mask); + if (retval == SI_FINISHED + && (mask & IT_READER_MAY_CLEAN) + && (*it)->channel_mask) { /* This last test will fail + ** with a terminated + ** cleanup iterator */ + int channel_mask = (*it)->channel_mask; + + SongIterator *old_it = *it; + *it = new CleanupSongIterator(channel_mask); + for(uint i = 0; i < MIDI_CHANNELS; i++) + (*it)->channel_remap[i] = old_it->channel_remap[i]; + song_iterator_transfer_death_listeners(*it, old_it); + if (mask & IT_READER_MAY_FREE) + delete old_it; + retval = -9999; /* Continue */ + } + } while (!( /* Until one of the following holds */ + (retval > 0 && (mask & IT_READER_MASK_DELAY)) + || (retval == 0 && (mask & IT_READER_MASK_MIDI)) + || (retval == SI_LOOP && (mask & IT_READER_MASK_LOOP)) + || (retval == SI_ABSOLUTE_CUE && + (mask & IT_READER_MASK_CUE)) + || (retval == SI_RELATIVE_CUE && + (mask & IT_READER_MASK_CUE)) + || (retval == SI_PCM && (mask & IT_READER_MASK_PCM)) + || (retval == SI_FINISHED) + )); + + if (retval == SI_FINISHED && (mask & IT_READER_MAY_FREE)) { + delete *it; + *it = NULL; + } + + return retval; +} + +SongIterator::SongIterator() { + ID = 0; + channel_mask = 0; + fade.action = FADE_ACTION_NONE; + priority = 0; + memset(_deathListeners, 0, sizeof(_deathListeners)); + + // By default, don't remap + for (uint i = 0; i < 16; i++) + channel_remap[i] = i; +} + +SongIterator::SongIterator(const SongIterator &si) { + ID = si.ID; + channel_mask = si.channel_mask; + fade = si.fade; + priority = si.priority; + memset(_deathListeners, 0, sizeof(_deathListeners)); + + for (uint i = 0; i < 16; i++) + channel_remap[i] = si.channel_remap[i]; +} + + +SongIterator::~SongIterator() { + for (int i = 0; i < SONGIT_MAX_LISTENERS; ++i) + if (_deathListeners[i]) + songit_tee_death_notification(_deathListeners[i], this); +} + +SongIterator *songit_new(byte *data, uint size, SongIteratorType type, songit_id_t id) { + BaseSongIterator *it; + + if (!data || size < 22) { + warning("Attempt to instantiate song iterator for null song data"); + return NULL; + } + + + switch (type) { + case SCI_SONG_ITERATOR_TYPE_SCI0: + it = new Sci0SongIterator(data, size, id); + break; + + case SCI_SONG_ITERATOR_TYPE_SCI1: + it = new Sci1SongIterator(data, size, id); + break; + + default: + /**-- Invalid/unsupported sound resources --**/ + warning("Attempt to instantiate invalid/unknown song iterator type %d", type); + return NULL; + } + + return it; +} + +int songit_handle_message(SongIterator **it_reg_p, SongIterator::Message msg) { + SongIterator *it = *it_reg_p; + SongIterator *newit; + + newit = it->handleMessage(msg); + + if (!newit) + return 0; /* Couldn't handle */ + + *it_reg_p = newit; /* Might have self-morphed */ + return 1; +} + +SongIterator *sfx_iterator_combine(SongIterator *it1, SongIterator *it2) { + if (it1 == NULL) + return it2; + if (it2 == NULL) + return it1; + + /* Both are non-NULL: */ + return new TeeSongIterator(it1, it2); +} + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS diff --git a/engines/sci/sound/iterator/iterator.h b/engines/sci/sound/iterator/iterator.h new file mode 100644 index 0000000000..e5c8f50702 --- /dev/null +++ b/engines/sci/sound/iterator/iterator.h @@ -0,0 +1,326 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* Song iterator declarations */ + +#ifndef SCI_SFX_SFX_ITERATOR_H +#define SCI_SFX_SFX_ITERATOR_H + +#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS + +#ifdef USE_OLD_MUSIC_FUNCTIONS +#include "sci/sound/drivers/mididriver.h" + +namespace Audio { + class AudioStream; +} + +namespace Sci { + +enum SongIteratorStatus { + SI_FINISHED = -1, /**< Song finished playing */ + SI_LOOP = -2, /**< Song just looped */ + SI_ABSOLUTE_CUE = -3, /**< Found a song cue (absolute) */ + SI_RELATIVE_CUE = -4, /**< Found a song cue (relative) */ + SI_PCM = -5, /**< Found a PCM */ + SI_IGNORE = -6, /**< This event got edited out by the remapper */ + SI_MORPH = -255 /**< Song iterator requested self-morph. */ +}; + +#define FADE_ACTION_NONE 0 +#define FADE_ACTION_FADE_AND_STOP 1 +#define FADE_ACTION_FADE_AND_CONT 2 + +struct fade_params_t { + int ticks_per_step; + int final_volume; + int step_size; + int action; +}; + +/* Helper defs for messages */ +enum { + _SIMSG_BASE, /* Any base decoder */ + _SIMSG_PLASTICWRAP /* Any "Plastic" (discardable) wrapper decoder */ +}; + +/* Base messages */ +enum { + _SIMSG_BASEMSG_SET_LOOPS, /* Set loops */ + _SIMSG_BASEMSG_SET_PLAYMASK, /* Set the current playmask for filtering */ + _SIMSG_BASEMSG_SET_RHYTHM, /* Activate/deactivate rhythm channel */ + _SIMSG_BASEMSG_ACK_MORPH, /* Acknowledge self-morph */ + _SIMSG_BASEMSG_STOP, /* Stop iterator */ + _SIMSG_BASEMSG_PRINT, /* Print self to stderr, after printing param1 tabs */ + _SIMSG_BASEMSG_SET_HOLD, /* Set value of hold parameter to expect */ + _SIMSG_BASEMSG_SET_FADE /* Set fade parameters */ +}; + +/* "Plastic" (discardable) wrapper messages */ +enum { + _SIMSG_PLASTICWRAP_ACK_MORPH = _SIMSG_BASEMSG_ACK_MORPH /* Acknowledge self-morph */ +}; + +/* Messages */ +#define SIMSG_SET_LOOPS(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_LOOPS,(x) +#define SIMSG_SET_PLAYMASK(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_PLAYMASK,(x) +#define SIMSG_SET_RHYTHM(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_RHYTHM,(x) +#define SIMSG_ACK_MORPH _SIMSG_PLASTICWRAP,_SIMSG_PLASTICWRAP_ACK_MORPH,0 +#define SIMSG_STOP _SIMSG_BASE,_SIMSG_BASEMSG_STOP,0 +#define SIMSG_PRINT(indentation) _SIMSG_BASE,_SIMSG_BASEMSG_PRINT,(indentation) +#define SIMSG_SET_HOLD(x) _SIMSG_BASE,_SIMSG_BASEMSG_SET_HOLD,(x) + +/* Message transmission macro: Takes song reference, message reference */ +#define SIMSG_SEND(o, m) songit_handle_message(&(o), SongIterator::Message((o)->ID, m)) +#define SIMSG_SEND_FADE(o, m) songit_handle_message(&(o), SongIterator::Message((o)->ID, _SIMSG_BASE, _SIMSG_BASEMSG_SET_FADE, m)) + +typedef unsigned long songit_id_t; + + +#define SONGIT_MAX_LISTENERS 2 + +class TeeSongIterator; + +class SongIterator { +public: + struct Message { + songit_id_t ID; + uint _class; /* Type of iterator supposed to receive this */ + uint _type; + union { + uint i; + void *p; + } _arg; + + Message() : ID(0), _class(0xFFFF), _type(0xFFFF) {} + + /** + * Create a song iterator message. + * + * @param id: song ID the message is targeted to + * @param recipient_class: Message recipient class + * @param type message type + * @param a argument + * + * @note You should only use this with the SIMSG_* macros + */ + Message(songit_id_t id, int recipient_class, int type, int a) + : ID(id), _class(recipient_class), _type(type) { + _arg.i = a; + } + + /** + * Create a song iterator message, wherein the first parameter is a pointer. + * + * @param id: song ID the message is targeted to + * @param recipient_class: Message recipient class + * @param type message type + * @param a argument + * + * @note You should only use this with the SIMSG_* macros + */ + Message(songit_id_t id, int recipient_class, int type, void *a) + : ID(id), _class(recipient_class), _type(type) { + _arg.p = a; + } + }; + +public: + songit_id_t ID; + uint16 channel_mask; /* Bitmask of all channels this iterator will use */ + fade_params_t fade; + int priority; + + /* Death listeners */ + /* These are not reset during initialisation */ + TeeSongIterator *_deathListeners[SONGIT_MAX_LISTENERS]; + + /* See songit_* for the constructor and non-virtual member functions */ + + byte channel_remap[MIDI_CHANNELS]; ///< Remapping for channels + +public: + SongIterator(); + SongIterator(const SongIterator &); + virtual ~SongIterator(); + + /** + * Resets/initializes the sound iterator. + */ + virtual void init() {} + + /** + * Reads the next MIDI operation _or_ delta time. + * @param buf The buffer to write to (needs to be able to store at least 4 bytes) + * @param result Number of bytes written to the buffer + * (equals the number of bytes that need to be passed + * to the lower layers) for 0, the cue value for SI_CUE, + * or the number of loops remaining for SI_LOOP. + * @return zero if a MIDI operation was written, SI_FINISHED + * if the song has finished playing, SI_LOOP if looping + * (after updating the loop variable), SI_CUE if we found + * a cue, SI_PCM if a PCM was found, or the number of ticks + * to wait before this function should be called next. + * + * @note If SI_PCM is returned, get_pcm() may be used to retrieve the associated + * PCM, but this must be done before any subsequent calls to next(). + * + * @todo The actual buffer size should either be specified or passed in, so that + * we can detect buffer overruns. + */ + virtual int nextCommand(byte *buf, int *result) = 0; + + /** + Checks for the presence of a pcm sample. + * @return NULL if no PCM data was found, an AudioStream otherwise. + */ + virtual Audio::AudioStream *getAudioStream() = 0; + + /** + * Handles a message to the song iterator. + * @param msg the message to handle + * @return NULL if the message was not understood, + * this if the message could be handled, or a new song iterator + * if the current iterator had to be morphed (but the message could + * still be handled) + * + * @note This function is not supposed to be called directly; use + * songit_handle_message() instead. It should not recurse, since songit_handle_message() + * takes care of that and makes sure that its delegate received the message (and + * was morphed) before self. + */ + virtual SongIterator *handleMessage(Message msg) = 0; + + /** + * Gets the song position to store in a savegame. + */ + virtual int getTimepos() = 0; + + /** + * Clone this song iterator. + * @param delta number of ticks that still need to elapse until the + * next item should be read from the song iterator + */ + virtual SongIterator *clone(int delta) = 0; + + +private: + // Make the assignment operator unreachable, just in case... + SongIterator& operator=(const SongIterator&); +}; + + +/********************************/ +/*-- Song iterator operations --*/ +/********************************/ + +enum SongIteratorType { + SCI_SONG_ITERATOR_TYPE_SCI0 = 0, + SCI_SONG_ITERATOR_TYPE_SCI1 = 1 +}; + +#define IT_READER_MASK_MIDI (1 << 0) +#define IT_READER_MASK_DELAY (1 << 1) +#define IT_READER_MASK_LOOP (1 << 2) +#define IT_READER_MASK_CUE (1 << 3) +#define IT_READER_MASK_PCM (1 << 4) +#define IT_READER_MAY_FREE (1 << 10) /* Free SI_FINISHED iterators */ +#define IT_READER_MAY_CLEAN (1 << 11) +/* MAY_CLEAN: May instantiate cleanup iterators +** (use for players; this closes open channels at the end of a song) */ + +#define IT_READER_MASK_ALL ( IT_READER_MASK_MIDI \ + | IT_READER_MASK_DELAY \ + | IT_READER_MASK_LOOP \ + | IT_READER_MASK_CUE \ + | IT_READER_MASK_PCM ) + +/* Convenience wrapper around it->next +** Parameters: (SongIterator **it) Reference to the iterator to access +** (byte *) buf: The buffer to write to (needs to be able to +** store at least 4 bytes) +** (int) mask: IT_READER_MASK options specifying the events to +** listen for +** Returns : (int) zero if a MIDI operation was written, SI_FINISHED +** if the song has finished playing, SI_LOOP if looping +** (after updating the loop variable), SI_CUE if we found +** a cue, SI_PCM if a PCM was found, or the number of ticks +** to wait before this function should be called next. +** (int) *result: Number of bytes written to the buffer +** (equals the number of bytes that need to be passed +** to the lower layers) for 0, the cue value for SI_CUE, +** or the number of loops remaining for SI_LOOP. +*/ +int songit_next(SongIterator **it, byte *buf, int *result, int mask); + +/* Constructs a new song iterator object +** Parameters: (byte *) data: The song data to iterate over +** (uint) size: Number of bytes in the song +** (int) type: One of the SCI_SONG_ITERATOR_TYPEs +** (songit_id_t) id: An ID for addressing the song iterator +** Returns : (SongIterator *) A newly allocated but uninitialized song +** iterator, or NULL if 'type' was invalid or unsupported +*/ +SongIterator *songit_new(byte *data, uint size, SongIteratorType type, songit_id_t id); + +/* Constructs a new song timer iterator object +** Parameters: (int) delta: The delta after which to fire SI_FINISHED +** Returns : (SongIterator *) A newly allocated but uninitialized song +** iterator +*/ +SongIterator *new_timer_iterator(int delta); + +/* Handles a message to the song iterator +** Parameters: (SongIterator **): A reference to the variable storing the song iterator +** Returns : (int) Non-zero if the message was understood +** The song iterator may polymorph as result of msg, so a writeable reference is required. +*/ +int songit_handle_message(SongIterator **it_reg, SongIterator::Message msg); + + +/* Creates a new song iterator which fast-forwards +** Parameters: (SongIterator *) it: The iterator to wrap +** (int) delta: The number of ticks to skip +** Returns : (SongIterator) A newly created song iterator +** which skips all delta times +** until 'delta' has been used up +*/ +SongIterator *new_fast_forward_iterator(SongIterator *it, int delta); + +/* Combines two song iterators into one +** Parameters: (sfx_iterator_t *) it1: One of the two iterators, or NULL +** (sfx_iterator_t *) it2: The other iterator, or NULL +** Returns : (sfx_iterator_t *) A combined iterator +** If a combined iterator is returned, it will be flagged to be allowed to +** dispose of 'it1' and 'it2', where applicable. This means that this +** call should be used by song players, but not by the core sound system +*/ +SongIterator *sfx_iterator_combine(SongIterator *it1, SongIterator *it2); + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS + +#endif // SCI_SFX_SFX_ITERATOR_H diff --git a/engines/sci/sound/iterator/iterator_internal.h b/engines/sci/sound/iterator/iterator_internal.h new file mode 100644 index 0000000000..5a0f0d3ec9 --- /dev/null +++ b/engines/sci/sound/iterator/iterator_internal.h @@ -0,0 +1,276 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCI_SFX_SFX_ITERATOR_INTERNAL +#define SCI_SFX_SFX_ITERATOR_INTERNAL + +#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS + +#ifdef USE_OLD_MUSIC_FUNCTIONS +#include "sci/sound/iterator/iterator.h" +#include "sci/sound/drivers/mididriver.h" + +#include "common/array.h" +#include "common/list.h" + +namespace Sci { + +/* Iterator types */ + +enum { + SI_STATE_UNINITIALISED = -1, + SI_STATE_DELTA_TIME = 0, ///< Now at a delta time + SI_STATE_COMMAND = 1, ///< Now at a MIDI operation + SI_STATE_PENDING = 2, ///< Pending for loop + SI_STATE_FINISHED = 3, ///< End of song + SI_STATE_PCM = 4, ///< Should report a PCM next (-> DELTA_TIME) + SI_STATE_PCM_MAGIC_DELTA = 5 ///< Should report a ``magic'' one tick delta time next (goes on to FINISHED) +}; + +struct SongIteratorChannel { + + int state; ///< State of this song iterator channel + int offset; ///< Offset into the data chunk */ + int end; ///< Last allowed byte in track */ + int id; ///< Some channel ID */ + + /** + * Number of ticks before the specified channel is next used, or + * CHANNEL_DELAY_MISSING to indicate that the delay has not yet + * been read. + */ + int delay; + + /* Two additional offsets for recovering: */ + int loop_offset; + int initial_offset; + + int playmask; ///< Active playmask (MIDI channels to play in here) */ + int loop_timepos; ///< Total delay for this channel's loop marker */ + int total_timepos; ///< Number of ticks since the beginning, ignoring loops */ + int timepos_increment; ///< Number of ticks until the next command (to add) */ + + byte last_cmd; ///< Last operation executed, for running status */ + +public: + void init(int id, int offset, int end); + void resetSynthChannels(); +}; + +class BaseSongIterator : public SongIterator { +public: + int _polyphony[MIDI_CHANNELS]; ///< # of simultaneous notes on each + + int _ccc; ///< Cumulative cue counter, for those who need it + byte _resetflag; ///< for 0x4C -- on DoSound StopSound, do we return to start? + int _deviceId; ///< ID of the device we generating events for + int _numActiveChannels; ///< Number of active channels + Common::Array<byte> _data; ///< Song data + + int _loops; ///< Number of loops remaining + +public: + BaseSongIterator(byte *data, uint size, songit_id_t id); + +protected: + int parseMidiCommand(byte *buf, int *result, SongIteratorChannel *channel, int flags); + int processMidi(byte *buf, int *result, SongIteratorChannel *channel, int flags); +}; + +/********************************/ +/*--------- SCI 0 --------------*/ +/********************************/ + +class Sci0SongIterator : public BaseSongIterator { +public: + SongIteratorChannel _channel; + +public: + Sci0SongIterator(byte *data, uint size, songit_id_t id); + + int nextCommand(byte *buf, int *result); + Audio::AudioStream *getAudioStream(); + SongIterator *handleMessage(Message msg); + void init(); + int getTimepos(); + SongIterator *clone(int delta); +}; + + +/********************************/ +/*--------- SCI 1 --------------*/ +/********************************/ + + +struct Sci1Sample { + /** + * Time left-- initially, this is 'Sample point 1'. + * After initialisation, it is 'sample point 1 minus the sample + * point of the previous sample' + */ + int delta; + int size; + bool announced; /* Announced for download (SI_PCM) */ + int rate; + byte *_data; +}; + +class Sci1SongIterator : public BaseSongIterator { +public: + SongIteratorChannel _channels[MIDI_CHANNELS]; + + /* Invariant: Whenever channels[i].delay == CHANNEL_DELAY_MISSING, + ** channel_offset[i] points to a delta time object. */ + + bool _initialised; /**!< Whether the MIDI channel setup has been initialised */ + int _numChannels; /**!< Number of channels actually used */ + Common::List<Sci1Sample> _samples; + int _numLoopedChannels; /**!< Number of channels that are ready to loop */ + + int _delayRemaining; /**!< Number of ticks that haven't been polled yet */ + int _hold; + +public: + Sci1SongIterator(byte *data, uint size, songit_id_t id); + ~Sci1SongIterator(); + + int nextCommand(byte *buf, int *result); + Audio::AudioStream *getAudioStream(); + SongIterator *handleMessage(Message msg); + void init(); + int getTimepos(); + SongIterator *clone(int delta); + +private: + int initSample(const int offset); + int initSong(); + + int getSmallestDelta() const; + + void updateDelta(int delta); + + /** Checks that none of the channels is waiting for its delta to be read */ + bool noDeltaTime() const; + + /** Determine the channel # of the next active event, or -1 */ + int getCommandIndex() const; +}; + +#define PLAYMASK_NONE 0x0 + +/***************************/ +/*--------- Timer ---------*/ +/***************************/ + +/** + * A song iterator which waits a specified time and then fires + * SI_FINISHED. Used by DoSound, where audio resources are played (SCI1) + */ +class TimerSongIterator : public SongIterator { +protected: + int _delta; /**!< Remaining time */ + +public: + TimerSongIterator(int delta) : _delta(delta) {} + + int nextCommand(byte *buf, int *result); + Audio::AudioStream *getAudioStream() { return NULL; } + SongIterator *handleMessage(Message msg) { return NULL; } + int getTimepos() { return 0; } + SongIterator *clone(int delta) { return new TimerSongIterator(*this); } +}; + +/**********************************/ +/*--------- Fast Forward ---------*/ +/**********************************/ + +/** + * A song iterator which fast-forwards another iterator. + * Skips all delta times until a specified 'delta' has been used up. + */ +class FastForwardSongIterator : public SongIterator { +protected: + SongIterator *_delegate; + int _delta; /**!< Remaining time */ + +public: + FastForwardSongIterator(SongIterator *capsit, int delta); + + int nextCommand(byte *buf, int *result); + Audio::AudioStream *getAudioStream(); + SongIterator *handleMessage(Message msg); + int getTimepos(); + SongIterator *clone(int delta); +}; + + +/**********************************/ +/*--------- Tee iterator ---------*/ +/**********************************/ + +enum { + TEE_LEFT = 0, + TEE_RIGHT = 1, + TEE_LEFT_ACTIVE = (1<<0), + TEE_RIGHT_ACTIVE = (1<<1), + TEE_LEFT_READY = (1<<2), /**!< left result is ready */ + TEE_RIGHT_READY = (1<<3), /**!< right result is ready */ + TEE_LEFT_PCM = (1<<4), + TEE_RIGHT_PCM = (1<<5) +}; + +/** + * This iterator combines two iterators, returns the next event available from either. + */ +class TeeSongIterator : public SongIterator { +public: + int _status; + + bool _readyToMorph; /**!< One of TEE_MORPH_* above */ + + struct { + SongIterator *it; + byte buf[4]; + int result; + int retval; + } _children[2]; + +public: + TeeSongIterator(SongIterator *left, SongIterator *right); + ~TeeSongIterator(); + + int nextCommand(byte *buf, int *result); + Audio::AudioStream *getAudioStream(); + SongIterator *handleMessage(Message msg); + void init(); + int getTimepos() { return 0; } + SongIterator *clone(int delta); +}; + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS + +#endif // SCI_SFX_SFX_ITERATOR_INTERNAL diff --git a/engines/sci/sound/iterator/songlib.cpp b/engines/sci/sound/iterator/songlib.cpp new file mode 100644 index 0000000000..8bc2e8f476 --- /dev/null +++ b/engines/sci/sound/iterator/songlib.cpp @@ -0,0 +1,189 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS + +#ifdef USE_OLD_MUSIC_FUNCTIONS +#include "sci/sound/iterator/core.h" +#include "sci/sound/iterator/iterator.h" + +namespace Sci { + +#define debug_stream stderr + +Song::Song() : _wakeupTime(0, SFX_TICKS_PER_SEC) { + _handle = 0; + _resourceNum = 0; + _priority = 0; + _status = SOUND_STATUS_STOPPED; + + _restoreBehavior = RESTORE_BEHAVIOR_CONTINUE; + _restoreTime = 0; + + _loops = 0; + _hold = 0; + + _it = 0; + _delay = 0; + + _next = NULL; + _nextPlaying = NULL; + _nextStopping = NULL; +} + +Song::Song(SongHandle handle, SongIterator *it, int priority) : _wakeupTime(0, SFX_TICKS_PER_SEC) { + _handle = handle; + _resourceNum = 0; + _priority = priority; + _status = SOUND_STATUS_STOPPED; + + _restoreBehavior = RESTORE_BEHAVIOR_CONTINUE; + _restoreTime = 0; + + _loops = 0; + _hold = 0; + + _it = it; + _delay = 0; + + _next = NULL; + _nextPlaying = NULL; + _nextStopping = NULL; +} + +void SongLibrary::addSong(Song *song) { + Song **seeker = NULL; + int pri = song->_priority; + + if (NULL == song) { + warning("addSong(): NULL passed for song"); + return; + } + + seeker = &_lib; + while (*seeker && ((*seeker)->_priority > pri)) + seeker = &((*seeker)->_next); + + song->_next = *seeker; + *seeker = song; +} + +void SongLibrary::freeSounds() { + Song *next = _lib; + while (next) { + Song *song = next; + delete song->_it; + song->_it = NULL; + next = song->_next; + delete song; + } + _lib = NULL; +} + + +Song *SongLibrary::findSong(SongHandle handle) { + Song *seeker = _lib; + + while (seeker) { + if (seeker->_handle == handle) + break; + seeker = seeker->_next; + } + + return seeker; +} + +Song *SongLibrary::findNextActive(Song *other) { + Song *seeker = other ? other->_next : _lib; + + while (seeker) { + if ((seeker->_status == SOUND_STATUS_WAITING) || + (seeker->_status == SOUND_STATUS_PLAYING)) + break; + seeker = seeker->_next; + } + + /* Only return songs that have equal priority */ + if (other && seeker && other->_priority > seeker->_priority) + return NULL; + + return seeker; +} + +Song *SongLibrary::findFirstActive() { + return findNextActive(NULL); +} + +int SongLibrary::removeSong(SongHandle handle) { + int retval; + Song *goner = _lib; + + if (!goner) + return -1; + + if (goner->_handle == handle) + _lib = goner->_next; + + else { + while ((goner->_next) && (goner->_next->_handle != handle)) + goner = goner->_next; + + if (goner->_next) { /* Found him? */ + Song *oldnext = goner->_next; + + goner->_next = goner->_next->_next; + goner = oldnext; + } else return -1; /* No. */ + } + + retval = goner->_status; + + delete goner->_it; + delete goner; + + return retval; +} + +int SongLibrary::countSongs() { + Song *seeker = _lib; + int retval = 0; + + while (seeker) { + retval++; + seeker = seeker->_next; + } + + return retval; +} + +void SongLibrary::setSongRestoreBehavior(SongHandle handle, RESTORE_BEHAVIOR action) { + Song *seeker = findSong(handle); + + seeker->_restoreBehavior = action; +} + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS diff --git a/engines/sci/sound/iterator/songlib.h b/engines/sci/sound/iterator/songlib.h new file mode 100644 index 0000000000..acb704edaa --- /dev/null +++ b/engines/sci/sound/iterator/songlib.h @@ -0,0 +1,171 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +/* Song library */ + +#ifndef SCI_SFX_SFX_SONGLIB_H +#define SCI_SFX_SFX_SONGLIB_H + +#include "common/scummsys.h" +#include "sound/timestamp.h" + +#include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS +#ifdef USE_OLD_MUSIC_FUNCTIONS + +namespace Sci { + +class SongIterator; + +#define SOUND_STATUS_STOPPED 0 +#define SOUND_STATUS_PLAYING 1 +#define SOUND_STATUS_SUSPENDED 2 +/* suspended: only if ordered from kernel space */ +#define SOUND_STATUS_WAITING 3 +/* "waiting" means "tagged for playing, but not active right now" */ + +typedef unsigned long SongHandle; + +enum RESTORE_BEHAVIOR { + RESTORE_BEHAVIOR_CONTINUE, /* restart a song when restored from + a saved game */ + RESTORE_BEHAVIOR_RESTART /* continue it from where it was */ +}; + +class Song { +public: + SongHandle _handle; + int _resourceNum; /**<! Resource number */ + int _priority; /**!< Song priority (more important if priority is higher) */ + int _status; /* See above */ + + int _restoreBehavior; + int _restoreTime; + + /* Grabbed from the sound iterator, for save/restore purposes */ + int _loops; + int _hold; + + SongIterator *_it; + int _delay; /**!< Delay before accessing the iterator, in ticks */ + + Audio::Timestamp _wakeupTime; /**!< Timestamp indicating the next MIDI event */ + + Song *_next; /**!< Next song or NULL if this is the last one */ + + /** + * Next playing song. Used by the core song system. + */ + Song *_nextPlaying; + + /** + * Next song pending stopping. Used exclusively by the core song system's + * _update_multi_song() + */ + Song *_nextStopping; + +public: + + Song(); + + /** + * Initializes a new song. + * @param handle the sound handle + * @param it the song + * @param priority the song's priority + * @return a freshly allocated song + */ + Song(SongHandle handle, SongIterator *it, int priority); +}; + + +class SongLibrary { +public: + Song *_lib; + +public: + SongLibrary() : _lib(0) {} + + /** Frees a song library. */ + void freeSounds(); + + /** + * Adds a song to a song library. + * @param song song to add + */ + void addSong(Song *song); + + /** + * Looks up the song with the specified handle. + * @param handle sound handle to look for + * @return the song or NULL if it wasn't found + */ + Song *findSong(SongHandle handle); + + /** + * Finds the first song playing with the highest priority. + * @return the song that should be played next, or NULL if there is none + */ + Song *findFirstActive(); + + /** + * Finds the next song playing with the highest priority. + * + * The functions 'findFirstActive' and 'findNextActive' + * allow to iterate over all songs that satisfy the requirement of + * being 'playable'. + * + * @param song a song previously returned from the song library + * @return the next song to play relative to 'song', or NULL if none are left + */ + Song *findNextActive(Song *song); + + /** + * Removes a song from the library. + * @param handle handle of the song to remove + * @return the status of the song that was removed + */ + int removeSong(SongHandle handle); + + /** + * Counts the number of songs in a song library. + * @return the number of songs + */ + int countSongs(); + + /** + * Determines what should be done with the song "handle" when restoring + * it from a saved game. + * @param handle sound handle being restored + * @param action desired action + */ + void setSongRestoreBehavior(SongHandle handle, + RESTORE_BEHAVIOR action); +}; + +} // End of namespace Sci + +#endif // USE_OLD_MUSIC_FUNCTIONS + +#endif // SCI_SSFX_SFX_SONGLIB_H diff --git a/engines/sci/sound/iterator/test-iterator.cpp b/engines/sci/sound/iterator/test-iterator.cpp new file mode 100644 index 0000000000..0d603a89fd --- /dev/null +++ b/engines/sci/sound/iterator/test-iterator.cpp @@ -0,0 +1,423 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "iterator.h" +#include "iterator_internal.h" +#include <stdarg.h> +#include <stdio.h> + +using namespace Sci; + +#define ASSERT_S(x) if (!(x)) { error("Failed assertion in L%d: " #x, __LINE__); return; } +#define ASSERT(x) ASSERT_S(x) + +/* Tests the song iterators */ + +int errors = 0; + +void error(char *fmt, ...) { + va_list ap; + + fprintf(stderr, "[ERROR] "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + ++errors; +} + + +/* The simple iterator will finish after a fixed amount of time. Before that, +** it emits (absolute) cues in ascending order. */ +struct simple_iterator : public SongIterator { + int lifetime_remaining; + char *cues; + int cue_counter; + int cue_progress; + int cues_nr; +}; + +int simple_it_next(SongIterator *_self, unsigned char *buf, int *result) { + simple_iterator *self = (simple_iterator *)_self; + + if (self->lifetime_remaining == -1) { + error("Song iterator called post mortem"); + return SI_FINISHED; + } + + if (self->lifetime_remaining) { + + if (self->cue_counter < self->cues_nr) { + int time_to_cue = self->cues[self->cue_counter]; + + if (self->cue_progress == time_to_cue) { + ++self->cue_counter; + self->cue_progress = 0; + *result = self->cue_counter; + return SI_ABSOLUTE_CUE; + } else { + int retval = time_to_cue - self->cue_progress; + self->cue_progress = time_to_cue; + + if (retval > self->lifetime_remaining) { + retval = self->lifetime_remaining; + self->lifetime_remaining = 0; + self->cue_progress = retval; + return retval; + } + + self->lifetime_remaining -= retval; + return retval; + } + } else { + int retval = self->lifetime_remaining; + self->lifetime_remaining = 0; + return retval; + } + + } else { + self->lifetime_remaining = -1; + return SI_FINISHED; + } +} + +Audio::AudioStream *simple_it_pcm_feed(SongIterator *_self) { + error("No PCM feed"); + return NULL; +} + +void simple_it_init(SongIterator *_self) { +} + +SongIterator *simple_it_handle_message(SongIterator *_self, SongIterator::Message msg) { + return NULL; +} + +void simple_it_cleanup(SongIterator *_self) { +} + +/* Initialises the simple iterator. +** Parameters: (int) delay: Number of ticks until the iterator finishes +** (int *) cues: An array of cue delays (cue values are [1,2...]) +** (int) cues_nr: Number of cues in ``cues'' +** The first cue is emitted after cues[0] ticks, and it is 1. After cues[1] additional ticks +** the next cue is emitted, and so on. */ +SongIterator *setup_simple_iterator(int delay, char *cues, int cues_nr) { + simple_iterator.lifetime_remaining = delay; + simple_iterator.cues = cues; + simple_iterator.cue_counter = 0; + simple_iterator.cues_nr = cues_nr; + simple_iterator.cue_progress = 0; + + simple_iterator.ID = 42; + simple_iterator.channel_mask = 0x004f; + simple_iterator.flags = 0; + simple_iterator.priority = 1; + + simple_iterator.death_listeners_nr = 0; + + simple_iterator.cleanup = simple_it_cleanup; + simple_iterator.init = simple_it_init; + simple_iterator.handle_message = simple_it_handle_message; + simple_iterator.get_pcm_feed = simple_it_pcm_feed; + simple_iterator.next = simple_it_next; + + return (SongIterator *) &simple_iterator; +} + +#define ASSERT_SIT ASSERT(it == simple_it) +#define ASSERT_FFIT ASSERT(it == ff_it) +#define ASSERT_NEXT(n) ASSERT(songit_next(&it, data, &result, IT_READER_MASK_ALL) == n) +#define ASSERT_RESULT(n) ASSERT(result == n) +#define ASSERT_CUE(n) ASSERT_NEXT(SI_ABSOLUTE_CUE); ASSERT_RESULT(n) + +void test_simple_it() { + SongIterator *it; + SongIterator *simple_it = (SongIterator *) & simple_iterator; + unsigned char data[4]; + int result; + puts("[TEST] simple iterator (test artifact)"); + + it = setup_simple_iterator(42, NULL, 0); + + ASSERT_SIT; + ASSERT_NEXT(42); + ASSERT_SIT; + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, "\003\004", 2); + ASSERT_SIT; + ASSERT_NEXT(3); + ASSERT_CUE(1); + ASSERT_SIT; + ASSERT_NEXT(4); + ASSERT_CUE(2); + ASSERT_SIT; +// warning("XXX => %d", songit_next(&it, data, &result, IT_READER_MASK_ALL)); + ASSERT_NEXT(35); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + puts("[TEST] Test OK."); +} + +void test_fastforward() { + SongIterator *it; + SongIterator *simple_it = (SongIterator *) & simple_iterator; + SongIterator *ff_it; + unsigned char data[4]; + int result; + puts("[TEST] fast-forward iterator"); + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 0); + ASSERT_FFIT; + ASSERT_NEXT(42); + ASSERT_SIT; /* Must have morphed back */ + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 1); + ASSERT_FFIT; + ASSERT_NEXT(41); + /* May or may not have morphed back here */ + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 41); + ASSERT_FFIT; + ASSERT_NEXT(1); + /* May or may not have morphed back here */ + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 42); + ASSERT_NEXT(SI_FINISHED); + /* May or may not have morphed back here */ + + it = setup_simple_iterator(42, NULL, 0); + ff_it = it = new_fast_forward_iterator(it, 10000); + ASSERT_NEXT(SI_FINISHED); + /* May or may not have morphed back here */ + + it = setup_simple_iterator(42, "\003\004", 2); + ff_it = it = new_fast_forward_iterator(it, 2); + ASSERT_FFIT; + ASSERT_NEXT(1); + ASSERT_CUE(1); + ASSERT_SIT; + ASSERT_NEXT(4); + ASSERT_CUE(2); + ASSERT_SIT; + ASSERT_NEXT(35); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, "\003\004", 2); + ff_it = it = new_fast_forward_iterator(it, 5); + ASSERT_FFIT; + ASSERT_CUE(1); + ASSERT_FFIT; + ASSERT_NEXT(2); + ASSERT_CUE(2); + ASSERT_SIT; + ASSERT_NEXT(35); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + it = setup_simple_iterator(42, "\003\004", 2); + ff_it = it = new_fast_forward_iterator(it, 41); + ASSERT_FFIT; + ASSERT_CUE(1); + ASSERT_FFIT; + ASSERT_CUE(2); + ASSERT_FFIT; + ASSERT_NEXT(1); + ASSERT_NEXT(SI_FINISHED); + ASSERT_SIT; + + puts("[TEST] Test OK."); +} + +#define SIMPLE_SONG_SIZE 50 + +static unsigned char simple_song[SIMPLE_SONG_SIZE] = { + 0x00, /* Regular song */ + /* Only use channel 0 for all devices */ + 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Song begins here */ + 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */ + 02, 64, 0x42, /* Play E after 2 more ticks, using running status mode */ + 0xf8, 10, 0x80, 60, 0x02, /* Stop C after 250 ticks */ + 0, 64, 0x00, /* Stop E immediately */ + 00, 0xfc /* Stop song */ +}; + +#define ASSERT_MIDI3(cmd, arg0, arg1) \ + ASSERT(data[0] == cmd); \ + ASSERT(data[1] == arg0); \ + ASSERT(data[2] == arg1); + +void test_iterator_sci0() { + SongIterator *it = songit_new(simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); + unsigned char data[4]; + int result; + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ + + puts("[TEST] SCI0-style song"); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(250); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x02); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + ASSERT_NEXT(SI_FINISHED); + puts("[TEST] Test OK."); +} + + + +void test_iterator_sci0_loop() { + SongIterator *it = songit_new(simple_song, SIMPLE_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); + unsigned char data[4]; + int result; + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ + SIMSG_SEND(it, SIMSG_SET_LOOPS(2)); /* Loop one additional time */ + + puts("[TEST] SCI0-style song with looping"); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(250); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x02); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + ASSERT_NEXT(SI_LOOP); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(250); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x02); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + ASSERT_NEXT(SI_FINISHED); + puts("[TEST] Test OK."); +} + + + +#define LOOP_SONG_SIZE 54 + +unsigned char loop_song[LOOP_SONG_SIZE] = { + 0x00, /* Regular song song */ + /* Only use channel 0 for all devices */ + 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Song begins here */ + 42, 0x90, 60, 0x7f, /* Play C after 42 ticks */ + 13, 0x80, 60, 0x00, /* Stop C after 13 ticks */ + 00, 0xCF, 0x7f, /* Set loop point */ + 02, 0x90, 64, 0x42, /* Play E after 2 more ticks, using running status mode */ + 03, 0x80, 64, 0x00, /* Stop E after 3 ticks */ + 00, 0xfc /* Stop song/loop */ +}; + + +void test_iterator_sci0_mark_loop() { + SongIterator *it = songit_new(loop_song, LOOP_SONG_SIZE, SCI_SONG_ITERATOR_TYPE_SCI0, 0l); + unsigned char data[4]; + int result; + SIMSG_SEND(it, SIMSG_SET_PLAYMASK(0x0001)); /* Initialise song, enabling channel 0 */ + SIMSG_SEND(it, SIMSG_SET_LOOPS(3)); /* Loop once more */ + + puts("[TEST] SCI0-style song with loop mark, looping"); + ASSERT_NEXT(42); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 60, 0x7f); + ASSERT_NEXT(13); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 60, 0x00); + /* Loop point here: we don't observe that in the iterator interface yet, though */ + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(3); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + /* Now we loop back to the loop pont */ + ASSERT_NEXT(SI_LOOP); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(3); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + /* ...and one final time */ + ASSERT_NEXT(SI_LOOP); + ASSERT_NEXT(2); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x90, 64, 0x42); + ASSERT_NEXT(3); + ASSERT_NEXT(0); + ASSERT_MIDI3(0x80, 64, 0x00); + + ASSERT_NEXT(SI_FINISHED); + puts("[TEST] Test OK."); +} + + + +int main(int argc, char **argv) { + test_simple_it(); + test_fastforward(); + test_iterator_sci0(); + test_iterator_sci0_loop(); + test_iterator_sci0_mark_loop(); + if (errors != 0) + warning("[ERROR] %d errors total", errors); + return (errors != 0); +} diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index fa4804ce7d..053bf597f8 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -844,7 +844,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { offsX = offsY = 0; } else { uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - assert(charOffs < 0x14000); + assert(charOffs < 0x10000); if (!charOffs) return; charPtr = _fontPtr + charOffs; diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index b5a4070f0b..e0582f79ef 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -868,7 +868,7 @@ bool ScummDebugger::Cmd_Passcode(int argc, const char **argv) { detach(); } else { - DebugPrintf("Current Passcode is %d \nUse 'passcode <SEGA CD Passcode>'\n",_vm->_scummVars[411]); + DebugPrintf("Use 'passcode <SEGA CD Passcode>'\n"); return true; } return false; diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 1e0bf6d4be..b160dac6f2 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -234,6 +234,19 @@ protected: #endif +class ConfigDialog : public GUI::OptionsDialog { +protected: +#ifdef SMALL_SCREEN_DEVICE + GUI::Dialog *_keysDialog; +#endif + +public: + ConfigDialog(); + ~ConfigDialog(); + + virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); +}; + #pragma mark - ScummDialog::ScummDialog(int x, int y, int w, int h) : GUI::Dialog(x, y, w, h) { @@ -246,31 +259,223 @@ ScummDialog::ScummDialog(String name) : GUI::Dialog(name) { #pragma mark - -#ifndef DISABLE_HELP +enum { + kSaveCmd = 'SAVE', + kLoadCmd = 'LOAD', + kPlayCmd = 'PLAY', + kOptionsCmd = 'OPTN', + kHelpCmd = 'HELP', + kAboutCmd = 'ABOU', + kQuitCmd = 'QUIT', + kChooseCmd = 'CHOS' +}; ScummMenuDialog::ScummMenuDialog(ScummEngine *scumm) - : MainMenuDialog(scumm) { + : ScummDialog("ScummMain"), _vm(scumm) { + + new GUI::ButtonWidget(this, "ScummMain.Resume", "Resume", kPlayCmd, 'P'); + + _loadButton = new GUI::ButtonWidget(this, "ScummMain.Load", "Load", kLoadCmd, 'L'); + _saveButton = new GUI::ButtonWidget(this, "ScummMain.Save", "Save", kSaveCmd, 'S'); + + new GUI::ButtonWidget(this, "ScummMain.Options", "Options", kOptionsCmd, 'O'); +#ifndef DISABLE_HELP + new GUI::ButtonWidget(this, "ScummMain.Help", "Help", kHelpCmd, 'H'); +#endif + new GUI::ButtonWidget(this, "ScummMain.About", "About", kAboutCmd, 'A'); + + new GUI::ButtonWidget(this, "ScummMain.Quit", "Quit", kQuitCmd, 'Q'); + + // + // Create the sub dialog(s) + // + _aboutDialog = new GUI::AboutDialog(); + _optionsDialog = new ConfigDialog(); +#ifndef DISABLE_HELP _helpDialog = new HelpDialog(scumm->_game); - _helpButton->setEnabled(true); +#endif + _saveDialog = new GUI::SaveLoadChooser("Save game:", "Save"); + _saveDialog->setSaveMode(true); + _loadDialog = new GUI::SaveLoadChooser("Load game:", "Load"); + _loadDialog->setSaveMode(false); } ScummMenuDialog::~ScummMenuDialog() { + delete _aboutDialog; + delete _optionsDialog; +#ifndef DISABLE_HELP delete _helpDialog; +#endif + delete _saveDialog; + delete _loadDialog; +} + +int ScummMenuDialog::runModal() { + _loadButton->setEnabled(_vm->canLoadGameStateCurrently()); + _saveButton->setEnabled(_vm->canSaveGameStateCurrently()); + return ScummDialog::runModal(); +} + +void ScummMenuDialog::reflowLayout() { + _loadButton->setEnabled(_vm->canLoadGameStateCurrently()); + _saveButton->setEnabled(_vm->canSaveGameStateCurrently()); + Dialog::reflowLayout(); } void ScummMenuDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { switch (cmd) { + case kSaveCmd: + save(); + break; + case kLoadCmd: + load(); + break; + case kPlayCmd: + close(); + break; + case kOptionsCmd: + _optionsDialog->runModal(); + break; + case kAboutCmd: + _aboutDialog->runModal(); + break; +#ifndef DISABLE_HELP case kHelpCmd: _helpDialog->runModal(); break; +#endif + case kQuitCmd: + _vm->quitGame(); + close(); + break; default: - MainMenuDialog::handleCommand(sender, cmd, data); + ScummDialog::handleCommand(sender, cmd, data); + } +} + +void ScummMenuDialog::save() { + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int idx = _saveDialog->runModal(plugin, ConfMan.getActiveDomainName()); + if (idx >= 0) { + String result(_saveDialog->getResultString()); + char buffer[20]; + const char *str; + if (result.empty()) { + // If the user was lazy and entered no save name, come up with a default name. + sprintf(buffer, "Save %d", idx); + str = buffer; + } else + str = result.c_str(); + _vm->requestSave(idx, str); + close(); + } +} + +void ScummMenuDialog::load() { + Common::String gameId = ConfMan.get("gameid"); + + const EnginePlugin *plugin = 0; + EngineMan.findGame(gameId, &plugin); + + int idx = _loadDialog->runModal(plugin, ConfMan.getActiveDomainName()); + if (idx >= 0) { + _vm->requestLoad(idx); + close(); } } #pragma mark - enum { + kKeysCmd = 'KEYS' +}; + +// FIXME: We use the empty string as domain name here. This tells the +// ConfigManager to use the 'default' domain for all its actions. We do that +// to get as close as possible to editing the 'active' settings. +// +// However, that requires bad & evil hacks in the ConfigManager code, +// and even then still doesn't work quite correctly. +// For example, if the transient domain contains 'false' for the 'fullscreen' +// flag, but the user used a hotkey to switch to windowed mode, then the dialog +// will display the wrong value anyway. +// +// Proposed solution consisting of multiple steps: +// 1) Add special code to the open() code that reads out everything stored +// in the transient domain that is controlled by this dialog, and updates +// the dialog accordingly. +// 2) Even more code is added to query the backend for current settings, like +// the fullscreen mode flag etc., and also updates the dialog accordingly. +// 3) The domain being edited is set to the active game domain. +// 4) If the dialog is closed with the "OK" button, then we remove everything +// stored in the transient domain (or at least everything corresponding to +// switches in this dialog. +// If OTOH the dialog is closed with "Cancel" we do no such thing. +// +// These changes will achieve two things at once: Allow us to get rid of using +// "" as value for the domain, and in fact provide a somewhat better user +// experience at the same time. +ConfigDialog::ConfigDialog() + : GUI::OptionsDialog("", "ScummConfig") { + + // + // Sound controllers + // + + addVolumeControls(this, "ScummConfig."); + + // + // Some misc options + // + + // SCUMM has a talkspeed range of 0-9 + addSubtitleControls(this, "ScummConfig.", 9); + + // + // Add the buttons + // + + new GUI::ButtonWidget(this, "ScummConfig.Ok", "OK", GUI::kOKCmd, 'O'); + new GUI::ButtonWidget(this, "ScummConfig.Cancel", "Cancel", GUI::kCloseCmd, 'C'); +#ifdef SMALL_SCREEN_DEVICE + new GUI::ButtonWidget(this, "ScummConfig.Keys", "Keys", kKeysCmd, 'K'); + _keysDialog = NULL; +#endif +} + +ConfigDialog::~ConfigDialog() { +#ifdef SMALL_SCREEN_DEVICE + delete _keysDialog; +#endif +} + +void ConfigDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) { + switch (cmd) { + case kKeysCmd: +#ifdef SMALL_SCREEN_DEVICE + // + // Create the sub dialog(s) + // + _keysDialog = new GUI::KeysDialog(); + _keysDialog->runModal(); + delete _keysDialog; + _keysDialog = NULL; +#endif + break; + default: + GUI::OptionsDialog::handleCommand (sender, cmd, data); + } +} + +#ifndef DISABLE_HELP + +#pragma mark - + +enum { kNextCmd = 'NEXT', kPrevCmd = 'PREV' }; diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index 41a8ec83c1..7889027dcf 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -27,8 +27,9 @@ #include "common/str.h" #include "gui/dialog.h" +#include "gui/options.h" #include "gui/widget.h" -#include "engines/dialogs.h" +#include "gui/saveload.h" #include "scumm/detection.h" @@ -51,17 +52,32 @@ protected: typedef Common::String String; }; -#ifndef DISABLE_HELP -class ScummMenuDialog : public MainMenuDialog { +class ScummMenuDialog : public ScummDialog { public: ScummMenuDialog(ScummEngine *scumm); ~ScummMenuDialog(); virtual void handleCommand(GUI::CommandSender *sender, uint32 cmd, uint32 data); + virtual void reflowLayout(); + + int runModal(); protected: + ScummEngine *_vm; + + GUI::Dialog *_aboutDialog; + GUI::Dialog *_optionsDialog; +#ifndef DISABLE_HELP GUI::Dialog *_helpDialog; -}; #endif + GUI::SaveLoadChooser *_saveDialog; + GUI::SaveLoadChooser *_loadDialog; + + GUI::ButtonWidget *_loadButton; + GUI::ButtonWidget *_saveButton; + + void save(); + void load(); +}; /** * A dialog which displays an arbitrary message to the user and returns diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp index c259c3ffd2..886ee99e57 100644 --- a/engines/scumm/he/resource_he.cpp +++ b/engines/scumm/he/resource_he.cpp @@ -633,10 +633,8 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary wr[c].children = fi->first_resource + (FROM_LE_32(dirent[c].offset_to_data) & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY); /* fill in wr->id, wr->numeric_id */ - if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) { - free(wr); + if (!decode_pe_resource_id(fi, wr + c, FROM_LE_32(dirent[c].name))) return NULL; - } } return wr; diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index dc3a5d26b3..8a9570f534 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -508,7 +508,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) { if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT), 0, 0, 0); - openMainMenuDialog(); // Display global main menu + scummMenuDialog(); // Display GUI if (VAR_SAVELOAD_SCRIPT != 0xFF && _currentRoom != 0) runScript(VAR(VAR_SAVELOAD_SCRIPT2), 0, 0, 0); diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index fc95060b6f..dcbe4e6c0a 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -108,7 +108,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _language(dr.language), _debugger(0), _currentScript(0xFF), // Let debug() work on init stage - _messageDialog(0), _pauseDialog(0), _versionDialog(0) { + _messageDialog(0), _pauseDialog(0), _scummMenuDialog(0), _versionDialog(0) { if (_game.platform == Common::kPlatformNES) { _gdi = new GdiNES(this); @@ -140,6 +140,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _fileHandle = 0; + // Init all vars _v0ObjectIndex = false; _v0ObjectInInventory = false; @@ -151,6 +152,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _sound = NULL; memset(&vm, 0, sizeof(vm)); _pauseDialog = NULL; + _scummMenuDialog = NULL; _versionDialog = NULL; _fastMode = 0; _actors = NULL; @@ -550,12 +552,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) for (int i = 0; i < ARRAYSIZE(debugChannels); ++i) DebugMan.addDebugChannel(debugChannels[i].flag, debugChannels[i].channel, debugChannels[i].desc); -#ifndef DISABLE_HELP - // Create custom GMM dialog providing a help subdialog - assert(!_mainMenuDialog); - _mainMenuDialog = new ScummMenuDialog(this); -#endif - g_eventRec.registerRandomSource(_rnd, "scumm"); } @@ -576,6 +572,7 @@ ScummEngine::~ScummEngine() { delete _charset; delete _messageDialog; delete _pauseDialog; + delete _scummMenuDialog; delete _versionDialog; delete _fileHandle; @@ -2447,6 +2444,13 @@ void ScummEngine::versionDialog() { runDialog(*_versionDialog); } +void ScummEngine::scummMenuDialog() { + if (!_scummMenuDialog) + _scummMenuDialog = new ScummMenuDialog(this); + runDialog(*_scummMenuDialog); + syncSoundSettings(); +} + void ScummEngine::confirmExitDialog() { ConfirmDialog d(this, 6); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 42322ba5a2..885ab790de 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -530,6 +530,7 @@ protected: Dialog *_pauseDialog; Dialog *_messageDialog; Dialog *_versionDialog; + Dialog *_scummMenuDialog; virtual int runDialog(Dialog &dialog); void confirmExitDialog(); @@ -537,6 +538,7 @@ protected: void pauseDialog(); void messageDialog(const char *message); void versionDialog(); + void scummMenuDialog(); char displayMessage(const char *altButton, const char *message, ...) GCC_PRINTF(3, 4); diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index 441e622184..c0e7be7758 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -23,6 +23,7 @@ * */ + #include "common/file.h" #include "sword1/sword1.h" #include "sword1/animation.h" @@ -71,9 +72,6 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSys _bgSoundStream = NULL; _decoderType = decoderType; _decoder = decoder; - - _white = 255; - _black = 0; } MoviePlayer::~MoviePlayer() { @@ -256,35 +254,9 @@ bool MoviePlayer::playVideo() { if (frame) _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); - if (_decoder->hasDirtyPalette()) { + if (_decoder->hasDirtyPalette()) _decoder->setSystemPalette(); - uint32 maxWeight = 0; - uint32 minWeight = 0xFFFFFFFF; - uint32 weight; - byte r, g, b; - - byte *palette = _decoder->getPalette(); - - for (int i = 0; i < 256; i++) { - r = *palette++; - g = *palette++; - b = *palette++; - - weight = 3 * r * r + 6 * g * g + 2 * b * b; - - if (weight >= maxWeight) { - maxWeight = weight; - _white = i; - } - - if (weight <= minWeight) { - minWeight = weight; - _black = i; - } - } - } - Graphics::Surface *screen = _vm->_system->lockScreen(); performPostProcessing((byte *)screen->pixels); _vm->_system->unlockScreen(); @@ -295,19 +267,17 @@ bool MoviePlayer::playVideo() { while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) return false; - - _vm->_system->delayMillis(10); } return !_vm->shouldQuit(); } byte MoviePlayer::findBlackPalIndex() { - return _black; + return 0; } byte MoviePlayer::findWhitePalIndex() { - return _white; + return 0xff; } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index 193d5cf7ca..82343f2800 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -85,7 +85,6 @@ protected: OSystem *_system; Common::Array<MovieText *> _movieTexts; int _textX, _textY, _textWidth, _textHeight; - byte _white, _black; DecoderType _decoderType; Graphics::VideoDecoder *_decoder; diff --git a/engines/sword2/animation.cpp b/engines/sword2/animation.cpp index 10895b2ec1..c3f3e796b2 100644 --- a/engines/sword2/animation.cpp +++ b/engines/sword2/animation.cpp @@ -51,9 +51,6 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, A _bgSoundStream = NULL; _decoderType = decoderType; _decoder = decoder; - - _white = 255; - _black = 0; } MoviePlayer:: ~MoviePlayer() { @@ -283,35 +280,9 @@ bool MoviePlayer::playVideo() { if (frame) _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); - if (_decoder->hasDirtyPalette()) { + if (_decoder->hasDirtyPalette()) _decoder->setSystemPalette(); - uint32 maxWeight = 0; - uint32 minWeight = 0xFFFFFFFF; - uint32 weight; - byte r, g, b; - - byte *palette = _decoder->getPalette(); - - for (int i = 0; i < 256; i++) { - r = *palette++; - g = *palette++; - b = *palette++; - - weight = 3 * r * r + 6 * g * g + 2 * b * b; - - if (weight >= maxWeight) { - maxWeight = weight; - _white = i; - } - - if (weight <= minWeight) { - minWeight = weight; - _black = i; - } - } - } - Graphics::Surface *screen = _vm->_system->lockScreen(); performPostProcessing((byte *)screen->pixels); _vm->_system->unlockScreen(); @@ -322,19 +293,17 @@ bool MoviePlayer::playVideo() { while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) return false; - - _vm->_system->delayMillis(10); } return !_vm->shouldQuit(); } byte MoviePlayer::findBlackPalIndex() { - return _black; + return 0; } byte MoviePlayer::findWhitePalIndex() { - return _white; + return 0xff; } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) diff --git a/engines/sword2/animation.h b/engines/sword2/animation.h index ee32b1d5f2..bbf83e264c 100644 --- a/engines/sword2/animation.h +++ b/engines/sword2/animation.h @@ -87,7 +87,6 @@ protected: uint32 _currentMovieText; byte *_textSurface; int _textX, _textY; - byte _white, _black; DecoderType _decoderType; Graphics::VideoDecoder *_decoder; diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index fdc4484a7c..df686f8bee 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -284,8 +284,7 @@ void LoadFile(MEMHANDLE *pH) { } // extract and zero terminate the filename - memcpy(szFilename, pH->szName, sizeof(pH->szName)); - szFilename[sizeof(pH->szName)] = 0; + Common::strlcpy(szFilename, pH->szName, sizeof(pH->szName)); if (f.open(szFilename)) { // read the data diff --git a/graphics/video/coktelvideo/coktelvideo.cpp b/graphics/video/coktelvideo/coktelvideo.cpp new file mode 100644 index 0000000000..4d93815b44 --- /dev/null +++ b/graphics/video/coktelvideo/coktelvideo.cpp @@ -0,0 +1,2657 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "graphics/video/coktelvideo/coktelvideo.h" + +#ifdef GRAPHICS_VIDEO_COKTELVIDEO_H + +#include "common/endian.h" +#include "common/system.h" + +#include "graphics/dither.h" +#include "graphics/video/coktelvideo/indeo3.h" + +#include "sound/audiostream.h" +#include "sound/decoders/raw.h" + +namespace Graphics { + +PreImd::PreImd() { + zeroData(); + + _forcedWidth = 0; + _forcedHeight = 0; +} + +PreImd::PreImd(int16 width, int16 height) { + zeroData(); + + _forcedWidth = width; + _forcedHeight = height; +} + +PreImd::~PreImd() { + deleteData(); + zeroData(); +} + +uint32 PreImd::getFeatures() const { + return _features; +} + +uint16 PreImd::getFlags() const { + return _flags; +} + +int16 PreImd::getX() const { + return _x; +} + +int16 PreImd::getY() const { + return _y; +} + +int16 PreImd::getWidth() const { + return _width; +} + +int16 PreImd::getHeight() const { + return _height; +} + +uint16 PreImd::getFramesCount() const { + return _framesCount; +} + +uint16 PreImd::getCurrentFrame() const { + return _curFrame; +} + +void PreImd::setFrameRate(int16 frameRate) { + if (frameRate == 0) + frameRate = 1; + + _frameRate = frameRate; + _frameLength = 1000 / _frameRate; +} + +int16 PreImd::getFrameRate() const { + return 0; +} + +uint32 PreImd::getSyncLag() const { + return 0; +} + +const byte *PreImd::getPalette() const { + return _palette; +} + +bool PreImd::getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height) { + + return false; +} + +bool PreImd::hasExtraData() const { + return false; +} + +bool PreImd::hasExtraData(const char *fileName) const { + return false; +} + +Common::MemoryReadStream *PreImd::getExtraData(const char *fileName) { + return 0; +} + +bool PreImd::load(Common::SeekableReadStream &stream) { + // Since PreIMDs don't have any width and height values stored, + // we need them to be specified in the constructor + assert((_forcedWidth > 0) && (_forcedHeight > 0)); + + unload(); + + _stream = &stream; + + _stream->seek(0); + + _framesCount = _stream->readUint16LE(); + + _width = _forcedWidth; + _height = _forcedHeight; + + _vidBufferSize = _width * _height; + _vidBuffer = new byte[_vidBufferSize]; + memset(_vidBuffer, 0, _vidBufferSize); + + return true; +} + +void PreImd::unload() { + clear(); +} + +void PreImd::setXY(int16 x, int16 y) { + _x = x; + _y = y; +} + +void PreImd::setVideoMemory(byte *vidMem, uint16 width, uint16 height) { + deleteVidMem(); + + _hasOwnVidMem = false; + _vidMem = vidMem; + _vidMemWidth = width; + _vidMemHeight = height; +} + +void PreImd::setVideoMemory() { + deleteVidMem(); + + if ((_width > 0) && (_height > 0)) { + setXY(0, 0); + _hasOwnVidMem = true; + _vidMem = new byte[_width * _height]; + _vidMemWidth = _width; + _vidMemHeight = _height; + + memset(_vidMem, 0, _width * _height); + } +} + +void PreImd::setDoubleMode(bool doubleMode) { +} + +void PreImd::enableSound(Audio::Mixer &mixer) { +} + +void PreImd::disableSound() { +} + +bool PreImd::isSoundPlaying() const { + return false; +} + +void PreImd::seekFrame(int32 frame, int16 whence, bool restart) { + if (!_stream) + // Nothing to do + return; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _framesCount - frame - 1; + else if (whence != SEEK_SET) + return; + + if ((frame < 0) || (frame >= _framesCount) || (frame == _curFrame)) + // Nothing to do + return; + + // Run through the frames + _curFrame = 0; + _stream->seek(2); + while (_curFrame != frame) { + uint16 frameSize = _stream->readUint16LE(); + + _stream->skip(frameSize + 2); + + _curFrame++; + } +} + +CoktelVideo::State PreImd::nextFrame() { + return processFrame(_curFrame); +} + +uint32 PreImd::getFrameWaitTime() { + return _frameLength; +} + +void PreImd::waitEndFrame() { + uint32 waitTime = getFrameWaitTime(); + if (waitTime > 0) + g_system->delayMillis(waitTime); +} + +void PreImd::copyCurrentFrame(byte *dest, + uint16 left, uint16 top, uint16 width, uint16 height, + uint16 x, uint16 y, uint16 pitch, int16 transp) { +} + +void PreImd::zeroVidMem() { + _hasOwnVidMem = false; + _vidMem = 0; + _vidMemWidth = 0; + _vidMemHeight = 0; +} + +void PreImd::deleteVidMem() { + if (_hasOwnVidMem) + delete[] _vidMem; + + zeroVidMem(); +} + +void PreImd::zeroData() { + _stream = 0; + + _features = 0; + _flags = 0; + + _x = 0; + _y = 0; + _width = 0; + _height = 0; + + _framesCount = 0; + _curFrame = 0; + _frameRate = 12; + _frameLength = 1000 / _frameRate; + + memset(_palette, 0, 768); + + zeroVidMem(); + + _vidBufferSize = 0; + _vidBuffer = 0; +} + +void PreImd::deleteData() { + deleteVidMem(); + + delete[] _vidBuffer; +} + +void PreImd::clear() { + deleteData(); + zeroData(); +} + +CoktelVideo::State PreImd::processFrame(uint16 frame) { + assert((_width > 0) && (_height > 0)); + + State state; + + if (!_stream || (frame >= _framesCount)) { + state.flags = kStateBreak; + return state; + } + + if (frame != _curFrame) { + state.flags |= kStateSeeked; + seekFrame(frame); + } + + if (!_vidMem) + setVideoMemory(); + + uint16 frameSize = _stream->readUint16LE(); + + uint32 nextFramePos = _stream->pos() + frameSize + 2; + + byte cmd; + + cmd = _stream->readByte(); + frameSize--; + + bool hasPalette = false; + if (cmd == 0) { + // Palette. Ignored by Fascination, though + + hasPalette = true; + + _stream->read(_palette, 768); + + frameSize -= 769; + + cmd = _stream->readByte(); + } + + if (cmd != 2) { + // Partial frame data + + uint32 fSize = frameSize; + uint32 vidSize = _vidBufferSize; + byte *vidBuffer = _vidBuffer; + + while ((fSize > 0) && (vidSize > 0)) { + uint32 n = _stream->readByte(); + fSize--; + + if ((n & 0x80) != 0) { + // Data + + n = MIN<uint32>((n & 0x7F) + 1, MIN(fSize, vidSize)); + + _stream->read(vidBuffer, n); + + vidBuffer += n; + vidSize -= n; + fSize -= n; + + } else { + // Skip + + n = MIN<uint32>(n + 1, vidSize); + + vidBuffer += n; + vidSize -= n; + } + } + + } else { + // Full direct frame + + uint32 vidSize = MIN<uint32>(_vidBufferSize, frameSize); + + _stream->read(_vidBuffer, vidSize); + } + + renderFrame(); + + _stream->seek(nextFramePos); + + _curFrame++; + + // Complete frame needs to be updated + state.left = _x; + state.top = _y; + state.right = _x + _width - 1; + state.bottom = _y + _height - 1; + + return state; +} + +// Simple blit +void PreImd::renderFrame() { + assert(_vidMem); + + uint16 w = MIN<uint16>(_vidMemWidth , _width); + uint16 h = MIN<uint16>(_vidMemHeight, _height); + + const byte *src = _vidBuffer; + byte *dst = _vidMem + (_y * _vidMemWidth) + _x; + + uint32 frameDataSize = _vidBufferSize; + + while (h-- > 0) { + uint32 n = MIN<uint32>(w, frameDataSize); + + memcpy(dst, src, n); + + src += _width; + dst += _vidMemWidth; + + frameDataSize -= n; + } +} + + +Imd::Imd() { + zeroData(); +} + +Imd::~Imd() { + deleteData(); + zeroData(); +} + +void Imd::setFrameRate(int16 frameRate) { + if (frameRate == 0) + frameRate = 1; + + _frameRate = frameRate; + _frameLength = 1000 / _frameRate; +} + +int16 Imd::getFrameRate() const { + if (!_hasSound) + return _frameRate; + + return 1000 / (_soundSliceLength >> 16); +} + +uint32 Imd::getSyncLag() const { + return _skipFrames; +} + +bool Imd::loadCoordinates() { + // Standard coordinates + if (_version >= 3) { + _stdX = _stream->readUint16LE(); + if (_stdX > 1) { + warning("IMD: More than one standard coordinate quad found (%d)", _stdX); + return false; + } + if (_stdX != 0) { + _stdX = _stream->readSint16LE(); + _stdY = _stream->readSint16LE(); + _stdWidth = _stream->readSint16LE(); + _stdHeight = _stream->readSint16LE(); + _features |= kFeaturesStdCoords; + } else + _stdX = -1; + } else + _stdX = -1; + + return true; +} + +bool Imd::loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos) { + framesPosPos = 0; + framesCoordsPos = 0; + + // Frame positions + if (_version >= 4) { + framesPosPos = _stream->readUint32LE(); + if (framesPosPos != 0) { + _framesPos = new uint32[_framesCount]; + assert(_framesPos); + _features |= kFeaturesFramesPos; + } + } + + // Frame coordinates + if (_features & kFeaturesFrameCoords) + framesCoordsPos = _stream->readUint32LE(); + + return true; +} + +bool Imd::assessVideoProperties() { + // Sizes of the frame data and extra video buffer + if (_features & kFeaturesDataSize) { + _frameDataSize = _stream->readUint16LE(); + if (_frameDataSize == 0) { + _frameDataSize = _stream->readUint32LE(); + _vidBufferSize = _stream->readUint32LE(); + } else + _vidBufferSize = _stream->readUint16LE(); + } else { + _frameDataSize = _width * _height + 500; + if (!(_flags & 0x100) || (_flags & 0x1000)) + _vidBufferSize = _frameDataSize; + } + + // Allocating working memory + _frameData = new byte[_frameDataSize + 500]; + assert(_frameData); + memset(_frameData, 0, _frameDataSize + 500); + _vidBuffer = new byte[_vidBufferSize + 500]; + assert(_vidBuffer); + memset(_vidBuffer, 0, _vidBufferSize + 500); + + return true; +} + +bool Imd::assessAudioProperties() { + if (_features & kFeaturesSound) { + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + + if (_soundFreq < 0) + _soundFreq = -_soundFreq; + + if (_soundSlicesCount < 0) + _soundSlicesCount = -_soundSlicesCount - 1; + + if (_soundSlicesCount > 40) { + warning("Imd::load(): More than 40 sound slices found (%d)", _soundSlicesCount); + return false; + } + + _soundSliceLength = (uint32) (((double) (1000 << 16)) / + ((double) _soundFreq / (double) _soundSliceSize)); + _frameLength = _soundSliceLength >> 16; + + _soundStage = 1; + _hasSound = true; + + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, false); + } else + _frameLength = 1000 / _frameRate; + + return true; +} + +bool Imd::loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos) { + // Positions table + if (_framesPos) { + _stream->seek(framesPosPos, SEEK_SET); + for (int i = 0; i < _framesCount; i++) + _framesPos[i] = _stream->readUint32LE(); + } + + // Coordinates table + if (_features & kFeaturesFrameCoords) { + _stream->seek(framesCoordsPos, SEEK_SET); + _frameCoords = new Coord[_framesCount]; + assert(_frameCoords); + for (int i = 0; i < _framesCount; i++) { + _frameCoords[i].left = _stream->readSint16LE(); + _frameCoords[i].top = _stream->readSint16LE(); + _frameCoords[i].right = _stream->readSint16LE(); + _frameCoords[i].bottom = _stream->readSint16LE(); + } + } + + return true; +} + +bool Imd::load(Common::SeekableReadStream &stream) { + unload(); + + _stream = &stream; + + uint16 handle; + + handle = _stream->readUint16LE(); + _version = _stream->readByte(); + + // Version checking + if ((handle != 0) || (_version < 2)) { + warning("Imd::load(): Version incorrect (%d,%X)", handle, _version); + unload(); + return false; + } + + // Rest header + _features = _stream->readByte(); + _framesCount = _stream->readUint16LE(); + _x = _stream->readSint16LE(); + _y = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + _flags = _stream->readUint16LE(); + _firstFramePos = _stream->readUint16LE(); + + // IMDs always have video + _features |= kFeaturesVideo; + // IMDs always have palettes + _features |= kFeaturesPalette; + + // Palette + _stream->read((byte *) _palette, 768); + + if (!loadCoordinates()) { + unload(); + return false; + } + + uint32 framesPosPos, frameCoordsPos; + if (!loadFrameTableOffsets(framesPosPos, frameCoordsPos)) { + unload(); + return false; + } + + if (!assessAudioProperties()) { + unload(); + return false; + } + + if (!assessVideoProperties()) { + unload(); + return false; + } + + if (!loadFrameTables(framesPosPos, frameCoordsPos)) { + unload(); + return false; + } + + // Seek to the first frame + _stream->seek(_firstFramePos, SEEK_SET); + + return true; +} + +void Imd::unload() { + clear(); +} + +void Imd::setXY(int16 x, int16 y) { + // Adjusting the standard coordinates + if (_stdX != -1) { + if (x >= 0) + _stdX = _stdX - _x + x; + if (y >= 0) + _stdY = _stdY - _y + y; + } + + // Going through the coordinate table as well + if (_frameCoords) { + for (int i = 0; i < _framesCount; i++) { + if (_frameCoords[i].left != -1) { + if (x >= 0) { + _frameCoords[i].left = _frameCoords[i].left - _x + x; + _frameCoords[i].right = _frameCoords[i].right - _x + x; + } + if (y >= 0) { + _frameCoords[i].top = _frameCoords[i].top - _y + y; + _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y; + } + } + } + } + + if (x >= 0) + _x = x; + if (y >= 0) + _y = y; +} + +void Imd::enableSound(Audio::Mixer &mixer) { + // Sanity check + if (mixer.getOutputRate() == 0) + return; + + // Only possible on the first frame + if (_curFrame > 0) + return; + + _mixer = &mixer; + _soundEnabled = true; +} + +void Imd::disableSound() { + if (_audioStream) { + + if (_soundStage == 2) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + } else + delete _audioStream; + + _audioStream = 0; + _soundStage = 0; + } + _soundEnabled = false; + _mixer = 0; +} + +bool Imd::isSoundPlaying() const { + if (_audioStream && _mixer && _mixer->isSoundHandleActive(_audioHandle)) + return true; + + return false; +} + +void Imd::seekFrame(int32 frame, int16 whence, bool restart) { + if (!_stream) + // Nothing to do + return; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _framesCount - frame - 1; + else if (whence != SEEK_SET) + return; + + if ((frame < 0) || (frame >= _framesCount) || (frame == _curFrame)) + // Nothing to do + return; + + // Try every possible way to find a file offset to that frame + uint32 framePos = 0; + if (frame == 0) { + framePos = _firstFramePos; + } else if (frame == 1) { + framePos = _firstFramePos; + _stream->seek(framePos, SEEK_SET); + framePos += _stream->readUint16LE() + 4; + } else if (_framesPos) { + framePos = _framesPos[frame]; + } else if (restart && (_soundStage == 0)) { + for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) + processFrame(i); + return; +//FIXME: This workaround is needed for Bargon Attack intro, which was broken by a fix concerning Ween in r42995. + } else if (_soundStage == 0) { + warning("Imd::seekFrame(): Avoiding \"Frame %d is not directly accessible\"", frame); + _curFrame = frame; +//End of fixme + } else + error("Imd::seekFrame(): Frame %d is not directly accessible", frame); + + // Seek + _stream->seek(framePos); + _curFrame = frame; +} + +CoktelVideo::State Imd::nextFrame() { + return processFrame(_curFrame); +} + +uint32 Imd::getFrameWaitTime() { + if (_soundEnabled && _hasSound) {; + if (_soundStage != 2) + return 0; + + if (_skipFrames == 0) { + int32 waitTime = (int16) (((_curFrame * _soundSliceLength) - + (_mixer->getSoundElapsedTime(_audioHandle) << 16)) >> 16); + + if (waitTime < 0) { + _skipFrames = -waitTime / (_soundSliceLength >> 16); + warning("Video A/V sync broken, skipping %d frame(s)", _skipFrames + 1); + } else if (waitTime > 0) + return waitTime; + + } else + _skipFrames--; + } else + return _frameLength; + + return 0; +} + +void Imd::copyCurrentFrame(byte *dest, + uint16 left, uint16 top, uint16 width, uint16 height, + uint16 x, uint16 y, uint16 pitch, int16 transp) { + + if (!_vidMem) + return; + + if (((left + width) > _width) || ((top + height) > _height)) + return; + + dest += pitch * y; + byte *vidMem = _vidMem + _width * top; + + if (transp < 0) { + // No transparency + if ((x > 0) || (left > 0) || (pitch != _width) || (width != _width)) { + // Copy row-by-row + for (int i = 0; i < height; i++) { + byte *d = dest + x; + byte *s = vidMem + left; + + memcpy(d, s, width); + + dest += pitch; + vidMem += _width; + } + } else + // Dimensions fit, copy everything at once + memcpy(dest, vidMem, width * height); + + return; + } + + for (int i = 0; i < height; i++) { + byte *d = dest + x; + byte *s = vidMem + left; + + for (int j = 0; j < width; j++) { + if (*s != transp) + *d = *s; + + s++; + d++; + } + + dest += pitch; + vidMem += _width; + } + +} + +void Imd::zeroData() { + PreImd::zeroData(); + + _version = 0; + + _stdX = 0; + _stdY = 0; + _stdWidth = 0; + _stdHeight = 0; + + _framesPos = 0; + _firstFramePos = 0; + _frameCoords = 0; + + _frameDataSize = 0; + _frameData = 0; + _frameDataLen = 0; + + _hasSound = false; + _soundEnabled = false; + _soundStage = 0; + _skipFrames = 0; + + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; + _soundSlicesCount = 0; + _soundSliceLength = 0; + _audioStream = 0; + + _lastFrameTime = 0; +} + +void Imd::deleteData() { + PreImd::deleteData(); + + delete[] _framesPos; + delete[] _frameCoords; + delete[] _frameData; + + disableSound(); +} + +void Imd::clear() { + deleteData(); + zeroData(); +} + +void Imd::nextSoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) { + _stream->seek(_soundSliceSize, SEEK_CUR); + return; + } + + byte *soundBuf = (byte *)malloc(_soundSliceSize); + + _stream->read(soundBuf, _soundSliceSize); + unsignedToSigned(soundBuf, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); +} + +bool Imd::initialSoundSlice(bool hasNextCmd) { + int dataLength = _soundSliceSize * _soundSlicesCount; + + if (hasNextCmd || !_soundEnabled) { + _stream->seek(dataLength, SEEK_CUR); + return false; + } + + byte *soundBuf = (byte *)malloc(dataLength); + + _stream->read(soundBuf, dataLength); + unsignedToSigned(soundBuf, dataLength); + + _audioStream->queueBuffer(soundBuf, dataLength, DisposeAfterUse::YES, 0); + + return _soundStage == 1; +} + +void Imd::emptySoundSlice(bool hasNextCmd) { + if (hasNextCmd || !_soundEnabled) + return; + + byte *soundBuf = (byte *)malloc(_soundSliceSize); + + memset(soundBuf, 0, _soundSliceSize); + + _audioStream->queueBuffer(soundBuf, _soundSliceSize, DisposeAfterUse::YES, 0); +} + +void Imd::videoData(uint32 size, State &state) { + _stream->read(_frameData, size); + _frameDataLen = size; + +/* + if (_vidMemWidth <= state.right) { + state.left = 0; + state.right -= state.left; + } + if (_vidMemWidth <= state.right) + state.right = _vidMemWidth - 1; + if (_vidMemHeight <= state.bottom) { + state.top = 0; + state.bottom -= state.top; + } + if (_vidMemHeight <= state.bottom) + state.bottom = _vidMemHeight -1; +*/ + + state.flags |= renderFrame(state.left, state.top, state.right, state.bottom); + state.flags |= _frameData[0]; +} + +void Imd::calcFrameCoords(uint16 frame, State &state) { + if (_stdX != -1) { + state.left = _stdX; + state.top = _stdY; + state.right = _stdWidth + state.left - 1; + state.bottom = _stdHeight + state.top - 1; + state.flags |= kStateStdCoords; + } + if (_frameCoords && + (_frameCoords[frame].left != -1)) { + state.left = _frameCoords[frame].left; + state.top = _frameCoords[frame].top; + state.right = _frameCoords[frame].right; + state.bottom = _frameCoords[frame].bottom; + state.flags |= kStateFrameCoords; + } +} + +CoktelVideo::State Imd::processFrame(uint16 frame) { + State state; + uint32 cmd = 0; + bool hasNextCmd = false; + bool startSound = false; + + if (!_stream || (frame >= _framesCount)) { + state.flags = kStateBreak; + return state; + } + + if (frame != _curFrame) { + state.flags |= kStateSeeked; + seekFrame(frame); + } + + if (!_vidMem) + setVideoMemory(); + + state.left = _x; + state.top = _y; + state.right = _width + state.left - 1; + state.bottom = _height + state.top - 1; + + do { + if (frame != 0) + calcFrameCoords(frame, state); + + cmd = _stream->readUint16LE(); + + if ((cmd & kCommandBreakMask) == kCommandBreak) { + // Flow control + + if (cmd == kCommandBreak) { + _stream->seek(2, SEEK_CUR); + cmd = _stream->readUint16LE(); + } + + // Break + if (cmd == kCommandBreakSkip0) { + state.flags = kStateBreak; + continue; + } else if (cmd == kCommandBreakSkip16) { + cmd = _stream->readUint16LE(); + _stream->seek(cmd, SEEK_CUR); + state.flags = kStateBreak; + continue; + } else if (cmd == kCommandBreakSkip32) { + cmd = _stream->readUint32LE(); + _stream->seek(cmd, SEEK_CUR); + state.flags = kStateBreak; + continue; + } + } + + // Audio + if (_soundStage != 0) { + if (cmd == kCommandNextSound) { + + nextSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); + + } else if (cmd == kCommandStartSound) { + + startSound = initialSoundSlice(hasNextCmd); + cmd = _stream->readUint16LE(); + + } else + emptySoundSlice(hasNextCmd); + } + + // Set palette + if (cmd == kCommandPalette) { + _stream->seek(2, SEEK_CUR); + state.flags |= kStatePalette; + + _stream->read(_palette, 768); + cmd = _stream->readUint16LE(); + } + + hasNextCmd = false; + + if (cmd == kCommandJump) { + // Jump to frame + + frame = _stream->readSint16LE(); + if (_framesPos) { + _curFrame = frame; + _stream->seek(_framesPos[frame], SEEK_SET); + + hasNextCmd = true; + state.flags |= kStateJump; + } + + } else if (cmd == kCommandVideoData) { + uint32 size = _stream->readUint32LE() + 2; + + videoData(size, state); + + state.flags |= 1; + + } else if (cmd != 0) { + uint32 size = cmd + 2; + + videoData(size, state); + + } else + state.flags |= kStateNoVideoData; + + } while (hasNextCmd); + + if (startSound && _soundEnabled) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream); + _skipFrames = 0; + _soundStage = 2; + } + + _curFrame++; + if ((_curFrame == _framesCount) && (_soundStage == 2)) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _soundStage = 0; + } + + _lastFrameTime = g_system->getMillis(); + return state; +} + +// A whole, completely filled block +void Imd::renderBlockWhole(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch) { + + for (int i = 0; i < height; i++) { + memcpy(dest, src, width); + + src += srcPitch; + dest += destPitch; + } +} + +// A quarter-wide whole, completely filled block +void Imd::renderBlockWhole4X(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch) { + + for (int i = 0; i < height; i++) { + byte *destRow = dest; + const byte *srcRow = src; + + for (int j = 0; j < width; j += 4, destRow += 4, srcRow++) + memset(destRow, *srcRow, 4); + + src += srcPitch; + dest += destPitch; + } +} + +// A half-high whole, completely filled block +void Imd::renderBlockWhole2Y(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch) { + + while (height > 1) { + memcpy(dest , src, width); + memcpy(dest + destPitch, src, width); + + height -= 2; + dest += 2 * destPitch; + src += srcPitch; + } + + if (height == 1) + memcpy(dest, src, width); +} + +// A sparse block +void Imd::renderBlockSparse(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch) { + + for (int i = 0; i < height; i++) { + byte *destRow = dest; + int16 pixWritten = 0; + + while (pixWritten < srcPitch) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, srcPitch - pixWritten); + copyCount = MAX<int16>(0, MIN<int16>(pixCount, width - pixWritten)); + memcpy(destRow, src, copyCount); + + pixWritten += pixCount; + destRow += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + destRow += pixCount + 1; + } + + } + + dest += destPitch; + } +} + +// A half-high sparse block +void Imd::renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch) { + + for (int i = 0; i < height; i += 2) { + byte *destRow = dest; + int16 pixWritten = 0; + + while (pixWritten < srcPitch) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { // Data + int16 copyCount; + + pixCount = MIN((pixCount & 0x7F) + 1, srcPitch - pixWritten); + copyCount = MAX<int16>(0, MIN<int16>(pixCount, width - pixWritten)); + memcpy(destRow , src, pixCount); + memcpy(destRow + destPitch, src, pixCount); + + pixWritten += pixCount; + destRow += pixCount; + src += pixCount; + } else { // "Hole" + pixWritten += pixCount + 1; + destRow += pixCount + 1; + } + + } + + dest += destPitch; + } +} + +uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { + if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) + return 0; + + uint32 retVal = 0; + + int16 width = right - left + 1; + int16 height = bottom - top + 1; + int16 sW = _vidMemWidth; + int16 sH = _vidMemHeight; + + byte *dataPtr = _frameData; + byte *imdVidMem = _vidMem + sW * top + left; + byte *srcPtr; + + uint8 type = *dataPtr++; + + if (type & 0x10) { // Palette data + // One byte index + int index = *dataPtr++; + // 16 entries with each 3 bytes (RGB) + memcpy(_palette + index * 3, dataPtr, MIN((255 - index) * 3, 48)); + + retVal = kStatePalette; + dataPtr += 48; + type ^= 0x10; + } + + srcPtr = dataPtr; + + if (type & 0x80) { + // Frame data is compressed + + srcPtr = _vidBuffer; + type &= 0x7F; + if ((type == 2) && (width == sW)) { + // Directly uncompress onto the video surface + deLZ77(imdVidMem, dataPtr); + return retVal; + } else + deLZ77(srcPtr, dataPtr); + } + + int16 drawWidth = MAX<int16>(0, MIN<int16>(width , sW - left)); + int16 drawHeight = MAX<int16>(0, MIN<int16>(height, sH - top )); + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); + else if (type == 0x02) + renderBlockWhole (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); + else if (type == 0x42) + renderBlockWhole4X (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); + else + renderBlockSparse2Y(imdVidMem, srcPtr, drawWidth, drawHeight, sW, width); + + return retVal; +} + +void Imd::deLZ77(byte *dest, byte *src) { + int i; + byte buf[4370]; + uint16 chunkLength; + uint32 frameLength; + uint16 bufPos1; + uint16 bufPos2; + uint16 tmp; + uint8 chunkBitField; + uint8 chunkCount; + bool mode; + + frameLength = READ_LE_UINT32(src); + src += 4; + + if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) { + src += 4; + bufPos1 = 273; + mode = 1; // 123Ch (cmp al, 12h) + } else { + bufPos1 = 4078; + mode = 0; // 275h (jnz +2) + } + + memset(buf, 32, bufPos1); + chunkCount = 1; + chunkBitField = 0; + + while (frameLength > 0) { + chunkCount--; + if (chunkCount == 0) { + tmp = *src++; + chunkCount = 8; + chunkBitField = tmp; + } + if (chunkBitField % 2) { + chunkBitField >>= 1; + buf[bufPos1] = *src; + *dest++ = *src++; + bufPos1 = (bufPos1 + 1) % 4096; + frameLength--; + continue; + } + chunkBitField >>= 1; + + tmp = READ_LE_UINT16(src); + src += 2; + chunkLength = ((tmp & 0xF00) >> 8) + 3; + + if ((mode && ((chunkLength & 0xFF) == 0x12)) || + (!mode && (chunkLength == 0))) + chunkLength = *src++ + 0x12; + + bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00); + if (((tmp + chunkLength) >= 4096) || + ((chunkLength + bufPos1) >= 4096)) { + + for (i = 0; i < chunkLength; i++, dest++) { + *dest = buf[bufPos2]; + buf[bufPos1] = buf[bufPos2]; + bufPos1 = (bufPos1 + 1) % 4096; + bufPos2 = (bufPos2 + 1) % 4096; + } + + } else if (((tmp + chunkLength) < bufPos1) || + ((chunkLength + bufPos1) < bufPos2)) { + + memcpy(dest, buf + bufPos2, chunkLength); + memmove(buf + bufPos1, buf + bufPos2, chunkLength); + + dest += chunkLength; + bufPos1 += chunkLength; + bufPos2 += chunkLength; + + } else { + + for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) { + *dest = buf[bufPos2]; + buf[bufPos1] = buf[bufPos2]; + } + + } + frameLength -= chunkLength; + + } +} + +inline void Imd::unsignedToSigned(byte *buffer, int length) { + while (length-- > 0) *buffer++ ^= 0x80; +} + + +Vmd::ExtraData::ExtraData() { + memset(name, 0, 16); + + offset = 0; + size = 0; + realSize = 0; +} + + +Vmd::Part::Part() { + type = kPartTypeSeparator; + field_1 = 0; + field_E = 0; + size = 0; + left = 0; + top = 0; + right = 0; + bottom = 0; + id = 0; + flags = 0; +} + + +Vmd::Frame::Frame() { + parts = 0; + offset = 0; +} + +Vmd::Frame::~Frame() { + delete[] parts; +} + + +const uint16 Vmd::_tableDPCM[128] = { + 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, + 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, + 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, + 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, + 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, + 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, + 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, + 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, + 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, + 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, + 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, + 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, + 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 +}; + +const int32 Vmd::_tableADPCM[] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 +}; + +const int32 Vmd::_tableADPCMStep[] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +Vmd::Vmd(Graphics::PaletteLUT *palLUT) : _palLUT(palLUT) { + zeroData(); +} + +Vmd::~Vmd() { + deleteData(); + zeroData(); +} + +bool Vmd::assessVideoProperties() { + if (_bytesPerPixel > 1) + _features |= kFeaturesFullColor; + else + _features |= kFeaturesPalette; + + if ((_version & 2) && !(_version & 8)) { + _externalCodec = true; + _frameDataSize = _vidBufferSize = 0; + } else + _externalCodec = false; + + if (_externalCodec) { + if (_videoCodec == MKID_BE('iv32')) { +#ifdef USE_INDEO3 + _features &= ~kFeaturesPalette; + _features |= kFeaturesFullColor; + _codecIndeo3 = new Indeo3(_width, _height, _palLUT); +#else + warning("Vmd::assessVideoProperties(): Indeo3 decoder not compiled in"); +#endif + } else { + char *fourcc = (char *) &_videoCodec; + + warning("Vmd::assessVideoProperties(): Unknow video codec FourCC \'%c%c%c%c\'", + fourcc[3], fourcc[2], fourcc[1], fourcc[0]); + return false; + } + } + + _preScaleX = 1; + _postScaleX = 1; + + if (_externalCodec) + _blitMode = 0; + else if (_bytesPerPixel == 1) + _blitMode = 0; + else if ((_bytesPerPixel == 2) || (_bytesPerPixel == 3)) { + int n = (_flags & 0x80) ? 2 : 3; + + _blitMode = n - 1; + + if (_bytesPerPixel == 2) { + _preScaleX = n; + _postScaleX = 1; + } else if (_bytesPerPixel == 3) { + _preScaleX = 1; + _postScaleX = n; + } + + _bytesPerPixel = n; + } + + _scaleExternalX = 1; + if (!_externalCodec && !(_flags & 0x1000)) + _scaleExternalX = _bytesPerPixel; + + if (_hasVideo) { + if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) + _frameDataSize = _width * _height + 1000; + if ((_vidBufferSize == 0) || (_vidBufferSize > 1048576)) + _vidBufferSize = _frameDataSize; + + _frameData = new byte[_frameDataSize]; + assert(_frameData); + memset(_frameData, 0, _frameDataSize); + + _vidBuffer = new byte[_vidBufferSize]; + assert(_vidBuffer); + memset(_vidBuffer, 0, _vidBufferSize); + + if (_blitMode > 0) { + _vidMemBuffer = new byte[_bytesPerPixel * (_width * _height + 1000)]; + memset(_vidMemBuffer, 0, _bytesPerPixel * (_width * _height + 1000)); + } + } + +#ifdef USE_INDEO3 + if (_externalCodec && _codecIndeo3) + _features |= kFeaturesSupportsDouble; +#endif + + return true; +} + +bool Vmd::assessAudioProperties() { + bool supportedFormat = true; + + _features |= kFeaturesSound; + + _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); + + if (_soundSliceSize < 0) { + _soundBytesPerSample = 2; + _soundSliceSize = -_soundSliceSize; + + if (_soundFlags & 0x10) { + _audioFormat = kAudioFormat16bitADPCM; + _soundHeaderSize = 3; + _soundDataSize = _soundSliceSize >> 1; + + if (_soundStereo > 0) + supportedFormat = false; + + } else { + _audioFormat = kAudioFormat16bitDPCM; + _soundHeaderSize = 1; + _soundDataSize = _soundSliceSize; + + if (_soundStereo == 1) { + supportedFormat = false; + } else if (_soundStereo == 2) { + _soundDataSize = 2 * _soundDataSize + 2; + _soundHeaderSize = 4; + } + + } + } else { + _soundBytesPerSample = 1; + _audioFormat = kAudioFormat8bitDirect; + _soundHeaderSize = 0; + _soundDataSize = _soundSliceSize; + + if (_soundStereo > 0) + supportedFormat = false; + } + + if (!supportedFormat) { + warning("Vmd::assessAudioProperties(): Unsupported audio format: %d bits, encoding %d, stereo %d", + _soundBytesPerSample * 8, _audioFormat, _soundStereo); + return false; + } + + _soundSliceLength = (uint32) (((double) (1000 << 16)) / + ((double) _soundFreq / (double) _soundSliceSize)); + _frameLength = _soundSliceLength >> 16; + + _soundStage = 1; + + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); + + return true; +} + +void Vmd::readFrameTable(int &numExtraData) { + numExtraData = 0; + + _stream->seek(_frameInfoOffset); + _frames = new Frame[_framesCount]; + for (uint16 i = 0; i < _framesCount; i++) { + _frames[i].parts = new Part[_partsPerFrame]; + _stream->skip(2); // Unknown + _frames[i].offset = _stream->readUint32LE(); + } + + for (uint16 i = 0; i < _framesCount; i++) { + bool separator = false; + + for (uint16 j = 0; j < _partsPerFrame; j++) { + + _frames[i].parts[j].type = (PartType) _stream->readByte(); + _frames[i].parts[j].field_1 = _stream->readByte(); + _frames[i].parts[j].size = _stream->readUint32LE(); + + if (_frames[i].parts[j].type == kPartTypeAudio) { + + _frames[i].parts[j].flags = _stream->readByte(); + _stream->skip(9); // Unknown + + } else if (_frames[i].parts[j].type == kPartTypeVideo) { + + _frames[i].parts[j].left = _stream->readUint16LE(); + _frames[i].parts[j].top = _stream->readUint16LE(); + _frames[i].parts[j].right = _stream->readUint16LE(); + _frames[i].parts[j].bottom = _stream->readUint16LE(); + _frames[i].parts[j].field_E = _stream->readByte(); + _frames[i].parts[j].flags = _stream->readByte(); + + } else if (_frames[i].parts[j].type == kPartTypeSpeech) { + _frames[i].parts[j].id = _stream->readUint16LE(); + // Speech text file name + _stream->skip(8); + } else if (_frames[i].parts[j].type == kPartTypeExtraData) { + if (!separator) + numExtraData++; + _stream->skip(10); + } else if (_frames[i].parts[j].type == kPartTypeSeparator) { + separator = true; + _stream->skip(10); + } else { + // Unknow type + _stream->skip(10); + } + + } + } +} + +void Vmd::readExtraData() { + uint32 ssize = _stream->size(); + for (uint16 i = 0; i < _framesCount; i++) { + _stream->seek(_frames[i].offset); + + for (uint16 j = 0; j < _partsPerFrame; j++) { + if (_frames[i].parts[j].type == kPartTypeSeparator) + break; + + if (_frames[i].parts[j].type == kPartTypeExtraData) { + ExtraData data; + + data.offset = _stream->pos() + 20; + data.size = _frames[i].parts[j].size; + data.realSize = _stream->readUint32LE(); + + _stream->read(data.name, 16); + data.name[15] = '\0'; + + _stream->skip(_frames[i].parts[j].size - 20); + + if ((((uint32) data.realSize) >= ssize) || (data.name[0] == 0)) + continue; + + _extraData.push_back(data); + + } else + _stream->skip(_frames[i].parts[j].size); + } + } +} + +bool Vmd::load(Common::SeekableReadStream &stream) { + unload(); + + _stream = &stream; + + uint16 headerLength; + uint16 handle; + + headerLength = _stream->readUint16LE(); + handle = _stream->readUint16LE(); + _version = _stream->readUint16LE(); + + bool readPalette; + + // Version checking + if (headerLength == 50) { + // Newer version, used in Addy 5 upwards + warning("Vmd::load(): TODO: Addy 5 videos"); + readPalette = false; + } else if (headerLength == 814) { + // Old version + readPalette = true; + } else { + warning("Vmd::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); + unload(); + return false; + } + + _framesCount = _stream->readUint16LE(); + + _x = _stream->readSint16LE(); + _y = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + + if ((_width != 0) && (_height != 0)) { + + _hasVideo = true; + _features |= kFeaturesVideo; + + } else + _hasVideo = false; + + _bytesPerPixel = 1; + if (_version & 4) + _bytesPerPixel = handle + 1; + + if (_bytesPerPixel > 3) { + warning("Vmd::load(): Requested %d bytes per pixel (%d, %d, %d)", _bytesPerPixel, headerLength, handle, _version); + unload(); + return false; + } + + _flags = _stream->readUint16LE(); + + _partsPerFrame = _stream->readUint16LE(); + _firstFramePos = _stream->readUint32LE(); + + _videoCodec = _stream->readUint32BE(); + + if (readPalette) + _stream->read((byte *) _palette, 768); + + _frameDataSize = _stream->readUint32LE(); + _vidBufferSize = _stream->readUint32LE(); + + _doubleMode = false; + + if (_hasVideo) { + if (!assessVideoProperties()) { + unload(); + return false; + } + } + + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); + _soundSlicesCount = _stream->readSint16LE(); + _soundFlags = _stream->readUint16LE(); + + _hasSound = (_soundFreq != 0); + + if (_hasSound) { + if (!assessAudioProperties()) { + unload(); + return false; + } + } else + _frameLength = 1000 / _frameRate; + + _frameInfoOffset = _stream->readUint32LE(); + + int numExtraData; + readFrameTable(numExtraData); + + _stream->seek(_firstFramePos); + + if (numExtraData == 0) + return true; + + _extraData.reserve(numExtraData); + readExtraData(); + + _stream->seek(_firstFramePos); + return true; +} + +void Vmd::unload() { + clear(); +} + +int16 Vmd::getWidth() const { + return preScaleX(_width); +} + +void Vmd::setXY(int16 x, int16 y) { + + x *= _scaleExternalX; + + for (int i = 0; i < _framesCount; i++) { + for (int j = 0; j < _partsPerFrame; j++) { + + if (_frames[i].parts[j].type == kPartTypeVideo) { + if (x >= 0) { + _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; + _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x; + } + if (y >= 0) { + _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; + _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y; + } + } + + } + } + + if (x >= 0) + _x = x; + if (y >= 0) + _y = y; +} + +void Vmd::setDoubleMode(bool doubleMode) { + if (_doubleMode == doubleMode) + return; + + if (_vidBuffer) { + delete[] _vidBuffer; + + if (doubleMode) + _vidBufferSize *= 4; + else + _vidBufferSize /= 4; + + _vidBuffer = new byte[_vidBufferSize]; + assert(_vidBuffer); + memset(_vidBuffer, 0, _vidBufferSize); + + } + +#ifdef USE_INDEO3 + if (_codecIndeo3) { + delete _codecIndeo3; + + _codecIndeo3 = new Indeo3(_width * (doubleMode ? 2 : 1), + _height * (doubleMode ? 2 : 1), _palLUT); + } +#endif + + _doubleMode = doubleMode; +} + +void Vmd::seekFrame(int32 frame, int16 whence, bool restart) { + if (!_stream) + // Nothing to do + return; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _framesCount - frame - 1; + else if (whence != SEEK_SET) + return; + + if ((frame < 0) || (frame >= _framesCount)) + // Nothing to do + return; + + // Restart sound + if (_hasSound && (frame == 0) && (_soundStage == 0) && !_audioStream) { + _soundStage = 1; + _audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0); + } + + // Seek + _stream->seek(_frames[frame].offset); + _curFrame = frame; +} + +CoktelVideo::State Vmd::nextFrame() { + State state; + + state = processFrame(_curFrame); + _curFrame++; + return state; +} + +void Vmd::zeroData() { + Imd::zeroData(); + + _hasVideo = true; + _videoCodec = 0; + +#ifdef USE_INDEO3 + _codecIndeo3 = 0; +#endif + + _partsPerFrame = 0; + _frames = 0; + + _extraData.clear(); + + _soundBytesPerSample = 1; + _soundStereo = 0; + _soundHeaderSize = 0; + _soundDataSize = 0; + _audioFormat = kAudioFormat8bitDirect; + + _externalCodec = false; + _doubleMode = false; + _blitMode = 0; + _bytesPerPixel = 1; + _preScaleX = 1; + _postScaleX = 1; + _scaleExternalX = 1; + _vidMemBuffer = 0; +} + +void Vmd::deleteData() { + Imd::deleteData(); + +#ifdef USE_INDEO3 + delete _codecIndeo3; +#endif + delete[] _frames; + delete[] _vidMemBuffer; +} + +void Vmd::clear() { + deleteData(); + zeroData(); +} + +CoktelVideo::State Vmd::processFrame(uint16 frame) { + State state; + bool startSound = false; + + seekFrame(frame); + + state.flags |= kStateNoVideoData; + state.left = 0x7FFF; + state.top = 0x7FFF; + state.right = 0; + state.bottom = 0; + + if (!_vidMem) + setVideoMemory(); + + for (uint16 i = 0; (i < _partsPerFrame) && (frame < _framesCount); i++) { + uint32 pos = _stream->pos(); + + Part &part = _frames[frame].parts[i]; + + if (part.type == kPartTypeAudio) { + // Next sound slice data + if (part.flags == 1) { + + if (_soundEnabled) { + filledSoundSlice(part.size); + + if (_soundStage == 1) + startSound = true; + + } else + _stream->skip(part.size); + + // Initial sound data (all slices) + } else if (part.flags == 2) { + + if (_soundEnabled) { + uint32 mask = _stream->readUint32LE(); + filledSoundSlices(part.size - 4, mask); + + if (_soundStage == 1) + startSound = true; + + } else + _stream->skip(part.size); + + // Empty sound slice + } else if (part.flags == 3) { + + if (_soundEnabled) { + emptySoundSlice(_soundDataSize * _soundBytesPerSample); + + if (_soundStage == 1) + startSound = true; + } + + _stream->skip(part.size); + } else if (part.flags == 4) { + warning("Vmd::processFrame(): TODO: Addy 5 sound type 4 (%d)", part.size); + disableSound(); + _stream->skip(part.size); + } else { + warning("Vmd::processFrame(): Unknown sound type %d", part.flags); + _stream->skip(part.size); + } + + _stream->seek(pos + part.size); + + } else if ((part.type == kPartTypeVideo) && !_hasVideo) { + warning("Vmd::processFrame(): Header claims there's no video, but video found (%d)", part.size); + _stream->skip(part.size); + } else if ((part.type == kPartTypeVideo) && _hasVideo) { + state.flags &= ~kStateNoVideoData; + + uint32 size = part.size; + + // New palette + if (part.flags & 2) { + uint8 index = _stream->readByte(); + uint8 count = _stream->readByte(); + + _stream->read(_palette + index * 3, (count + 1) * 3); + _stream->skip((255 - count) * 3); + + state.flags |= kStatePalette; + + size -= (768 + 2); + } + + _stream->read(_frameData, size); + _frameDataLen = size; + + int16 l = part.left, t = part.top, r = part.right, b = part.bottom; + if (renderFrame(l, t, r, b)) { + if (!_externalCodec) { + l = preScaleX(l); + r = preScaleX(r); + } + // Rendering succeeded, merging areas + state.left = MIN(state.left, l); + state.top = MIN(state.top, t); + state.right = MAX(state.right, r); + state.bottom = MAX(state.bottom, b); + } + + } else if (part.type == kPartTypeSeparator) { + } else if (part.type == kPartTypeExtraData) { + _stream->skip(part.size); + } else if (part.type == kPartType4) { + // Unknown + _stream->skip(part.size); + } else if (part.type == kPartTypeSpeech) { + state.flags |= kStateSpeech; + state.speechId = part.id; + // Always triggers when speech starts + _stream->skip(part.size); + } else { + warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); + } + } + + if (startSound && _soundEnabled) { + if (_hasSound && _audioStream) { + _mixer->playStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream); + _skipFrames = 0; + _soundStage = 2; + } else + _soundStage = 0; + } + + if ((_curFrame == (_framesCount - 1)) && (_soundStage == 2)) { + _audioStream->finish(); + _mixer->stopHandle(_audioHandle); + _audioStream = 0; + _soundStage = 0; + } + + // If these are still 0x7FFF, no video data has been processed + if ((state.left == 0x7FFF) || (state.top == 0x7FFF)) + state.left = state.top = state.right = state.bottom = 0; + + _lastFrameTime = g_system->getMillis(); + return state; +} + +void Vmd::deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen) { + srcPtr++; + + if (srcLen & 1) { + byte data = *srcPtr++; + + if (destLen > 0) { + *destPtr++ = data; + destLen--; + } + } + + srcLen >>= 1; + + while (srcLen > 0) { + uint8 tmp = *srcPtr++; + if (tmp & 0x80) { // Verbatim copy + tmp &= 0x7F; + + int16 copyCount = MAX<int16>(0, MIN<int16>(destLen, tmp * 2)); + + memcpy(destPtr, srcPtr, copyCount); + + srcPtr += tmp * 2; + destPtr += copyCount; + destLen -= copyCount; + } else { // 2 bytes tmp times + for (int i = 0; (i < tmp) && (destLen > 0); i++) { + for (int j = 0; j < 2; j++) { + if (destLen <= 0) + break; + + *destPtr++ = srcPtr[j]; + destLen--; + } + } + srcPtr += 2; + } + srcLen -= tmp; + } +} + +// A run-length-encoded sparse block +void Vmd::renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch) { + + for (int i = 0; i < height; i++) { + byte *destRow = dest; + int16 pixWritten = 0; + + while (pixWritten < srcPitch) { + int16 pixCount = *src++; + + if (pixCount & 0x80) { + int16 copyCount; + + pixCount = (pixCount & 0x7F) + 1; + copyCount = MAX<int16>(0, MIN<int16>(pixCount, width - pixWritten)); + + if (*src != 0xFF) { // Normal copy + + memcpy(destRow, src, copyCount); + destRow += copyCount; + src += pixCount; + } else + deRLE(destRow, src, copyCount, pixCount); + + pixWritten += pixCount; + } else { // "Hole" + int16 copyCount = MAX<int16>(0, MIN<int16>(pixCount + 1, width - pixWritten)); + + destRow += copyCount; + pixWritten += pixCount + 1; + } + + } + + dest += destPitch; + } + +} + +uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { + if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) + return 0; + + int16 width = right - left + 1; + int16 height = bottom - top + 1; + int16 sW = _vidMemWidth; + int16 sH = _vidMemHeight; + + byte *dataPtr = _frameData; + byte *imdVidMem = _vidMem + sW * top + left; + byte *srcPtr; + + if ((left < 0) || (top < 0) || (right < 0) || (bottom < 0)) + return 1; + if ((width <= 0) || (height <= 0)) + return 1; + + uint8 type; + byte *dest = imdVidMem; + +#ifdef USE_INDEO3 + uint32 dataLen = _frameDataLen; + + if (Indeo3::isIndeo3(dataPtr, dataLen)) { + if (!_codecIndeo3) + return 0; + + if (!_codecIndeo3->decompressFrame(dataPtr, dataLen, _vidBuffer, + width * (_doubleMode ? 2 : 1), height * (_doubleMode ? 2 : 1))) + return 0; + + type = 2; + srcPtr = _vidBuffer; + width = _width * (_doubleMode ? 2 : 1); + height = _height * (_doubleMode ? 2 : 1); + right = left + width - 1; + bottom = top + height - 1; + + } else { + + if (_externalCodec) { + warning("Unknown external codec"); + return 0; + } + +#else + + if (_externalCodec) { + return 0; + } else { + +#endif + + type = *dataPtr++; + srcPtr = dataPtr; + + if (_blitMode > 0) { + dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX((left - _x)); + imdVidMem = _vidMem + _vidMemWidth * top + preScaleX(left); + sW = postScaleX(_width); + sH = _height; + } + + if (type & 0x80) { + // Frame data is compressed + + srcPtr = _vidBuffer; + type &= 0x7F; + if ((type == 2) && (postScaleX(width) == sW)) { + // Directly uncompress onto the video surface + deLZ77(dest, dataPtr); + blit(imdVidMem, dest, width, height); + return 1; + } else + deLZ77(srcPtr, dataPtr); + } + + } + + width = postScaleX(width); + + int16 drawWidth = MAX<int16>(0, MIN<int16>(width , sW - left)); + int16 drawHeight = MAX<int16>(0, MIN<int16>(height, sH - top )); + + // Evaluate the block type + if (type == 0x01) + renderBlockSparse (dest, srcPtr, drawWidth, drawHeight, sW, width); + else if (type == 0x02) + renderBlockWhole (dest, srcPtr, drawWidth, drawHeight, sW, width); + else if (type == 0x03) + renderBlockRLE (dest, srcPtr, drawWidth, drawHeight, sW, width); + else if (type == 0x42) + renderBlockWhole4X (dest, srcPtr, drawWidth, drawHeight, sW, width); + else if ((type & 0x0F) == 0x02) + renderBlockWhole2Y (dest, srcPtr, drawWidth, drawHeight, sW, width); + else + renderBlockSparse2Y(dest, srcPtr, drawWidth, drawHeight, sW, width); + + dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX(left - _x); + blit(imdVidMem, dest, width, height); + + return 1; +} + +inline int32 Vmd::preScaleX(int32 x) const { + return x / _preScaleX; +} + +inline int32 Vmd::postScaleX(int32 x) const { + return x * _postScaleX; +} + +void Vmd::blit(byte *dest, byte *src, int16 width, int16 height) { + if (_blitMode == 0) + return; + + if (_blitMode == 1) + blit16(dest, src, preScaleX(_width), preScaleX(width), height); + else if (_blitMode == 2) + blit24(dest, src, preScaleX(_width), preScaleX(width), height); +} + +void Vmd::blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height) { + assert(_palLUT); + + Graphics::SierraLight *dither = + new Graphics::SierraLight(width, _palLUT); + + for (int i = 0; i < height; i++) { + byte *d = dest; + byte *s = src; + + for (int j = 0; j < width; j++, s += 2) { + uint16 data = READ_LE_UINT16(s); + byte r = ((data & 0x7C00) >> 10); + byte g = ((data & 0x03E0) >> 5); + byte b = ((data & 0x001F) >> 0); + byte dY, dU, dV; + + Graphics::PaletteLUT::RGB2YUV(r << 3, g << 3, b << 3, dY, dU, dV); + + byte p = dither->dither(dY, dU, dV, j); + + if ((dY == 0) || ((r == 0) && (g == 0) && (b == 0))) + *d++ = 0; + else + *d++ = p; + } + + dither->nextLine(); + dest += _vidMemWidth; + src += 2 * srcPitch; + } + + delete dither; +} + +void Vmd::blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height) { + assert(_palLUT); + + Graphics::SierraLight *dither = + new Graphics::SierraLight(width, _palLUT); + + for (int i = 0; i < height; i++) { + byte *d = dest; + byte *s = src; + + for (int j = 0; j < width; j++, s += 3) { + byte r = s[2]; + byte g = s[1]; + byte b = s[0]; + byte dY, dU, dV; + + Graphics::PaletteLUT::RGB2YUV(r, g, b, dY, dU, dV); + + byte p = dither->dither(dY, dU, dV, j); + + if ((dY == 0) || ((r == 0) && (g == 0) && (b == 0))) + *d++ = 0; + else + *d++ = p; + } + + dither->nextLine(); + dest += _vidMemWidth; + src += 3 * srcPitch; + } + + delete dither; +} + +byte *Vmd::deDPCM(const byte *data, uint32 &size, int32 init[2]) { + if (!data || (size == 0)) + return 0; + + int channels = (_soundStereo > 0) ? 2 : 1; + + uint32 inSize = size; + uint32 outSize = size + channels; + + int16 *out = (int16 *)malloc(outSize * 2); + byte *sound = (byte *) out; + + int channel = 0; + + for (int i = 0; i < channels; i++) { + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + while (inSize-- > 0) { + if (*data & 0x80) + init[channel] -= _tableDPCM[*data++ & 0x7F]; + else + init[channel] += _tableDPCM[*data++]; + + init[channel] = CLIP<int32>(init[channel], -32768, 32767); + *out++ = TO_BE_16(init[channel]); + + channel = (channel + 1) % channels; + } + + size = outSize * 2; + return sound; +} + +// Yet another IMA ADPCM variant +byte *Vmd::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { + if (!data || (size == 0)) + return 0; + + uint32 outSize = size * 2; + + int16 *out = (int16 *)malloc(outSize * 2); + byte *sound = (byte *) out; + + index = CLIP<int32>(index, 0, 88); + + int32 predictor = _tableADPCM[index]; + + uint32 dataByte = 0; + bool newByte = true; + + size *= 2; + while (size -- > 0) { + byte code = 0; + + if (newByte) { + dataByte = *data++; + code = (dataByte >> 4) & 0xF; + } else + code = dataByte & 0xF; + + newByte = !newByte; + + index += _tableADPCMStep[code]; + index = CLIP<int32>(index, 0, 88); + + int32 value = predictor / 8; + + if (code & 4) + value += predictor; + if (code & 2) + value += predictor / 2; + if (code & 1) + value += predictor / 4; + + if (code & 8) + init -= value; + else + init += value; + + init = CLIP<int32>(init, -32768, 32767); + + predictor = _tableADPCM[index]; + + *out++ = TO_BE_16(init); + } + + size = outSize * 2; + return sound; +} + +byte *Vmd::soundEmpty(uint32 &size) { + if (!_audioStream) + return 0; + + byte *soundBuf = (byte *)malloc(size); + memset(soundBuf, 0, size); + + return soundBuf; +} + +byte *Vmd::sound8bitDirect(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + byte *soundBuf = (byte *)malloc(size); + _stream->read(soundBuf, size); + unsignedToSigned(soundBuf, size); + + return soundBuf; +} + +byte *Vmd::sound16bitDPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init[2]; + + init[0] = _stream->readSint16LE(); + size -= 2; + + if (_soundStereo > 0) { + init[1] = _stream->readSint16LE(); + size -= 2; + } + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deDPCM(data, size, init); + + delete[] data; + + return sound; +} + +byte *Vmd::sound16bitADPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init = _stream->readSint16LE(); + size -= 2; + + int32 index = _stream->readByte(); + size--; + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deADPCM(data, size, init, index); + + delete[] data; + + return sound; +} + +void Vmd::emptySoundSlice(uint32 size) { + byte *sound = soundEmpty(size); + + if (sound) { + uint32 flags = 0; + flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; + + _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + } +} + +void Vmd::filledSoundSlice(uint32 size) { + byte *sound = 0; + if (_audioFormat == kAudioFormat8bitDirect) + sound = sound8bitDirect(size); + else if (_audioFormat == kAudioFormat16bitDPCM) + sound = sound16bitDPCM(size); + else if (_audioFormat == kAudioFormat16bitADPCM) + sound = sound16bitADPCM(size); + + if (sound) { + uint32 flags = 0; + flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; + flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; + + _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + } +} + +uint8 Vmd::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) { + max = MIN<int>(_soundSlicesCount - 1, 31); + + uint8 n = 0; + for (int i = 0; i < max; i++) { + + if (!(mask & 1)) { + n++; + *fillInfo++ = true; + } else + *fillInfo++ = false; + + mask >>= 1; + } + + return n; +} + +void Vmd::filledSoundSlices(uint32 size, uint32 mask) { + bool fillInfo[32]; + + uint8 max; + uint8 n = evaluateMask(mask, fillInfo, max); + + int32 extraSize; + + extraSize = size - n * _soundDataSize; + + if (_soundSlicesCount > 32) + extraSize -= (_soundSlicesCount - 32) * _soundDataSize; + + if (n > 0) + extraSize /= n; + + for (uint8 i = 0; i < max; i++) + if (fillInfo[i]) + filledSoundSlice(_soundDataSize + extraSize); + else + emptySoundSlice(_soundDataSize * _soundBytesPerSample); + + if (_soundSlicesCount > 32) + filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize); +} + +bool Vmd::getPartCoords(int16 frame, PartType type, + int16 &x, int16 &y, int16 &width, int16 &height) { + + if (frame >= _framesCount) + return false; + + Frame &f = _frames[frame]; + + // Look for a part matching the requested type, stopping at a separator + Part *part = 0; + for (int i = 0; i < _partsPerFrame; i++) { + Part &p = f.parts[i]; + + if ((p.type == kPartTypeSeparator) || (p.type == type)) { + part = &p; + break; + } + } + + if (!part) + return false; + + x = part->left; + y = part->top; + width = part->right - part->left + 1; + height = part->bottom - part->top + 1; + + return true; +} + +bool Vmd::getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height) { + + return getPartCoords(frame, kPartTypeVideo, x, y, width, height); +} + +bool Vmd::hasExtraData() const { + return !_extraData.empty(); +} + +bool Vmd::hasExtraData(const char *fileName) const { + for (uint i = 0; i < _extraData.size(); i++) + if (!scumm_stricmp(_extraData[i].name, fileName)) + return true; + + return false; +} + +Common::MemoryReadStream *Vmd::getExtraData(const char *fileName) { + uint i = 0; + + for (i = 0; i < _extraData.size(); i++) + if (!scumm_stricmp(_extraData[i].name, fileName)) + break; + + if (i >= _extraData.size()) + return 0; + + if ((_extraData[i].size - 20) != _extraData[i].realSize) { + warning("Vmd::getExtraData(): Sizes for \"%s\" differ! (%d, %d)", + fileName, (_extraData[i].size - 20), _extraData[i].realSize); + return 0; + } + + if (!_stream->seek(_extraData[i].offset)) { + warning("Vmd::getExtraData(): Can't seek to offset %d to (file \"%s\")", + _extraData[i].offset, fileName); + return 0; + } + + byte *data = (byte *) malloc(_extraData[i].realSize); + if (_stream->read(data, _extraData[i].realSize) != _extraData[i].realSize) { + free(data); + warning("Vmd::getExtraData(): Couldn't read %d bytes (file \"%s\")", + _extraData[i].realSize, fileName); + } + + Common::MemoryReadStream *stream = + new Common::MemoryReadStream(data, _extraData[i].realSize, DisposeAfterUse::YES); + + return stream; +} + +} // End of namespace Graphics + +#endif // GRAPHICS_VIDEO_COKTELVIDEO_H diff --git a/graphics/video/coktelvideo/coktelvideo.h b/graphics/video/coktelvideo/coktelvideo.h new file mode 100644 index 0000000000..8fbd861e82 --- /dev/null +++ b/graphics/video/coktelvideo/coktelvideo.h @@ -0,0 +1,598 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +// Currently, only GOB and SCI32 games play IMDs and VMDs, so skip compiling if GOB and SCI32 is disabled. +#if !(defined(ENABLE_GOB) || defined(ENABLE_SCI32) || defined(DYNAMIC_MODULES)) + +// Do not compile the CoktelVideo code + +#else + +#ifndef GRAPHICS_VIDEO_COKTELVIDEO_H +#define GRAPHICS_VIDEO_COKTELVIDEO_H + +#include "common/scummsys.h" +#include "common/stream.h" +#include "common/array.h" +#include "graphics/dither.h" +#include "sound/mixer.h" + +namespace Audio { + class QueuingAudioStream; +} + +namespace Graphics { + +#ifdef USE_INDEO3 +class Indeo3; +#endif + +/** Common interface for handling Coktel Vision videos and derivated formats. */ +class CoktelVideo { +public: + enum Features { + kFeaturesNone = 0, + /** Has an own palette. */ + kFeaturesPalette = 8, + /** Suggests a data size. */ + kFeaturesDataSize = 0x20, + /** Has sound. */ + kFeaturesSound = 0x40, + /** Has specific frame coordinates. */ + kFeaturesFrameCoords = 0x80, + /** Has general standard coordinates. */ + kFeaturesStdCoords = 0x100, + /** Has a frame positions table. */ + kFeaturesFramesPos = 0x200, + /** Has video. */ + kFeaturesVideo = 0x400, + /** Is a full color (non-paletted) video. */ + kFeaturesFullColor = 0x4000, + /** Supports automatic doubling. */ + kFeaturesSupportsDouble = 0x40000000 + }; + + enum StateFlags { + kStateNone = 0, + /** Changed the palette. */ + kStatePalette = 0x10, + /** Performed a jump to another frame. */ + kStateJump = 0x200, + /** Updated according to the specific frame coordinates. */ + kStateFrameCoords = 0x400, + /** Got no frame data. */ + kStateNoVideoData = 0x800, + /** Updated according to the general standard coordinates. */ + kStateStdCoords = 0x1000, + /** Had to explicitely seek to the frame. */ + kStateSeeked = 0x2000, + /** Reached a break-point. */ + kStateBreak = 0x8000, + /** Frame marks the beginning of speech. */ + kStateSpeech = 0x4000000 + }; + + struct State { + /** Left-most value of the updated rectangle. */ + int16 left; + /** Top-most value of the updated rectangle. */ + int16 top; + /** Right-most value of the updated rectangle. */ + int16 right; + /** Bottom-most value of the updated rectangle. */ + int16 bottom; + /** Set accordingly to what was done. */ + uint32 flags; + /** The id of the spoken words. */ + uint16 speechId; + + State() : left(0), top(0), right(0), bottom(0), flags(0), speechId(0) { } + }; + + virtual ~CoktelVideo() { } + + /** Returns the features the loaded video possesses. */ + virtual uint32 getFeatures() const = 0; + /** Returns the flags the loaded video possesses. */ + virtual uint16 getFlags() const = 0; + /** Returns the x coordinate of the video. */ + virtual int16 getX() const = 0; + /** Returns the y coordinate of the video. */ + virtual int16 getY() const = 0; + /** Returns the width of the video. */ + virtual int16 getWidth() const = 0; + /** Returns the height of the video. */ + virtual int16 getHeight() const = 0; + + /** Returns the number of frames the loaded video has. */ + virtual uint16 getFramesCount() const = 0; + /** Returns the current frame number. + * + * This is the current frame after the last nextFrame()-call, + * i.e. it's 0 after loading, 1 after the first nextFrame()-call, etc.. + */ + virtual uint16 getCurrentFrame() const = 0; + /** Returns the frame rate. */ + virtual int16 getFrameRate() const = 0; + /** Returns the number of frames the video lags behind the audio. */ + virtual uint32 getSyncLag() const = 0; + /** Returns the current frame's palette. */ + virtual const byte *getPalette() const = 0; + + /** Returns the frame's coordinates */ + virtual bool getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height) = 0; + + /** Returns whether any extra data files are embedded in this video. */ + virtual bool hasExtraData() const = 0; + /** Returns whether that extra data file exists */ + virtual bool hasExtraData(const char *fileName) const = 0; + /** Returns an extra data file */ + virtual Common::MemoryReadStream *getExtraData(const char *fileName) = 0; + + /** Load a video out of a stream. */ + virtual bool load(Common::SeekableReadStream &stream) = 0; + /** Unload the currently loaded video. */ + virtual void unload() = 0; + + /** Set the frame rate. */ + virtual void setFrameRate(int16 frameRate) = 0; + + /** Set the coordinations where to draw the video. */ + virtual void setXY(int16 x, int16 y) = 0; + /** Use a specific memory block as video memory. */ + virtual void setVideoMemory(byte *vidMem, uint16 width, uint16 height) = 0; + /** Use an own memory block as video memory. */ + virtual void setVideoMemory() = 0; + + /** Double the video's resolution. */ + virtual void setDoubleMode(bool doubleMode) = 0; + + /** Play sound (if the video has sound). */ + virtual void enableSound(Audio::Mixer &mixer) = 0; + /** Don't play sound or stop currently playing sound. */ + virtual void disableSound() = 0; + + /** Is sound currently playing? */ + virtual bool isSoundPlaying() const = 0; + + /** Seek to a specific frame. + * + * @param frame The frame to which to seek. + * @param whence The offset from whence the frame is given. + * @param restart Restart the video to reach an otherwise inaccessible frame? + */ + virtual void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false) = 0; + + /** Render the next frame. */ + virtual State nextFrame() = 0; + /** Get the time in ms until the next frame can be displayed. Already includes A/V sync measures. */ + virtual uint32 getFrameWaitTime() = 0; + /** Wait for the frame to end. */ + virtual void waitEndFrame() = 0; + + /** Copy the current frame. + * + * @param dest The memory to which to copy the current frame. + * @param left The x position within the frame. + * @param top The y position within the frame. + * @param width The width of the area to copy. + * @param height The height of the area to copy. + * @param x The x position to where to copy. + * @param y The y position to where to copy. + * @param pitch The buffer's width. + * @param transp Which color should be seen as transparent? + */ + virtual void copyCurrentFrame(byte *dest, + uint16 left, uint16 top, uint16 width, uint16 height, + uint16 x, uint16 y, uint16 pitch, int16 transp = -1) = 0; +}; + +/** Coktel Vision's first simple IMD format, used by Fascination. + */ +class PreImd : public CoktelVideo { +public: + PreImd(); + PreImd(int16 width, int16 height); + ~PreImd(); + + uint32 getFeatures() const; + uint16 getFlags() const; + + int16 getX() const; + int16 getY() const; + int16 getWidth() const; + int16 getHeight() const; + + uint16 getFramesCount() const; + uint16 getCurrentFrame() const; + + void setFrameRate(int16 frameRate); + int16 getFrameRate() const; + + uint32 getSyncLag() const; + + const byte *getPalette() const; + + bool getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height); + + bool hasExtraData() const; + bool hasExtraData(const char *fileName) const; + Common::MemoryReadStream *getExtraData(const char *fileName); + + bool load(Common::SeekableReadStream &stream); + void unload(); + + void setXY(int16 x, int16 y); + void setVideoMemory(byte *vidMem, uint16 width, uint16 height); + void setVideoMemory(); + + void setDoubleMode(bool doubleMode); + + void enableSound(Audio::Mixer &mixer); + void disableSound(); + + bool isSoundPlaying() const; + + void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false); + + State nextFrame(); + uint32 getFrameWaitTime(); + void waitEndFrame(); + + void copyCurrentFrame(byte *dest, + uint16 left, uint16 top, uint16 width, uint16 height, + uint16 x, uint16 y, uint16 pitch, int16 transp = -1); + +protected: + Common::SeekableReadStream *_stream; + + // Properties + uint32 _features; + uint16 _flags; + + int16 _forcedWidth; + int16 _forcedHeight; + + // Current coordinates + int16 _x; + int16 _y; + int16 _width; + int16 _height; + + uint16 _framesCount; + uint16 _curFrame; + int16 _frameRate; + uint32 _frameLength; + + byte _palette[768]; + + // Video memory + bool _hasOwnVidMem; + byte *_vidMem; + uint16 _vidMemWidth; + uint16 _vidMemHeight; + + byte *_vidBuffer; + uint32 _vidBufferSize; + + void clear(); + void zeroData(); + void deleteData(); + + void zeroVidMem(); + void deleteVidMem(); + + State processFrame(uint16 frame); + void renderFrame(); +}; + +/** Coktel Vision's IMD files. + */ +class Imd : public PreImd { +public: + Imd(); + ~Imd(); + + void setFrameRate(int16 frameRate); + int16 getFrameRate() const; + + uint32 getSyncLag() const; + + bool load(Common::SeekableReadStream &stream); + void unload(); + + void setXY(int16 x, int16 y); + + void enableSound(Audio::Mixer &mixer); + void disableSound(); + + bool isSoundPlaying() const; + + void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false); + + State nextFrame(); + uint32 getFrameWaitTime(); + + void copyCurrentFrame(byte *dest, + uint16 left, uint16 top, uint16 width, uint16 height, + uint16 x, uint16 y, uint16 pitch, int16 transp = -1); + +protected: + enum Command { + kCommandNextSound = 0xFF00, + kCommandStartSound = 0xFF01, + + kCommandBreak = 0xFFF0, + kCommandBreakSkip0 = 0xFFF1, + kCommandBreakSkip16 = 0xFFF2, + kCommandBreakSkip32 = 0xFFF3, + kCommandBreakMask = 0xFFF8, + + kCommandPalette = 0xFFF4, + kCommandVideoData = 0xFFFC, + + kCommandJump = 0xFFFD + }; + + struct Coord { + int16 left; + int16 top; + int16 right; + int16 bottom; + } PACKED_STRUCT; + + // Properties + uint16 _version; + + // Standard coordinates gives by the header + int16 _stdX; + int16 _stdY; + int16 _stdWidth; + int16 _stdHeight; + + uint32 *_framesPos; + uint32 _firstFramePos; + Coord *_frameCoords; + + // Buffer for raw frame data + byte *_frameData; + uint32 _frameDataSize; + uint32 _frameDataLen; + + // Sound properties + uint16 _soundFlags; + int16 _soundFreq; + int16 _soundSliceSize; + int16 _soundSlicesCount; + uint32 _soundSliceLength; + + // Current sound state + bool _hasSound; + bool _soundEnabled; + uint8 _soundStage; // (0: no sound, 1: loaded, 2: playing) + uint32 _skipFrames; + + Audio::QueuingAudioStream *_audioStream; + Audio::SoundHandle _audioHandle; + + uint32 _lastFrameTime; + + Audio::Mixer *_mixer; + + void clear(); + void zeroData(); + void deleteData(); + + void unsignedToSigned(byte *buffer, int length); + + bool loadCoordinates(); + bool loadFrameTableOffsets(uint32 &framesPosPos, uint32 &framesCoordsPos); + bool assessVideoProperties(); + bool assessAudioProperties(); + bool loadFrameTables(uint32 framesPosPos, uint32 framesCoordsPos); + + State processFrame(uint16 frame); + uint32 renderFrame(int16 left, int16 top, int16 right, int16 bottom); + void deLZ77(byte *dest, byte *src); + + void renderBlockWhole (byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch); + void renderBlockWhole4X (byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch); + void renderBlockWhole2Y (byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch); + void renderBlockSparse (byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch); + void renderBlockSparse2Y(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch); + + void calcFrameCoords(uint16 frame, State &state); + + void nextSoundSlice(bool hasNextCmd); + bool initialSoundSlice(bool hasNextCmd); + void emptySoundSlice(bool hasNextCmd); + + void videoData(uint32 size, State &state); +}; + +class Vmd : public Imd { +public: + Vmd(Graphics::PaletteLUT *palLUT = 0); + ~Vmd(); + + bool getFrameCoords(int16 frame, + int16 &x, int16 &y, int16 &width, int16 &height); + + bool hasExtraData() const; + bool hasExtraData(const char *fileName) const; + Common::MemoryReadStream *getExtraData(const char *fileName); + + bool load(Common::SeekableReadStream &stream); + void unload(); + + int16 getWidth() const; + + void setXY(int16 x, int16 y); + + void setDoubleMode(bool doubleMode); + + void seekFrame(int32 frame, int16 whence = SEEK_SET, bool restart = false); + + State nextFrame(); + +protected: + enum PartType { + kPartTypeSeparator = 0, + kPartTypeAudio = 1, + kPartTypeVideo = 2, + kPartTypeExtraData = 3, + kPartType4 = 4, + kPartTypeSpeech = 5 + }; + + enum AudioFormat { + kAudioFormat8bitDirect = 0, + kAudioFormat16bitDPCM = 1, + kAudioFormat16bitADPCM = 2 + }; + + struct ExtraData { + char name[16]; + uint32 offset; + uint32 size; + uint32 realSize; + + ExtraData(); + } PACKED_STRUCT; + + struct Part { + PartType type; + byte field_1; + byte field_E; + uint32 size; + int16 left; + int16 top; + int16 right; + int16 bottom; + uint16 id; + byte flags; + + Part(); + } PACKED_STRUCT; + + struct Frame { + uint32 offset; + Part *parts; + + Frame(); + ~Frame(); + } PACKED_STRUCT; + + // Tables for the audio decompressors + static const uint16 _tableDPCM[128]; + static const int32 _tableADPCM[]; + static const int32 _tableADPCMStep[]; + + bool _hasVideo; + uint32 _videoCodec; + + uint32 _frameInfoOffset; + uint16 _partsPerFrame; + Frame *_frames; + + Common::Array<ExtraData> _extraData; + + // Sound properties + byte _soundBytesPerSample; + byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) + uint32 _soundHeaderSize; + uint32 _soundDataSize; + AudioFormat _audioFormat; + + // Video properties + bool _externalCodec; + byte _blitMode; + byte _bytesPerPixel; + byte _preScaleX; + byte _postScaleX; + byte _scaleExternalX; + byte *_vidMemBuffer; + + bool _doubleMode; + + Graphics::PaletteLUT *_palLUT; + +#ifdef USE_INDEO3 + Indeo3 *_codecIndeo3; +#endif + + void clear(); + void zeroData(); + void deleteData(); + + bool getPartCoords(int16 frame, PartType type, + int16 &x, int16 &y, int16 &width, int16 &height); + + bool assessVideoProperties(); + bool assessAudioProperties(); + void readFrameTable(int &numExtraData); + void readExtraData(); + + State processFrame(uint16 frame); + uint32 renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom); + + void renderBlockRLE(byte *dest, const byte *src, int16 width, int16 height, + int16 destPitch, int16 srcPitch); + + void deRLE(byte *&destPtr, const byte *&srcPtr, int16 destLen, int16 srcLen); + + inline int32 preScaleX(int32 x) const; + inline int32 postScaleX(int32 x) const; + + void blit(byte *dest, byte *src, int16 width, int16 height); + void blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); + void blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); + + byte *deDPCM(const byte *data, uint32 &size, int32 init[2]); + byte *deADPCM(const byte *data, uint32 &size, int32 init, int32 v28); + + byte *soundEmpty(uint32 &size); + byte *sound8bitDirect(uint32 &size); + byte *sound16bitDPCM(uint32 &size); + byte *sound16bitADPCM(uint32 &size); + + uint8 evaluateMask(uint32 mask, bool *fillInfo, uint8 &max); + void emptySoundSlice(uint32 size); + void filledSoundSlice(uint32 size); + void filledSoundSlices(uint32 size, uint32 mask); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_VIDEO_COKTELVIDEO_H + +#endif // Engine and dynamic plugins guard diff --git a/graphics/video/coktelvideo/indeo3.cpp b/graphics/video/coktelvideo/indeo3.cpp new file mode 100644 index 0000000000..983705ab9c --- /dev/null +++ b/graphics/video/coktelvideo/indeo3.cpp @@ -0,0 +1,3485 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/scummsys.h" + +#ifdef USE_INDEO3 + +/* Intel Indeo 3 decompressor, derived from ffmpeg. + * + * Original copyright note: * Intel Indeo 3 (IV31, IV32, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "common/system.h" +#include "common/endian.h" +#include "common/frac.h" +#include "common/file.h" + +#include "graphics/dither.h" +#include "graphics/video/coktelvideo/indeo3.h" + +namespace Graphics { + +Indeo3::Indeo3(int16 width, int16 height, Graphics::PaletteLUT *palLUT) { + assert((width > 0) && (height > 0)); + + _width = width; + _height = height; + _palLUT = palLUT; + + _ditherSL = 0; + setDither(kDitherSierraLight); + + buildModPred(); + allocFrames(); +} + +Indeo3::~Indeo3() { + delete[] _iv_frame[0].the_buf; + delete[] _ModPred; + delete[] _corrector_type; + delete _ditherSL; +} + +bool Indeo3::isIndeo3(byte *data, uint32 dataLen) { + // No data, no Indeo 3 + if (!data) + return false; + + // Less than 16 bytes? This can't be right + if (dataLen < 16) + return false; + + // Unknown, but according to the docs, always 0 + if (READ_LE_UINT32(data + 4) != 0) + return false; + + uint32 id; + id = READ_LE_UINT32(data ); // frame number + id ^= READ_LE_UINT32(data + 4); // unknown + id ^= READ_LE_UINT32(data + 8); // checksum + id ^= READ_LE_UINT32(data + 12); // frame data length + + // These 4 uint32s XOR'd need to spell "FRMH" + if (id != MKID_BE('FRMH')) + return false; + + return true; +} + +void Indeo3::setDither(DitherAlgorithm dither) { + delete _ditherSL; + _ditherSL = 0; + + _dither = dither; + + switch (dither) { + case kDitherSierraLight: + _ditherSL = new Graphics::SierraLight(_width, _palLUT); + break; + + default: + return; + } +} + +void Indeo3::buildModPred() { + _ModPred = new byte[8 * 128]; + + for (int i = 0; i < 128; i++) { + _ModPred[i+0*128] = (i > 126) ? 254 : 2*((i + 1) - ((i + 1) % 2)); + _ModPred[i+1*128] = (i == 7) ? 20 : ((i == 119 || i == 120) + ? 236 : 2*((i + 2) - ((i + 1) % 3))); + _ModPred[i+2*128] = (i > 125) ? 248 : 2*((i + 2) - ((i + 2) % 4)); + _ModPred[i+3*128] = 2*((i + 1) - ((i - 3) % 5)); + _ModPred[i+4*128] = (i == 8) ? 20 : 2*((i + 1) - ((i - 3) % 6)); + _ModPred[i+5*128] = 2*((i + 4) - ((i + 3) % 7)); + _ModPred[i+6*128] = (i > 123) ? 240 : 2*((i + 4) - ((i + 4) % 8)); + _ModPred[i+7*128] = 2*((i + 5) - ((i + 4) % 9)); + } + + _corrector_type = new uint16[24 * 256]; + + for (int i = 0; i < 24; i++) { + for (int j = 0; j < 256; j++) { + _corrector_type[i*256+j] = + (j < _corrector_type_0[i]) ? 1 : + ((j < 248 || (i == 16 && j == 248)) ? 0 : + _corrector_type_2[j - 248]); + } + } +} + +void Indeo3::allocFrames() { + int32 luma_width = (_width + 3) & (~3); + int32 luma_height = (_height + 3) & (~3); + + int32 chroma_width = ((luma_width >> 2) + 3) & (~3); + int32 chroma_height = ((luma_height>> 2) + 3) & (~3); + + int32 luma_pixels = luma_width * luma_height; + int32 chroma_pixels = chroma_width * chroma_height; + + uint32 bufsize = luma_pixels * 2 + luma_width * 3 + + (chroma_pixels + chroma_width) * 4; + + _iv_frame[0].y_w = _iv_frame[1].y_w = luma_width; + _iv_frame[0].y_h = _iv_frame[1].y_h = luma_height; + _iv_frame[0].uv_w = _iv_frame[1].uv_w = chroma_width; + _iv_frame[0].uv_h = _iv_frame[1].uv_h = chroma_height; + + _iv_frame[0].the_buf_size = bufsize; + _iv_frame[1].the_buf_size = 0; + + _iv_frame[0].the_buf = new byte[bufsize]; + memset(_iv_frame[0].the_buf, 0, bufsize); + _iv_frame[1].the_buf = 0; + + uint32 offs = 0; + + _iv_frame[0].Ybuf = _iv_frame[0].the_buf + luma_width; + offs += luma_pixels + luma_width * 2; + _iv_frame[1].Ybuf = _iv_frame[0].the_buf + offs; + offs += (luma_pixels + luma_width); + _iv_frame[0].Ubuf = _iv_frame[0].the_buf + offs; + offs += (chroma_pixels + chroma_width); + _iv_frame[1].Ubuf = _iv_frame[0].the_buf + offs; + offs += (chroma_pixels + chroma_width); + _iv_frame[0].Vbuf = _iv_frame[0].the_buf + offs; + offs += (chroma_pixels + chroma_width); + _iv_frame[1].Vbuf = _iv_frame[0].the_buf + offs; + + for (int i = 1; i <= luma_width; i++) + _iv_frame[0].Ybuf[-i] = _iv_frame[1].Ybuf[-i] = + _iv_frame[0].Ubuf[-i] = 0x80; + + for (int i = 1; i <= chroma_width; i++) { + _iv_frame[1].Ubuf[-i] = 0x80; + _iv_frame[0].Vbuf[-i] = 0x80; + _iv_frame[1].Vbuf[-i] = 0x80; + _iv_frame[1].Vbuf[chroma_pixels+i-1] = 0x80; + } +} + +bool Indeo3::decompressFrame(byte *inData, uint32 dataLen, + byte *outData, uint16 width, uint16 height) { + + // Not Indeo 3? Fail + if (!isIndeo3(inData, dataLen)) + return false; + + assert(outData); + assert(_palLUT); + + uint32 frameDataLen = READ_LE_UINT32(inData + 12); + + // Less data than the frame should have? Fail + if (dataLen < (frameDataLen - 16)) + return false; + + Common::MemoryReadStream frame(inData, dataLen); + + frame.skip(16); // Header + frame.skip(2); // Unknown + + uint16 flags1 = frame.readUint16LE(); + uint32 flags3 = frame.readUint32LE(); + uint8 flags2 = frame.readByte(); + + // Finding the reference frame + if (flags1 & 0x200) { + _cur_frame = _iv_frame + 1; + _ref_frame = _iv_frame; + } else { + _cur_frame = _iv_frame; + _ref_frame = _iv_frame + 1; + } + + if (flags3 == 0x80) + return true; + + frame.skip(3); + + uint16 fHeight = frame.readUint16LE(); + uint16 fWidth = frame.readUint16LE(); + + uint32 chromaHeight = ((fHeight >> 2) + 3) & 0x7FFC; + uint32 chromaWidth = ((fWidth >> 2) + 3) & 0x7FFC; + + uint32 offs; + uint32 offsY = frame.readUint32LE() + 16; + uint32 offsU = frame.readUint32LE() + 16; + uint32 offsV = frame.readUint32LE() + 16; + + frame.skip(4); + + uint32 hPos = frame.pos(); + + byte *hdr_pos = inData + hPos; + byte *buf_pos; + + // Luminance Y + frame.seek(offsY); + buf_pos = inData + offsY + 4; + offs = frame.readUint32LE(); + decodeChunk(_cur_frame->Ybuf, _ref_frame->Ybuf, fWidth, fHeight, + buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(fWidth, 160)); + + // Chrominance U + frame.seek(offsU); + buf_pos = inData + offsU + 4; + offs = frame.readUint32LE(); + decodeChunk(_cur_frame->Vbuf, _ref_frame->Vbuf, chromaWidth, chromaHeight, + buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); + + // Chrominance V + frame.seek(offsV); + buf_pos = inData + offsV + 4; + offs = frame.readUint32LE(); + decodeChunk(_cur_frame->Ubuf, _ref_frame->Ubuf, chromaWidth, chromaHeight, + buf_pos + offs * 2, flags2, hdr_pos, buf_pos, MIN<int>(chromaWidth, 40)); + + BlitState blitState; + + blitState.widthY = _cur_frame->y_w; + blitState.widthUV = _cur_frame->uv_w; + blitState.uwidthUV = chromaWidth; + blitState.uwidthOut = fWidth; + blitState.heightY = _cur_frame->y_h; + blitState.heightUV = _cur_frame->uv_h; + blitState.uheightUV = chromaHeight; + blitState.uheightOut = fHeight; + blitState.scaleWYUV = blitState.widthY / blitState.widthUV; + blitState.scaleHYUV = blitState.heightY / blitState.heightUV; + blitState.scaleWYOut = blitState.widthY / blitState.uwidthOut; + blitState.scaleHYOut = blitState.heightY / blitState.uheightOut; + blitState.lineWidthOut = blitState.scaleWYOut * blitState.uwidthOut; + blitState.lineHeightOut = blitState.scaleHYOut * blitState.uheightOut; + blitState.bufY = _cur_frame->Ybuf; + blitState.bufU = _cur_frame->Ubuf; + blitState.bufV = _cur_frame->Vbuf; + blitState.bufOut = outData; + + blitFrame(blitState); + + return true; +} + +void Indeo3::blitFrame(BlitState &s) { + if (_ditherSL) + _ditherSL->newFrame(); + + for (s.curY = 0; s.curY < s.uheightOut; s.curY++) { + if (_dither == kDitherNone) + blitLine(s); + else + blitLineDither(s); + } +} + +void Indeo3::blitLine(BlitState &s) { + byte *lineU = s.bufU + (s.curY >> 2) * s.uwidthUV; + byte *lineV = s.bufV + (s.curY >> 2) * s.uwidthUV; + + for (s.curX = 0; s.curX < s.uwidthOut; s.curX++) { + byte dataY = *s.bufY++; + byte dataU = lineU[s.curX >> 2]; + byte dataV = lineV[s.curX >> 2]; + + for (int n = 0; n < s.scaleWYOut; n++) + *s.bufOut++ = _palLUT->findNearest(dataY, dataU, dataV); + } + + byte *lineDest = s.bufOut - s.lineWidthOut; + for (int n = 1; n < s.scaleHYOut; n++) { + memcpy(s.bufOut, lineDest, s.lineWidthOut); + s.bufOut += s.lineWidthOut; + } +} + +void Indeo3::blitLineDither(BlitState &s) { + byte *lineU = s.bufU + (s.curY >> 2) * s.uwidthUV; + byte *lineV = s.bufV + (s.curY >> 2) * s.uwidthUV; + + for (uint16 i = 0; i < s.scaleHYOut; i++) { + byte *bufY = s.bufY; + + for (s.curX = 0; s.curX < s.uwidthOut; s.curX++) { + byte dataY = *bufY++; + byte dataU = lineU[s.curX >> 2]; + byte dataV = lineV[s.curX >> 2]; + + for (int n = 0; n < s.scaleWYOut; n++) + *s.bufOut++ = _ditherSL->dither(dataY, dataU, dataV, s.curX * s.scaleWYOut + n); + + } + + _ditherSL->nextLine(); + } + + s.bufY += s.uwidthOut; +} + +typedef struct { + int32 xpos; + int32 ypos; + int32 width; + int32 height; + int32 split_flag; + int32 split_direction; + int32 usl7; +} ustr_t; + +/* ---------------------------------------------------------------------- */ + +#define LV1_CHECK(buf1,rle_v3,lv1,lp2) \ + if ((lv1 & 0x80) != 0) { \ + if (rle_v3 != 0) \ + rle_v3 = 0; \ + else { \ + rle_v3 = 1; \ + buf1 -= 2; \ + } \ + } \ + lp2 = 4; + + +#define RLE_V3_CHECK(buf1,rle_v1,rle_v2,rle_v3) \ + if (rle_v3 == 0) { \ + rle_v2 = *buf1; \ + rle_v1 = 1; \ + if (rle_v2 > 32) { \ + rle_v2 -= 32; \ + rle_v1 = 0; \ + } \ + rle_v3 = 1; \ + } \ + buf1--; + + +#define LP2_CHECK(buf1,rle_v3,lp2) \ + if (lp2 == 0 && rle_v3 != 0) \ + rle_v3 = 0; \ + else { \ + buf1--; \ + rle_v3 = 1; \ + } + + +#define RLE_V2_CHECK(buf1,rle_v2, rle_v3,lp2) \ + rle_v2--; \ + if (rle_v2 == 0) { \ + rle_v3 = 0; \ + buf1 += 2; \ + } \ + lp2 = 4; + +void Indeo3::decodeChunk(byte *cur, byte *ref, int width, int height, + const byte *buf1, uint32 fflags2, const byte *hdr, + const byte *buf2, int min_width_160) { + + byte bit_buf; + uint32 bit_pos, lv, lv1, lv2; + int32 *width_tbl, width_tbl_arr[10]; + const int8 *ref_vectors; + byte *cur_frm_pos, *ref_frm_pos, *cp, *cp2; + uint32 *cur_lp, *ref_lp; + const uint32 *correction_lp[2], *correctionloworder_lp[2], *correctionhighorder_lp[2]; + uint16 *correction_type_sp[2]; + ustr_t strip_tbl[20], *strip; + int i, j, k, lp1, lp2, flag1, cmd; + int blks_width, blks_height, region_160_width; + int rle_v1, rle_v2, rle_v3; + uint16 res; + + bit_buf = 0; + ref_vectors = NULL; + + width_tbl = width_tbl_arr + 1; + i = (width < 0 ? width + 3 : width)/4; + for (j = -1; j < 8; j++) + width_tbl[j] = i * j; + + strip = strip_tbl; + + for (region_160_width = 0; region_160_width < (width - min_width_160); region_160_width += min_width_160) + ; + + strip->ypos = strip->xpos = 0; + for (strip->width = min_width_160; width > strip->width; strip->width *= 2) + ; + strip->height = height; + strip->split_direction = 0; + strip->split_flag = 0; + strip->usl7 = 0; + + bit_pos = 0; + + rle_v1 = rle_v2 = rle_v3 = 0; + + while (strip >= strip_tbl) { + if (bit_pos <= 0) { + bit_pos = 8; + bit_buf = *buf1++; + } + + bit_pos -= 2; + cmd = (bit_buf >> bit_pos) & 0x03; + + if (cmd == 0) { + strip++; + memcpy(strip, strip-1, sizeof(ustr_t)); + strip->split_flag = 1; + strip->split_direction = 0; + strip->height = (strip->height > 8 ? ((strip->height+8)>>4)<<3 : 4); + continue; + } else if (cmd == 1) { + strip++; + memcpy(strip, strip-1, sizeof(ustr_t)); + strip->split_flag = 1; + strip->split_direction = 1; + strip->width = (strip->width > 8 ? ((strip->width+8)>>4)<<3 : 4); + continue; + } else if (cmd == 2) { + if (strip->usl7 == 0) { + strip->usl7 = 1; + ref_vectors = NULL; + continue; + } + } else if (cmd == 3) { + if (strip->usl7 == 0) { + strip->usl7 = 1; + ref_vectors = (const signed char*)buf2 + (*buf1 * 2); + buf1++; + continue; + } + } + + cur_frm_pos = cur + width * strip->ypos + strip->xpos; + + if ((blks_width = strip->width) < 0) + blks_width += 3; + blks_width >>= 2; + blks_height = strip->height; + + if (ref_vectors != NULL) { + ref_frm_pos = ref + (ref_vectors[0] + strip->ypos) * width + + ref_vectors[1] + strip->xpos; + } else + ref_frm_pos = cur_frm_pos - width_tbl[4]; + + if (cmd == 2) { + if (bit_pos <= 0) { + bit_pos = 8; + bit_buf = *buf1++; + } + + bit_pos -= 2; + cmd = (bit_buf >> bit_pos) & 0x03; + + if (cmd == 0 || ref_vectors != NULL) { + for (lp1 = 0; lp1 < blks_width; lp1++) { + for (i = 0, j = 0; i < blks_height; i++, j += width_tbl[1]) + ((uint32 *)cur_frm_pos)[j] = ((uint32 *)ref_frm_pos)[j]; + cur_frm_pos += 4; + ref_frm_pos += 4; + } + } else if (cmd != 1) + return; + } else { + k = *buf1 >> 4; + j = *buf1 & 0x0f; + buf1++; + lv = j + fflags2; + + if ((lv - 8) <= 7 && (k == 0 || k == 3 || k == 10)) { + cp2 = _ModPred + ((lv - 8) << 7); + cp = ref_frm_pos; + for (i = 0; i < blks_width << 2; i++) { + int v = *cp >> 1; + *(cp++) = cp2[v]; + } + } + + if (k == 1 || k == 4) { + lv = (hdr[j] & 0xf) + fflags2; + correction_type_sp[0] = _corrector_type + (lv << 8); + correction_lp[0] = correction + (lv << 8); + lv = (hdr[j] >> 4) + fflags2; + correction_lp[1] = correction + (lv << 8); + correction_type_sp[1] = _corrector_type + (lv << 8); + } else { + correctionloworder_lp[0] = correctionloworder_lp[1] = correctionloworder + (lv << 8); + correctionhighorder_lp[0] = correctionhighorder_lp[1] = correctionhighorder + (lv << 8); + correction_type_sp[0] = correction_type_sp[1] = _corrector_type + (lv << 8); + correction_lp[0] = correction_lp[1] = correction + (lv << 8); + } + + switch (k) { + case 1: + case 0: /********** CASE 0 **********/ + for ( ; blks_height > 0; blks_height -= 4) { + for (lp1 = 0; lp1 < blks_width; lp1++) { + for (lp2 = 0; lp2 < 4; ) { + k = *buf1++; + cur_lp = ((uint32 *)cur_frm_pos) + width_tbl[lp2]; + ref_lp = ((uint32 *)ref_frm_pos) + width_tbl[lp2]; + + switch (correction_type_sp[0][k]) { + case 0: + *cur_lp = FROM_LE_32(((FROM_LE_32(*ref_lp) >> 1) + correction_lp[lp2 & 0x01][k]) << 1); + lp2++; + break; + case 1: + res = ((FROM_LE_16(((uint16 *)(ref_lp))[0]) >> 1) + correction_lp[lp2 & 0x01][*buf1]) << 1; + ((uint16 *)cur_lp)[0] = FROM_LE_16(res); + res = ((FROM_LE_16(((uint16 *)(ref_lp))[1]) >> 1) + correction_lp[lp2 & 0x01][k]) << 1; + ((uint16 *)cur_lp)[1] = FROM_LE_16(res); + buf1++; + lp2++; + break; + case 2: + if (lp2 == 0) { + for (i = 0, j = 0; i < 2; i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + lp2 += 2; + } + break; + case 3: + if (lp2 < 2) { + for (i = 0, j = 0; i < (3 - lp2); i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + lp2 = 3; + } + break; + case 8: + if (lp2 == 0) { + RLE_V3_CHECK(buf1,rle_v1,rle_v2,rle_v3) + + if (rle_v1 == 1 || ref_vectors != NULL) { + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + } + + RLE_V2_CHECK(buf1,rle_v2, rle_v3,lp2) + break; + } else { + rle_v1 = 1; + rle_v2 = *buf1 - 1; + } + case 5: + LP2_CHECK(buf1,rle_v3,lp2) + case 4: + for (i = 0, j = 0; i < (4 - lp2); i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + lp2 = 4; + break; + + case 7: + if (rle_v3 != 0) + rle_v3 = 0; + else { + buf1--; + rle_v3 = 1; + } + case 6: + if (ref_vectors != NULL) { + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + } + lp2 = 4; + break; + + case 9: + lv1 = *buf1++; + lv = (lv1 & 0x7F) << 1; + lv += (lv << 8); + lv += (lv << 16); + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = lv; + + LV1_CHECK(buf1,rle_v3,lv1,lp2) + break; + default: + return; + } + } + + cur_frm_pos += 4; + ref_frm_pos += 4; + } + + cur_frm_pos += ((width - blks_width) * 4); + ref_frm_pos += ((width - blks_width) * 4); + } + break; + + case 4: + case 3: /********** CASE 3 **********/ + if (ref_vectors != NULL) + return; + flag1 = 1; + + for ( ; blks_height > 0; blks_height -= 8) { + for (lp1 = 0; lp1 < blks_width; lp1++) { + for (lp2 = 0; lp2 < 4; ) { + k = *buf1++; + + cur_lp = ((uint32 *)cur_frm_pos) + width_tbl[lp2 * 2]; + ref_lp = ((uint32 *)cur_frm_pos) + width_tbl[(lp2 * 2) - 1]; + + switch (correction_type_sp[lp2 & 0x01][k]) { + case 0: + cur_lp[width_tbl[1]] = FROM_LE_32(((FROM_LE_32(*ref_lp) >> 1) + correction_lp[lp2 & 0x01][k]) << 1); + if (lp2 > 0 || flag1 == 0 || strip->ypos != 0) + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + else + cur_lp[0] = FROM_LE_32(((FROM_LE_32(*ref_lp) >> 1) + correction_lp[lp2 & 0x01][k]) << 1); + lp2++; + break; + + case 1: + res = ((FROM_LE_16(((uint16 *)ref_lp)[0]) >> 1) + correction_lp[lp2 & 0x01][*buf1]) << 1; + ((uint16 *)cur_lp)[width_tbl[2]] = FROM_LE_16(res); + res = ((FROM_LE_16(((uint16 *)ref_lp)[1]) >> 1) + correction_lp[lp2 & 0x01][k]) << 1; + ((uint16 *)cur_lp)[width_tbl[2]+1] = FROM_LE_16(res); + + if (lp2 > 0 || flag1 == 0 || strip->ypos != 0) + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + else + cur_lp[0] = cur_lp[width_tbl[1]]; + buf1++; + lp2++; + break; + + case 2: + if (lp2 == 0) { + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = *ref_lp; + lp2 += 2; + } + break; + + case 3: + if (lp2 < 2) { + for (i = 0, j = 0; i < 6 - (lp2 * 2); i++, j += width_tbl[1]) + cur_lp[j] = *ref_lp; + lp2 = 3; + } + break; + + case 6: + lp2 = 4; + break; + + case 7: + if (rle_v3 != 0) + rle_v3 = 0; + else { + buf1--; + rle_v3 = 1; + } + lp2 = 4; + break; + + case 8: + if (lp2 == 0) { + RLE_V3_CHECK(buf1,rle_v1,rle_v2,rle_v3) + + if (rle_v1 == 1) { + for (i = 0, j = 0; i < 8; i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + } + + RLE_V2_CHECK(buf1,rle_v2, rle_v3,lp2) + break; + } else { + rle_v2 = (*buf1) - 1; + rle_v1 = 1; + } + case 5: + LP2_CHECK(buf1,rle_v3,lp2) + case 4: + for (i = 0, j = 0; i < 8 - (lp2 * 2); i++, j += width_tbl[1]) + cur_lp[j] = *ref_lp; + lp2 = 4; + break; + + case 9: + warning("Indeo3::decodeChunk: Untested (1)"); + lv1 = *buf1++; + lv = (lv1 & 0x7F) << 1; + lv += (lv << 8); + lv += (lv << 16); + + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = lv; + + LV1_CHECK(buf1,rle_v3,lv1,lp2) + break; + + default: + return; + } + } + + cur_frm_pos += 4; + } + + cur_frm_pos += (((width * 2) - blks_width) * 4); + flag1 = 0; + } + break; + + case 10: /********** CASE 10 **********/ + if (ref_vectors == NULL) { + flag1 = 1; + + for ( ; blks_height > 0; blks_height -= 8) { + for (lp1 = 0; lp1 < blks_width; lp1 += 2) { + for (lp2 = 0; lp2 < 4; ) { + k = *buf1++; + cur_lp = ((uint32 *)cur_frm_pos) + width_tbl[lp2 * 2]; + ref_lp = ((uint32 *)cur_frm_pos) + width_tbl[(lp2 * 2) - 1]; + lv1 = ref_lp[0]; + lv2 = ref_lp[1]; + if (lp2 == 0 && flag1 != 0) { +#if defined(SCUMM_BIG_ENDIAN) + lv1 = lv1 & 0xFF00FF00; + lv1 = (lv1 >> 8) | lv1; + lv2 = lv2 & 0xFF00FF00; + lv2 = (lv2 >> 8) | lv2; +#else + lv1 = lv1 & 0x00FF00FF; + lv1 = (lv1 << 8) | lv1; + lv2 = lv2 & 0x00FF00FF; + lv2 = (lv2 << 8) | lv2; +#endif + } + + switch (correction_type_sp[lp2 & 0x01][k]) { + case 0: + cur_lp[width_tbl[1]] = FROM_LE_32(((FROM_LE_32(lv1) >> 1) + correctionloworder_lp[lp2 & 0x01][k]) << 1); + cur_lp[width_tbl[1]+1] = FROM_LE_32(((FROM_LE_32(lv2) >> 1) + correctionhighorder_lp[lp2 & 0x01][k]) << 1); + if (lp2 > 0 || strip->ypos != 0 || flag1 == 0) { + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + cur_lp[1] = ((cur_lp[-width_tbl[1]+1] >> 1) + (cur_lp[width_tbl[1]+1] >> 1)) & 0xFEFEFEFE; + } else { + cur_lp[0] = cur_lp[width_tbl[1]]; + cur_lp[1] = cur_lp[width_tbl[1]+1]; + } + lp2++; + break; + + case 1: + cur_lp[width_tbl[1]] = FROM_LE_32(((FROM_LE_32(lv1) >> 1) + correctionloworder_lp[lp2 & 0x01][*buf1]) << 1); + cur_lp[width_tbl[1]+1] = FROM_LE_32(((FROM_LE_32(lv2) >> 1) + correctionloworder_lp[lp2 & 0x01][k]) << 1); + if (lp2 > 0 || strip->ypos != 0 || flag1 == 0) { + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + cur_lp[1] = ((cur_lp[-width_tbl[1]+1] >> 1) + (cur_lp[width_tbl[1]+1] >> 1)) & 0xFEFEFEFE; + } else { + cur_lp[0] = cur_lp[width_tbl[1]]; + cur_lp[1] = cur_lp[width_tbl[1]+1]; + } + buf1++; + lp2++; + break; + + case 2: + if (lp2 == 0) { + if (flag1 != 0) { + for (i = 0, j = width_tbl[1]; i < 3; i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + cur_lp[1] = ((cur_lp[-width_tbl[1]+1] >> 1) + (cur_lp[width_tbl[1]+1] >> 1)) & 0xFEFEFEFE; + } else { + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + } + lp2 += 2; + } + break; + + case 3: + if (lp2 < 2) { + if (lp2 == 0 && flag1 != 0) { + for (i = 0, j = width_tbl[1]; i < 5; i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + cur_lp[1] = ((cur_lp[-width_tbl[1]+1] >> 1) + (cur_lp[width_tbl[1]+1] >> 1)) & 0xFEFEFEFE; + } else { + for (i = 0, j = 0; i < 6 - (lp2 * 2); i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + } + lp2 = 3; + } + break; + + case 8: + if (lp2 == 0) { + RLE_V3_CHECK(buf1,rle_v1,rle_v2,rle_v3) + if (rle_v1 == 1) { + if (flag1 != 0) { + for (i = 0, j = width_tbl[1]; i < 7; i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + cur_lp[1] = ((cur_lp[-width_tbl[1]+1] >> 1) + (cur_lp[width_tbl[1]+1] >> 1)) & 0xFEFEFEFE; + } else { + for (i = 0, j = 0; i < 8; i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + } + } + RLE_V2_CHECK(buf1,rle_v2, rle_v3,lp2) + break; + } else { + rle_v1 = 1; + rle_v2 = (*buf1) - 1; + } + case 5: + LP2_CHECK(buf1,rle_v3,lp2) + case 4: + if (lp2 == 0 && flag1 != 0) { + for (i = 0, j = width_tbl[1]; i < 7; i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + cur_lp[0] = ((cur_lp[-width_tbl[1]] >> 1) + (cur_lp[width_tbl[1]] >> 1)) & 0xFEFEFEFE; + cur_lp[1] = ((cur_lp[-width_tbl[1]+1] >> 1) + (cur_lp[width_tbl[1]+1] >> 1)) & 0xFEFEFEFE; + } else { + for (i = 0, j = 0; i < 8 - (lp2 * 2); i++, j += width_tbl[1]) { + cur_lp[j] = lv1; + cur_lp[j+1] = lv2; + } + } + lp2 = 4; + break; + + case 6: + lp2 = 4; + break; + + case 7: + if (lp2 == 0) { + if (rle_v3 != 0) + rle_v3 = 0; + else { + buf1--; + rle_v3 = 1; + } + lp2 = 4; + } + break; + + case 9: + warning("Indeo3::decodeChunk: Untested (2)"); + lv1 = *buf1; + lv = (lv1 & 0x7F) << 1; + lv += (lv << 8); + lv += (lv << 16); + for (i = 0, j = 0; i < 8; i++, j += width_tbl[1]) + cur_lp[j] = lv; + LV1_CHECK(buf1,rle_v3,lv1,lp2) + break; + + default: + return; + } + } + + cur_frm_pos += 8; + } + + cur_frm_pos += (((width * 2) - blks_width) * 4); + flag1 = 0; + } + } else { + for ( ; blks_height > 0; blks_height -= 8) { + for (lp1 = 0; lp1 < blks_width; lp1 += 2) { + for (lp2 = 0; lp2 < 4; ) { + k = *buf1++; + cur_lp = ((uint32 *)cur_frm_pos) + width_tbl[lp2 * 2]; + ref_lp = ((uint32 *)ref_frm_pos) + width_tbl[lp2 * 2]; + + switch (correction_type_sp[lp2 & 0x01][k]) { + case 0: + lv1 = correctionloworder_lp[lp2 & 0x01][k]; + lv2 = correctionhighorder_lp[lp2 & 0x01][k]; + cur_lp[0] = FROM_LE_32(((FROM_LE_32(ref_lp[0]) >> 1) + lv1) << 1); + cur_lp[1] = FROM_LE_32(((FROM_LE_32(ref_lp[1]) >> 1) + lv2) << 1); + cur_lp[width_tbl[1]] = FROM_LE_32(((FROM_LE_32(ref_lp[width_tbl[1]]) >> 1) + lv1) << 1); + cur_lp[width_tbl[1]+1] = FROM_LE_32(((FROM_LE_32(ref_lp[width_tbl[1]+1]) >> 1) + lv2) << 1); + lp2++; + break; + + case 1: + lv1 = correctionloworder_lp[lp2 & 0x01][*buf1++]; + lv2 = correctionloworder_lp[lp2 & 0x01][k]; + cur_lp[0] = FROM_LE_32(((FROM_LE_32(ref_lp[0]) >> 1) + lv1) << 1); + cur_lp[1] = FROM_LE_32(((FROM_LE_32(ref_lp[1]) >> 1) + lv2) << 1); + cur_lp[width_tbl[1]] = FROM_LE_32(((FROM_LE_32(ref_lp[width_tbl[1]]) >> 1) + lv1) << 1); + cur_lp[width_tbl[1]+1] = FROM_LE_32(((FROM_LE_32(ref_lp[width_tbl[1]+1]) >> 1) + lv2) << 1); + lp2++; + break; + + case 2: + if (lp2 == 0) { + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) { + cur_lp[j] = ref_lp[j]; + cur_lp[j+1] = ref_lp[j+1]; + } + lp2 += 2; + } + break; + + case 3: + if (lp2 < 2) { + for (i = 0, j = 0; i < 6 - (lp2 * 2); i++, j += width_tbl[1]) { + cur_lp[j] = ref_lp[j]; + cur_lp[j+1] = ref_lp[j+1]; + } + lp2 = 3; + } + break; + + case 8: + if (lp2 == 0) { + RLE_V3_CHECK(buf1,rle_v1,rle_v2,rle_v3) + for (i = 0, j = 0; i < 8; i++, j += width_tbl[1]) { + ((uint32 *)cur_frm_pos)[j] = ((uint32 *)ref_frm_pos)[j]; + ((uint32 *)cur_frm_pos)[j+1] = ((uint32 *)ref_frm_pos)[j+1]; + } + RLE_V2_CHECK(buf1,rle_v2, rle_v3,lp2) + break; + } else { + rle_v1 = 1; + rle_v2 = (*buf1) - 1; + } + case 5: + case 7: + LP2_CHECK(buf1,rle_v3,lp2) + case 6: + case 4: + for (i = 0, j = 0; i < 8 - (lp2 * 2); i++, j += width_tbl[1]) { + cur_lp[j] = ref_lp[j]; + cur_lp[j+1] = ref_lp[j+1]; + } + lp2 = 4; + break; + + case 9: + warning("Indeo3::decodeChunk: Untested (3)"); + lv1 = *buf1; + lv = (lv1 & 0x7F) << 1; + lv += (lv << 8); + lv += (lv << 16); + for (i = 0, j = 0; i < 8; i++, j += width_tbl[1]) + ((uint32 *)cur_frm_pos)[j] = ((uint32 *)cur_frm_pos)[j+1] = lv; + LV1_CHECK(buf1,rle_v3,lv1,lp2) + break; + + default: + return; + } + } + + cur_frm_pos += 8; + ref_frm_pos += 8; + } + + cur_frm_pos += (((width * 2) - blks_width) * 4); + ref_frm_pos += (((width * 2) - blks_width) * 4); + } + } + break; + + case 11: /********** CASE 11 **********/ + if (ref_vectors == NULL) + return; + + for ( ; blks_height > 0; blks_height -= 8) { + for (lp1 = 0; lp1 < blks_width; lp1++) { + for (lp2 = 0; lp2 < 4; ) { + k = *buf1++; + cur_lp = ((uint32 *)cur_frm_pos) + width_tbl[lp2 * 2]; + ref_lp = ((uint32 *)ref_frm_pos) + width_tbl[lp2 * 2]; + + switch (correction_type_sp[lp2 & 0x01][k]) { + case 0: + cur_lp[0] = FROM_LE_32(((FROM_LE_32(*ref_lp) >> 1) + correction_lp[lp2 & 0x01][k]) << 1); + cur_lp[width_tbl[1]] = FROM_LE_32(((FROM_LE_32(ref_lp[width_tbl[1]]) >> 1) + correction_lp[lp2 & 0x01][k]) << 1); + lp2++; + break; + + case 1: + lv1 = (uint16)(correction_lp[lp2 & 0x01][*buf1++]); + lv2 = (uint16)(correction_lp[lp2 & 0x01][k]); + res = (uint16)(((FROM_LE_16(((uint16 *)ref_lp)[0]) >> 1) + lv1) << 1); + ((uint16 *)cur_lp)[0] = FROM_LE_16(res); + res = (uint16)(((FROM_LE_16(((uint16 *)ref_lp)[1]) >> 1) + lv2) << 1); + ((uint16 *)cur_lp)[1] = FROM_LE_16(res); + res = (uint16)(((FROM_LE_16(((uint16 *)ref_lp)[width_tbl[2]]) >> 1) + lv1) << 1); + ((uint16 *)cur_lp)[width_tbl[2]] = FROM_LE_16(res); + res = (uint16)(((FROM_LE_16(((uint16 *)ref_lp)[width_tbl[2]+1]) >> 1) + lv2) << 1); + ((uint16 *)cur_lp)[width_tbl[2]+1] = FROM_LE_16(res); + lp2++; + break; + + case 2: + if (lp2 == 0) { + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + lp2 += 2; + } + break; + + case 3: + if (lp2 < 2) { + for (i = 0, j = 0; i < 6 - (lp2 * 2); i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + lp2 = 3; + } + break; + + case 8: + if (lp2 == 0) { + RLE_V3_CHECK(buf1,rle_v1,rle_v2,rle_v3) + + for (i = 0, j = 0; i < 8; i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + + RLE_V2_CHECK(buf1,rle_v2, rle_v3,lp2) + break; + } else { + rle_v1 = 1; + rle_v2 = (*buf1) - 1; + } + case 5: + case 7: + LP2_CHECK(buf1,rle_v3,lp2) + case 4: + case 6: + for (i = 0, j = 0; i < 8 - (lp2 * 2); i++, j += width_tbl[1]) + cur_lp[j] = ref_lp[j]; + lp2 = 4; + break; + + case 9: + warning("Indeo3::decodeChunk: Untested (4)"); + lv1 = *buf1++; + lv = (lv1 & 0x7F) << 1; + lv += (lv << 8); + lv += (lv << 16); + for (i = 0, j = 0; i < 4; i++, j += width_tbl[1]) + cur_lp[j] = lv; + LV1_CHECK(buf1,rle_v3,lv1,lp2) + break; + + default: + return; + } + } + + cur_frm_pos += 4; + ref_frm_pos += 4; + } + + cur_frm_pos += (((width * 2) - blks_width) * 4); + ref_frm_pos += (((width * 2) - blks_width) * 4); + } + break; + + default: + // FIXME: I've seen case 13 happen in Urban + // Runner. Perhaps it uses a more recent form of + // Indeo 3? There appears to have been several. + warning("Indeo3::decodeChunk: Unknown case %d", k); + return; + } + } + + if (strip < strip_tbl) + return; + + for ( ; strip >= strip_tbl; strip--) { + if (strip->split_flag != 0) { + strip->split_flag = 0; + strip->usl7 = (strip-1)->usl7; + + if (strip->split_direction) { + strip->xpos += strip->width; + strip->width = (strip-1)->width - strip->width; + if (region_160_width <= strip->xpos && width < strip->width + strip->xpos) + strip->width = width - strip->xpos; + } else { + strip->ypos += strip->height; + strip->height = (strip-1)->height - strip->height; + } + break; + } + } + } +} + +// static data + +const int Indeo3::_corrector_type_0[24] = { + 195, 159, 133, 115, 101, 93, 87, 77, + 195, 159, 133, 115, 101, 93, 87, 77, + 128, 79, 79, 79, 79, 79, 79, 79 +}; + +const int Indeo3::_corrector_type_2[8] = { 9, 7, 6, 8, 5, 4, 3, 2 }; + +const uint32 Indeo3::correction[] = { + 0x00000000, 0x00000202, 0xfffffdfe, 0x000002ff, 0xfffffd01, 0xffffff03, 0x000000fd, 0x00000404, + 0xfffffbfc, 0x00000501, 0xfffffaff, 0x00000105, 0xfffffefb, 0x000003fc, 0xfffffc04, 0x000005fe, + 0xfffffa02, 0xfffffe06, 0x000001fa, 0x00000904, 0xfffff6fc, 0x00000409, 0xfffffbf7, 0x00000909, + 0xfffff6f7, 0x00000a01, 0xfffff5ff, 0x0000010a, 0xfffffef6, 0x000007fb, 0xfffff805, 0xfffffb08, + 0x000004f8, 0x00000f09, 0xfffff0f7, 0x0000090f, 0xfffff6f1, 0x00000bfd, 0xfffff403, 0xfffffd0c, + 0x000002f4, 0x00001004, 0xffffeffc, 0x00000410, 0xfffffbf0, 0x00001010, 0xffffeff0, 0x00001200, + 0xffffee00, 0x00000012, 0xffffffee, 0x00000bf4, 0xfffff40c, 0x00000ff7, 0xfffff009, 0xfffff710, + 0x000008f0, 0x00001b0b, 0xffffe4f5, 0x00000b1b, 0xfffff4e5, 0x00001c13, 0xffffe3ed, 0x0000131c, + 0xffffece4, 0x000015fa, 0xffffea06, 0xfffffa16, 0x000005ea, 0x00001d04, 0xffffe2fc, 0x0000041d, + 0xfffffbe3, 0x00001e1e, 0xffffe1e2, 0x000020fe, 0xffffdf02, 0xfffffe21, 0x000001df, 0x000016ee, + 0xffffe912, 0xffffee17, 0x000011e9, 0x00001df1, 0xffffe20f, 0xfffff11e, 0x00000ee2, 0x00002e16, + 0xffffd1ea, 0x0000162e, 0xffffe9d2, 0x00002f0d, 0xffffd0f3, 0x00000d2f, 0xfffff2d1, 0x00003123, + 0xffffcedd, 0x00002331, 0xffffdccf, 0x000028f5, 0xffffd70b, 0xfffff529, 0x00000ad7, 0x00003304, + 0xffffccfc, 0x00000433, 0xfffffbcd, 0x00003636, 0xffffc9ca, 0x000021de, 0xffffde22, 0x000029e3, + 0xffffd61d, 0xffffe32a, 0x00001cd6, 0x00003bfa, 0xffffc406, 0xfffffa3c, 0x000005c4, 0x00004c1b, + 0xffffb3e5, 0x00001b4c, 0xffffe4b4, 0x00004d2b, 0xffffb2d5, 0x00002b4d, 0xffffd4b3, 0x000036e8, + 0xffffc918, 0xffffe837, 0x000017c9, 0x00004f0e, 0xffffb0f2, 0x00000e4f, 0xfffff1b1, 0x0000533f, + 0xffffacc1, 0x00003f53, 0xffffc0ad, 0x000049ec, 0xffffb614, 0xffffec4a, 0x000013b6, 0x00005802, + 0xffffa7fe, 0x00000258, 0xfffffda8, 0x00005d5d, 0xffffa2a3, 0x00003ccc, 0xffffc334, 0xffffcc3d, + 0x000033c3, 0x00007834, 0xffff87cc, 0x00003478, 0xffffcb88, 0x00004ad3, 0xffffb52d, 0xffffd34b, + 0x00002cb5, 0x00007d4b, 0xffff82b5, 0x00004b7d, 0xffffb483, 0x00007a21, 0xffff85df, 0x0000217a, + 0xffffde86, 0x000066f3, 0xffff990d, 0xfffff367, 0x00000c99, 0x00005fd8, 0xffffa028, 0xffffd860, + 0x000027a0, 0x00007ede, 0xffff8122, 0xffffde7f, 0x00002181, 0x000058a7, 0xffffa759, 0x000068b2, + 0xffff974e, 0xffffb269, 0x00004d97, 0x00000c0c, 0xfffff3f4, 0x00001717, 0xffffe8e9, 0x00002a2a, + 0xffffd5d6, 0x00004949, 0xffffb6b7, 0x00000000, 0x02020000, 0xfdfe0000, 0x02ff0000, 0xfd010000, + 0xff030000, 0x00fd0000, 0x00000202, 0x02020202, 0xfdfe0202, 0x02ff0202, 0xfd010202, 0xff030202, + 0x00fd0202, 0xfffffdfe, 0x0201fdfe, 0xfdfdfdfe, 0x02fefdfe, 0xfd00fdfe, 0xff02fdfe, 0x00fcfdfe, + 0x000002ff, 0x020202ff, 0xfdfe02ff, 0x02ff02ff, 0xfd0102ff, 0xff0302ff, 0x00fd02ff, 0xfffffd01, + 0x0201fd01, 0xfdfdfd01, 0x02fefd01, 0xfd00fd01, 0xff02fd01, 0x00fcfd01, 0xffffff03, 0x0201ff03, + 0xfdfdff03, 0x02feff03, 0xfd00ff03, 0xff02ff03, 0x00fcff03, 0x000000fd, 0x020200fd, 0xfdfe00fd, + 0x02ff00fd, 0xfd0100fd, 0xff0300fd, 0x00fd00fd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000303, 0xfffffcfd, 0x000003ff, 0xfffffc01, 0xffffff04, 0x000000fc, 0x00000707, + 0xfffff8f9, 0x00000802, 0xfffff7fe, 0x00000208, 0xfffffdf8, 0x000008fe, 0xfffff702, 0xfffffe09, + 0x000001f7, 0x000005fa, 0xfffffa06, 0x00000d06, 0xfffff2fa, 0x0000060d, 0xfffff9f3, 0x00000d0d, + 0xfffff2f3, 0x00000e01, 0xfffff1ff, 0x0000010e, 0xfffffef2, 0x00000bf8, 0xfffff408, 0xfffff80c, + 0x000007f4, 0x0000170e, 0xffffe8f2, 0x00000e17, 0xfffff1e9, 0x000011fb, 0xffffee05, 0xfffffb12, + 0x000004ee, 0x00001806, 0xffffe7fa, 0x00000618, 0xfffff9e8, 0x00001818, 0xffffe7e8, 0x00001aff, + 0xffffe501, 0xffffff1b, 0x000000e5, 0x000010ef, 0xffffef11, 0x000016f3, 0xffffe90d, 0xfffff317, + 0x00000ce9, 0x00002810, 0xffffd7f0, 0x00001028, 0xffffefd8, 0x0000291c, 0xffffd6e4, 0x00001c29, + 0xffffe3d7, 0x000020f7, 0xffffdf09, 0xfffff721, 0x000008df, 0x00002b06, 0xffffd4fa, 0x0000062b, + 0xfffff9d5, 0x00002e2e, 0xffffd1d2, 0x000031fc, 0xffffce04, 0xfffffc32, 0x000003ce, 0x000021e5, + 0xffffde1b, 0xffffe522, 0x00001ade, 0x00002cea, 0xffffd316, 0xffffea2d, 0x000015d3, 0x00004522, + 0xffffbade, 0x00002245, 0xffffddbb, 0x00004613, 0xffffb9ed, 0x00001346, 0xffffecba, 0x00004935, + 0xffffb6cb, 0x00003549, 0xffffcab7, 0x00003def, 0xffffc211, 0xffffef3e, 0x000010c2, 0x00004d05, + 0xffffb2fb, 0x0000054d, 0xfffffab3, 0x00005252, 0xffffadae, 0x000032cd, 0xffffcd33, 0x00003fd5, + 0xffffc02b, 0xffffd540, 0x00002ac0, 0x000059f6, 0xffffa60a, 0xfffff65a, 0x000009a6, 0x00007229, + 0xffff8dd7, 0x00002972, 0xffffd68e, 0x00007440, 0xffff8bc0, 0x00004074, 0xffffbf8c, 0x000051db, + 0xffffae25, 0xffffdb52, 0x000024ae, 0x00007716, 0xffff88ea, 0x00001677, 0xffffe989, 0x00007c5f, + 0xffff83a1, 0x00005f7c, 0xffffa084, 0x00006ee2, 0xffff911e, 0xffffe26f, 0x00001d91, 0x00005bb2, + 0xffffa44e, 0xffffb25c, 0x00004da4, 0x000070bc, 0xffff8f44, 0xffffbc71, 0x0000438f, 0x00001212, + 0xffffedee, 0x00002222, 0xffffddde, 0x00003f3f, 0xffffc0c1, 0x00006d6d, 0xffff9293, 0x00000000, + 0x03030000, 0xfcfd0000, 0x03ff0000, 0xfc010000, 0xff040000, 0x00fc0000, 0x07070000, 0xf8f90000, + 0x00000303, 0x03030303, 0xfcfd0303, 0x03ff0303, 0xfc010303, 0xff040303, 0x00fc0303, 0x07070303, + 0xf8f90303, 0xfffffcfd, 0x0302fcfd, 0xfcfcfcfd, 0x03fefcfd, 0xfc00fcfd, 0xff03fcfd, 0x00fbfcfd, + 0x0706fcfd, 0xf8f8fcfd, 0x000003ff, 0x030303ff, 0xfcfd03ff, 0x03ff03ff, 0xfc0103ff, 0xff0403ff, + 0x00fc03ff, 0x070703ff, 0xf8f903ff, 0xfffffc01, 0x0302fc01, 0xfcfcfc01, 0x03fefc01, 0xfc00fc01, + 0xff03fc01, 0x00fbfc01, 0x0706fc01, 0xf8f8fc01, 0xffffff04, 0x0302ff04, 0xfcfcff04, 0x03feff04, + 0xfc00ff04, 0xff03ff04, 0x00fbff04, 0x0706ff04, 0xf8f8ff04, 0x000000fc, 0x030300fc, 0xfcfd00fc, + 0x03ff00fc, 0xfc0100fc, 0xff0400fc, 0x00fc00fc, 0x070700fc, 0xf8f900fc, 0x00000707, 0x03030707, + 0xfcfd0707, 0x03ff0707, 0xfc010707, 0xff040707, 0x00fc0707, 0x07070707, 0xf8f90707, 0xfffff8f9, + 0x0302f8f9, 0xfcfcf8f9, 0x03fef8f9, 0xfc00f8f9, 0xff03f8f9, 0x00fbf8f9, 0x0706f8f9, 0xf8f8f8f9, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000404, 0xfffffbfc, 0x000004ff, 0xfffffb01, 0xffffff05, 0x000000fb, 0x00000a03, + 0xfffff5fd, 0x0000030a, 0xfffffcf6, 0x00000909, 0xfffff6f7, 0x000006f9, 0xfffff907, 0x00000bfd, + 0xfffff403, 0xfffffd0c, 0x000002f4, 0x00001108, 0xffffeef8, 0x00000811, 0xfffff7ef, 0x00001111, + 0xffffeeef, 0x00001301, 0xffffecff, 0x00000113, 0xfffffeed, 0x00000ff5, 0xfffff00b, 0xfffff510, + 0x00000af0, 0x000016fa, 0xffffe906, 0xfffffa17, 0x000005e9, 0x00001f12, 0xffffe0ee, 0x0000121f, + 0xffffede1, 0x00002008, 0xffffdff8, 0x00000820, 0xfffff7e0, 0x00002121, 0xffffdedf, 0x000023ff, + 0xffffdc01, 0xffffff24, 0x000000dc, 0x000016e9, 0xffffe917, 0x00001eef, 0xffffe111, 0xffffef1f, + 0x000010e1, 0x00003615, 0xffffc9eb, 0x00001536, 0xffffeaca, 0x00003725, 0xffffc8db, 0x00002537, + 0xffffdac9, 0x00002bf4, 0xffffd40c, 0xfffff42c, 0x00000bd4, 0x00003908, 0xffffc6f8, 0x00000839, + 0xfffff7c7, 0x00003d3d, 0xffffc2c3, 0x000041fb, 0xffffbe05, 0xfffffb42, 0x000004be, 0x00002cdc, + 0xffffd324, 0xffffdc2d, 0x000023d3, 0x00003be3, 0xffffc41d, 0xffffe33c, 0x00001cc4, 0x00005c2d, + 0xffffa3d3, 0x00002d5c, 0xffffd2a4, 0x00005d19, 0xffffa2e7, 0x0000195d, 0xffffe6a3, 0x00006147, + 0xffff9eb9, 0x00004761, 0xffffb89f, 0x000052ea, 0xffffad16, 0xffffea53, 0x000015ad, 0x00006607, + 0xffff99f9, 0x00000766, 0xfffff89a, 0x00006d6d, 0xffff9293, 0x000043bc, 0xffffbc44, 0x000054c7, + 0xffffab39, 0xffffc755, 0x000038ab, 0x000077f3, 0xffff880d, 0xfffff378, 0x00000c88, 0x00006dcf, + 0xffff9231, 0xffffcf6e, 0x00003092, 0x00007a98, 0xffff8568, 0xffff987b, 0x00006785, 0x00001818, + 0xffffe7e8, 0x00002e2e, 0xffffd1d2, 0x00005454, 0xffffabac, 0x00000000, 0x04040000, 0xfbfc0000, + 0x04ff0000, 0xfb010000, 0xff050000, 0x00fb0000, 0x0a030000, 0xf5fd0000, 0x030a0000, 0x00000404, + 0x04040404, 0xfbfc0404, 0x04ff0404, 0xfb010404, 0xff050404, 0x00fb0404, 0x0a030404, 0xf5fd0404, + 0x030a0404, 0xfffffbfc, 0x0403fbfc, 0xfbfbfbfc, 0x04fefbfc, 0xfb00fbfc, 0xff04fbfc, 0x00fafbfc, + 0x0a02fbfc, 0xf5fcfbfc, 0x0309fbfc, 0x000004ff, 0x040404ff, 0xfbfc04ff, 0x04ff04ff, 0xfb0104ff, + 0xff0504ff, 0x00fb04ff, 0x0a0304ff, 0xf5fd04ff, 0x030a04ff, 0xfffffb01, 0x0403fb01, 0xfbfbfb01, + 0x04fefb01, 0xfb00fb01, 0xff04fb01, 0x00fafb01, 0x0a02fb01, 0xf5fcfb01, 0x0309fb01, 0xffffff05, + 0x0403ff05, 0xfbfbff05, 0x04feff05, 0xfb00ff05, 0xff04ff05, 0x00faff05, 0x0a02ff05, 0xf5fcff05, + 0x0309ff05, 0x000000fb, 0x040400fb, 0xfbfc00fb, 0x04ff00fb, 0xfb0100fb, 0xff0500fb, 0x00fb00fb, + 0x0a0300fb, 0xf5fd00fb, 0x030a00fb, 0x00000a03, 0x04040a03, 0xfbfc0a03, 0x04ff0a03, 0xfb010a03, + 0xff050a03, 0x00fb0a03, 0x0a030a03, 0xf5fd0a03, 0x030a0a03, 0xfffff5fd, 0x0403f5fd, 0xfbfbf5fd, + 0x04fef5fd, 0xfb00f5fd, 0xff04f5fd, 0x00faf5fd, 0x0a02f5fd, 0xf5fcf5fd, 0x0309f5fd, 0x0000030a, + 0x0404030a, 0xfbfc030a, 0x04ff030a, 0xfb01030a, 0xff05030a, 0x00fb030a, 0x0a03030a, 0xf5fd030a, + 0x030a030a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000505, 0xfffffafb, 0x000006fe, 0xfffff902, 0xfffffe07, 0x000001f9, 0x00000b0b, + 0xfffff4f5, 0x00000d03, 0xfffff2fd, 0x0000030d, 0xfffffcf3, 0x000008f7, 0xfffff709, 0x00000efc, + 0xfffff104, 0xfffffc0f, 0x000003f1, 0x0000160b, 0xffffe9f5, 0x00000b16, 0xfffff4ea, 0x00001515, + 0xffffeaeb, 0x00001802, 0xffffe7fe, 0x00000218, 0xfffffde8, 0x000013f2, 0xffffec0e, 0xfffff214, + 0x00000dec, 0x00002617, 0xffffd9e9, 0x00001726, 0xffffe8da, 0x00001cf8, 0xffffe308, 0xfffff81d, + 0x000007e3, 0x0000270b, 0xffffd8f5, 0x00000b27, 0xfffff4d9, 0x00002929, 0xffffd6d7, 0x00002cff, + 0xffffd301, 0xffffff2d, 0x000000d3, 0x00001ce3, 0xffffe31d, 0x000026ea, 0xffffd916, 0xffffea27, + 0x000015d9, 0x0000431b, 0xffffbce5, 0x00001b43, 0xffffe4bd, 0x0000452f, 0xffffbad1, 0x00002f45, + 0xffffd0bb, 0x000037f1, 0xffffc80f, 0xfffff138, 0x00000ec8, 0x0000470b, 0xffffb8f5, 0x00000b47, + 0xfffff4b9, 0x00004c4c, 0xffffb3b4, 0x000052fa, 0xffffad06, 0xfffffa53, 0x000005ad, 0x000038d3, + 0xffffc72d, 0xffffd339, 0x00002cc7, 0x00004adc, 0xffffb524, 0xffffdc4b, 0x000023b5, 0x00007338, + 0xffff8cc8, 0x00003873, 0xffffc78d, 0x0000751f, 0xffff8ae1, 0x00001f75, 0xffffe08b, 0x00007a58, + 0xffff85a8, 0x0000587a, 0xffffa786, 0x000067e4, 0xffff981c, 0xffffe468, 0x00001b98, 0x000054ab, + 0xffffab55, 0x000069b8, 0xffff9648, 0xffffb86a, 0x00004796, 0x00001e1e, 0xffffe1e2, 0x00003a3a, + 0xffffc5c6, 0x00006969, 0xffff9697, 0x00000000, 0x05050000, 0xfafb0000, 0x06fe0000, 0xf9020000, + 0xfe070000, 0x01f90000, 0x0b0b0000, 0xf4f50000, 0x0d030000, 0xf2fd0000, 0x00000505, 0x05050505, + 0xfafb0505, 0x06fe0505, 0xf9020505, 0xfe070505, 0x01f90505, 0x0b0b0505, 0xf4f50505, 0x0d030505, + 0xf2fd0505, 0xfffffafb, 0x0504fafb, 0xfafafafb, 0x06fdfafb, 0xf901fafb, 0xfe06fafb, 0x01f8fafb, + 0x0b0afafb, 0xf4f4fafb, 0x0d02fafb, 0xf2fcfafb, 0x000006fe, 0x050506fe, 0xfafb06fe, 0x06fe06fe, + 0xf90206fe, 0xfe0706fe, 0x01f906fe, 0x0b0b06fe, 0xf4f506fe, 0x0d0306fe, 0xf2fd06fe, 0xfffff902, + 0x0504f902, 0xfafaf902, 0x06fdf902, 0xf901f902, 0xfe06f902, 0x01f8f902, 0x0b0af902, 0xf4f4f902, + 0x0d02f902, 0xf2fcf902, 0xfffffe07, 0x0504fe07, 0xfafafe07, 0x06fdfe07, 0xf901fe07, 0xfe06fe07, + 0x01f8fe07, 0x0b0afe07, 0xf4f4fe07, 0x0d02fe07, 0xf2fcfe07, 0x000001f9, 0x050501f9, 0xfafb01f9, + 0x06fe01f9, 0xf90201f9, 0xfe0701f9, 0x01f901f9, 0x0b0b01f9, 0xf4f501f9, 0x0d0301f9, 0xf2fd01f9, + 0x00000b0b, 0x05050b0b, 0xfafb0b0b, 0x06fe0b0b, 0xf9020b0b, 0xfe070b0b, 0x01f90b0b, 0x0b0b0b0b, + 0xf4f50b0b, 0x0d030b0b, 0xf2fd0b0b, 0xfffff4f5, 0x0504f4f5, 0xfafaf4f5, 0x06fdf4f5, 0xf901f4f5, + 0xfe06f4f5, 0x01f8f4f5, 0x0b0af4f5, 0xf4f4f4f5, 0x0d02f4f5, 0xf2fcf4f5, 0x00000d03, 0x05050d03, + 0xfafb0d03, 0x06fe0d03, 0xf9020d03, 0xfe070d03, 0x01f90d03, 0x0b0b0d03, 0xf4f50d03, 0x0d030d03, + 0xf2fd0d03, 0xfffff2fd, 0x0504f2fd, 0xfafaf2fd, 0x06fdf2fd, 0xf901f2fd, 0xfe06f2fd, 0x01f8f2fd, + 0x0b0af2fd, 0xf4f4f2fd, 0x0d02f2fd, 0xf2fcf2fd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000606, 0xfffff9fa, 0x000007fe, 0xfffff802, 0xfffffe08, 0x000001f8, 0x00000d0d, + 0xfffff2f3, 0x00000f04, 0xfffff0fc, 0x0000040f, 0xfffffbf1, 0x00000af5, 0xfffff50b, 0x000011fb, + 0xffffee05, 0xfffffb12, 0x000004ee, 0x00001a0d, 0xffffe5f3, 0x00000d1a, 0xfffff2e6, 0x00001a1a, + 0xffffe5e6, 0x00001d02, 0xffffe2fe, 0x0000021d, 0xfffffde3, 0x000017f0, 0xffffe810, 0xfffff018, + 0x00000fe8, 0x00002e1c, 0xffffd1e4, 0x00001c2e, 0xffffe3d2, 0x000022f7, 0xffffdd09, 0xfffff723, + 0x000008dd, 0x00002f0d, 0xffffd0f3, 0x00000d2f, 0xfffff2d1, 0x00003131, 0xffffcecf, 0x000035ff, + 0xffffca01, 0xffffff36, 0x000000ca, 0x000022dd, 0xffffdd23, 0x00002ee6, 0xffffd11a, 0xffffe62f, + 0x000019d1, 0x00005120, 0xffffaee0, 0x00002051, 0xffffdfaf, 0x00005338, 0xffffacc8, 0x00003853, + 0xffffc7ad, 0x000042ee, 0xffffbd12, 0xffffee43, 0x000011bd, 0x0000560d, 0xffffa9f3, 0x00000d56, + 0xfffff2aa, 0x00005b5b, 0xffffa4a5, 0x000062f9, 0xffff9d07, 0xfffff963, 0x0000069d, 0x000043ca, + 0xffffbc36, 0xffffca44, 0x000035bc, 0x000059d4, 0xffffa62c, 0xffffd45a, 0x00002ba6, 0x00007bdf, + 0xffff8421, 0xffffdf7c, 0x00002084, 0x00006699, 0xffff9967, 0x00007eaa, 0xffff8156, 0xffffaa7f, + 0x00005581, 0x00002525, 0xffffdadb, 0x00004545, 0xffffbabb, 0x00000000, 0x06060000, 0xf9fa0000, + 0x07fe0000, 0xf8020000, 0xfe080000, 0x01f80000, 0x0d0d0000, 0xf2f30000, 0x0f040000, 0xf0fc0000, + 0x040f0000, 0x00000606, 0x06060606, 0xf9fa0606, 0x07fe0606, 0xf8020606, 0xfe080606, 0x01f80606, + 0x0d0d0606, 0xf2f30606, 0x0f040606, 0xf0fc0606, 0x040f0606, 0xfffff9fa, 0x0605f9fa, 0xf9f9f9fa, + 0x07fdf9fa, 0xf801f9fa, 0xfe07f9fa, 0x01f7f9fa, 0x0d0cf9fa, 0xf2f2f9fa, 0x0f03f9fa, 0xf0fbf9fa, + 0x040ef9fa, 0x000007fe, 0x060607fe, 0xf9fa07fe, 0x07fe07fe, 0xf80207fe, 0xfe0807fe, 0x01f807fe, + 0x0d0d07fe, 0xf2f307fe, 0x0f0407fe, 0xf0fc07fe, 0x040f07fe, 0xfffff802, 0x0605f802, 0xf9f9f802, + 0x07fdf802, 0xf801f802, 0xfe07f802, 0x01f7f802, 0x0d0cf802, 0xf2f2f802, 0x0f03f802, 0xf0fbf802, + 0x040ef802, 0xfffffe08, 0x0605fe08, 0xf9f9fe08, 0x07fdfe08, 0xf801fe08, 0xfe07fe08, 0x01f7fe08, + 0x0d0cfe08, 0xf2f2fe08, 0x0f03fe08, 0xf0fbfe08, 0x040efe08, 0x000001f8, 0x060601f8, 0xf9fa01f8, + 0x07fe01f8, 0xf80201f8, 0xfe0801f8, 0x01f801f8, 0x0d0d01f8, 0xf2f301f8, 0x0f0401f8, 0xf0fc01f8, + 0x040f01f8, 0x00000d0d, 0x06060d0d, 0xf9fa0d0d, 0x07fe0d0d, 0xf8020d0d, 0xfe080d0d, 0x01f80d0d, + 0x0d0d0d0d, 0xf2f30d0d, 0x0f040d0d, 0xf0fc0d0d, 0x040f0d0d, 0xfffff2f3, 0x0605f2f3, 0xf9f9f2f3, + 0x07fdf2f3, 0xf801f2f3, 0xfe07f2f3, 0x01f7f2f3, 0x0d0cf2f3, 0xf2f2f2f3, 0x0f03f2f3, 0xf0fbf2f3, + 0x040ef2f3, 0x00000f04, 0x06060f04, 0xf9fa0f04, 0x07fe0f04, 0xf8020f04, 0xfe080f04, 0x01f80f04, + 0x0d0d0f04, 0xf2f30f04, 0x0f040f04, 0xf0fc0f04, 0x040f0f04, 0xfffff0fc, 0x0605f0fc, 0xf9f9f0fc, + 0x07fdf0fc, 0xf801f0fc, 0xfe07f0fc, 0x01f7f0fc, 0x0d0cf0fc, 0xf2f2f0fc, 0x0f03f0fc, 0xf0fbf0fc, + 0x040ef0fc, 0x0000040f, 0x0606040f, 0xf9fa040f, 0x07fe040f, 0xf802040f, 0xfe08040f, 0x01f8040f, + 0x0d0d040f, 0xf2f3040f, 0x0f04040f, 0xf0fc040f, 0x040f040f, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000707, 0xfffff8f9, 0x000009fd, 0xfffff603, 0xfffffd0a, 0x000002f6, 0x00001010, + 0xffffeff0, 0x00001205, 0xffffedfb, 0x00000512, 0xfffffaee, 0x00000cf3, 0xfffff30d, 0x000014fa, + 0xffffeb06, 0xfffffa15, 0x000005eb, 0x00001e0f, 0xffffe1f1, 0x00000f1e, 0xfffff0e2, 0x00001e1e, + 0xffffe1e2, 0x00002202, 0xffffddfe, 0x00000222, 0xfffffdde, 0x00001bed, 0xffffe413, 0xffffed1c, + 0x000012e4, 0x00003620, 0xffffc9e0, 0x00002036, 0xffffdfca, 0x000028f5, 0xffffd70b, 0xfffff529, + 0x00000ad7, 0x0000370f, 0xffffc8f1, 0x00000f37, 0xfffff0c9, 0x00003939, 0xffffc6c7, 0x00003eff, + 0xffffc101, 0xffffff3f, 0x000000c1, 0x000027d8, 0xffffd828, 0x000036e2, 0xffffc91e, 0xffffe237, + 0x00001dc9, 0x00005e25, 0xffffa1db, 0x0000255e, 0xffffdaa2, 0x00006041, 0xffff9fbf, 0x00004160, + 0xffffbea0, 0x00004deb, 0xffffb215, 0xffffeb4e, 0x000014b2, 0x0000640f, 0xffff9bf1, 0x00000f64, + 0xfffff09c, 0x00006a6a, 0xffff9596, 0x000073f8, 0xffff8c08, 0xfffff874, 0x0000078c, 0x00004ec1, + 0xffffb13f, 0xffffc14f, 0x00003eb1, 0x000068cd, 0xffff9733, 0xffffcd69, 0x00003297, 0x00007788, + 0xffff8878, 0x00002b2b, 0xffffd4d5, 0x00005050, 0xffffafb0, 0x00000000, 0x07070000, 0xf8f90000, + 0x09fd0000, 0xf6030000, 0xfd0a0000, 0x02f60000, 0x10100000, 0xeff00000, 0x12050000, 0xedfb0000, + 0x05120000, 0x00000707, 0x07070707, 0xf8f90707, 0x09fd0707, 0xf6030707, 0xfd0a0707, 0x02f60707, + 0x10100707, 0xeff00707, 0x12050707, 0xedfb0707, 0x05120707, 0xfffff8f9, 0x0706f8f9, 0xf8f8f8f9, + 0x09fcf8f9, 0xf602f8f9, 0xfd09f8f9, 0x02f5f8f9, 0x100ff8f9, 0xefeff8f9, 0x1204f8f9, 0xedfaf8f9, + 0x0511f8f9, 0x000009fd, 0x070709fd, 0xf8f909fd, 0x09fd09fd, 0xf60309fd, 0xfd0a09fd, 0x02f609fd, + 0x101009fd, 0xeff009fd, 0x120509fd, 0xedfb09fd, 0x051209fd, 0xfffff603, 0x0706f603, 0xf8f8f603, + 0x09fcf603, 0xf602f603, 0xfd09f603, 0x02f5f603, 0x100ff603, 0xefeff603, 0x1204f603, 0xedfaf603, + 0x0511f603, 0xfffffd0a, 0x0706fd0a, 0xf8f8fd0a, 0x09fcfd0a, 0xf602fd0a, 0xfd09fd0a, 0x02f5fd0a, + 0x100ffd0a, 0xefeffd0a, 0x1204fd0a, 0xedfafd0a, 0x0511fd0a, 0x000002f6, 0x070702f6, 0xf8f902f6, + 0x09fd02f6, 0xf60302f6, 0xfd0a02f6, 0x02f602f6, 0x101002f6, 0xeff002f6, 0x120502f6, 0xedfb02f6, + 0x051202f6, 0x00001010, 0x07071010, 0xf8f91010, 0x09fd1010, 0xf6031010, 0xfd0a1010, 0x02f61010, + 0x10101010, 0xeff01010, 0x12051010, 0xedfb1010, 0x05121010, 0xffffeff0, 0x0706eff0, 0xf8f8eff0, + 0x09fceff0, 0xf602eff0, 0xfd09eff0, 0x02f5eff0, 0x100feff0, 0xefefeff0, 0x1204eff0, 0xedfaeff0, + 0x0511eff0, 0x00001205, 0x07071205, 0xf8f91205, 0x09fd1205, 0xf6031205, 0xfd0a1205, 0x02f61205, + 0x10101205, 0xeff01205, 0x12051205, 0xedfb1205, 0x05121205, 0xffffedfb, 0x0706edfb, 0xf8f8edfb, + 0x09fcedfb, 0xf602edfb, 0xfd09edfb, 0x02f5edfb, 0x100fedfb, 0xefefedfb, 0x1204edfb, 0xedfaedfb, + 0x0511edfb, 0x00000512, 0x07070512, 0xf8f90512, 0x09fd0512, 0xf6030512, 0xfd0a0512, 0x02f60512, + 0x10100512, 0xeff00512, 0x12050512, 0xedfb0512, 0x05120512, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000808, 0xfffff7f8, 0x00000afd, 0xfffff503, 0xfffffd0b, 0x000002f5, 0x00001212, + 0xffffedee, 0x00001405, 0xffffebfb, 0x00000514, 0xfffffaec, 0x00000ef1, 0xfffff10f, 0x000017f9, + 0xffffe807, 0xfffff918, 0x000006e8, 0x00002311, 0xffffdcef, 0x00001123, 0xffffeedd, 0x00002222, + 0xffffddde, 0x00002603, 0xffffd9fd, 0x00000326, 0xfffffcda, 0x00001fea, 0xffffe016, 0xffffea20, + 0x000015e0, 0x00003d25, 0xffffc2db, 0x0000253d, 0xffffdac3, 0x00002ef3, 0xffffd10d, 0xfffff32f, + 0x00000cd1, 0x00003f11, 0xffffc0ef, 0x0000113f, 0xffffeec1, 0x00004141, 0xffffbebf, 0x000047ff, + 0xffffb801, 0xffffff48, 0x000000b8, 0x00002dd2, 0xffffd22e, 0x00003edd, 0xffffc123, 0xffffdd3f, + 0x000022c1, 0x00006b2b, 0xffff94d5, 0x00002b6b, 0xffffd495, 0x00006e4b, 0xffff91b5, 0x00004b6e, + 0xffffb492, 0x000058e8, 0xffffa718, 0xffffe859, 0x000017a7, 0x00007211, 0xffff8def, 0x00001172, + 0xffffee8e, 0x00007979, 0xffff8687, 0x00005ab8, 0xffffa548, 0xffffb85b, 0x000047a5, 0x000077c6, + 0xffff883a, 0xffffc678, 0x00003988, 0x00003131, 0xffffcecf, 0x00005c5c, 0xffffa3a4, 0x00000000, + 0x08080000, 0xf7f80000, 0x0afd0000, 0xf5030000, 0xfd0b0000, 0x02f50000, 0x12120000, 0xedee0000, + 0x14050000, 0xebfb0000, 0x05140000, 0x00000808, 0x08080808, 0xf7f80808, 0x0afd0808, 0xf5030808, + 0xfd0b0808, 0x02f50808, 0x12120808, 0xedee0808, 0x14050808, 0xebfb0808, 0x05140808, 0xfffff7f8, + 0x0807f7f8, 0xf7f7f7f8, 0x0afcf7f8, 0xf502f7f8, 0xfd0af7f8, 0x02f4f7f8, 0x1211f7f8, 0xededf7f8, + 0x1404f7f8, 0xebfaf7f8, 0x0513f7f8, 0x00000afd, 0x08080afd, 0xf7f80afd, 0x0afd0afd, 0xf5030afd, + 0xfd0b0afd, 0x02f50afd, 0x12120afd, 0xedee0afd, 0x14050afd, 0xebfb0afd, 0x05140afd, 0xfffff503, + 0x0807f503, 0xf7f7f503, 0x0afcf503, 0xf502f503, 0xfd0af503, 0x02f4f503, 0x1211f503, 0xededf503, + 0x1404f503, 0xebfaf503, 0x0513f503, 0xfffffd0b, 0x0807fd0b, 0xf7f7fd0b, 0x0afcfd0b, 0xf502fd0b, + 0xfd0afd0b, 0x02f4fd0b, 0x1211fd0b, 0xededfd0b, 0x1404fd0b, 0xebfafd0b, 0x0513fd0b, 0x000002f5, + 0x080802f5, 0xf7f802f5, 0x0afd02f5, 0xf50302f5, 0xfd0b02f5, 0x02f502f5, 0x121202f5, 0xedee02f5, + 0x140502f5, 0xebfb02f5, 0x051402f5, 0x00001212, 0x08081212, 0xf7f81212, 0x0afd1212, 0xf5031212, + 0xfd0b1212, 0x02f51212, 0x12121212, 0xedee1212, 0x14051212, 0xebfb1212, 0x05141212, 0xffffedee, + 0x0807edee, 0xf7f7edee, 0x0afcedee, 0xf502edee, 0xfd0aedee, 0x02f4edee, 0x1211edee, 0xedededee, + 0x1404edee, 0xebfaedee, 0x0513edee, 0x00001405, 0x08081405, 0xf7f81405, 0x0afd1405, 0xf5031405, + 0xfd0b1405, 0x02f51405, 0x12121405, 0xedee1405, 0x14051405, 0xebfb1405, 0x05141405, 0xffffebfb, + 0x0807ebfb, 0xf7f7ebfb, 0x0afcebfb, 0xf502ebfb, 0xfd0aebfb, 0x02f4ebfb, 0x1211ebfb, 0xededebfb, + 0x1404ebfb, 0xebfaebfb, 0x0513ebfb, 0x00000514, 0x08080514, 0xf7f80514, 0x0afd0514, 0xf5030514, + 0xfd0b0514, 0x02f50514, 0x12120514, 0xedee0514, 0x14050514, 0xebfb0514, 0x05140514, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000909, 0xfffff6f7, 0x00000bfd, 0xfffff403, 0xfffffd0c, 0x000002f4, 0x00001414, + 0xffffebec, 0x00001706, 0xffffe8fa, 0x00000617, 0xfffff9e9, 0x000010ef, 0xffffef11, 0x00001af9, + 0xffffe507, 0xfffff91b, 0x000006e5, 0x00002713, 0xffffd8ed, 0x00001327, 0xffffecd9, 0x00002727, + 0xffffd8d9, 0x00002b03, 0xffffd4fd, 0x0000032b, 0xfffffcd5, 0x000023e8, 0xffffdc18, 0xffffe824, + 0x000017dc, 0x0000452a, 0xffffbad6, 0x00002a45, 0xffffd5bb, 0x000034f2, 0xffffcb0e, 0xfffff235, + 0x00000dcb, 0x00004713, 0xffffb8ed, 0x00001347, 0xffffecb9, 0x00004949, 0xffffb6b7, 0x00004ffe, + 0xffffb002, 0xfffffe50, 0x000001b0, 0x000033cc, 0xffffcc34, 0x000045d9, 0xffffba27, 0xffffd946, + 0x000026ba, 0x00007930, 0xffff86d0, 0x00003079, 0xffffcf87, 0x00007c54, 0xffff83ac, 0x0000547c, + 0xffffab84, 0x000063e5, 0xffff9c1b, 0xffffe564, 0x00001a9c, 0x000065af, 0xffff9a51, 0xffffaf66, + 0x0000509a, 0x00003737, 0xffffc8c9, 0x00006868, 0xffff9798, 0x00000000, 0x09090000, 0xf6f70000, + 0x0bfd0000, 0xf4030000, 0xfd0c0000, 0x02f40000, 0x14140000, 0xebec0000, 0x17060000, 0xe8fa0000, + 0x06170000, 0xf9e90000, 0x00000909, 0x09090909, 0xf6f70909, 0x0bfd0909, 0xf4030909, 0xfd0c0909, + 0x02f40909, 0x14140909, 0xebec0909, 0x17060909, 0xe8fa0909, 0x06170909, 0xf9e90909, 0xfffff6f7, + 0x0908f6f7, 0xf6f6f6f7, 0x0bfcf6f7, 0xf402f6f7, 0xfd0bf6f7, 0x02f3f6f7, 0x1413f6f7, 0xebebf6f7, + 0x1705f6f7, 0xe8f9f6f7, 0x0616f6f7, 0xf9e8f6f7, 0x00000bfd, 0x09090bfd, 0xf6f70bfd, 0x0bfd0bfd, + 0xf4030bfd, 0xfd0c0bfd, 0x02f40bfd, 0x14140bfd, 0xebec0bfd, 0x17060bfd, 0xe8fa0bfd, 0x06170bfd, + 0xf9e90bfd, 0xfffff403, 0x0908f403, 0xf6f6f403, 0x0bfcf403, 0xf402f403, 0xfd0bf403, 0x02f3f403, + 0x1413f403, 0xebebf403, 0x1705f403, 0xe8f9f403, 0x0616f403, 0xf9e8f403, 0xfffffd0c, 0x0908fd0c, + 0xf6f6fd0c, 0x0bfcfd0c, 0xf402fd0c, 0xfd0bfd0c, 0x02f3fd0c, 0x1413fd0c, 0xebebfd0c, 0x1705fd0c, + 0xe8f9fd0c, 0x0616fd0c, 0xf9e8fd0c, 0x000002f4, 0x090902f4, 0xf6f702f4, 0x0bfd02f4, 0xf40302f4, + 0xfd0c02f4, 0x02f402f4, 0x141402f4, 0xebec02f4, 0x170602f4, 0xe8fa02f4, 0x061702f4, 0xf9e902f4, + 0x00001414, 0x09091414, 0xf6f71414, 0x0bfd1414, 0xf4031414, 0xfd0c1414, 0x02f41414, 0x14141414, + 0xebec1414, 0x17061414, 0xe8fa1414, 0x06171414, 0xf9e91414, 0xffffebec, 0x0908ebec, 0xf6f6ebec, + 0x0bfcebec, 0xf402ebec, 0xfd0bebec, 0x02f3ebec, 0x1413ebec, 0xebebebec, 0x1705ebec, 0xe8f9ebec, + 0x0616ebec, 0xf9e8ebec, 0x00001706, 0x09091706, 0xf6f71706, 0x0bfd1706, 0xf4031706, 0xfd0c1706, + 0x02f41706, 0x14141706, 0xebec1706, 0x17061706, 0xe8fa1706, 0x06171706, 0xf9e91706, 0xffffe8fa, + 0x0908e8fa, 0xf6f6e8fa, 0x0bfce8fa, 0xf402e8fa, 0xfd0be8fa, 0x02f3e8fa, 0x1413e8fa, 0xebebe8fa, + 0x1705e8fa, 0xe8f9e8fa, 0x0616e8fa, 0xf9e8e8fa, 0x00000617, 0x09090617, 0xf6f70617, 0x0bfd0617, + 0xf4030617, 0xfd0c0617, 0x02f40617, 0x14140617, 0xebec0617, 0x17060617, 0xe8fa0617, 0x06170617, + 0xf9e90617, 0xfffff9e9, 0x0908f9e9, 0xf6f6f9e9, 0x0bfcf9e9, 0xf402f9e9, 0xfd0bf9e9, 0x02f3f9e9, + 0x1413f9e9, 0xebebf9e9, 0x1705f9e9, 0xe8f9f9e9, 0x0616f9e9, 0xf9e8f9e9, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000200, 0xfffffe00, 0x00000002, 0xfffffffe, 0x00000404, + 0xfffffbfc, 0x00000400, 0xfffffc00, 0x00000004, 0xfffffffc, 0x000003fc, 0xfffffc04, 0x000005fe, + 0xfffffa02, 0xfffffe06, 0x000001fa, 0x00000804, 0xfffff7fc, 0x00000408, 0xfffffbf8, 0x00000808, + 0xfffff7f8, 0x00000a00, 0xfffff600, 0x0000000a, 0xfffffff6, 0x000007fc, 0xfffff804, 0xfffffc08, + 0x000003f8, 0x00000e08, 0xfffff1f8, 0x0000080e, 0xfffff7f2, 0x00000bfe, 0xfffff402, 0xfffffe0c, + 0x000001f4, 0x00001004, 0xffffeffc, 0x00000410, 0xfffffbf0, 0x00001010, 0xffffeff0, 0x00001200, + 0xffffee00, 0x00000012, 0xffffffee, 0x00000bf4, 0xfffff40c, 0x00000ff8, 0xfffff008, 0xfffff810, + 0x000007f0, 0x00001a0a, 0xffffe5f6, 0x00000a1a, 0xfffff5e6, 0x00001c12, 0xffffe3ee, 0x0000121c, + 0xffffede4, 0x000015fa, 0xffffea06, 0xfffffa16, 0x000005ea, 0x00001c04, 0xffffe3fc, 0x0000041c, + 0xfffffbe4, 0x00001e1e, 0xffffe1e2, 0x00001ffe, 0xffffe002, 0xfffffe20, 0x000001e0, 0x000015ee, + 0xffffea12, 0xffffee16, 0x000011ea, 0x00001df2, 0xffffe20e, 0xfffff21e, 0x00000de2, 0x00002e16, + 0xffffd1ea, 0x0000162e, 0xffffe9d2, 0x00002e0c, 0xffffd1f4, 0x00000c2e, 0xfffff3d2, 0x00003022, + 0xffffcfde, 0x00002230, 0xffffddd0, 0x000027f6, 0xffffd80a, 0xfffff628, 0x000009d8, 0x00003204, + 0xffffcdfc, 0x00000432, 0xfffffbce, 0x00003636, 0xffffc9ca, 0x000021de, 0xffffde22, 0x000029e4, + 0xffffd61c, 0xffffe42a, 0x00001bd6, 0x00003bfa, 0xffffc406, 0xfffffa3c, 0x000005c4, 0x00004c1a, + 0xffffb3e6, 0x00001a4c, 0xffffe5b4, 0x00004c2a, 0xffffb3d6, 0x00002a4c, 0xffffd5b4, 0x000035e8, + 0xffffca18, 0xffffe836, 0x000017ca, 0x00004e0e, 0xffffb1f2, 0x00000e4e, 0xfffff1b2, 0x0000523e, + 0xffffadc2, 0x00003e52, 0xffffc1ae, 0x000049ec, 0xffffb614, 0xffffec4a, 0x000013b6, 0x00005802, + 0xffffa7fe, 0x00000258, 0xfffffda8, 0x00005c5c, 0xffffa3a4, 0x00003bcc, 0xffffc434, 0xffffcc3c, + 0x000033c4, 0x00007634, 0xffff89cc, 0x00003476, 0xffffcb8a, 0x000049d4, 0xffffb62c, 0xffffd44a, + 0x00002bb6, 0x0000764a, 0xffff89b6, 0x00004a76, 0xffffb58a, 0x00007620, 0xffff89e0, 0x00002076, + 0xffffdf8a, 0x000065f4, 0xffff9a0c, 0xfffff466, 0x00000b9a, 0x00005fd8, 0xffffa028, 0xffffd860, + 0x000027a0, 0x000075de, 0xffff8a22, 0xffffde76, 0x0000218a, 0x000057a8, 0xffffa858, 0x000067b2, + 0xffff984e, 0xffffb268, 0x00004d98, 0x00000c0c, 0xfffff3f4, 0x00001616, 0xffffe9ea, 0x00002a2a, + 0xffffd5d6, 0x00004848, 0xffffb7b8, 0x00000000, 0x02020000, 0xfdfe0000, 0x02000000, 0xfe000000, + 0x00020000, 0xfffe0000, 0x00000202, 0x02020202, 0xfdfe0202, 0x02000202, 0xfe000202, 0x00020202, + 0xfffe0202, 0xfffffdfe, 0x0201fdfe, 0xfdfdfdfe, 0x01fffdfe, 0xfdfffdfe, 0x0001fdfe, 0xfffdfdfe, + 0x00000200, 0x02020200, 0xfdfe0200, 0x02000200, 0xfe000200, 0x00020200, 0xfffe0200, 0xfffffe00, + 0x0201fe00, 0xfdfdfe00, 0x01fffe00, 0xfdfffe00, 0x0001fe00, 0xfffdfe00, 0x00000002, 0x02020002, + 0xfdfe0002, 0x02000002, 0xfe000002, 0x00020002, 0xfffe0002, 0xfffffffe, 0x0201fffe, 0xfdfdfffe, + 0x01fffffe, 0xfdfffffe, 0x0001fffe, 0xfffdfffe, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000303, 0xfffffcfd, 0x00000300, 0xfffffd00, 0x00000003, 0xfffffffd, 0x00000606, + 0xfffff9fa, 0x00000903, 0xfffff6fd, 0x00000309, 0xfffffcf7, 0x000008fd, 0xfffff703, 0xfffffd09, + 0x000002f7, 0x000005fa, 0xfffffa06, 0x00000c06, 0xfffff3fa, 0x0000060c, 0xfffff9f4, 0x00000c0c, + 0xfffff3f4, 0x00000f00, 0xfffff100, 0x0000000f, 0xfffffff1, 0x00000bf7, 0xfffff409, 0xfffff70c, + 0x000008f4, 0x0000180f, 0xffffe7f1, 0x00000f18, 0xfffff0e8, 0x000011fa, 0xffffee06, 0xfffffa12, + 0x000005ee, 0x00001806, 0xffffe7fa, 0x00000618, 0xfffff9e8, 0x00001818, 0xffffe7e8, 0x00001b00, + 0xffffe500, 0x0000001b, 0xffffffe5, 0x000011ee, 0xffffee12, 0x000017f4, 0xffffe80c, 0xfffff418, + 0x00000be8, 0x0000270f, 0xffffd8f1, 0x00000f27, 0xfffff0d9, 0x00002a1b, 0xffffd5e5, 0x00001b2a, + 0xffffe4d6, 0x000020f7, 0xffffdf09, 0xfffff721, 0x000008df, 0x00002a06, 0xffffd5fa, 0x0000062a, + 0xfffff9d6, 0x00002d2d, 0xffffd2d3, 0x000032fd, 0xffffcd03, 0xfffffd33, 0x000002cd, 0x000020e5, + 0xffffdf1b, 0xffffe521, 0x00001adf, 0x00002ceb, 0xffffd315, 0xffffeb2d, 0x000014d3, 0x00004521, + 0xffffbadf, 0x00002145, 0xffffdebb, 0x00004512, 0xffffbaee, 0x00001245, 0xffffedbb, 0x00004836, + 0xffffb7ca, 0x00003648, 0xffffc9b8, 0x00003eee, 0xffffc112, 0xffffee3f, 0x000011c1, 0x00004e06, + 0xffffb1fa, 0x0000064e, 0xfffff9b2, 0x00005151, 0xffffaeaf, 0x000032cd, 0xffffcd33, 0x00003ed6, + 0xffffc12a, 0xffffd63f, 0x000029c1, 0x000059f7, 0xffffa609, 0xfffff75a, 0x000008a6, 0x0000722a, + 0xffff8dd6, 0x00002a72, 0xffffd58e, 0x0000753f, 0xffff8ac1, 0x00003f75, 0xffffc08b, 0x000050dc, + 0xffffaf24, 0xffffdc51, 0x000023af, 0x00007815, 0xffff87eb, 0x00001578, 0xffffea88, 0x00007b60, + 0xffff84a0, 0x0000607b, 0xffff9f85, 0x00006ee2, 0xffff911e, 0xffffe26f, 0x00001d91, 0x00005cb2, + 0xffffa34e, 0xffffb25d, 0x00004da3, 0x000071bb, 0xffff8e45, 0xffffbb72, 0x0000448e, 0x00001212, + 0xffffedee, 0x00002121, 0xffffdedf, 0x00003f3f, 0xffffc0c1, 0x00006c6c, 0xffff9394, 0x00000000, + 0x03030000, 0xfcfd0000, 0x03000000, 0xfd000000, 0x00030000, 0xfffd0000, 0x06060000, 0xf9fa0000, + 0x00000303, 0x03030303, 0xfcfd0303, 0x03000303, 0xfd000303, 0x00030303, 0xfffd0303, 0x06060303, + 0xf9fa0303, 0xfffffcfd, 0x0302fcfd, 0xfcfcfcfd, 0x02fffcfd, 0xfcfffcfd, 0x0002fcfd, 0xfffcfcfd, + 0x0605fcfd, 0xf9f9fcfd, 0x00000300, 0x03030300, 0xfcfd0300, 0x03000300, 0xfd000300, 0x00030300, + 0xfffd0300, 0x06060300, 0xf9fa0300, 0xfffffd00, 0x0302fd00, 0xfcfcfd00, 0x02fffd00, 0xfcfffd00, + 0x0002fd00, 0xfffcfd00, 0x0605fd00, 0xf9f9fd00, 0x00000003, 0x03030003, 0xfcfd0003, 0x03000003, + 0xfd000003, 0x00030003, 0xfffd0003, 0x06060003, 0xf9fa0003, 0xfffffffd, 0x0302fffd, 0xfcfcfffd, + 0x02fffffd, 0xfcfffffd, 0x0002fffd, 0xfffcfffd, 0x0605fffd, 0xf9f9fffd, 0x00000606, 0x03030606, + 0xfcfd0606, 0x03000606, 0xfd000606, 0x00030606, 0xfffd0606, 0x06060606, 0xf9fa0606, 0xfffff9fa, + 0x0302f9fa, 0xfcfcf9fa, 0x02fff9fa, 0xfcfff9fa, 0x0002f9fa, 0xfffcf9fa, 0x0605f9fa, 0xf9f9f9fa, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000404, 0xfffffbfc, 0x00000400, 0xfffffc00, 0x00000004, 0xfffffffc, 0x00000804, + 0xfffff7fc, 0x00000408, 0xfffffbf8, 0x00000808, 0xfffff7f8, 0x000007f8, 0xfffff808, 0x00000bfc, + 0xfffff404, 0xfffffc0c, 0x000003f4, 0x00001008, 0xffffeff8, 0x00000810, 0xfffff7f0, 0x00001010, + 0xffffeff0, 0x00001400, 0xffffec00, 0x00000014, 0xffffffec, 0x00000ff4, 0xfffff00c, 0xfffff410, + 0x00000bf0, 0x000017fc, 0xffffe804, 0xfffffc18, 0x000003e8, 0x00002010, 0xffffdff0, 0x00001020, + 0xffffefe0, 0x00002008, 0xffffdff8, 0x00000820, 0xfffff7e0, 0x00002020, 0xffffdfe0, 0x00002400, + 0xffffdc00, 0x00000024, 0xffffffdc, 0x000017e8, 0xffffe818, 0x00001ff0, 0xffffe010, 0xfffff020, + 0x00000fe0, 0x00003414, 0xffffcbec, 0x00001434, 0xffffebcc, 0x00003824, 0xffffc7dc, 0x00002438, + 0xffffdbc8, 0x00002bf4, 0xffffd40c, 0xfffff42c, 0x00000bd4, 0x00003808, 0xffffc7f8, 0x00000838, + 0xfffff7c8, 0x00003c3c, 0xffffc3c4, 0x00003ffc, 0xffffc004, 0xfffffc40, 0x000003c0, 0x00002bdc, + 0xffffd424, 0xffffdc2c, 0x000023d4, 0x00003be4, 0xffffc41c, 0xffffe43c, 0x00001bc4, 0x00005c2c, + 0xffffa3d4, 0x00002c5c, 0xffffd3a4, 0x00005c18, 0xffffa3e8, 0x0000185c, 0xffffe7a4, 0x00006048, + 0xffff9fb8, 0x00004860, 0xffffb7a0, 0x000053ec, 0xffffac14, 0xffffec54, 0x000013ac, 0x00006408, + 0xffff9bf8, 0x00000864, 0xfffff79c, 0x00006c6c, 0xffff9394, 0x000043bc, 0xffffbc44, 0x000053c8, + 0xffffac38, 0xffffc854, 0x000037ac, 0x000077f4, 0xffff880c, 0xfffff478, 0x00000b88, 0x00006bd0, + 0xffff9430, 0xffffd06c, 0x00002f94, 0x00007b98, 0xffff8468, 0xffff987c, 0x00006784, 0x00001818, + 0xffffe7e8, 0x00002c2c, 0xffffd3d4, 0x00005454, 0xffffabac, 0x00000000, 0x04040000, 0xfbfc0000, + 0x04000000, 0xfc000000, 0x00040000, 0xfffc0000, 0x08040000, 0xf7fc0000, 0x04080000, 0x00000404, + 0x04040404, 0xfbfc0404, 0x04000404, 0xfc000404, 0x00040404, 0xfffc0404, 0x08040404, 0xf7fc0404, + 0x04080404, 0xfffffbfc, 0x0403fbfc, 0xfbfbfbfc, 0x03fffbfc, 0xfbfffbfc, 0x0003fbfc, 0xfffbfbfc, + 0x0803fbfc, 0xf7fbfbfc, 0x0407fbfc, 0x00000400, 0x04040400, 0xfbfc0400, 0x04000400, 0xfc000400, + 0x00040400, 0xfffc0400, 0x08040400, 0xf7fc0400, 0x04080400, 0xfffffc00, 0x0403fc00, 0xfbfbfc00, + 0x03fffc00, 0xfbfffc00, 0x0003fc00, 0xfffbfc00, 0x0803fc00, 0xf7fbfc00, 0x0407fc00, 0x00000004, + 0x04040004, 0xfbfc0004, 0x04000004, 0xfc000004, 0x00040004, 0xfffc0004, 0x08040004, 0xf7fc0004, + 0x04080004, 0xfffffffc, 0x0403fffc, 0xfbfbfffc, 0x03fffffc, 0xfbfffffc, 0x0003fffc, 0xfffbfffc, + 0x0803fffc, 0xf7fbfffc, 0x0407fffc, 0x00000804, 0x04040804, 0xfbfc0804, 0x04000804, 0xfc000804, + 0x00040804, 0xfffc0804, 0x08040804, 0xf7fc0804, 0x04080804, 0xfffff7fc, 0x0403f7fc, 0xfbfbf7fc, + 0x03fff7fc, 0xfbfff7fc, 0x0003f7fc, 0xfffbf7fc, 0x0803f7fc, 0xf7fbf7fc, 0x0407f7fc, 0x00000408, + 0x04040408, 0xfbfc0408, 0x04000408, 0xfc000408, 0x00040408, 0xfffc0408, 0x08040408, 0xf7fc0408, + 0x04080408, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000505, 0xfffffafb, 0x00000500, 0xfffffb00, 0x00000005, 0xfffffffb, 0x00000a0a, + 0xfffff5f6, 0x00000f05, 0xfffff0fb, 0x0000050f, 0xfffffaf1, 0x000009f6, 0xfffff60a, 0x00000efb, + 0xfffff105, 0xfffffb0f, 0x000004f1, 0x0000140a, 0xffffebf6, 0x00000a14, 0xfffff5ec, 0x00001414, + 0xffffebec, 0x00001900, 0xffffe700, 0x00000019, 0xffffffe7, 0x000013f1, 0xffffec0f, 0xfffff114, + 0x00000eec, 0x00002819, 0xffffd7e7, 0x00001928, 0xffffe6d8, 0x00001df6, 0xffffe20a, 0xfffff61e, + 0x000009e2, 0x0000280a, 0xffffd7f6, 0x00000a28, 0xfffff5d8, 0x00002828, 0xffffd7d8, 0x00002d00, + 0xffffd300, 0x0000002d, 0xffffffd3, 0x00001de2, 0xffffe21e, 0x000027ec, 0xffffd814, 0xffffec28, + 0x000013d8, 0x00004119, 0xffffbee7, 0x00001941, 0xffffe6bf, 0x0000462d, 0xffffb9d3, 0x00002d46, + 0xffffd2ba, 0x000036f1, 0xffffc90f, 0xfffff137, 0x00000ec9, 0x0000460a, 0xffffb9f6, 0x00000a46, + 0xfffff5ba, 0x00004b4b, 0xffffb4b5, 0x000054fb, 0xffffab05, 0xfffffb55, 0x000004ab, 0x000036d3, + 0xffffc92d, 0xffffd337, 0x00002cc9, 0x00004add, 0xffffb523, 0xffffdd4b, 0x000022b5, 0x00007337, + 0xffff8cc9, 0x00003773, 0xffffc88d, 0x0000731e, 0xffff8ce2, 0x00001e73, 0xffffe18d, 0x0000785a, + 0xffff87a6, 0x00005a78, 0xffffa588, 0x000068e2, 0xffff971e, 0xffffe269, 0x00001d97, 0x000054ab, + 0xffffab55, 0x000068ba, 0xffff9746, 0xffffba69, 0x00004597, 0x00001e1e, 0xffffe1e2, 0x00003c3c, + 0xffffc3c4, 0x00006969, 0xffff9697, 0x00000000, 0x05050000, 0xfafb0000, 0x05000000, 0xfb000000, + 0x00050000, 0xfffb0000, 0x0a0a0000, 0xf5f60000, 0x0f050000, 0xf0fb0000, 0x00000505, 0x05050505, + 0xfafb0505, 0x05000505, 0xfb000505, 0x00050505, 0xfffb0505, 0x0a0a0505, 0xf5f60505, 0x0f050505, + 0xf0fb0505, 0xfffffafb, 0x0504fafb, 0xfafafafb, 0x04fffafb, 0xfafffafb, 0x0004fafb, 0xfffafafb, + 0x0a09fafb, 0xf5f5fafb, 0x0f04fafb, 0xf0fafafb, 0x00000500, 0x05050500, 0xfafb0500, 0x05000500, + 0xfb000500, 0x00050500, 0xfffb0500, 0x0a0a0500, 0xf5f60500, 0x0f050500, 0xf0fb0500, 0xfffffb00, + 0x0504fb00, 0xfafafb00, 0x04fffb00, 0xfafffb00, 0x0004fb00, 0xfffafb00, 0x0a09fb00, 0xf5f5fb00, + 0x0f04fb00, 0xf0fafb00, 0x00000005, 0x05050005, 0xfafb0005, 0x05000005, 0xfb000005, 0x00050005, + 0xfffb0005, 0x0a0a0005, 0xf5f60005, 0x0f050005, 0xf0fb0005, 0xfffffffb, 0x0504fffb, 0xfafafffb, + 0x04fffffb, 0xfafffffb, 0x0004fffb, 0xfffafffb, 0x0a09fffb, 0xf5f5fffb, 0x0f04fffb, 0xf0fafffb, + 0x00000a0a, 0x05050a0a, 0xfafb0a0a, 0x05000a0a, 0xfb000a0a, 0x00050a0a, 0xfffb0a0a, 0x0a0a0a0a, + 0xf5f60a0a, 0x0f050a0a, 0xf0fb0a0a, 0xfffff5f6, 0x0504f5f6, 0xfafaf5f6, 0x04fff5f6, 0xfafff5f6, + 0x0004f5f6, 0xfffaf5f6, 0x0a09f5f6, 0xf5f5f5f6, 0x0f04f5f6, 0xf0faf5f6, 0x00000f05, 0x05050f05, + 0xfafb0f05, 0x05000f05, 0xfb000f05, 0x00050f05, 0xfffb0f05, 0x0a0a0f05, 0xf5f60f05, 0x0f050f05, + 0xf0fb0f05, 0xfffff0fb, 0x0504f0fb, 0xfafaf0fb, 0x04fff0fb, 0xfafff0fb, 0x0004f0fb, 0xfffaf0fb, + 0x0a09f0fb, 0xf5f5f0fb, 0x0f04f0fb, 0xf0faf0fb, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000606, 0xfffff9fa, 0x00000600, 0xfffffa00, 0x00000006, 0xfffffffa, 0x00000c0c, + 0xfffff3f4, 0x00000c06, 0xfffff3fa, 0x0000060c, 0xfffff9f4, 0x00000bf4, 0xfffff40c, 0x000011fa, + 0xffffee06, 0xfffffa12, 0x000005ee, 0x0000180c, 0xffffe7f4, 0x00000c18, 0xfffff3e8, 0x00001818, + 0xffffe7e8, 0x00001e00, 0xffffe200, 0x0000001e, 0xffffffe2, 0x000017ee, 0xffffe812, 0xffffee18, + 0x000011e8, 0x0000301e, 0xffffcfe2, 0x00001e30, 0xffffe1d0, 0x000023fa, 0xffffdc06, 0xfffffa24, + 0x000005dc, 0x0000300c, 0xffffcff4, 0x00000c30, 0xfffff3d0, 0x00003030, 0xffffcfd0, 0x00003600, + 0xffffca00, 0x00000036, 0xffffffca, 0x000023dc, 0xffffdc24, 0x00002fe8, 0xffffd018, 0xffffe830, + 0x000017d0, 0x00004e1e, 0xffffb1e2, 0x00001e4e, 0xffffe1b2, 0x00005436, 0xffffabca, 0x00003654, + 0xffffc9ac, 0x000041ee, 0xffffbe12, 0xffffee42, 0x000011be, 0x0000540c, 0xffffabf4, 0x00000c54, + 0xfffff3ac, 0x00005a5a, 0xffffa5a6, 0x00005ffa, 0xffffa006, 0xfffffa60, 0x000005a0, 0x000041ca, + 0xffffbe36, 0xffffca42, 0x000035be, 0x000059d6, 0xffffa62a, 0xffffd65a, 0x000029a6, 0x00007de2, + 0xffff821e, 0xffffe27e, 0x00001d82, 0x0000659a, 0xffff9a66, 0x00007dac, 0xffff8254, 0xffffac7e, + 0x00005382, 0x00002424, 0xffffdbdc, 0x00004242, 0xffffbdbe, 0x00000000, 0x06060000, 0xf9fa0000, + 0x06000000, 0xfa000000, 0x00060000, 0xfffa0000, 0x0c0c0000, 0xf3f40000, 0x0c060000, 0xf3fa0000, + 0x060c0000, 0x00000606, 0x06060606, 0xf9fa0606, 0x06000606, 0xfa000606, 0x00060606, 0xfffa0606, + 0x0c0c0606, 0xf3f40606, 0x0c060606, 0xf3fa0606, 0x060c0606, 0xfffff9fa, 0x0605f9fa, 0xf9f9f9fa, + 0x05fff9fa, 0xf9fff9fa, 0x0005f9fa, 0xfff9f9fa, 0x0c0bf9fa, 0xf3f3f9fa, 0x0c05f9fa, 0xf3f9f9fa, + 0x060bf9fa, 0x00000600, 0x06060600, 0xf9fa0600, 0x06000600, 0xfa000600, 0x00060600, 0xfffa0600, + 0x0c0c0600, 0xf3f40600, 0x0c060600, 0xf3fa0600, 0x060c0600, 0xfffffa00, 0x0605fa00, 0xf9f9fa00, + 0x05fffa00, 0xf9fffa00, 0x0005fa00, 0xfff9fa00, 0x0c0bfa00, 0xf3f3fa00, 0x0c05fa00, 0xf3f9fa00, + 0x060bfa00, 0x00000006, 0x06060006, 0xf9fa0006, 0x06000006, 0xfa000006, 0x00060006, 0xfffa0006, + 0x0c0c0006, 0xf3f40006, 0x0c060006, 0xf3fa0006, 0x060c0006, 0xfffffffa, 0x0605fffa, 0xf9f9fffa, + 0x05fffffa, 0xf9fffffa, 0x0005fffa, 0xfff9fffa, 0x0c0bfffa, 0xf3f3fffa, 0x0c05fffa, 0xf3f9fffa, + 0x060bfffa, 0x00000c0c, 0x06060c0c, 0xf9fa0c0c, 0x06000c0c, 0xfa000c0c, 0x00060c0c, 0xfffa0c0c, + 0x0c0c0c0c, 0xf3f40c0c, 0x0c060c0c, 0xf3fa0c0c, 0x060c0c0c, 0xfffff3f4, 0x0605f3f4, 0xf9f9f3f4, + 0x05fff3f4, 0xf9fff3f4, 0x0005f3f4, 0xfff9f3f4, 0x0c0bf3f4, 0xf3f3f3f4, 0x0c05f3f4, 0xf3f9f3f4, + 0x060bf3f4, 0x00000c06, 0x06060c06, 0xf9fa0c06, 0x06000c06, 0xfa000c06, 0x00060c06, 0xfffa0c06, + 0x0c0c0c06, 0xf3f40c06, 0x0c060c06, 0xf3fa0c06, 0x060c0c06, 0xfffff3fa, 0x0605f3fa, 0xf9f9f3fa, + 0x05fff3fa, 0xf9fff3fa, 0x0005f3fa, 0xfff9f3fa, 0x0c0bf3fa, 0xf3f3f3fa, 0x0c05f3fa, 0xf3f9f3fa, + 0x060bf3fa, 0x0000060c, 0x0606060c, 0xf9fa060c, 0x0600060c, 0xfa00060c, 0x0006060c, 0xfffa060c, + 0x0c0c060c, 0xf3f4060c, 0x0c06060c, 0xf3fa060c, 0x060c060c, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000707, 0xfffff8f9, 0x00000700, 0xfffff900, 0x00000007, 0xfffffff9, 0x00000e0e, + 0xfffff1f2, 0x00001507, 0xffffeaf9, 0x00000715, 0xfffff8eb, 0x00000df2, 0xfffff20e, 0x000014f9, + 0xffffeb07, 0xfffff915, 0x000006eb, 0x00001c0e, 0xffffe3f2, 0x00000e1c, 0xfffff1e4, 0x00001c1c, + 0xffffe3e4, 0x00002300, 0xffffdd00, 0x00000023, 0xffffffdd, 0x00001beb, 0xffffe415, 0xffffeb1c, + 0x000014e4, 0x00003823, 0xffffc7dd, 0x00002338, 0xffffdcc8, 0x000029f2, 0xffffd60e, 0xfffff22a, + 0x00000dd6, 0x0000380e, 0xffffc7f2, 0x00000e38, 0xfffff1c8, 0x00003838, 0xffffc7c8, 0x00003f00, + 0xffffc100, 0x0000003f, 0xffffffc1, 0x000029d6, 0xffffd62a, 0x000037e4, 0xffffc81c, 0xffffe438, + 0x00001bc8, 0x00005b23, 0xffffa4dd, 0x0000235b, 0xffffdca5, 0x0000623f, 0xffff9dc1, 0x00003f62, + 0xffffc09e, 0x00004ceb, 0xffffb315, 0xffffeb4d, 0x000014b3, 0x0000620e, 0xffff9df2, 0x00000e62, + 0xfffff19e, 0x00006969, 0xffff9697, 0x000076f9, 0xffff8907, 0xfffff977, 0x00000689, 0x00004cc1, + 0xffffb33f, 0xffffc14d, 0x00003eb3, 0x000068cf, 0xffff9731, 0xffffcf69, 0x00003097, 0x00007689, + 0xffff8977, 0x00002a2a, 0xffffd5d6, 0x00004d4d, 0xffffb2b3, 0x00000000, 0x07070000, 0xf8f90000, + 0x07000000, 0xf9000000, 0x00070000, 0xfff90000, 0x0e0e0000, 0xf1f20000, 0x15070000, 0xeaf90000, + 0x07150000, 0x00000707, 0x07070707, 0xf8f90707, 0x07000707, 0xf9000707, 0x00070707, 0xfff90707, + 0x0e0e0707, 0xf1f20707, 0x15070707, 0xeaf90707, 0x07150707, 0xfffff8f9, 0x0706f8f9, 0xf8f8f8f9, + 0x06fff8f9, 0xf8fff8f9, 0x0006f8f9, 0xfff8f8f9, 0x0e0df8f9, 0xf1f1f8f9, 0x1506f8f9, 0xeaf8f8f9, + 0x0714f8f9, 0x00000700, 0x07070700, 0xf8f90700, 0x07000700, 0xf9000700, 0x00070700, 0xfff90700, + 0x0e0e0700, 0xf1f20700, 0x15070700, 0xeaf90700, 0x07150700, 0xfffff900, 0x0706f900, 0xf8f8f900, + 0x06fff900, 0xf8fff900, 0x0006f900, 0xfff8f900, 0x0e0df900, 0xf1f1f900, 0x1506f900, 0xeaf8f900, + 0x0714f900, 0x00000007, 0x07070007, 0xf8f90007, 0x07000007, 0xf9000007, 0x00070007, 0xfff90007, + 0x0e0e0007, 0xf1f20007, 0x15070007, 0xeaf90007, 0x07150007, 0xfffffff9, 0x0706fff9, 0xf8f8fff9, + 0x06fffff9, 0xf8fffff9, 0x0006fff9, 0xfff8fff9, 0x0e0dfff9, 0xf1f1fff9, 0x1506fff9, 0xeaf8fff9, + 0x0714fff9, 0x00000e0e, 0x07070e0e, 0xf8f90e0e, 0x07000e0e, 0xf9000e0e, 0x00070e0e, 0xfff90e0e, + 0x0e0e0e0e, 0xf1f20e0e, 0x15070e0e, 0xeaf90e0e, 0x07150e0e, 0xfffff1f2, 0x0706f1f2, 0xf8f8f1f2, + 0x06fff1f2, 0xf8fff1f2, 0x0006f1f2, 0xfff8f1f2, 0x0e0df1f2, 0xf1f1f1f2, 0x1506f1f2, 0xeaf8f1f2, + 0x0714f1f2, 0x00001507, 0x07071507, 0xf8f91507, 0x07001507, 0xf9001507, 0x00071507, 0xfff91507, + 0x0e0e1507, 0xf1f21507, 0x15071507, 0xeaf91507, 0x07151507, 0xffffeaf9, 0x0706eaf9, 0xf8f8eaf9, + 0x06ffeaf9, 0xf8ffeaf9, 0x0006eaf9, 0xfff8eaf9, 0x0e0deaf9, 0xf1f1eaf9, 0x1506eaf9, 0xeaf8eaf9, + 0x0714eaf9, 0x00000715, 0x07070715, 0xf8f90715, 0x07000715, 0xf9000715, 0x00070715, 0xfff90715, + 0x0e0e0715, 0xf1f20715, 0x15070715, 0xeaf90715, 0x07150715, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000808, 0xfffff7f8, 0x00000800, 0xfffff800, 0x00000008, 0xfffffff8, 0x00001010, + 0xffffeff0, 0x00001008, 0xffffeff8, 0x00000810, 0xfffff7f0, 0x00000ff0, 0xfffff010, 0x000017f8, + 0xffffe808, 0xfffff818, 0x000007e8, 0x00002010, 0xffffdff0, 0x00001020, 0xffffefe0, 0x00002020, + 0xffffdfe0, 0x00002800, 0xffffd800, 0x00000028, 0xffffffd8, 0x00001fe8, 0xffffe018, 0xffffe820, + 0x000017e0, 0x00004028, 0xffffbfd8, 0x00002840, 0xffffd7c0, 0x00002ff0, 0xffffd010, 0xfffff030, + 0x00000fd0, 0x00004010, 0xffffbff0, 0x00001040, 0xffffefc0, 0x00004040, 0xffffbfc0, 0x00004800, + 0xffffb800, 0x00000048, 0xffffffb8, 0x00002fd0, 0xffffd030, 0x00003fe0, 0xffffc020, 0xffffe040, + 0x00001fc0, 0x00006828, 0xffff97d8, 0x00002868, 0xffffd798, 0x00007048, 0xffff8fb8, 0x00004870, + 0xffffb790, 0x000057e8, 0xffffa818, 0xffffe858, 0x000017a8, 0x00007010, 0xffff8ff0, 0x00001070, + 0xffffef90, 0x00007878, 0xffff8788, 0x000057b8, 0xffffa848, 0xffffb858, 0x000047a8, 0x000077c8, + 0xffff8838, 0xffffc878, 0x00003788, 0x00003030, 0xffffcfd0, 0x00005858, 0xffffa7a8, 0x00000000, + 0x08080000, 0xf7f80000, 0x08000000, 0xf8000000, 0x00080000, 0xfff80000, 0x10100000, 0xeff00000, + 0x10080000, 0xeff80000, 0x08100000, 0x00000808, 0x08080808, 0xf7f80808, 0x08000808, 0xf8000808, + 0x00080808, 0xfff80808, 0x10100808, 0xeff00808, 0x10080808, 0xeff80808, 0x08100808, 0xfffff7f8, + 0x0807f7f8, 0xf7f7f7f8, 0x07fff7f8, 0xf7fff7f8, 0x0007f7f8, 0xfff7f7f8, 0x100ff7f8, 0xefeff7f8, + 0x1007f7f8, 0xeff7f7f8, 0x080ff7f8, 0x00000800, 0x08080800, 0xf7f80800, 0x08000800, 0xf8000800, + 0x00080800, 0xfff80800, 0x10100800, 0xeff00800, 0x10080800, 0xeff80800, 0x08100800, 0xfffff800, + 0x0807f800, 0xf7f7f800, 0x07fff800, 0xf7fff800, 0x0007f800, 0xfff7f800, 0x100ff800, 0xefeff800, + 0x1007f800, 0xeff7f800, 0x080ff800, 0x00000008, 0x08080008, 0xf7f80008, 0x08000008, 0xf8000008, + 0x00080008, 0xfff80008, 0x10100008, 0xeff00008, 0x10080008, 0xeff80008, 0x08100008, 0xfffffff8, + 0x0807fff8, 0xf7f7fff8, 0x07fffff8, 0xf7fffff8, 0x0007fff8, 0xfff7fff8, 0x100ffff8, 0xefeffff8, + 0x1007fff8, 0xeff7fff8, 0x080ffff8, 0x00001010, 0x08081010, 0xf7f81010, 0x08001010, 0xf8001010, + 0x00081010, 0xfff81010, 0x10101010, 0xeff01010, 0x10081010, 0xeff81010, 0x08101010, 0xffffeff0, + 0x0807eff0, 0xf7f7eff0, 0x07ffeff0, 0xf7ffeff0, 0x0007eff0, 0xfff7eff0, 0x100feff0, 0xefefeff0, + 0x1007eff0, 0xeff7eff0, 0x080feff0, 0x00001008, 0x08081008, 0xf7f81008, 0x08001008, 0xf8001008, + 0x00081008, 0xfff81008, 0x10101008, 0xeff01008, 0x10081008, 0xeff81008, 0x08101008, 0xffffeff8, + 0x0807eff8, 0xf7f7eff8, 0x07ffeff8, 0xf7ffeff8, 0x0007eff8, 0xfff7eff8, 0x100feff8, 0xefefeff8, + 0x1007eff8, 0xeff7eff8, 0x080feff8, 0x00000810, 0x08080810, 0xf7f80810, 0x08000810, 0xf8000810, + 0x00080810, 0xfff80810, 0x10100810, 0xeff00810, 0x10080810, 0xeff80810, 0x08100810, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000909, 0xfffff6f7, 0x00000900, 0xfffff700, 0x00000009, 0xfffffff7, 0x00001212, + 0xffffedee, 0x00001b09, 0xffffe4f7, 0x0000091b, 0xfffff6e5, 0x000011ee, 0xffffee12, 0x00001af7, + 0xffffe509, 0xfffff71b, 0x000008e5, 0x00002412, 0xffffdbee, 0x00001224, 0xffffeddc, 0x00002424, + 0xffffdbdc, 0x00002d00, 0xffffd300, 0x0000002d, 0xffffffd3, 0x000023e5, 0xffffdc1b, 0xffffe524, + 0x00001adc, 0x0000482d, 0xffffb7d3, 0x00002d48, 0xffffd2b8, 0x000035ee, 0xffffca12, 0xffffee36, + 0x000011ca, 0x00004812, 0xffffb7ee, 0x00001248, 0xffffedb8, 0x00004848, 0xffffb7b8, 0x00005100, + 0xffffaf00, 0x00000051, 0xffffffaf, 0x000035ca, 0xffffca36, 0x000047dc, 0xffffb824, 0xffffdc48, + 0x000023b8, 0x0000752d, 0xffff8ad3, 0x00002d75, 0xffffd28b, 0x00007e51, 0xffff81af, 0x0000517e, + 0xffffae82, 0x000062e5, 0xffff9d1b, 0xffffe563, 0x00001a9d, 0x000062af, 0xffff9d51, 0xffffaf63, + 0x0000509d, 0x00003636, 0xffffc9ca, 0x00006c6c, 0xffff9394, 0x00000000, 0x09090000, 0xf6f70000, + 0x09000000, 0xf7000000, 0x00090000, 0xfff70000, 0x12120000, 0xedee0000, 0x1b090000, 0xe4f70000, + 0x091b0000, 0xf6e50000, 0x00000909, 0x09090909, 0xf6f70909, 0x09000909, 0xf7000909, 0x00090909, + 0xfff70909, 0x12120909, 0xedee0909, 0x1b090909, 0xe4f70909, 0x091b0909, 0xf6e50909, 0xfffff6f7, + 0x0908f6f7, 0xf6f6f6f7, 0x08fff6f7, 0xf6fff6f7, 0x0008f6f7, 0xfff6f6f7, 0x1211f6f7, 0xededf6f7, + 0x1b08f6f7, 0xe4f6f6f7, 0x091af6f7, 0xf6e4f6f7, 0x00000900, 0x09090900, 0xf6f70900, 0x09000900, + 0xf7000900, 0x00090900, 0xfff70900, 0x12120900, 0xedee0900, 0x1b090900, 0xe4f70900, 0x091b0900, + 0xf6e50900, 0xfffff700, 0x0908f700, 0xf6f6f700, 0x08fff700, 0xf6fff700, 0x0008f700, 0xfff6f700, + 0x1211f700, 0xededf700, 0x1b08f700, 0xe4f6f700, 0x091af700, 0xf6e4f700, 0x00000009, 0x09090009, + 0xf6f70009, 0x09000009, 0xf7000009, 0x00090009, 0xfff70009, 0x12120009, 0xedee0009, 0x1b090009, + 0xe4f70009, 0x091b0009, 0xf6e50009, 0xfffffff7, 0x0908fff7, 0xf6f6fff7, 0x08fffff7, 0xf6fffff7, + 0x0008fff7, 0xfff6fff7, 0x1211fff7, 0xededfff7, 0x1b08fff7, 0xe4f6fff7, 0x091afff7, 0xf6e4fff7, + 0x00001212, 0x09091212, 0xf6f71212, 0x09001212, 0xf7001212, 0x00091212, 0xfff71212, 0x12121212, + 0xedee1212, 0x1b091212, 0xe4f71212, 0x091b1212, 0xf6e51212, 0xffffedee, 0x0908edee, 0xf6f6edee, + 0x08ffedee, 0xf6ffedee, 0x0008edee, 0xfff6edee, 0x1211edee, 0xedededee, 0x1b08edee, 0xe4f6edee, + 0x091aedee, 0xf6e4edee, 0x00001b09, 0x09091b09, 0xf6f71b09, 0x09001b09, 0xf7001b09, 0x00091b09, + 0xfff71b09, 0x12121b09, 0xedee1b09, 0x1b091b09, 0xe4f71b09, 0x091b1b09, 0xf6e51b09, 0xffffe4f7, + 0x0908e4f7, 0xf6f6e4f7, 0x08ffe4f7, 0xf6ffe4f7, 0x0008e4f7, 0xfff6e4f7, 0x1211e4f7, 0xedede4f7, + 0x1b08e4f7, 0xe4f6e4f7, 0x091ae4f7, 0xf6e4e4f7, 0x0000091b, 0x0909091b, 0xf6f7091b, 0x0900091b, + 0xf700091b, 0x0009091b, 0xfff7091b, 0x1212091b, 0xedee091b, 0x1b09091b, 0xe4f7091b, 0x091b091b, + 0xf6e5091b, 0xfffff6e5, 0x0908f6e5, 0xf6f6f6e5, 0x08fff6e5, 0xf6fff6e5, 0x0008f6e5, 0xfff6f6e5, + 0x1211f6e5, 0xededf6e5, 0x1b08f6e5, 0xe4f6f6e5, 0x091af6e5, 0xf6e4f6e5, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000300, 0xfffffd00, 0x00000003, 0xfffffffd, 0x00000606, + 0xfffff9fa, 0x00000700, 0xfffff900, 0x00000007, 0xfffffff9, 0x000004fb, 0xfffffb05, 0xfffffb05, + 0x000004fb, 0x00000b06, 0xfffff4fa, 0x0000060b, 0xfffff9f5, 0x00000800, 0xfffff800, 0x00000008, + 0xfffffff8, 0x00000b0b, 0xfffff4f5, 0x00000c00, 0xfffff400, 0x0000000c, 0xfffffff4, 0x0000110c, + 0xffffeef4, 0x00000c11, 0xfffff3ef, 0x00001111, 0xffffeeef, 0x00001206, 0xffffedfa, 0x00000612, + 0xfffff9ee, 0x00000af8, 0xfffff508, 0xfffff80b, 0x000007f5, 0x00000f00, 0xfffff100, 0x0000000f, + 0xfffffff1, 0x00001400, 0xffffec00, 0x00000014, 0xffffffec, 0x00001912, 0xffffe6ee, 0x00001219, + 0xffffede7, 0x0000190b, 0xffffe6f5, 0x00000b19, 0xfffff4e7, 0x00001919, 0xffffe6e7, 0x00000df2, + 0xfffff20e, 0xfffff20e, 0x00000df2, 0x00001a00, 0xffffe600, 0x0000001a, 0xffffffe6, 0x000011f5, + 0xffffee0b, 0xfffff512, 0x00000aee, 0x000015f9, 0xffffea07, 0xfffff916, 0x000006ea, 0x0000221a, + 0xffffdde6, 0x00001a22, 0xffffe5de, 0x00002212, 0xffffddee, 0x00001222, 0xffffedde, 0x00002222, + 0xffffddde, 0x0000230b, 0xffffdcf5, 0x00000b23, 0xfffff4dd, 0x00001d00, 0xffffe300, 0x0000001d, + 0xffffffe3, 0x000015ed, 0xffffea13, 0xffffed16, 0x000012ea, 0x000019f1, 0xffffe60f, 0xfffff11a, + 0x00000ee6, 0x00002500, 0xffffdb00, 0x00000025, 0xffffffdb, 0x00002c1b, 0xffffd3e5, 0x00001b2c, + 0xffffe4d4, 0x00002c24, 0xffffd3dc, 0x0000242c, 0xffffdbd4, 0x00002c12, 0xffffd3ee, 0x0000122c, + 0xffffedd4, 0x000020f6, 0xffffdf0a, 0xfffff621, 0x000009df, 0x00002d2d, 0xffffd2d3, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000300, 0xfffffd00, 0x00000003, 0xfffffffd, 0x00000606, + 0xfffff9fa, 0x00000700, 0xfffff900, 0x02020000, 0x02020202, 0x0201fdfe, 0x02020300, 0x0201fd00, + 0x02020003, 0x0201fffd, 0x02020606, 0x0201f9fa, 0x02020700, 0x0201f900, 0xfdfe0000, 0xfdfe0202, + 0xfdfdfdfe, 0xfdfe0300, 0xfdfdfd00, 0xfdfe0003, 0xfdfdfffd, 0xfdfe0606, 0xfdfdf9fa, 0xfdfe0700, + 0xfdfdf900, 0x03000000, 0x03000202, 0x02fffdfe, 0x03000300, 0x02fffd00, 0x03000003, 0x02fffffd, + 0x03000606, 0x02fff9fa, 0x03000700, 0x02fff900, 0xfd000000, 0xfd000202, 0xfcfffdfe, 0xfd000300, + 0xfcfffd00, 0xfd000003, 0xfcfffffd, 0xfd000606, 0xfcfff9fa, 0xfd000700, 0xfcfff900, 0x00030000, + 0x00030202, 0x0002fdfe, 0x00030300, 0x0002fd00, 0x00030003, 0x0002fffd, 0x00030606, 0x0002f9fa, + 0x00030700, 0x0002f900, 0xfffd0000, 0xfffd0202, 0xfffcfdfe, 0xfffd0300, 0xfffcfd00, 0xfffd0003, + 0xfffcfffd, 0xfffd0606, 0xfffcf9fa, 0xfffd0700, 0xfffcf900, 0x06060000, 0x06060202, 0x0605fdfe, + 0x06060300, 0x0605fd00, 0x06060003, 0x0605fffd, 0x06060606, 0x0605f9fa, 0x06060700, 0x0605f900, + 0xf9fa0000, 0xf9fa0202, 0xf9f9fdfe, 0xf9fa0300, 0xf9f9fd00, 0xf9fa0003, 0xf9f9fffd, 0xf9fa0606, + 0xf9f9f9fa, 0xf9fa0700, 0xf9f9f900, 0x07000000, 0x07000202, 0x06fffdfe, 0x07000300, 0x06fffd00, + 0x07000003, 0x06fffffd, 0x07000606, 0x06fff9fa, 0x07000700, 0x06fff900, 0xf9000000, 0xf9000202, + 0xf8fffdfe, 0xf9000300, 0xf8fffd00, 0xf9000003, 0xf8fffffd, 0xf9000606, 0xf8fff9fa, 0xf9000700, + 0xf8fff900, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000200, 0xfffffe00, 0x00000002, 0xfffffffe, 0x00000202, 0xfffffdfe, 0x00000606, + 0xfffff9fa, 0x00000600, 0xfffffa00, 0x00000006, 0xfffffffa, 0x000003fc, 0xfffffc04, 0xfffffa0a, + 0x000005f6, 0xfffff400, 0x00000c00, 0xfffff3fa, 0xfffff406, 0x00000bfa, 0x00000c06, 0xfffffff2, + 0x0000000e, 0x00000c0c, 0xfffff3f4, 0xffffee00, 0x00001200, 0xfffff40e, 0x00000bf2, 0xfffff9ee, + 0xfffffa12, 0x000005ee, 0x00000612, 0xffffedf6, 0xffffee0a, 0x000011f6, 0x0000120a, 0xffffffea, + 0x00000016, 0xffffe800, 0x00001800, 0xfffff3ea, 0xfffff416, 0x00000bea, 0x00000c16, 0xffffe7f8, + 0xffffe808, 0x000017f8, 0x00001808, 0xfffff9e6, 0xfffffa1a, 0x000005e6, 0x0000061a, 0xffffffe4, + 0x0000001c, 0x00001414, 0xffffebec, 0xffffe5f2, 0x00001a0e, 0xfffff3e2, 0x00000c1e, 0xffffdff6, + 0x0000200a, 0xffffdfee, 0x00002012, 0xffffe5e6, 0x00001a1a, 0xffffebde, 0x00001422, 0xfffff3da, + 0x00000c26, 0xffffdfe0, 0x00002020, 0x00002020, 0xffffd7ea, 0xffffddde, 0x00002222, 0x00000000, + 0x00000200, 0xfffffe00, 0x00000002, 0xfffffffe, 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, + 0x00000600, 0xfffffa00, 0x00000006, 0xfffffffa, 0x02000000, 0x02000200, 0x01fffe00, 0x02000002, + 0x01fffffe, 0x02000202, 0x01fffdfe, 0x02000606, 0x01fff9fa, 0x02000600, 0x01fffa00, 0x02000006, + 0x01fffffa, 0xfe000000, 0xfe000200, 0xfdfffe00, 0xfe000002, 0xfdfffffe, 0xfe000202, 0xfdfffdfe, + 0xfe000606, 0xfdfff9fa, 0xfe000600, 0xfdfffa00, 0xfe000006, 0xfdfffffa, 0x00020000, 0x00020200, + 0x0001fe00, 0x00020002, 0x0001fffe, 0x00020202, 0x0001fdfe, 0x00020606, 0x0001f9fa, 0x00020600, + 0x0001fa00, 0x00020006, 0x0001fffa, 0xfffe0000, 0xfffe0200, 0xfffdfe00, 0xfffe0002, 0xfffdfffe, + 0xfffe0202, 0xfffdfdfe, 0xfffe0606, 0xfffdf9fa, 0xfffe0600, 0xfffdfa00, 0xfffe0006, 0xfffdfffa, + 0x02020000, 0x02020200, 0x0201fe00, 0x02020002, 0x0201fffe, 0x02020202, 0x0201fdfe, 0x02020606, + 0x0201f9fa, 0x02020600, 0x0201fa00, 0x02020006, 0x0201fffa, 0xfdfe0000, 0xfdfe0200, 0xfdfdfe00, + 0xfdfe0002, 0xfdfdfffe, 0xfdfe0202, 0xfdfdfdfe, 0xfdfe0606, 0xfdfdf9fa, 0xfdfe0600, 0xfdfdfa00, + 0xfdfe0006, 0xfdfdfffa, 0x06060000, 0x06060200, 0x0605fe00, 0x06060002, 0x0605fffe, 0x06060202, + 0x0605fdfe, 0x06060606, 0x0605f9fa, 0x06060600, 0x0605fa00, 0x06060006, 0x0605fffa, 0xf9fa0000, + 0xf9fa0200, 0xf9f9fe00, 0xf9fa0002, 0xf9f9fffe, 0xf9fa0202, 0xf9f9fdfe, 0xf9fa0606, 0xf9f9f9fa, + 0xf9fa0600, 0xf9f9fa00, 0xf9fa0006, 0xf9f9fffa, 0x06000000, 0x06000200, 0x05fffe00, 0x06000002, + 0x05fffffe, 0x06000202, 0x05fffdfe, 0x06000606, 0x05fff9fa, 0x06000600, 0x05fffa00, 0x06000006, + 0x05fffffa, 0xfa000000, 0xfa000200, 0xf9fffe00, 0xfa000002, 0xf9fffffe, 0xfa000202, 0xf9fffdfe, + 0xfa000606, 0xf9fff9fa, 0xfa000600, 0xf9fffa00, 0xfa000006, 0xf9fffffa, 0x00060000, 0x00060200, + 0x0005fe00, 0x00060002, 0x0005fffe, 0x00060202, 0x0005fdfe, 0x00060606, 0x0005f9fa, 0x00060600, + 0x0005fa00, 0x00060006, 0x0005fffa, 0xfffa0000, 0xfffa0200, 0xfff9fe00, 0xfffa0002, 0xfff9fffe, + 0xfffa0202, 0xfff9fdfe, 0xfffa0606, 0xfff9f9fa, 0xfffa0600, 0xfff9fa00, 0xfffa0006, 0xfff9fffa, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000200, 0xfffffe00, 0x00000002, 0xfffffffe, 0x00000404, 0xfffffbfc, 0x00000a0a, + 0xfffff5f6, 0x00000a00, 0xfffff600, 0x0000000a, 0xfffffff6, 0x000005fa, 0xfffffa06, 0xfffff80e, + 0x000007f2, 0xffffffee, 0x00000012, 0xfffff00a, 0x00000ff6, 0xffffe800, 0x00001800, 0xfffff7e8, + 0xfffff818, 0x000007e8, 0x00000818, 0x00001212, 0xffffedee, 0xfffff014, 0x00000fec, 0xffffe5f2, + 0xffffe60e, 0x000019f2, 0x00001a0e, 0xffffffe2, 0x0000001e, 0xffffde00, 0x00002200, 0xfffff7de, + 0xfffff822, 0x000007de, 0x00000822, 0xffffede2, 0xffffee1e, 0x000011e2, 0x0000121e, 0xffffddf6, + 0xffffde0a, 0x000021f6, 0x0000220a, 0xffffddec, 0x00002214, 0xffffffd8, 0x00000028, 0x00001e1e, + 0xffffe1e2, 0xffffedd8, 0x00001228, 0xffffd400, 0x00002c00, 0xffffd3f0, 0x00002c10, 0xffffdbdc, + 0xffffdbdc, 0x00002424, 0xffffd3e6, 0x00002c1a, 0xffffe5d2, 0x00001a2e, 0xffffedcc, 0x00001234, + 0xffffc9ec, 0xffffd3d4, 0x00002c2c, 0xffffc9e0, 0xffffd1d2, 0xffffd1d2, 0x00002e2e, 0x00000000, + 0x00000200, 0xfffffe00, 0x00000002, 0xfffffffe, 0x00000404, 0xfffffbfc, 0x00000a0a, 0xfffff5f6, + 0x00000a00, 0xfffff600, 0x0000000a, 0xfffffff6, 0x02000000, 0x02000200, 0x01fffe00, 0x02000002, + 0x01fffffe, 0x02000404, 0x01fffbfc, 0x02000a0a, 0x01fff5f6, 0x02000a00, 0x01fff600, 0x0200000a, + 0x01fffff6, 0xfe000000, 0xfe000200, 0xfdfffe00, 0xfe000002, 0xfdfffffe, 0xfe000404, 0xfdfffbfc, + 0xfe000a0a, 0xfdfff5f6, 0xfe000a00, 0xfdfff600, 0xfe00000a, 0xfdfffff6, 0x00020000, 0x00020200, + 0x0001fe00, 0x00020002, 0x0001fffe, 0x00020404, 0x0001fbfc, 0x00020a0a, 0x0001f5f6, 0x00020a00, + 0x0001f600, 0x0002000a, 0x0001fff6, 0xfffe0000, 0xfffe0200, 0xfffdfe00, 0xfffe0002, 0xfffdfffe, + 0xfffe0404, 0xfffdfbfc, 0xfffe0a0a, 0xfffdf5f6, 0xfffe0a00, 0xfffdf600, 0xfffe000a, 0xfffdfff6, + 0x04040000, 0x04040200, 0x0403fe00, 0x04040002, 0x0403fffe, 0x04040404, 0x0403fbfc, 0x04040a0a, + 0x0403f5f6, 0x04040a00, 0x0403f600, 0x0404000a, 0x0403fff6, 0xfbfc0000, 0xfbfc0200, 0xfbfbfe00, + 0xfbfc0002, 0xfbfbfffe, 0xfbfc0404, 0xfbfbfbfc, 0xfbfc0a0a, 0xfbfbf5f6, 0xfbfc0a00, 0xfbfbf600, + 0xfbfc000a, 0xfbfbfff6, 0x0a0a0000, 0x0a0a0200, 0x0a09fe00, 0x0a0a0002, 0x0a09fffe, 0x0a0a0404, + 0x0a09fbfc, 0x0a0a0a0a, 0x0a09f5f6, 0x0a0a0a00, 0x0a09f600, 0x0a0a000a, 0x0a09fff6, 0xf5f60000, + 0xf5f60200, 0xf5f5fe00, 0xf5f60002, 0xf5f5fffe, 0xf5f60404, 0xf5f5fbfc, 0xf5f60a0a, 0xf5f5f5f6, + 0xf5f60a00, 0xf5f5f600, 0xf5f6000a, 0xf5f5fff6, 0x0a000000, 0x0a000200, 0x09fffe00, 0x0a000002, + 0x09fffffe, 0x0a000404, 0x09fffbfc, 0x0a000a0a, 0x09fff5f6, 0x0a000a00, 0x09fff600, 0x0a00000a, + 0x09fffff6, 0xf6000000, 0xf6000200, 0xf5fffe00, 0xf6000002, 0xf5fffffe, 0xf6000404, 0xf5fffbfc, + 0xf6000a0a, 0xf5fff5f6, 0xf6000a00, 0xf5fff600, 0xf600000a, 0xf5fffff6, 0x000a0000, 0x000a0200, + 0x0009fe00, 0x000a0002, 0x0009fffe, 0x000a0404, 0x0009fbfc, 0x000a0a0a, 0x0009f5f6, 0x000a0a00, + 0x0009f600, 0x000a000a, 0x0009fff6, 0xfff60000, 0xfff60200, 0xfff5fe00, 0xfff60002, 0xfff5fffe, + 0xfff60404, 0xfff5fbfc, 0xfff60a0a, 0xfff5f5f6, 0xfff60a00, 0xfff5f600, 0xfff6000a, 0xfff5fff6, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000400, 0xfffffc00, 0x00000004, 0xfffffffc, 0x00000404, 0xfffffbfc, 0x00000c0c, + 0xfffff3f4, 0x00000c00, 0xfffff400, 0x0000000c, 0xfffffff4, 0x000007f8, 0xfffff808, 0xfffff008, + 0x00000ff8, 0xffffe800, 0x00001800, 0xfffff7e8, 0xfffff818, 0x000007e8, 0x00000818, 0xfffff014, + 0x00000fec, 0xffffffe4, 0x0000001c, 0xffffe7f0, 0xffffe810, 0x000017f0, 0x00001810, 0xffffe000, + 0x00002000, 0xffffefe4, 0xfffff01c, 0x00000fe4, 0x0000101c, 0xffffdff8, 0xffffe008, 0xfffff7e0, + 0xfffff820, 0x000007e0, 0x00000820, 0x00001ff8, 0x00002008, 0x00001818, 0xffffe7e8, 0xffffe818, + 0x000017e8, 0xffffdfec, 0x00002014, 0xffffffd8, 0x00000028, 0xffffefd8, 0x00001028, 0xffffd400, + 0xffffd400, 0xffffffd4, 0x0000002c, 0x00002c00, 0x00002c00, 0xffffdfe0, 0x00002020, 0xffffd3f0, + 0x00002c10, 0xffffd3e8, 0xffffe7d4, 0x0000182c, 0x00002c18, 0xffffefd0, 0x00001030, 0xffffdbdc, + 0xffffdbdc, 0x00002424, 0x00002424, 0xffffcbec, 0x00002828, 0xffffd7d8, 0xffffcbe0, 0x00000000, + 0x00000400, 0xfffffc00, 0x00000004, 0xfffffffc, 0x00000404, 0xfffffbfc, 0x00000c0c, 0xfffff3f4, + 0x00000c00, 0xfffff400, 0x0000000c, 0xfffffff4, 0x04000000, 0x04000400, 0x03fffc00, 0x04000004, + 0x03fffffc, 0x04000404, 0x03fffbfc, 0x04000c0c, 0x03fff3f4, 0x04000c00, 0x03fff400, 0x0400000c, + 0x03fffff4, 0xfc000000, 0xfc000400, 0xfbfffc00, 0xfc000004, 0xfbfffffc, 0xfc000404, 0xfbfffbfc, + 0xfc000c0c, 0xfbfff3f4, 0xfc000c00, 0xfbfff400, 0xfc00000c, 0xfbfffff4, 0x00040000, 0x00040400, + 0x0003fc00, 0x00040004, 0x0003fffc, 0x00040404, 0x0003fbfc, 0x00040c0c, 0x0003f3f4, 0x00040c00, + 0x0003f400, 0x0004000c, 0x0003fff4, 0xfffc0000, 0xfffc0400, 0xfffbfc00, 0xfffc0004, 0xfffbfffc, + 0xfffc0404, 0xfffbfbfc, 0xfffc0c0c, 0xfffbf3f4, 0xfffc0c00, 0xfffbf400, 0xfffc000c, 0xfffbfff4, + 0x04040000, 0x04040400, 0x0403fc00, 0x04040004, 0x0403fffc, 0x04040404, 0x0403fbfc, 0x04040c0c, + 0x0403f3f4, 0x04040c00, 0x0403f400, 0x0404000c, 0x0403fff4, 0xfbfc0000, 0xfbfc0400, 0xfbfbfc00, + 0xfbfc0004, 0xfbfbfffc, 0xfbfc0404, 0xfbfbfbfc, 0xfbfc0c0c, 0xfbfbf3f4, 0xfbfc0c00, 0xfbfbf400, + 0xfbfc000c, 0xfbfbfff4, 0x0c0c0000, 0x0c0c0400, 0x0c0bfc00, 0x0c0c0004, 0x0c0bfffc, 0x0c0c0404, + 0x0c0bfbfc, 0x0c0c0c0c, 0x0c0bf3f4, 0x0c0c0c00, 0x0c0bf400, 0x0c0c000c, 0x0c0bfff4, 0xf3f40000, + 0xf3f40400, 0xf3f3fc00, 0xf3f40004, 0xf3f3fffc, 0xf3f40404, 0xf3f3fbfc, 0xf3f40c0c, 0xf3f3f3f4, + 0xf3f40c00, 0xf3f3f400, 0xf3f4000c, 0xf3f3fff4, 0x0c000000, 0x0c000400, 0x0bfffc00, 0x0c000004, + 0x0bfffffc, 0x0c000404, 0x0bfffbfc, 0x0c000c0c, 0x0bfff3f4, 0x0c000c00, 0x0bfff400, 0x0c00000c, + 0x0bfffff4, 0xf4000000, 0xf4000400, 0xf3fffc00, 0xf4000004, 0xf3fffffc, 0xf4000404, 0xf3fffbfc, + 0xf4000c0c, 0xf3fff3f4, 0xf4000c00, 0xf3fff400, 0xf400000c, 0xf3fffff4, 0x000c0000, 0x000c0400, + 0x000bfc00, 0x000c0004, 0x000bfffc, 0x000c0404, 0x000bfbfc, 0x000c0c0c, 0x000bf3f4, 0x000c0c00, + 0x000bf400, 0x000c000c, 0x000bfff4, 0xfff40000, 0xfff40400, 0xfff3fc00, 0xfff40004, 0xfff3fffc, + 0xfff40404, 0xfff3fbfc, 0xfff40c0c, 0xfff3f3f4, 0xfff40c00, 0xfff3f400, 0xfff4000c, 0xfff3fff4, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, + 0xffffebec, 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, 0xffffebec, + 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x02020000, 0x02020202, 0x0201fdfe, 0x02020606, + 0x0201f9fa, 0x02020c0c, 0x0201f3f4, 0x02021414, 0x0201ebec, 0x02022020, 0x0201dfe0, 0x02022e2e, + 0x0201d1d2, 0xfdfe0000, 0xfdfe0202, 0xfdfdfdfe, 0xfdfe0606, 0xfdfdf9fa, 0xfdfe0c0c, 0xfdfdf3f4, + 0xfdfe1414, 0xfdfdebec, 0xfdfe2020, 0xfdfddfe0, 0xfdfe2e2e, 0xfdfdd1d2, 0x06060000, 0x06060202, + 0x0605fdfe, 0x06060606, 0x0605f9fa, 0x06060c0c, 0x0605f3f4, 0x06061414, 0x0605ebec, 0x06062020, + 0x0605dfe0, 0x06062e2e, 0x0605d1d2, 0xf9fa0000, 0xf9fa0202, 0xf9f9fdfe, 0xf9fa0606, 0xf9f9f9fa, + 0xf9fa0c0c, 0xf9f9f3f4, 0xf9fa1414, 0xf9f9ebec, 0xf9fa2020, 0xf9f9dfe0, 0xf9fa2e2e, 0xf9f9d1d2, + 0x0c0c0000, 0x0c0c0202, 0x0c0bfdfe, 0x0c0c0606, 0x0c0bf9fa, 0x0c0c0c0c, 0x0c0bf3f4, 0x0c0c1414, + 0x0c0bebec, 0x0c0c2020, 0x0c0bdfe0, 0x0c0c2e2e, 0x0c0bd1d2, 0xf3f40000, 0xf3f40202, 0xf3f3fdfe, + 0xf3f40606, 0xf3f3f9fa, 0xf3f40c0c, 0xf3f3f3f4, 0xf3f41414, 0xf3f3ebec, 0xf3f42020, 0xf3f3dfe0, + 0xf3f42e2e, 0xf3f3d1d2, 0x14140000, 0x14140202, 0x1413fdfe, 0x14140606, 0x1413f9fa, 0x14140c0c, + 0x1413f3f4, 0x14141414, 0x1413ebec, 0x14142020, 0x1413dfe0, 0x14142e2e, 0x1413d1d2, 0xebec0000, + 0xebec0202, 0xebebfdfe, 0xebec0606, 0xebebf9fa, 0xebec0c0c, 0xebebf3f4, 0xebec1414, 0xebebebec, + 0xebec2020, 0xebebdfe0, 0xebec2e2e, 0xebebd1d2, 0x20200000, 0x20200202, 0x201ffdfe, 0x20200606, + 0x201ff9fa, 0x20200c0c, 0x201ff3f4, 0x20201414, 0x201febec, 0x20202020, 0x201fdfe0, 0x20202e2e, + 0x201fd1d2, 0xdfe00000, 0xdfe00202, 0xdfdffdfe, 0xdfe00606, 0xdfdff9fa, 0xdfe00c0c, 0xdfdff3f4, + 0xdfe01414, 0xdfdfebec, 0xdfe02020, 0xdfdfdfe0, 0xdfe02e2e, 0xdfdfd1d2, 0x2e2e0000, 0x2e2e0202, + 0x2e2dfdfe, 0x2e2e0606, 0x2e2df9fa, 0x2e2e0c0c, 0x2e2df3f4, 0x2e2e1414, 0x2e2debec, 0x2e2e2020, + 0x2e2ddfe0, 0x2e2e2e2e, 0x2e2dd1d2, 0xd1d20000, 0xd1d20202, 0xd1d1fdfe, 0xd1d20606, 0xd1d1f9fa, + 0xd1d20c0c, 0xd1d1f3f4, 0xd1d21414, 0xd1d1ebec, 0xd1d22020, 0xd1d1dfe0, 0xd1d22e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, + 0xffffebec, 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, 0xffffebec, + 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x02020000, 0x02020202, 0x0201fdfe, 0x02020606, + 0x0201f9fa, 0x02020c0c, 0x0201f3f4, 0x02021414, 0x0201ebec, 0x02022020, 0x0201dfe0, 0x02022e2e, + 0x0201d1d2, 0xfdfe0000, 0xfdfe0202, 0xfdfdfdfe, 0xfdfe0606, 0xfdfdf9fa, 0xfdfe0c0c, 0xfdfdf3f4, + 0xfdfe1414, 0xfdfdebec, 0xfdfe2020, 0xfdfddfe0, 0xfdfe2e2e, 0xfdfdd1d2, 0x06060000, 0x06060202, + 0x0605fdfe, 0x06060606, 0x0605f9fa, 0x06060c0c, 0x0605f3f4, 0x06061414, 0x0605ebec, 0x06062020, + 0x0605dfe0, 0x06062e2e, 0x0605d1d2, 0xf9fa0000, 0xf9fa0202, 0xf9f9fdfe, 0xf9fa0606, 0xf9f9f9fa, + 0xf9fa0c0c, 0xf9f9f3f4, 0xf9fa1414, 0xf9f9ebec, 0xf9fa2020, 0xf9f9dfe0, 0xf9fa2e2e, 0xf9f9d1d2, + 0x0c0c0000, 0x0c0c0202, 0x0c0bfdfe, 0x0c0c0606, 0x0c0bf9fa, 0x0c0c0c0c, 0x0c0bf3f4, 0x0c0c1414, + 0x0c0bebec, 0x0c0c2020, 0x0c0bdfe0, 0x0c0c2e2e, 0x0c0bd1d2, 0xf3f40000, 0xf3f40202, 0xf3f3fdfe, + 0xf3f40606, 0xf3f3f9fa, 0xf3f40c0c, 0xf3f3f3f4, 0xf3f41414, 0xf3f3ebec, 0xf3f42020, 0xf3f3dfe0, + 0xf3f42e2e, 0xf3f3d1d2, 0x14140000, 0x14140202, 0x1413fdfe, 0x14140606, 0x1413f9fa, 0x14140c0c, + 0x1413f3f4, 0x14141414, 0x1413ebec, 0x14142020, 0x1413dfe0, 0x14142e2e, 0x1413d1d2, 0xebec0000, + 0xebec0202, 0xebebfdfe, 0xebec0606, 0xebebf9fa, 0xebec0c0c, 0xebebf3f4, 0xebec1414, 0xebebebec, + 0xebec2020, 0xebebdfe0, 0xebec2e2e, 0xebebd1d2, 0x20200000, 0x20200202, 0x201ffdfe, 0x20200606, + 0x201ff9fa, 0x20200c0c, 0x201ff3f4, 0x20201414, 0x201febec, 0x20202020, 0x201fdfe0, 0x20202e2e, + 0x201fd1d2, 0xdfe00000, 0xdfe00202, 0xdfdffdfe, 0xdfe00606, 0xdfdff9fa, 0xdfe00c0c, 0xdfdff3f4, + 0xdfe01414, 0xdfdfebec, 0xdfe02020, 0xdfdfdfe0, 0xdfe02e2e, 0xdfdfd1d2, 0x2e2e0000, 0x2e2e0202, + 0x2e2dfdfe, 0x2e2e0606, 0x2e2df9fa, 0x2e2e0c0c, 0x2e2df3f4, 0x2e2e1414, 0x2e2debec, 0x2e2e2020, + 0x2e2ddfe0, 0x2e2e2e2e, 0x2e2dd1d2, 0xd1d20000, 0xd1d20202, 0xd1d1fdfe, 0xd1d20606, 0xd1d1f9fa, + 0xd1d20c0c, 0xd1d1f3f4, 0xd1d21414, 0xd1d1ebec, 0xd1d22020, 0xd1d1dfe0, 0xd1d22e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, + 0xffffebec, 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, 0xffffebec, + 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x02020000, 0x02020202, 0x0201fdfe, 0x02020606, + 0x0201f9fa, 0x02020c0c, 0x0201f3f4, 0x02021414, 0x0201ebec, 0x02022020, 0x0201dfe0, 0x02022e2e, + 0x0201d1d2, 0xfdfe0000, 0xfdfe0202, 0xfdfdfdfe, 0xfdfe0606, 0xfdfdf9fa, 0xfdfe0c0c, 0xfdfdf3f4, + 0xfdfe1414, 0xfdfdebec, 0xfdfe2020, 0xfdfddfe0, 0xfdfe2e2e, 0xfdfdd1d2, 0x06060000, 0x06060202, + 0x0605fdfe, 0x06060606, 0x0605f9fa, 0x06060c0c, 0x0605f3f4, 0x06061414, 0x0605ebec, 0x06062020, + 0x0605dfe0, 0x06062e2e, 0x0605d1d2, 0xf9fa0000, 0xf9fa0202, 0xf9f9fdfe, 0xf9fa0606, 0xf9f9f9fa, + 0xf9fa0c0c, 0xf9f9f3f4, 0xf9fa1414, 0xf9f9ebec, 0xf9fa2020, 0xf9f9dfe0, 0xf9fa2e2e, 0xf9f9d1d2, + 0x0c0c0000, 0x0c0c0202, 0x0c0bfdfe, 0x0c0c0606, 0x0c0bf9fa, 0x0c0c0c0c, 0x0c0bf3f4, 0x0c0c1414, + 0x0c0bebec, 0x0c0c2020, 0x0c0bdfe0, 0x0c0c2e2e, 0x0c0bd1d2, 0xf3f40000, 0xf3f40202, 0xf3f3fdfe, + 0xf3f40606, 0xf3f3f9fa, 0xf3f40c0c, 0xf3f3f3f4, 0xf3f41414, 0xf3f3ebec, 0xf3f42020, 0xf3f3dfe0, + 0xf3f42e2e, 0xf3f3d1d2, 0x14140000, 0x14140202, 0x1413fdfe, 0x14140606, 0x1413f9fa, 0x14140c0c, + 0x1413f3f4, 0x14141414, 0x1413ebec, 0x14142020, 0x1413dfe0, 0x14142e2e, 0x1413d1d2, 0xebec0000, + 0xebec0202, 0xebebfdfe, 0xebec0606, 0xebebf9fa, 0xebec0c0c, 0xebebf3f4, 0xebec1414, 0xebebebec, + 0xebec2020, 0xebebdfe0, 0xebec2e2e, 0xebebd1d2, 0x20200000, 0x20200202, 0x201ffdfe, 0x20200606, + 0x201ff9fa, 0x20200c0c, 0x201ff3f4, 0x20201414, 0x201febec, 0x20202020, 0x201fdfe0, 0x20202e2e, + 0x201fd1d2, 0xdfe00000, 0xdfe00202, 0xdfdffdfe, 0xdfe00606, 0xdfdff9fa, 0xdfe00c0c, 0xdfdff3f4, + 0xdfe01414, 0xdfdfebec, 0xdfe02020, 0xdfdfdfe0, 0xdfe02e2e, 0xdfdfd1d2, 0x2e2e0000, 0x2e2e0202, + 0x2e2dfdfe, 0x2e2e0606, 0x2e2df9fa, 0x2e2e0c0c, 0x2e2df3f4, 0x2e2e1414, 0x2e2debec, 0x2e2e2020, + 0x2e2ddfe0, 0x2e2e2e2e, 0x2e2dd1d2, 0xd1d20000, 0xd1d20202, 0xd1d1fdfe, 0xd1d20606, 0xd1d1f9fa, + 0xd1d20c0c, 0xd1d1f3f4, 0xd1d21414, 0xd1d1ebec, 0xd1d22020, 0xd1d1dfe0, 0xd1d22e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, + 0xffffebec, 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000202, 0xfffffdfe, 0x00000606, 0xfffff9fa, 0x00000c0c, 0xfffff3f4, 0x00001414, 0xffffebec, + 0x00002020, 0xffffdfe0, 0x00002e2e, 0xffffd1d2, 0x02020000, 0x02020202, 0x0201fdfe, 0x02020606, + 0x0201f9fa, 0x02020c0c, 0x0201f3f4, 0x02021414, 0x0201ebec, 0x02022020, 0x0201dfe0, 0x02022e2e, + 0x0201d1d2, 0xfdfe0000, 0xfdfe0202, 0xfdfdfdfe, 0xfdfe0606, 0xfdfdf9fa, 0xfdfe0c0c, 0xfdfdf3f4, + 0xfdfe1414, 0xfdfdebec, 0xfdfe2020, 0xfdfddfe0, 0xfdfe2e2e, 0xfdfdd1d2, 0x06060000, 0x06060202, + 0x0605fdfe, 0x06060606, 0x0605f9fa, 0x06060c0c, 0x0605f3f4, 0x06061414, 0x0605ebec, 0x06062020, + 0x0605dfe0, 0x06062e2e, 0x0605d1d2, 0xf9fa0000, 0xf9fa0202, 0xf9f9fdfe, 0xf9fa0606, 0xf9f9f9fa, + 0xf9fa0c0c, 0xf9f9f3f4, 0xf9fa1414, 0xf9f9ebec, 0xf9fa2020, 0xf9f9dfe0, 0xf9fa2e2e, 0xf9f9d1d2, + 0x0c0c0000, 0x0c0c0202, 0x0c0bfdfe, 0x0c0c0606, 0x0c0bf9fa, 0x0c0c0c0c, 0x0c0bf3f4, 0x0c0c1414, + 0x0c0bebec, 0x0c0c2020, 0x0c0bdfe0, 0x0c0c2e2e, 0x0c0bd1d2, 0xf3f40000, 0xf3f40202, 0xf3f3fdfe, + 0xf3f40606, 0xf3f3f9fa, 0xf3f40c0c, 0xf3f3f3f4, 0xf3f41414, 0xf3f3ebec, 0xf3f42020, 0xf3f3dfe0, + 0xf3f42e2e, 0xf3f3d1d2, 0x14140000, 0x14140202, 0x1413fdfe, 0x14140606, 0x1413f9fa, 0x14140c0c, + 0x1413f3f4, 0x14141414, 0x1413ebec, 0x14142020, 0x1413dfe0, 0x14142e2e, 0x1413d1d2, 0xebec0000, + 0xebec0202, 0xebebfdfe, 0xebec0606, 0xebebf9fa, 0xebec0c0c, 0xebebf3f4, 0xebec1414, 0xebebebec, + 0xebec2020, 0xebebdfe0, 0xebec2e2e, 0xebebd1d2, 0x20200000, 0x20200202, 0x201ffdfe, 0x20200606, + 0x201ff9fa, 0x20200c0c, 0x201ff3f4, 0x20201414, 0x201febec, 0x20202020, 0x201fdfe0, 0x20202e2e, + 0x201fd1d2, 0xdfe00000, 0xdfe00202, 0xdfdffdfe, 0xdfe00606, 0xdfdff9fa, 0xdfe00c0c, 0xdfdff3f4, + 0xdfe01414, 0xdfdfebec, 0xdfe02020, 0xdfdfdfe0, 0xdfe02e2e, 0xdfdfd1d2, 0x2e2e0000, 0x2e2e0202, + 0x2e2dfdfe, 0x2e2e0606, 0x2e2df9fa, 0x2e2e0c0c, 0x2e2df3f4, 0x2e2e1414, 0x2e2debec, 0x2e2e2020, + 0x2e2ddfe0, 0x2e2e2e2e, 0x2e2dd1d2, 0xd1d20000, 0xd1d20202, 0xd1d1fdfe, 0xd1d20606, 0xd1d1f9fa, + 0xd1d20c0c, 0xd1d1f3f4, 0xd1d21414, 0xd1d1ebec, 0xd1d22020, 0xd1d1dfe0, 0xd1d22e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + + +const uint32 Indeo3::correctionloworder[] = { + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, 0x04040404, + 0xfbfbfbfc, 0x05050101, 0xfafafeff, 0x01010505, 0xfefefafb, 0x0403fbfc, 0xfbfc0404, 0x0605fdfe, + 0xf9fa0202, 0xfdfe0606, 0x0201f9fa, 0x09090404, 0xf6f6fbfc, 0x04040909, 0xfbfbf6f7, 0x09090909, + 0xf6f6f6f7, 0x0a0a0101, 0xf5f5feff, 0x01010a0a, 0xfefef5f6, 0x0807fafb, 0xf7f80505, 0xfafb0808, + 0x0504f7f8, 0x0f0f0909, 0xf0f0f6f7, 0x09090f0f, 0xf6f6f0f1, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, + 0x0302f3f4, 0x10100404, 0xefeffbfc, 0x04041010, 0xfbfbeff0, 0x10101010, 0xefefeff0, 0x12120000, + 0xedee0000, 0x00001212, 0xffffedee, 0x0c0bf3f4, 0xf3f40c0c, 0x100ff6f7, 0xeff00909, 0xf6f71010, + 0x0908eff0, 0x1b1b0b0b, 0xe4e4f4f5, 0x0b0b1b1b, 0xf4f4e4e5, 0x1c1c1313, 0xe3e3eced, 0x13131c1c, + 0xecece3e4, 0x1615f9fa, 0xe9ea0606, 0xf9fa1616, 0x0605e9ea, 0x1d1d0404, 0xe2e2fbfc, 0x04041d1d, + 0xfbfbe2e3, 0x1e1e1e1e, 0xe1e1e1e2, 0x2120fdfe, 0xdedf0202, 0xfdfe2121, 0x0201dedf, 0x1716edee, + 0xe8e91212, 0xedee1717, 0x1211e8e9, 0x1e1df0f1, 0xe1e20f0f, 0xf0f11e1e, 0x0f0ee1e2, 0x2e2e1616, + 0xd1d1e9ea, 0x16162e2e, 0xe9e9d1d2, 0x2f2f0d0d, 0xd0d0f2f3, 0x0d0d2f2f, 0xf2f2d0d1, 0x31312323, + 0xcecedcdd, 0x23233131, 0xdcdccecf, 0x2928f4f5, 0xd6d70b0b, 0xf4f52929, 0x0b0ad6d7, 0x33330404, + 0xccccfbfc, 0x04043333, 0xfbfbcccd, 0x36363636, 0xc9c9c9ca, 0x2221ddde, 0xddde2222, 0x2a29e2e3, + 0xd5d61d1d, 0xe2e32a2a, 0x1d1cd5d6, 0x3c3bf9fa, 0xc3c40606, 0xf9fa3c3c, 0x0605c3c4, 0x4c4c1b1b, + 0xb3b3e4e5, 0x1b1b4c4c, 0xe4e4b3b4, 0x4d4d2b2b, 0xb2b2d4d5, 0x2b2b4d4d, 0xd4d4b2b3, 0x3736e7e8, + 0xc8c91818, 0xe7e83737, 0x1817c8c9, 0x4f4f0e0e, 0xb0b0f1f2, 0x0e0e4f4f, 0xf1f1b0b1, 0x53533f3f, + 0xacacc0c1, 0x3f3f5353, 0xc0c0acad, 0x4a49ebec, 0xb5b61414, 0xebec4a4a, 0x1413b5b6, 0x58580202, + 0xa7a7fdfe, 0x02025858, 0xfdfda7a8, 0x5d5d5d5d, 0xa2a2a2a3, 0x3d3ccbcc, 0xc2c33434, 0xcbcc3d3d, + 0x3433c2c3, 0x78783434, 0x8787cbcc, 0x34347878, 0xcbcb8788, 0x4b4ad2d3, 0xb4b52d2d, 0xd2d34b4b, + 0x2d2cb4b5, 0x7d7d4b4b, 0x8282b4b5, 0x4b4b7d7d, 0xb4b48283, 0x7a7a2121, 0x8585dedf, 0x21217a7a, + 0xdede8586, 0x6766f2f3, 0x98990d0d, 0xf2f36767, 0x0d0c9899, 0x605fd7d8, 0x9fa02828, 0xd7d86060, + 0x28279fa0, 0x7f7eddde, 0x80812222, 0xddde7f7f, 0x22218081, 0x5958a6a7, 0xa6a75959, 0x6968b1b2, + 0x96974e4e, 0xb1b26969, 0x4e4d9697, 0x0c0c0c0c, 0xf3f3f3f4, 0x17171717, 0xe8e8e8e9, 0x2a2a2a2a, + 0xd5d5d5d6, 0x49494949, 0xb6b6b6b7, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0x0302feff, 0x0302feff, 0x0302feff, 0x0302feff, 0x0302feff, 0x0302feff, 0x0302feff, 0xfcfd0101, + 0xfcfd0101, 0xfcfd0101, 0xfcfd0101, 0xfcfd0101, 0xfcfd0101, 0xfcfd0101, 0xfeff0303, 0xfeff0303, + 0xfeff0303, 0xfeff0303, 0xfeff0303, 0xfeff0303, 0xfeff0303, 0x0100fcfd, 0x0100fcfd, 0x0100fcfd, + 0x0100fcfd, 0x0100fcfd, 0x0100fcfd, 0x0100fcfd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, + 0xf8f8f8f9, 0x08080202, 0xf7f7fdfe, 0x02020808, 0xfdfdf7f8, 0x0908fdfe, 0xf6f70202, 0xfdfe0909, + 0x0201f6f7, 0x0605f9fa, 0xf9fa0606, 0x0d0d0606, 0xf2f2f9fa, 0x06060d0d, 0xf9f9f2f3, 0x0d0d0d0d, + 0xf2f2f2f3, 0x0e0e0101, 0xf1f1feff, 0x01010e0e, 0xfefef1f2, 0x0c0bf7f8, 0xf3f40808, 0xf7f80c0c, + 0x0807f3f4, 0x17170e0e, 0xe8e8f1f2, 0x0e0e1717, 0xf1f1e8e9, 0x1211fafb, 0xedee0505, 0xfafb1212, + 0x0504edee, 0x18180606, 0xe7e7f9fa, 0x06061818, 0xf9f9e7e8, 0x18181818, 0xe7e7e7e8, 0x1b1afeff, + 0xe4e50101, 0xfeff1b1b, 0x0100e4e5, 0x1110eeef, 0xeeef1111, 0x1716f2f3, 0xe8e90d0d, 0xf2f31717, + 0x0d0ce8e9, 0x28281010, 0xd7d7eff0, 0x10102828, 0xefefd7d8, 0x29291c1c, 0xd6d6e3e4, 0x1c1c2929, + 0xe3e3d6d7, 0x2120f6f7, 0xdedf0909, 0xf6f72121, 0x0908dedf, 0x2b2b0606, 0xd4d4f9fa, 0x06062b2b, + 0xf9f9d4d5, 0x2e2e2e2e, 0xd1d1d1d2, 0x3231fbfc, 0xcdce0404, 0xfbfc3232, 0x0403cdce, 0x2221e4e5, + 0xddde1b1b, 0xe4e52222, 0x1b1addde, 0x2d2ce9ea, 0xd2d31616, 0xe9ea2d2d, 0x1615d2d3, 0x45452222, + 0xbabaddde, 0x22224545, 0xddddbabb, 0x46461313, 0xb9b9eced, 0x13134646, 0xececb9ba, 0x49493535, + 0xb6b6cacb, 0x35354949, 0xcacab6b7, 0x3e3deeef, 0xc1c21111, 0xeeef3e3e, 0x1110c1c2, 0x4d4d0505, + 0xb2b2fafb, 0x05054d4d, 0xfafab2b3, 0x52525252, 0xadadadae, 0x3332cccd, 0xcccd3333, 0x403fd4d5, + 0xbfc02b2b, 0xd4d54040, 0x2b2abfc0, 0x5a59f5f6, 0xa5a60a0a, 0xf5f65a5a, 0x0a09a5a6, 0x72722929, + 0x8d8dd6d7, 0x29297272, 0xd6d68d8e, 0x74744040, 0x8b8bbfc0, 0x40407474, 0xbfbf8b8c, 0x5251dadb, + 0xadae2525, 0xdadb5252, 0x2524adae, 0x77771616, 0x8888e9ea, 0x16167777, 0xe9e98889, 0x7c7c5f5f, + 0x8383a0a1, 0x5f5f7c7c, 0xa0a08384, 0x6f6ee1e2, 0x90911e1e, 0xe1e26f6f, 0x1e1d9091, 0x5c5bb1b2, + 0xa3a44e4e, 0xb1b25c5c, 0x4e4da3a4, 0x7170bbbc, 0x8e8f4444, 0xbbbc7171, 0x44438e8f, 0x12121212, + 0xedededee, 0x22222222, 0xddddddde, 0x3f3f3f3f, 0xc0c0c0c1, 0x6d6d6d6d, 0x92929293, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, + 0x03030303, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, + 0xfcfcfcfd, 0xfcfcfcfd, 0x0403feff, 0x0403feff, 0x0403feff, 0x0403feff, 0x0403feff, 0x0403feff, + 0x0403feff, 0x0403feff, 0x0403feff, 0xfbfc0101, 0xfbfc0101, 0xfbfc0101, 0xfbfc0101, 0xfbfc0101, + 0xfbfc0101, 0xfbfc0101, 0xfbfc0101, 0xfbfc0101, 0xfeff0404, 0xfeff0404, 0xfeff0404, 0xfeff0404, + 0xfeff0404, 0xfeff0404, 0xfeff0404, 0xfeff0404, 0xfeff0404, 0x0100fbfc, 0x0100fbfc, 0x0100fbfc, + 0x0100fbfc, 0x0100fbfc, 0x0100fbfc, 0x0100fbfc, 0x0100fbfc, 0x0100fbfc, 0x07070707, 0x07070707, + 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0xf8f8f8f9, + 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, + 0xf5f5fcfd, 0x03030a0a, 0xfcfcf5f6, 0x09090909, 0xf6f6f6f7, 0x0706f8f9, 0xf8f90707, 0x0c0bfcfd, + 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x11110808, 0xeeeef7f8, 0x08081111, 0xf7f7eeef, 0x11111111, + 0xeeeeeeef, 0x13130101, 0xececfeff, 0x01011313, 0xfefeeced, 0x100ff4f5, 0xeff00b0b, 0xf4f51010, + 0x0b0aeff0, 0x1716f9fa, 0xe8e90606, 0xf9fa1717, 0x0605e8e9, 0x1f1f1212, 0xe0e0edee, 0x12121f1f, + 0xedede0e1, 0x20200808, 0xdfdff7f8, 0x08082020, 0xf7f7dfe0, 0x21212121, 0xdedededf, 0x2423feff, + 0xdbdc0101, 0xfeff2424, 0x0100dbdc, 0x1716e8e9, 0xe8e91717, 0x1f1eeeef, 0xe0e11111, 0xeeef1f1f, + 0x1110e0e1, 0x36361515, 0xc9c9eaeb, 0x15153636, 0xeaeac9ca, 0x37372525, 0xc8c8dadb, 0x25253737, + 0xdadac8c9, 0x2c2bf3f4, 0xd3d40c0c, 0xf3f42c2c, 0x0c0bd3d4, 0x39390808, 0xc6c6f7f8, 0x08083939, + 0xf7f7c6c7, 0x3d3d3d3d, 0xc2c2c2c3, 0x4241fafb, 0xbdbe0505, 0xfafb4242, 0x0504bdbe, 0x2d2cdbdc, + 0xd2d32424, 0xdbdc2d2d, 0x2423d2d3, 0x3c3be2e3, 0xc3c41d1d, 0xe2e33c3c, 0x1d1cc3c4, 0x5c5c2d2d, + 0xa3a3d2d3, 0x2d2d5c5c, 0xd2d2a3a4, 0x5d5d1919, 0xa2a2e6e7, 0x19195d5d, 0xe6e6a2a3, 0x61614747, + 0x9e9eb8b9, 0x47476161, 0xb8b89e9f, 0x5352e9ea, 0xacad1616, 0xe9ea5353, 0x1615acad, 0x66660707, + 0x9999f8f9, 0x07076666, 0xf8f8999a, 0x6d6d6d6d, 0x92929293, 0x4443bbbc, 0xbbbc4444, 0x5554c6c7, + 0xaaab3939, 0xc6c75555, 0x3938aaab, 0x7877f2f3, 0x87880d0d, 0xf2f37878, 0x0d0c8788, 0x6e6dcecf, + 0x91923131, 0xcecf6e6e, 0x31309192, 0x7b7a9798, 0x84856868, 0x97987b7b, 0x68678485, 0x18181818, + 0xe7e7e7e8, 0x2e2e2e2e, 0xd1d1d1d2, 0x54545454, 0xabababac, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04040404, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, + 0x04040404, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, + 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0x0504feff, 0x0504feff, 0x0504feff, 0x0504feff, 0x0504feff, + 0x0504feff, 0x0504feff, 0x0504feff, 0x0504feff, 0x0504feff, 0xfafb0101, 0xfafb0101, 0xfafb0101, + 0xfafb0101, 0xfafb0101, 0xfafb0101, 0xfafb0101, 0xfafb0101, 0xfafb0101, 0xfafb0101, 0xfeff0505, + 0xfeff0505, 0xfeff0505, 0xfeff0505, 0xfeff0505, 0xfeff0505, 0xfeff0505, 0xfeff0505, 0xfeff0505, + 0xfeff0505, 0x0100fafb, 0x0100fafb, 0x0100fafb, 0x0100fafb, 0x0100fafb, 0x0100fafb, 0x0100fafb, + 0x0100fafb, 0x0100fafb, 0x0100fafb, 0x0a0a0303, 0x0a0a0303, 0x0a0a0303, 0x0a0a0303, 0x0a0a0303, + 0x0a0a0303, 0x0a0a0303, 0x0a0a0303, 0x0a0a0303, 0x0a0a0303, 0xf5f5fcfd, 0xf5f5fcfd, 0xf5f5fcfd, + 0xf5f5fcfd, 0xf5f5fcfd, 0xf5f5fcfd, 0xf5f5fcfd, 0xf5f5fcfd, 0xf5f5fcfd, 0xf5f5fcfd, 0x03030a0a, + 0x03030a0a, 0x03030a0a, 0x03030a0a, 0x03030a0a, 0x03030a0a, 0x03030a0a, 0x03030a0a, 0x03030a0a, + 0x03030a0a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, + 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x03030d0d, 0xfcfcf2f3, 0x0908f6f7, 0xf6f70909, 0x0f0efbfc, + 0xf0f10404, 0xfbfc0f0f, 0x0403f0f1, 0x16160b0b, 0xe9e9f4f5, 0x0b0b1616, 0xf4f4e9ea, 0x15151515, + 0xeaeaeaeb, 0x18180202, 0xe7e7fdfe, 0x02021818, 0xfdfde7e8, 0x1413f1f2, 0xebec0e0e, 0xf1f21414, + 0x0e0debec, 0x26261717, 0xd9d9e8e9, 0x17172626, 0xe8e8d9da, 0x1d1cf7f8, 0xe2e30808, 0xf7f81d1d, + 0x0807e2e3, 0x27270b0b, 0xd8d8f4f5, 0x0b0b2727, 0xf4f4d8d9, 0x29292929, 0xd6d6d6d7, 0x2d2cfeff, + 0xd2d30101, 0xfeff2d2d, 0x0100d2d3, 0x1d1ce2e3, 0xe2e31d1d, 0x2726e9ea, 0xd8d91616, 0xe9ea2727, + 0x1615d8d9, 0x43431b1b, 0xbcbce4e5, 0x1b1b4343, 0xe4e4bcbd, 0x45452f2f, 0xbabad0d1, 0x2f2f4545, + 0xd0d0babb, 0x3837f0f1, 0xc7c80f0f, 0xf0f13838, 0x0f0ec7c8, 0x47470b0b, 0xb8b8f4f5, 0x0b0b4747, + 0xf4f4b8b9, 0x4c4c4c4c, 0xb3b3b3b4, 0x5352f9fa, 0xacad0606, 0xf9fa5353, 0x0605acad, 0x3938d2d3, + 0xc6c72d2d, 0xd2d33939, 0x2d2cc6c7, 0x4b4adbdc, 0xb4b52424, 0xdbdc4b4b, 0x2423b4b5, 0x73733838, + 0x8c8cc7c8, 0x38387373, 0xc7c78c8d, 0x75751f1f, 0x8a8ae0e1, 0x1f1f7575, 0xe0e08a8b, 0x7a7a5858, + 0x8585a7a8, 0x58587a7a, 0xa7a78586, 0x6867e3e4, 0x97981c1c, 0xe3e46868, 0x1c1b9798, 0x5554aaab, + 0xaaab5555, 0x6a69b7b8, 0x95964848, 0xb7b86a6a, 0x48479596, 0x1e1e1e1e, 0xe1e1e1e2, 0x3a3a3a3a, + 0xc5c5c5c6, 0x69696969, 0x96969697, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05050505, 0x05050505, + 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, + 0x05050505, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, + 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, + 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, 0x0706fdfe, 0xf8f90202, + 0xf8f90202, 0xf8f90202, 0xf8f90202, 0xf8f90202, 0xf8f90202, 0xf8f90202, 0xf8f90202, 0xf8f90202, + 0xf8f90202, 0xf8f90202, 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, + 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, 0xfdfe0707, 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, + 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, 0x0201f8f9, + 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, + 0x0b0b0b0b, 0x0b0b0b0b, 0x0b0b0b0b, 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, + 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, 0xf4f4f4f5, 0x0d0d0303, 0x0d0d0303, + 0x0d0d0303, 0x0d0d0303, 0x0d0d0303, 0x0d0d0303, 0x0d0d0303, 0x0d0d0303, 0x0d0d0303, 0x0d0d0303, + 0x0d0d0303, 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, + 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, 0xf2f2fcfd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, + 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0xfbfbf0f1, 0x0b0af4f5, 0xf4f50b0b, 0x1211fafb, + 0xedee0505, 0xfafb1212, 0x0504edee, 0x1a1a0d0d, 0xe5e5f2f3, 0x0d0d1a1a, 0xf2f2e5e6, 0x1a1a1a1a, + 0xe5e5e5e6, 0x1d1d0202, 0xe2e2fdfe, 0x02021d1d, 0xfdfde2e3, 0x1817eff0, 0xe7e81010, 0xeff01818, + 0x100fe7e8, 0x2e2e1c1c, 0xd1d1e3e4, 0x1c1c2e2e, 0xe3e3d1d2, 0x2322f6f7, 0xdcdd0909, 0xf6f72323, + 0x0908dcdd, 0x2f2f0d0d, 0xd0d0f2f3, 0x0d0d2f2f, 0xf2f2d0d1, 0x31313131, 0xcecececf, 0x3635feff, + 0xc9ca0101, 0xfeff3636, 0x0100c9ca, 0x2322dcdd, 0xdcdd2323, 0x2f2ee5e6, 0xd0d11a1a, 0xe5e62f2f, + 0x1a19d0d1, 0x51512020, 0xaeaedfe0, 0x20205151, 0xdfdfaeaf, 0x53533838, 0xacacc7c8, 0x38385353, + 0xc7c7acad, 0x4342edee, 0xbcbd1212, 0xedee4343, 0x1211bcbd, 0x56560d0d, 0xa9a9f2f3, 0x0d0d5656, + 0xf2f2a9aa, 0x5b5b5b5b, 0xa4a4a4a5, 0x6362f8f9, 0x9c9d0707, 0xf8f96363, 0x07069c9d, 0x4443c9ca, + 0xbbbc3636, 0xc9ca4444, 0x3635bbbc, 0x5a59d3d4, 0xa5a62c2c, 0xd3d45a5a, 0x2c2ba5a6, 0x7c7bdedf, + 0x83842121, 0xdedf7c7c, 0x21208384, 0x67669899, 0x98996767, 0x7f7ea9aa, 0x80815656, 0xa9aa7f7f, + 0x56558081, 0x25252525, 0xdadadadb, 0x45454545, 0xbabababb, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, + 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0x0807fdfe, 0xf7f80202, 0xf7f80202, 0xf7f80202, + 0xf7f80202, 0xf7f80202, 0xf7f80202, 0xf7f80202, 0xf7f80202, 0xf7f80202, 0xf7f80202, 0xf7f80202, + 0xf7f80202, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, + 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0xfdfe0808, 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, + 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, 0x0201f7f8, + 0x0201f7f8, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, + 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0x0d0d0d0d, 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, + 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, 0xf2f2f2f3, + 0xf2f2f2f3, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, + 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0x0f0f0404, 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, + 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, 0xf0f0fbfc, + 0xf0f0fbfc, 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x04040f0f, + 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x04040f0f, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, + 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0xfafaedee, 0x0d0cf2f3, 0xf2f30d0d, 0x1514f9fa, + 0xeaeb0606, 0xf9fa1515, 0x0605eaeb, 0x1e1e0f0f, 0xe1e1f0f1, 0x0f0f1e1e, 0xf0f0e1e2, 0x1e1e1e1e, + 0xe1e1e1e2, 0x22220202, 0xddddfdfe, 0x02022222, 0xfdfdddde, 0x1c1beced, 0xe3e41313, 0xeced1c1c, + 0x1312e3e4, 0x36362020, 0xc9c9dfe0, 0x20203636, 0xdfdfc9ca, 0x2928f4f5, 0xd6d70b0b, 0xf4f52929, + 0x0b0ad6d7, 0x37370f0f, 0xc8c8f0f1, 0x0f0f3737, 0xf0f0c8c9, 0x39393939, 0xc6c6c6c7, 0x3f3efeff, + 0xc0c10101, 0xfeff3f3f, 0x0100c0c1, 0x2827d7d8, 0xd7d82828, 0x3736e1e2, 0xc8c91e1e, 0xe1e23737, + 0x1e1dc8c9, 0x5e5e2525, 0xa1a1dadb, 0x25255e5e, 0xdadaa1a2, 0x60604141, 0x9f9fbebf, 0x41416060, + 0xbebe9fa0, 0x4e4deaeb, 0xb1b21515, 0xeaeb4e4e, 0x1514b1b2, 0x64640f0f, 0x9b9bf0f1, 0x0f0f6464, + 0xf0f09b9c, 0x6a6a6a6a, 0x95959596, 0x7473f7f8, 0x8b8c0808, 0xf7f87474, 0x08078b8c, 0x4f4ec0c1, + 0xb0b13f3f, 0xc0c14f4f, 0x3f3eb0b1, 0x6968cccd, 0x96973333, 0xcccd6969, 0x33329697, 0x78778788, + 0x87887878, 0x2b2b2b2b, 0xd4d4d4d5, 0x50505050, 0xafafafb0, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, + 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, + 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, + 0xf8f8f8f9, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, + 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0x0a09fcfd, 0xf5f60303, 0xf5f60303, 0xf5f60303, + 0xf5f60303, 0xf5f60303, 0xf5f60303, 0xf5f60303, 0xf5f60303, 0xf5f60303, 0xf5f60303, 0xf5f60303, + 0xf5f60303, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, + 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0xfcfd0a0a, 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, + 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, 0x0302f5f6, + 0x0302f5f6, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0xefefeff0, 0xefefeff0, 0xefefeff0, + 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, + 0xefefeff0, 0x12120505, 0x12120505, 0x12120505, 0x12120505, 0x12120505, 0x12120505, 0x12120505, + 0x12120505, 0x12120505, 0x12120505, 0x12120505, 0x12120505, 0xededfafb, 0xededfafb, 0xededfafb, + 0xededfafb, 0xededfafb, 0xededfafb, 0xededfafb, 0xededfafb, 0xededfafb, 0xededfafb, 0xededfafb, + 0xededfafb, 0x05051212, 0x05051212, 0x05051212, 0x05051212, 0x05051212, 0x05051212, 0x05051212, + 0x05051212, 0x05051212, 0x05051212, 0x05051212, 0x05051212, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, + 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0xfafaebec, 0x0f0ef0f1, 0xf0f10f0f, 0x1817f8f9, + 0xe7e80707, 0xf8f91818, 0x0706e7e8, 0x23231111, 0xdcdceeef, 0x11112323, 0xeeeedcdd, 0x22222222, + 0xddddddde, 0x26260303, 0xd9d9fcfd, 0x03032626, 0xfcfcd9da, 0x201fe9ea, 0xdfe01616, 0xe9ea2020, + 0x1615dfe0, 0x3d3d2525, 0xc2c2dadb, 0x25253d3d, 0xdadac2c3, 0x2f2ef2f3, 0xd0d10d0d, 0xf2f32f2f, + 0x0d0cd0d1, 0x3f3f1111, 0xc0c0eeef, 0x11113f3f, 0xeeeec0c1, 0x41414141, 0xbebebebf, 0x4847feff, + 0xb7b80101, 0xfeff4848, 0x0100b7b8, 0x2e2dd1d2, 0xd1d22e2e, 0x3f3edcdd, 0xc0c12323, 0xdcdd3f3f, + 0x2322c0c1, 0x6b6b2b2b, 0x9494d4d5, 0x2b2b6b6b, 0xd4d49495, 0x6e6e4b4b, 0x9191b4b5, 0x4b4b6e6e, + 0xb4b49192, 0x5958e7e8, 0xa6a71818, 0xe7e85959, 0x1817a6a7, 0x72721111, 0x8d8deeef, 0x11117272, + 0xeeee8d8e, 0x79797979, 0x86868687, 0x5b5ab7b8, 0xa4a54848, 0xb7b85b5b, 0x4847a4a5, 0x7877c5c6, + 0x87883a3a, 0xc5c67878, 0x3a398788, 0x31313131, 0xcecececf, 0x5c5c5c5c, 0xa3a3a3a4, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, + 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0xf7f7f7f8, + 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, + 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, + 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0x0b0afcfd, 0xf4f50303, + 0xf4f50303, 0xf4f50303, 0xf4f50303, 0xf4f50303, 0xf4f50303, 0xf4f50303, 0xf4f50303, 0xf4f50303, + 0xf4f50303, 0xf4f50303, 0xf4f50303, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, + 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0xfcfd0b0b, 0x0302f4f5, + 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, + 0x0302f4f5, 0x0302f4f5, 0x0302f4f5, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, + 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0xedededee, + 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, + 0xedededee, 0xedededee, 0xedededee, 0x14140505, 0x14140505, 0x14140505, 0x14140505, 0x14140505, + 0x14140505, 0x14140505, 0x14140505, 0x14140505, 0x14140505, 0x14140505, 0x14140505, 0xebebfafb, + 0xebebfafb, 0xebebfafb, 0xebebfafb, 0xebebfafb, 0xebebfafb, 0xebebfafb, 0xebebfafb, 0xebebfafb, + 0xebebfafb, 0xebebfafb, 0xebebfafb, 0x05051414, 0x05051414, 0x05051414, 0x05051414, 0x05051414, + 0x05051414, 0x05051414, 0x05051414, 0x05051414, 0x05051414, 0x05051414, 0x05051414, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, + 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x1110eeef, 0xeeef1111, 0x1b1af8f9, + 0xe4e50707, 0xf8f91b1b, 0x0706e4e5, 0x27271313, 0xd8d8eced, 0x13132727, 0xececd8d9, 0x27272727, + 0xd8d8d8d9, 0x2b2b0303, 0xd4d4fcfd, 0x03032b2b, 0xfcfcd4d5, 0x2423e7e8, 0xdbdc1818, 0xe7e82424, + 0x1817dbdc, 0x45452a2a, 0xbabad5d6, 0x2a2a4545, 0xd5d5babb, 0x3534f1f2, 0xcacb0e0e, 0xf1f23535, + 0x0e0dcacb, 0x47471313, 0xb8b8eced, 0x13134747, 0xececb8b9, 0x49494949, 0xb6b6b6b7, 0x504ffdfe, + 0xafb00202, 0xfdfe5050, 0x0201afb0, 0x3433cbcc, 0xcbcc3434, 0x4645d8d9, 0xb9ba2727, 0xd8d94646, + 0x2726b9ba, 0x79793030, 0x8686cfd0, 0x30307979, 0xcfcf8687, 0x7c7c5454, 0x8383abac, 0x54547c7c, + 0xabab8384, 0x6463e4e5, 0x9b9c1b1b, 0xe4e56464, 0x1b1a9b9c, 0x6665aeaf, 0x999a5151, 0xaeaf6666, + 0x5150999a, 0x37373737, 0xc8c8c8c9, 0x68686868, 0x97979798, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, + 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0xf6f6f6f7, + 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, + 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, + 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, 0x0c0bfcfd, + 0x0c0bfcfd, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, + 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xf3f40303, 0xfcfd0c0c, 0xfcfd0c0c, + 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, + 0xfcfd0c0c, 0xfcfd0c0c, 0xfcfd0c0c, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, + 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, 0x0302f3f4, + 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, + 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0xebebebec, 0xebebebec, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, + 0xebebebec, 0xebebebec, 0x17170606, 0x17170606, 0x17170606, 0x17170606, 0x17170606, 0x17170606, + 0x17170606, 0x17170606, 0x17170606, 0x17170606, 0x17170606, 0x17170606, 0x17170606, 0xe8e8f9fa, + 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, + 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0xe8e8f9fa, 0x06061717, 0x06061717, 0x06061717, 0x06061717, + 0x06061717, 0x06061717, 0x06061717, 0x06061717, 0x06061717, 0x06061717, 0x06061717, 0x06061717, + 0x06061717, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, + 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0xf9f9e8e9, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, + 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x0403fbfc, 0xfbfc0404, 0x0605fdfe, + 0xf9fa0202, 0xfdfe0606, 0x0201f9fa, 0x08080404, 0xf7f7fbfc, 0x04040808, 0xfbfbf7f8, 0x08080808, + 0xf7f7f7f8, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x0807fbfc, 0xf7f80404, 0xfbfc0808, + 0x0403f7f8, 0x0e0e0808, 0xf1f1f7f8, 0x08080e0e, 0xf7f7f1f2, 0x0c0bfdfe, 0xf3f40202, 0xfdfe0c0c, + 0x0201f3f4, 0x10100404, 0xefeffbfc, 0x04041010, 0xfbfbeff0, 0x10101010, 0xefefeff0, 0x12120000, + 0xedee0000, 0x00001212, 0xffffedee, 0x0c0bf3f4, 0xf3f40c0c, 0x100ff7f8, 0xeff00808, 0xf7f81010, + 0x0807eff0, 0x1a1a0a0a, 0xe5e5f5f6, 0x0a0a1a1a, 0xf5f5e5e6, 0x1c1c1212, 0xe3e3edee, 0x12121c1c, + 0xedede3e4, 0x1615f9fa, 0xe9ea0606, 0xf9fa1616, 0x0605e9ea, 0x1c1c0404, 0xe3e3fbfc, 0x04041c1c, + 0xfbfbe3e4, 0x1e1e1e1e, 0xe1e1e1e2, 0x201ffdfe, 0xdfe00202, 0xfdfe2020, 0x0201dfe0, 0x1615edee, + 0xe9ea1212, 0xedee1616, 0x1211e9ea, 0x1e1df1f2, 0xe1e20e0e, 0xf1f21e1e, 0x0e0de1e2, 0x2e2e1616, + 0xd1d1e9ea, 0x16162e2e, 0xe9e9d1d2, 0x2e2e0c0c, 0xd1d1f3f4, 0x0c0c2e2e, 0xf3f3d1d2, 0x30302222, + 0xcfcfddde, 0x22223030, 0xddddcfd0, 0x2827f5f6, 0xd7d80a0a, 0xf5f62828, 0x0a09d7d8, 0x32320404, + 0xcdcdfbfc, 0x04043232, 0xfbfbcdce, 0x36363636, 0xc9c9c9ca, 0x2221ddde, 0xddde2222, 0x2a29e3e4, + 0xd5d61c1c, 0xe3e42a2a, 0x1c1bd5d6, 0x3c3bf9fa, 0xc3c40606, 0xf9fa3c3c, 0x0605c3c4, 0x4c4c1a1a, + 0xb3b3e5e6, 0x1a1a4c4c, 0xe5e5b3b4, 0x4c4c2a2a, 0xb3b3d5d6, 0x2a2a4c4c, 0xd5d5b3b4, 0x3635e7e8, + 0xc9ca1818, 0xe7e83636, 0x1817c9ca, 0x4e4e0e0e, 0xb1b1f1f2, 0x0e0e4e4e, 0xf1f1b1b2, 0x52523e3e, + 0xadadc1c2, 0x3e3e5252, 0xc1c1adae, 0x4a49ebec, 0xb5b61414, 0xebec4a4a, 0x1413b5b6, 0x58580202, + 0xa7a7fdfe, 0x02025858, 0xfdfda7a8, 0x5c5c5c5c, 0xa3a3a3a4, 0x3c3bcbcc, 0xc3c43434, 0xcbcc3c3c, + 0x3433c3c4, 0x76763434, 0x8989cbcc, 0x34347676, 0xcbcb898a, 0x4a49d3d4, 0xb5b62c2c, 0xd3d44a4a, + 0x2c2bb5b6, 0x76764a4a, 0x8989b5b6, 0x4a4a7676, 0xb5b5898a, 0x76762020, 0x8989dfe0, 0x20207676, + 0xdfdf898a, 0x6665f3f4, 0x999a0c0c, 0xf3f46666, 0x0c0b999a, 0x605fd7d8, 0x9fa02828, 0xd7d86060, + 0x28279fa0, 0x7675ddde, 0x898a2222, 0xddde7676, 0x2221898a, 0x5857a7a8, 0xa7a85858, 0x6867b1b2, + 0x97984e4e, 0xb1b26868, 0x4e4d9798, 0x0c0c0c0c, 0xf3f3f3f4, 0x16161616, 0xe9e9e9ea, 0x2a2a2a2a, + 0xd5d5d5d6, 0x48484848, 0xb7b7b7b8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0xfdfe0000, + 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0x00000202, 0x00000202, + 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, + 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, + 0xf9f9f9fa, 0x09090303, 0xf6f6fcfd, 0x03030909, 0xfcfcf6f7, 0x0908fcfd, 0xf6f70303, 0xfcfd0909, + 0x0302f6f7, 0x0605f9fa, 0xf9fa0606, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0xf9f9f3f4, 0x0c0c0c0c, + 0xf3f3f3f4, 0x0f0f0000, 0xf0f10000, 0x00000f0f, 0xfffff0f1, 0x0c0bf6f7, 0xf3f40909, 0xf6f70c0c, + 0x0908f3f4, 0x18180f0f, 0xe7e7f0f1, 0x0f0f1818, 0xf0f0e7e8, 0x1211f9fa, 0xedee0606, 0xf9fa1212, + 0x0605edee, 0x18180606, 0xe7e7f9fa, 0x06061818, 0xf9f9e7e8, 0x18181818, 0xe7e7e7e8, 0x1b1b0000, + 0xe4e50000, 0x00001b1b, 0xffffe4e5, 0x1211edee, 0xedee1212, 0x1817f3f4, 0xe7e80c0c, 0xf3f41818, + 0x0c0be7e8, 0x27270f0f, 0xd8d8f0f1, 0x0f0f2727, 0xf0f0d8d9, 0x2a2a1b1b, 0xd5d5e4e5, 0x1b1b2a2a, + 0xe4e4d5d6, 0x2120f6f7, 0xdedf0909, 0xf6f72121, 0x0908dedf, 0x2a2a0606, 0xd5d5f9fa, 0x06062a2a, + 0xf9f9d5d6, 0x2d2d2d2d, 0xd2d2d2d3, 0x3332fcfd, 0xcccd0303, 0xfcfd3333, 0x0302cccd, 0x2120e4e5, + 0xdedf1b1b, 0xe4e52121, 0x1b1adedf, 0x2d2ceaeb, 0xd2d31515, 0xeaeb2d2d, 0x1514d2d3, 0x45452121, + 0xbabadedf, 0x21214545, 0xdedebabb, 0x45451212, 0xbabaedee, 0x12124545, 0xededbabb, 0x48483636, + 0xb7b7c9ca, 0x36364848, 0xc9c9b7b8, 0x3f3eedee, 0xc0c11212, 0xedee3f3f, 0x1211c0c1, 0x4e4e0606, + 0xb1b1f9fa, 0x06064e4e, 0xf9f9b1b2, 0x51515151, 0xaeaeaeaf, 0x3332cccd, 0xcccd3333, 0x3f3ed5d6, + 0xc0c12a2a, 0xd5d63f3f, 0x2a29c0c1, 0x5a59f6f7, 0xa5a60909, 0xf6f75a5a, 0x0908a5a6, 0x72722a2a, + 0x8d8dd5d6, 0x2a2a7272, 0xd5d58d8e, 0x75753f3f, 0x8a8ac0c1, 0x3f3f7575, 0xc0c08a8b, 0x5150dbdc, + 0xaeaf2424, 0xdbdc5151, 0x2423aeaf, 0x78781515, 0x8787eaeb, 0x15157878, 0xeaea8788, 0x7b7b6060, + 0x84849fa0, 0x60607b7b, 0x9f9f8485, 0x6f6ee1e2, 0x90911e1e, 0xe1e26f6f, 0x1e1d9091, 0x5d5cb1b2, + 0xa2a34e4e, 0xb1b25d5d, 0x4e4da2a3, 0x7271babb, 0x8d8e4545, 0xbabb7272, 0x45448d8e, 0x12121212, + 0xedededee, 0x21212121, 0xdedededf, 0x3f3f3f3f, 0xc0c0c0c1, 0x6c6c6c6c, 0x93939394, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, 0x03030303, + 0x03030303, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, 0xfcfcfcfd, + 0xfcfcfcfd, 0xfcfcfcfd, 0x03030000, 0x03030000, 0x03030000, 0x03030000, 0x03030000, 0x03030000, + 0x03030000, 0x03030000, 0x03030000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, + 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0x00000303, 0x00000303, 0x00000303, 0x00000303, + 0x00000303, 0x00000303, 0x00000303, 0x00000303, 0x00000303, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, + 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, + 0xf7f7fbfc, 0x04040808, 0xfbfbf7f8, 0x08080808, 0xf7f7f7f8, 0x0807f7f8, 0xf7f80808, 0x0c0bfbfc, + 0xf3f40404, 0xfbfc0c0c, 0x0403f3f4, 0x10100808, 0xefeff7f8, 0x08081010, 0xf7f7eff0, 0x10101010, + 0xefefeff0, 0x14140000, 0xebec0000, 0x00001414, 0xffffebec, 0x100ff3f4, 0xeff00c0c, 0xf3f41010, + 0x0c0beff0, 0x1817fbfc, 0xe7e80404, 0xfbfc1818, 0x0403e7e8, 0x20201010, 0xdfdfeff0, 0x10102020, + 0xefefdfe0, 0x20200808, 0xdfdff7f8, 0x08082020, 0xf7f7dfe0, 0x20202020, 0xdfdfdfe0, 0x24240000, + 0xdbdc0000, 0x00002424, 0xffffdbdc, 0x1817e7e8, 0xe7e81818, 0x201feff0, 0xdfe01010, 0xeff02020, + 0x100fdfe0, 0x34341414, 0xcbcbebec, 0x14143434, 0xebebcbcc, 0x38382424, 0xc7c7dbdc, 0x24243838, + 0xdbdbc7c8, 0x2c2bf3f4, 0xd3d40c0c, 0xf3f42c2c, 0x0c0bd3d4, 0x38380808, 0xc7c7f7f8, 0x08083838, + 0xf7f7c7c8, 0x3c3c3c3c, 0xc3c3c3c4, 0x403ffbfc, 0xbfc00404, 0xfbfc4040, 0x0403bfc0, 0x2c2bdbdc, + 0xd3d42424, 0xdbdc2c2c, 0x2423d3d4, 0x3c3be3e4, 0xc3c41c1c, 0xe3e43c3c, 0x1c1bc3c4, 0x5c5c2c2c, + 0xa3a3d3d4, 0x2c2c5c5c, 0xd3d3a3a4, 0x5c5c1818, 0xa3a3e7e8, 0x18185c5c, 0xe7e7a3a4, 0x60604848, + 0x9f9fb7b8, 0x48486060, 0xb7b79fa0, 0x5453ebec, 0xabac1414, 0xebec5454, 0x1413abac, 0x64640808, + 0x9b9bf7f8, 0x08086464, 0xf7f79b9c, 0x6c6c6c6c, 0x93939394, 0x4443bbbc, 0xbbbc4444, 0x5453c7c8, + 0xabac3838, 0xc7c85454, 0x3837abac, 0x7877f3f4, 0x87880c0c, 0xf3f47878, 0x0c0b8788, 0x6c6bcfd0, + 0x93943030, 0xcfd06c6c, 0x302f9394, 0x7c7b9798, 0x83846868, 0x97987c7c, 0x68678384, 0x18181818, + 0xe7e7e7e8, 0x2c2c2c2c, 0xd3d3d3d4, 0x54545454, 0xabababac, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04040404, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, + 0x04040404, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, + 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, + 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, + 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0x00000404, + 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, + 0x00000404, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, + 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0x08080404, 0x08080404, 0x08080404, 0x08080404, 0x08080404, + 0x08080404, 0x08080404, 0x08080404, 0x08080404, 0x08080404, 0xf7f7fbfc, 0xf7f7fbfc, 0xf7f7fbfc, + 0xf7f7fbfc, 0xf7f7fbfc, 0xf7f7fbfc, 0xf7f7fbfc, 0xf7f7fbfc, 0xf7f7fbfc, 0xf7f7fbfc, 0x04040808, + 0x04040808, 0x04040808, 0x04040808, 0x04040808, 0x04040808, 0x04040808, 0x04040808, 0x04040808, + 0x04040808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, + 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x05050f0f, 0xfafaf0f1, 0x0a09f5f6, 0xf5f60a0a, 0x0f0efafb, + 0xf0f10505, 0xfafb0f0f, 0x0504f0f1, 0x14140a0a, 0xebebf5f6, 0x0a0a1414, 0xf5f5ebec, 0x14141414, + 0xebebebec, 0x19190000, 0xe6e70000, 0x00001919, 0xffffe6e7, 0x1413f0f1, 0xebec0f0f, 0xf0f11414, + 0x0f0eebec, 0x28281919, 0xd7d7e6e7, 0x19192828, 0xe6e6d7d8, 0x1e1df5f6, 0xe1e20a0a, 0xf5f61e1e, + 0x0a09e1e2, 0x28280a0a, 0xd7d7f5f6, 0x0a0a2828, 0xf5f5d7d8, 0x28282828, 0xd7d7d7d8, 0x2d2d0000, + 0xd2d30000, 0x00002d2d, 0xffffd2d3, 0x1e1de1e2, 0xe1e21e1e, 0x2827ebec, 0xd7d81414, 0xebec2828, + 0x1413d7d8, 0x41411919, 0xbebee6e7, 0x19194141, 0xe6e6bebf, 0x46462d2d, 0xb9b9d2d3, 0x2d2d4646, + 0xd2d2b9ba, 0x3736f0f1, 0xc8c90f0f, 0xf0f13737, 0x0f0ec8c9, 0x46460a0a, 0xb9b9f5f6, 0x0a0a4646, + 0xf5f5b9ba, 0x4b4b4b4b, 0xb4b4b4b5, 0x5554fafb, 0xaaab0505, 0xfafb5555, 0x0504aaab, 0x3736d2d3, + 0xc8c92d2d, 0xd2d33737, 0x2d2cc8c9, 0x4b4adcdd, 0xb4b52323, 0xdcdd4b4b, 0x2322b4b5, 0x73733737, + 0x8c8cc8c9, 0x37377373, 0xc8c88c8d, 0x73731e1e, 0x8c8ce1e2, 0x1e1e7373, 0xe1e18c8d, 0x78785a5a, + 0x8787a5a6, 0x5a5a7878, 0xa5a58788, 0x6968e1e2, 0x96971e1e, 0xe1e26969, 0x1e1d9697, 0x5554aaab, + 0xaaab5555, 0x6968b9ba, 0x96974646, 0xb9ba6969, 0x46459697, 0x1e1e1e1e, 0xe1e1e1e2, 0x3c3c3c3c, + 0xc3c3c3c4, 0x69696969, 0x96969697, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05050505, 0x05050505, + 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, 0x05050505, + 0x05050505, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, + 0xfafafafb, 0xfafafafb, 0xfafafafb, 0xfafafafb, 0x05050000, 0x05050000, 0x05050000, 0x05050000, + 0x05050000, 0x05050000, 0x05050000, 0x05050000, 0x05050000, 0x05050000, 0x05050000, 0xfafb0000, + 0xfafb0000, 0xfafb0000, 0xfafb0000, 0xfafb0000, 0xfafb0000, 0xfafb0000, 0xfafb0000, 0xfafb0000, + 0xfafb0000, 0xfafb0000, 0x00000505, 0x00000505, 0x00000505, 0x00000505, 0x00000505, 0x00000505, + 0x00000505, 0x00000505, 0x00000505, 0x00000505, 0x00000505, 0xfffffafb, 0xfffffafb, 0xfffffafb, + 0xfffffafb, 0xfffffafb, 0xfffffafb, 0xfffffafb, 0xfffffafb, 0xfffffafb, 0xfffffafb, 0xfffffafb, + 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, + 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, + 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0x0f0f0505, 0x0f0f0505, + 0x0f0f0505, 0x0f0f0505, 0x0f0f0505, 0x0f0f0505, 0x0f0f0505, 0x0f0f0505, 0x0f0f0505, 0x0f0f0505, + 0x0f0f0505, 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, + 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, 0xf0f0fafb, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, + 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0xf9f9f3f4, 0x0c0bf3f4, 0xf3f40c0c, 0x1211f9fa, + 0xedee0606, 0xf9fa1212, 0x0605edee, 0x18180c0c, 0xe7e7f3f4, 0x0c0c1818, 0xf3f3e7e8, 0x18181818, + 0xe7e7e7e8, 0x1e1e0000, 0xe1e20000, 0x00001e1e, 0xffffe1e2, 0x1817edee, 0xe7e81212, 0xedee1818, + 0x1211e7e8, 0x30301e1e, 0xcfcfe1e2, 0x1e1e3030, 0xe1e1cfd0, 0x2423f9fa, 0xdbdc0606, 0xf9fa2424, + 0x0605dbdc, 0x30300c0c, 0xcfcff3f4, 0x0c0c3030, 0xf3f3cfd0, 0x30303030, 0xcfcfcfd0, 0x36360000, + 0xc9ca0000, 0x00003636, 0xffffc9ca, 0x2423dbdc, 0xdbdc2424, 0x302fe7e8, 0xcfd01818, 0xe7e83030, + 0x1817cfd0, 0x4e4e1e1e, 0xb1b1e1e2, 0x1e1e4e4e, 0xe1e1b1b2, 0x54543636, 0xababc9ca, 0x36365454, + 0xc9c9abac, 0x4241edee, 0xbdbe1212, 0xedee4242, 0x1211bdbe, 0x54540c0c, 0xababf3f4, 0x0c0c5454, + 0xf3f3abac, 0x5a5a5a5a, 0xa5a5a5a6, 0x605ff9fa, 0x9fa00606, 0xf9fa6060, 0x06059fa0, 0x4241c9ca, + 0xbdbe3636, 0xc9ca4242, 0x3635bdbe, 0x5a59d5d6, 0xa5a62a2a, 0xd5d65a5a, 0x2a29a5a6, 0x7e7de1e2, + 0x81821e1e, 0xe1e27e7e, 0x1e1d8182, 0x6665999a, 0x999a6666, 0x7e7dabac, 0x81825454, 0xabac7e7e, + 0x54538182, 0x24242424, 0xdbdbdbdc, 0x42424242, 0xbdbdbdbe, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, + 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, + 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, + 0xf9fa0000, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, + 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, + 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, + 0xfffff9fa, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, + 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0x0c0c0606, 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, + 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, 0xf3f3f9fa, + 0xf3f3f9fa, 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x06060c0c, + 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x06060c0c, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, + 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0xf8f8eaeb, 0x0e0df1f2, 0xf1f20e0e, 0x1514f8f9, + 0xeaeb0707, 0xf8f91515, 0x0706eaeb, 0x1c1c0e0e, 0xe3e3f1f2, 0x0e0e1c1c, 0xf1f1e3e4, 0x1c1c1c1c, + 0xe3e3e3e4, 0x23230000, 0xdcdd0000, 0x00002323, 0xffffdcdd, 0x1c1beaeb, 0xe3e41515, 0xeaeb1c1c, + 0x1514e3e4, 0x38382323, 0xc7c7dcdd, 0x23233838, 0xdcdcc7c8, 0x2a29f1f2, 0xd5d60e0e, 0xf1f22a2a, + 0x0e0dd5d6, 0x38380e0e, 0xc7c7f1f2, 0x0e0e3838, 0xf1f1c7c8, 0x38383838, 0xc7c7c7c8, 0x3f3f0000, + 0xc0c10000, 0x00003f3f, 0xffffc0c1, 0x2a29d5d6, 0xd5d62a2a, 0x3837e3e4, 0xc7c81c1c, 0xe3e43838, + 0x1c1bc7c8, 0x5b5b2323, 0xa4a4dcdd, 0x23235b5b, 0xdcdca4a5, 0x62623f3f, 0x9d9dc0c1, 0x3f3f6262, + 0xc0c09d9e, 0x4d4ceaeb, 0xb2b31515, 0xeaeb4d4d, 0x1514b2b3, 0x62620e0e, 0x9d9df1f2, 0x0e0e6262, + 0xf1f19d9e, 0x69696969, 0x96969697, 0x7776f8f9, 0x88890707, 0xf8f97777, 0x07068889, 0x4d4cc0c1, + 0xb2b33f3f, 0xc0c14d4d, 0x3f3eb2b3, 0x6968cecf, 0x96973131, 0xcecf6969, 0x31309697, 0x77768889, + 0x88897777, 0x2a2a2a2a, 0xd5d5d5d6, 0x4d4d4d4d, 0xb2b2b2b3, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, + 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0x07070707, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, + 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, 0xf8f8f8f9, + 0xf8f8f8f9, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, + 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0xf8f90000, 0xf8f90000, 0xf8f90000, + 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, + 0xf8f90000, 0x00000707, 0x00000707, 0x00000707, 0x00000707, 0x00000707, 0x00000707, 0x00000707, + 0x00000707, 0x00000707, 0x00000707, 0x00000707, 0x00000707, 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, + 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, 0xfffff8f9, + 0xfffff8f9, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, + 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0x0e0e0e0e, 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, + 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, 0xf1f1f1f2, + 0xf1f1f1f2, 0x15150707, 0x15150707, 0x15150707, 0x15150707, 0x15150707, 0x15150707, 0x15150707, + 0x15150707, 0x15150707, 0x15150707, 0x15150707, 0x15150707, 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, + 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, 0xeaeaf8f9, + 0xeaeaf8f9, 0x07071515, 0x07071515, 0x07071515, 0x07071515, 0x07071515, 0x07071515, 0x07071515, + 0x07071515, 0x07071515, 0x07071515, 0x07071515, 0x07071515, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, + 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0xf7f7eff0, 0x100feff0, 0xeff01010, 0x1817f7f8, + 0xe7e80808, 0xf7f81818, 0x0807e7e8, 0x20201010, 0xdfdfeff0, 0x10102020, 0xefefdfe0, 0x20202020, + 0xdfdfdfe0, 0x28280000, 0xd7d80000, 0x00002828, 0xffffd7d8, 0x201fe7e8, 0xdfe01818, 0xe7e82020, + 0x1817dfe0, 0x40402828, 0xbfbfd7d8, 0x28284040, 0xd7d7bfc0, 0x302feff0, 0xcfd01010, 0xeff03030, + 0x100fcfd0, 0x40401010, 0xbfbfeff0, 0x10104040, 0xefefbfc0, 0x40404040, 0xbfbfbfc0, 0x48480000, + 0xb7b80000, 0x00004848, 0xffffb7b8, 0x302fcfd0, 0xcfd03030, 0x403fdfe0, 0xbfc02020, 0xdfe04040, + 0x201fbfc0, 0x68682828, 0x9797d7d8, 0x28286868, 0xd7d79798, 0x70704848, 0x8f8fb7b8, 0x48487070, + 0xb7b78f90, 0x5857e7e8, 0xa7a81818, 0xe7e85858, 0x1817a7a8, 0x70701010, 0x8f8feff0, 0x10107070, + 0xefef8f90, 0x78787878, 0x87878788, 0x5857b7b8, 0xa7a84848, 0xb7b85858, 0x4847a7a8, 0x7877c7c8, + 0x87883838, 0xc7c87878, 0x38378788, 0x30303030, 0xcfcfcfd0, 0x58585858, 0xa7a7a7a8, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, + 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0xf7f7f7f8, + 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, + 0xf7f7f7f8, 0xf7f7f7f8, 0xf7f7f7f8, 0x08080000, 0x08080000, 0x08080000, 0x08080000, 0x08080000, + 0x08080000, 0x08080000, 0x08080000, 0x08080000, 0x08080000, 0x08080000, 0x08080000, 0xf7f80000, + 0xf7f80000, 0xf7f80000, 0xf7f80000, 0xf7f80000, 0xf7f80000, 0xf7f80000, 0xf7f80000, 0xf7f80000, + 0xf7f80000, 0xf7f80000, 0xf7f80000, 0x00000808, 0x00000808, 0x00000808, 0x00000808, 0x00000808, + 0x00000808, 0x00000808, 0x00000808, 0x00000808, 0x00000808, 0x00000808, 0x00000808, 0xfffff7f8, + 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, + 0xfffff7f8, 0xfffff7f8, 0xfffff7f8, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0x10101010, 0xefefeff0, + 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, 0xefefeff0, + 0xefefeff0, 0xefefeff0, 0xefefeff0, 0x10100808, 0x10100808, 0x10100808, 0x10100808, 0x10100808, + 0x10100808, 0x10100808, 0x10100808, 0x10100808, 0x10100808, 0x10100808, 0x10100808, 0xefeff7f8, + 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, + 0xefeff7f8, 0xefeff7f8, 0xefeff7f8, 0x08081010, 0x08081010, 0x08081010, 0x08081010, 0x08081010, + 0x08081010, 0x08081010, 0x08081010, 0x08081010, 0x08081010, 0x08081010, 0x08081010, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, + 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x1211edee, 0xedee1212, 0x1b1af6f7, + 0xe4e50909, 0xf6f71b1b, 0x0908e4e5, 0x24241212, 0xdbdbedee, 0x12122424, 0xededdbdc, 0x24242424, + 0xdbdbdbdc, 0x2d2d0000, 0xd2d30000, 0x00002d2d, 0xffffd2d3, 0x2423e4e5, 0xdbdc1b1b, 0xe4e52424, + 0x1b1adbdc, 0x48482d2d, 0xb7b7d2d3, 0x2d2d4848, 0xd2d2b7b8, 0x3635edee, 0xc9ca1212, 0xedee3636, + 0x1211c9ca, 0x48481212, 0xb7b7edee, 0x12124848, 0xededb7b8, 0x48484848, 0xb7b7b7b8, 0x51510000, + 0xaeaf0000, 0x00005151, 0xffffaeaf, 0x3635c9ca, 0xc9ca3636, 0x4847dbdc, 0xb7b82424, 0xdbdc4848, + 0x2423b7b8, 0x75752d2d, 0x8a8ad2d3, 0x2d2d7575, 0xd2d28a8b, 0x7e7e5151, 0x8181aeaf, 0x51517e7e, + 0xaeae8182, 0x6362e4e5, 0x9c9d1b1b, 0xe4e56363, 0x1b1a9c9d, 0x6362aeaf, 0x9c9d5151, 0xaeaf6363, + 0x51509c9d, 0x36363636, 0xc9c9c9ca, 0x6c6c6c6c, 0x93939394, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, + 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0x09090909, 0xf6f6f6f7, + 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, + 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0xf6f6f6f7, 0x09090000, 0x09090000, 0x09090000, 0x09090000, + 0x09090000, 0x09090000, 0x09090000, 0x09090000, 0x09090000, 0x09090000, 0x09090000, 0x09090000, + 0x09090000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, + 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0xf6f70000, 0x00000909, 0x00000909, + 0x00000909, 0x00000909, 0x00000909, 0x00000909, 0x00000909, 0x00000909, 0x00000909, 0x00000909, + 0x00000909, 0x00000909, 0x00000909, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, + 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, 0xfffff6f7, + 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, + 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0x12121212, 0xedededee, 0xedededee, 0xedededee, + 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, 0xedededee, + 0xedededee, 0xedededee, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, + 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0x1b1b0909, 0xe4e4f6f7, + 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, + 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0xe4e4f6f7, 0x09091b1b, 0x09091b1b, 0x09091b1b, 0x09091b1b, + 0x09091b1b, 0x09091b1b, 0x09091b1b, 0x09091b1b, 0x09091b1b, 0x09091b1b, 0x09091b1b, 0x09091b1b, + 0x09091b1b, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, + 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0xf6f6e4e5, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, + 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0504fafb, 0xfafb0505, 0xfafb0505, + 0x0504fafb, 0x0b0b0606, 0xf4f4f9fa, 0x06060b0b, 0xf9f9f4f5, 0x08080000, 0xf7f80000, 0x00000808, + 0xfffff7f8, 0x0b0b0b0b, 0xf4f4f4f5, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x11110c0c, + 0xeeeef3f4, 0x0c0c1111, 0xf3f3eeef, 0x11111111, 0xeeeeeeef, 0x12120606, 0xededf9fa, 0x06061212, + 0xf9f9edee, 0x0b0af7f8, 0xf4f50808, 0xf7f80b0b, 0x0807f4f5, 0x0f0f0000, 0xf0f10000, 0x00000f0f, + 0xfffff0f1, 0x14140000, 0xebec0000, 0x00001414, 0xffffebec, 0x19191212, 0xe6e6edee, 0x12121919, + 0xedede6e7, 0x19190b0b, 0xe6e6f4f5, 0x0b0b1919, 0xf4f4e6e7, 0x19191919, 0xe6e6e6e7, 0x0e0df1f2, + 0xf1f20e0e, 0xf1f20e0e, 0x0e0df1f2, 0x1a1a0000, 0xe5e60000, 0x00001a1a, 0xffffe5e6, 0x1211f4f5, + 0xedee0b0b, 0xf4f51212, 0x0b0aedee, 0x1615f8f9, 0xe9ea0707, 0xf8f91616, 0x0706e9ea, 0x22221a1a, + 0xdddde5e6, 0x1a1a2222, 0xe5e5ddde, 0x22221212, 0xddddedee, 0x12122222, 0xededddde, 0x22222222, + 0xddddddde, 0x23230b0b, 0xdcdcf4f5, 0x0b0b2323, 0xf4f4dcdd, 0x1d1d0000, 0xe2e30000, 0x00001d1d, + 0xffffe2e3, 0x1615eced, 0xe9ea1313, 0xeced1616, 0x1312e9ea, 0x1a19f0f1, 0xe5e60f0f, 0xf0f11a1a, + 0x0f0ee5e6, 0x25250000, 0xdadb0000, 0x00002525, 0xffffdadb, 0x2c2c1b1b, 0xd3d3e4e5, 0x1b1b2c2c, + 0xe4e4d3d4, 0x2c2c2424, 0xd3d3dbdc, 0x24242c2c, 0xdbdbd3d4, 0x2c2c1212, 0xd3d3edee, 0x12122c2c, + 0xededd3d4, 0x2120f5f6, 0xdedf0a0a, 0xf5f62121, 0x0a09dedf, 0x2d2d2d2d, 0xd2d2d2d3, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, + 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, + 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, + 0xf8f90000, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, + 0x06060606, 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, + 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, + 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, + 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, 0xf8f90000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, + 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x03030000, 0xfcfd0000, + 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, 0xf8f90000, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x07070000, + 0xf8f90000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0403fbfc, 0xfbfc0404, 0xf9fa0a0a, + 0x0605f5f6, 0xf3f40000, 0x0c0c0000, 0xf3f3f9fa, 0xf3f40606, 0x0c0bf9fa, 0x0c0c0606, 0xfffff1f2, + 0x00000e0e, 0x0c0c0c0c, 0xf3f3f3f4, 0xedee0000, 0x12120000, 0xf3f40e0e, 0x0c0bf1f2, 0xf9f9edee, + 0xf9fa1212, 0x0605edee, 0x06061212, 0xededf5f6, 0xedee0a0a, 0x1211f5f6, 0x12120a0a, 0xffffe9ea, + 0x00001616, 0xe7e80000, 0x18180000, 0xf3f3e9ea, 0xf3f41616, 0x0c0be9ea, 0x0c0c1616, 0xe7e7f7f8, + 0xe7e80808, 0x1817f7f8, 0x18180808, 0xf9f9e5e6, 0xf9fa1a1a, 0x0605e5e6, 0x06061a1a, 0xffffe3e4, + 0x00001c1c, 0x14141414, 0xebebebec, 0xe5e5f1f2, 0x1a1a0e0e, 0xf3f3e1e2, 0x0c0c1e1e, 0xdfdff5f6, + 0x20200a0a, 0xdfdfedee, 0x20201212, 0xe5e5e5e6, 0x1a1a1a1a, 0xebebddde, 0x14142222, 0xf3f3d9da, + 0x0c0c2626, 0xdfdfdfe0, 0x20202020, 0x20202020, 0xd7d7e9ea, 0xddddddde, 0x22222222, 0x00000000, + 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, + 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, + 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, + 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, + 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, + 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, + 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, + 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, + 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, + 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, + 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, + 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, + 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, + 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, + 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x0605f9fa, 0xf9fa0606, 0xf7f80e0e, + 0x0807f1f2, 0xffffedee, 0x00001212, 0xeff00a0a, 0x100ff5f6, 0xe7e80000, 0x18180000, 0xf7f7e7e8, + 0xf7f81818, 0x0807e7e8, 0x08081818, 0x12121212, 0xedededee, 0xeff01414, 0x100febec, 0xe5e5f1f2, + 0xe5e60e0e, 0x1a19f1f2, 0x1a1a0e0e, 0xffffe1e2, 0x00001e1e, 0xddde0000, 0x22220000, 0xf7f7ddde, + 0xf7f82222, 0x0807ddde, 0x08082222, 0xedede1e2, 0xedee1e1e, 0x1211e1e2, 0x12121e1e, 0xddddf5f6, + 0xddde0a0a, 0x2221f5f6, 0x22220a0a, 0xddddebec, 0x22221414, 0xffffd7d8, 0x00002828, 0x1e1e1e1e, + 0xe1e1e1e2, 0xededd7d8, 0x12122828, 0xd3d40000, 0x2c2c0000, 0xd3d3eff0, 0x2c2c1010, 0xdbdbdbdc, + 0xdbdbdbdc, 0x24242424, 0xd3d3e5e6, 0x2c2c1a1a, 0xe5e5d1d2, 0x1a1a2e2e, 0xededcbcc, 0x12123434, + 0xc9c9ebec, 0xd3d3d3d4, 0x2c2c2c2c, 0xc9c9dfe0, 0xd1d1d1d2, 0xd1d1d1d2, 0x2e2e2e2e, 0x00000000, + 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, + 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, + 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, + 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, + 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, + 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, + 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, + 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, + 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, + 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, + 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, + 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, + 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, + 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, + 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, + 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, + 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, + 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, + 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, + 0xf5f60000, 0x00000a0a, 0xfffff5f6, 0x00000000, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, + 0x04040404, 0xfbfbfbfc, 0x0a0a0a0a, 0xf5f5f5f6, 0x0a0a0000, 0xf5f60000, 0x00000a0a, 0xfffff5f6, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, + 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x0807f7f8, 0xf7f80808, 0xeff00808, + 0x100ff7f8, 0xe7e80000, 0x18180000, 0xf7f7e7e8, 0xf7f81818, 0x0807e7e8, 0x08081818, 0xeff01414, + 0x100febec, 0xffffe3e4, 0x00001c1c, 0xe7e7eff0, 0xe7e81010, 0x1817eff0, 0x18181010, 0xdfe00000, + 0x20200000, 0xefefe3e4, 0xeff01c1c, 0x100fe3e4, 0x10101c1c, 0xdfdff7f8, 0xdfe00808, 0xf7f7dfe0, + 0xf7f82020, 0x0807dfe0, 0x08082020, 0x201ff7f8, 0x20200808, 0x18181818, 0xe7e7e7e8, 0xe7e81818, + 0x1817e7e8, 0xdfdfebec, 0x20201414, 0xffffd7d8, 0x00002828, 0xefefd7d8, 0x10102828, 0xd3d40000, + 0xd3d40000, 0xffffd3d4, 0x00002c2c, 0x2c2c0000, 0x2c2c0000, 0xdfdfdfe0, 0x20202020, 0xd3d3eff0, + 0x2c2c1010, 0xd3d3e7e8, 0xe7e7d3d4, 0x18182c2c, 0x2c2c1818, 0xefefcfd0, 0x10103030, 0xdbdbdbdc, + 0xdbdbdbdc, 0x24242424, 0x24242424, 0xcbcbebec, 0x28282828, 0xd7d7d7d8, 0xcbcbdfe0, 0x00000000, + 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, + 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, + 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, + 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, + 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, + 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, + 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, + 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, + 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, + 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, + 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, + 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, + 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, + 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, + 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, + 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, + 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, + 0xf3f40000, 0x00000c0c, 0xfffff3f4, 0x00000000, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, + 0x04040404, 0xfbfbfbfc, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0000, 0xf3f40000, 0x00000c0c, 0xfffff3f4, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, + 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, + 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, + 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, + 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, + 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, + 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, + 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, + 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, + 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, + 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, + 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, + 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, + 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, + 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x06060606, 0xf9f9f9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x14141414, 0xebebebec, 0x20202020, 0xdfdfdfe0, 0x2e2e2e2e, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + + +const uint32 Indeo3::correctionhighorder[] = { + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, + 0xfeff0303, 0x0100fcfd, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, + 0x0100fcfd, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x0302feff, 0xfcfd0101, 0xfeff0303, 0x0100fcfd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, 0xf8f8f8f9, + 0x00000000, 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, + 0xf8f8f8f9, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, + 0x07070707, 0xf8f8f8f9, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, + 0x0100fbfc, 0x07070707, 0xf8f8f8f9, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, + 0xfeff0404, 0x0100fbfc, 0x07070707, 0xf8f8f8f9, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x0403feff, + 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, 0xf8f8f8f9, 0x00000000, 0x03030303, 0xfcfcfcfd, + 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, 0xf8f8f8f9, 0x00000000, 0x03030303, + 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, 0xf8f8f8f9, 0x00000000, + 0x03030303, 0xfcfcfcfd, 0x0403feff, 0xfbfc0101, 0xfeff0404, 0x0100fbfc, 0x07070707, 0xf8f8f8f9, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x04040404, 0xfbfbfbfc, + 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, + 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, + 0x03030a0a, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, + 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, + 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, 0x04040404, 0xfbfbfbfc, + 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, + 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, + 0x03030a0a, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, + 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, + 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, 0x04040404, 0xfbfbfbfc, + 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, 0x03030a0a, 0x00000000, + 0x04040404, 0xfbfbfbfc, 0x0504feff, 0xfafb0101, 0xfeff0505, 0x0100fafb, 0x0a0a0303, 0xf5f5fcfd, + 0x03030a0a, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, + 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x05050505, + 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, + 0xf2f2fcfd, 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, + 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, + 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, + 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, + 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, + 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x05050505, 0xfafafafb, + 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, + 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, + 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, + 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x05050505, + 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, + 0xf2f2fcfd, 0x00000000, 0x05050505, 0xfafafafb, 0x0706fdfe, 0xf8f90202, 0xfdfe0707, 0x0201f8f9, + 0x0b0b0b0b, 0xf4f4f4f5, 0x0d0d0303, 0xf2f2fcfd, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, + 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, + 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, + 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, + 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, + 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, + 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, + 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, + 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, + 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, + 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, + 0x04040f0f, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x0807fdfe, 0xf7f80202, 0xfdfe0808, 0x0201f7f8, + 0x0d0d0d0d, 0xf2f2f2f3, 0x0f0f0404, 0xf0f0fbfc, 0x04040f0f, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, + 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, + 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, + 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, + 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, + 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, + 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, + 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, + 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, + 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, + 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, + 0x05051212, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x0a09fcfd, 0xf5f60303, 0xfcfd0a0a, 0x0302f5f6, + 0x10101010, 0xefefeff0, 0x12120505, 0xededfafb, 0x05051212, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, + 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, + 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, + 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, + 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, + 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, + 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, + 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, + 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, + 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, + 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, + 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x0b0afcfd, 0xf4f50303, + 0xfcfd0b0b, 0x0302f4f5, 0x12121212, 0xedededee, 0x14140505, 0xebebfafb, 0x05051414, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x09090909, 0xf6f6f6f7, + 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, + 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, + 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, + 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, + 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, + 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, + 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, + 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, + 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, + 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, + 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, + 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, + 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, + 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, + 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, + 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, + 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, + 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, + 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, + 0xf9f9e8e9, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x0c0bfcfd, 0xf3f40303, 0xfcfd0c0c, 0x0302f3f4, + 0x14141414, 0xebebebec, 0x17170606, 0xe8e8f9fa, 0x06061717, 0xf9f9e8e9, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x02020000, 0xfdfe0000, + 0x00000202, 0xfffffdfe, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x02020000, 0xfdfe0000, 0x00000202, + 0xfffffdfe, 0x00000000, 0x02020202, 0xfdfdfdfe, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, + 0x00000000, 0x02020202, 0xfdfdfdfe, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x00000000, + 0x02020202, 0xfdfdfdfe, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x00000000, 0x02020202, + 0xfdfdfdfe, 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x00000000, 0x02020202, 0xfdfdfdfe, + 0x02020000, 0xfdfe0000, 0x00000202, 0xfffffdfe, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, + 0x00000000, 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, + 0xf9f9f9fa, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, + 0x06060606, 0xf9f9f9fa, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, + 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, + 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x00000000, 0x03030303, 0xfcfcfcfd, 0x03030000, + 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x00000000, 0x03030303, 0xfcfcfcfd, + 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x00000000, 0x03030303, + 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, 0x00000000, + 0x03030303, 0xfcfcfcfd, 0x03030000, 0xfcfd0000, 0x00000303, 0xfffffcfd, 0x06060606, 0xf9f9f9fa, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x04040404, 0xfbfbfbfc, + 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, + 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, + 0x04040808, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, + 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, + 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, 0x04040404, 0xfbfbfbfc, + 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, + 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, + 0x04040808, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, + 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, + 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, 0x04040404, 0xfbfbfbfc, + 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, 0x04040808, 0x00000000, + 0x04040404, 0xfbfbfbfc, 0x04040000, 0xfbfc0000, 0x00000404, 0xfffffbfc, 0x08080404, 0xf7f7fbfc, + 0x04040808, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, + 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x05050505, + 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, + 0xf0f0fafb, 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, + 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, + 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, + 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, + 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, + 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x05050505, 0xfafafafb, + 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, + 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, + 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, + 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x05050505, + 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, + 0xf0f0fafb, 0x00000000, 0x05050505, 0xfafafafb, 0x05050000, 0xfafb0000, 0x00000505, 0xfffffafb, + 0x0a0a0a0a, 0xf5f5f5f6, 0x0f0f0505, 0xf0f0fafb, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, + 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, + 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, + 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, + 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, + 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, + 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, + 0x06060c0c, 0x00000000, 0x06060606, 0xf9f9f9fa, 0x06060000, 0xf9fa0000, 0x00000606, 0xfffff9fa, + 0x0c0c0c0c, 0xf3f3f3f4, 0x0c0c0606, 0xf3f3f9fa, 0x06060c0c, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, + 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, + 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, + 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, + 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, + 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, + 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, + 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, + 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, + 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, + 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, + 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, + 0x07071515, 0x00000000, 0x07070707, 0xf8f8f8f9, 0x07070000, 0xf8f90000, 0x00000707, 0xfffff8f9, + 0x0e0e0e0e, 0xf1f1f1f2, 0x15150707, 0xeaeaf8f9, 0x07071515, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, + 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, + 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, + 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, + 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, + 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, + 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, + 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, + 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, + 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, + 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, + 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, + 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, 0x08080808, 0xf7f7f7f8, 0x08080000, 0xf7f80000, + 0x00000808, 0xfffff7f8, 0x10101010, 0xefefeff0, 0x10100808, 0xefeff7f8, 0x08081010, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, 0x09090909, 0xf6f6f6f7, + 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, + 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, + 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, + 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, + 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, + 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, + 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, + 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, + 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, + 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, + 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, + 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, + 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, + 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, + 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, + 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, + 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, + 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, + 0xf6f70000, 0x00000909, 0xfffff6f7, 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, + 0xf6f6e4e5, 0x00000000, 0x09090909, 0xf6f6f6f7, 0x09090000, 0xf6f70000, 0x00000909, 0xfffff6f7, + 0x12121212, 0xedededee, 0x1b1b0909, 0xe4e4f6f7, 0x09091b1b, 0xf6f6e4e5, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0x03030000, 0x03030000, 0x03030000, 0x03030000, 0x03030000, 0x03030000, 0x03030000, + 0x03030000, 0x03030000, 0x03030000, 0x03030000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, + 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0xfcfd0000, 0x00000303, + 0x00000303, 0x00000303, 0x00000303, 0x00000303, 0x00000303, 0x00000303, 0x00000303, 0x00000303, + 0x00000303, 0x00000303, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, + 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0xfffffcfd, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, + 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0x07070000, 0xf8f90000, 0xf8f90000, + 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, 0xf8f90000, + 0xf8f90000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, + 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, + 0x02020000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, + 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0x00000202, 0x00000202, + 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, + 0x00000202, 0x00000202, 0x00000202, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, + 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0x06060000, 0x06060000, 0x06060000, 0x06060000, + 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, 0x06060000, + 0x06060000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, + 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0xf9fa0000, 0x00000606, 0x00000606, + 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, 0x00000606, + 0x00000606, 0x00000606, 0x00000606, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, + 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, 0xfffff9fa, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, + 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, 0x02020000, + 0x02020000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, + 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0xfdfe0000, 0x00000202, 0x00000202, + 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, 0x00000202, + 0x00000202, 0x00000202, 0x00000202, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, + 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, 0xfffffdfe, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, + 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, + 0xfbfbfbfc, 0xfbfbfbfc, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, + 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0x0a0a0a0a, 0xf5f5f5f6, + 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, + 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0xf5f5f5f6, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, + 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, 0x0a0a0000, + 0x0a0a0000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, + 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0xf5f60000, 0x00000a0a, 0x00000a0a, + 0x00000a0a, 0x00000a0a, 0x00000a0a, 0x00000a0a, 0x00000a0a, 0x00000a0a, 0x00000a0a, 0x00000a0a, + 0x00000a0a, 0x00000a0a, 0x00000a0a, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, + 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, 0xfffff5f6, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, + 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, 0x04040000, + 0x04040000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, + 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0xfbfc0000, 0x00000404, 0x00000404, + 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, 0x00000404, + 0x00000404, 0x00000404, 0x00000404, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, + 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, 0xfffffbfc, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, + 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, 0xfbfbfbfc, + 0xfbfbfbfc, 0xfbfbfbfc, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, + 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, 0x0c0c0000, + 0x0c0c0000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, + 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0xf3f40000, 0x00000c0c, 0x00000c0c, + 0x00000c0c, 0x00000c0c, 0x00000c0c, 0x00000c0c, 0x00000c0c, 0x00000c0c, 0x00000c0c, 0x00000c0c, + 0x00000c0c, 0x00000c0c, 0x00000c0c, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, + 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, 0xfffff3f4, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, + 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, + 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, + 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, + 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, + 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, + 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0x02020202, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, + 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0xfdfdfdfe, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, 0x06060606, + 0x06060606, 0x06060606, 0x06060606, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, 0xf9f9f9fa, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, + 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0x0c0c0c0c, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, 0xf3f3f3f4, + 0xf3f3f3f4, 0xf3f3f3f4, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, + 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0x14141414, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, + 0xebebebec, 0xebebebec, 0xebebebec, 0xebebebec, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0x20202020, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, + 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0xdfdfdfe0, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, + 0x2e2e2e2e, 0x2e2e2e2e, 0x2e2e2e2e, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, 0xd1d1d1d2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +} // End of namespace Graphics + +#endif // USE_INDEO3 diff --git a/graphics/video/coktelvideo/indeo3.h b/graphics/video/coktelvideo/indeo3.h new file mode 100644 index 0000000000..af6ef26449 --- /dev/null +++ b/graphics/video/coktelvideo/indeo3.h @@ -0,0 +1,126 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/scummsys.h" + +#ifdef USE_INDEO3 + +/* Intel Indeo 3 decompressor, derived from ffmpeg. + * + * Original copyright note: + * Intel Indeo 3 (IV31, IV32, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#ifndef GRAPHICS_VIDEO_INDEO3_H +#define GRAPHICS_VIDEO_INDEO3_H + +#include "common/stream.h" + +namespace Graphics { + class PaletteLUT; + class SierraLight; +} + +namespace Graphics { + +class Indeo3 { +public: + enum DitherAlgorithm { + kDitherNone = 0, + kDitherSierraLight + }; + + Indeo3(int16 width, int16 height, Graphics::PaletteLUT *palLUT); + ~Indeo3(); + + static bool isIndeo3(byte *data, uint32 dataLen); + + void setDither(DitherAlgorithm dither); + + bool decompressFrame(byte *inData, uint32 dataLen, + byte *outData, uint16 width, uint16 height); + +private: + static const int _corrector_type_0[24]; + static const int _corrector_type_2[8]; + static const uint32 correction[]; + static const uint32 correctionloworder[]; + static const uint32 correctionhighorder[]; + + struct YUVBufs { + byte *Ybuf; + byte *Ubuf; + byte *Vbuf; + byte *the_buf; + uint32 the_buf_size; + uint16 y_w, y_h; + uint16 uv_w, uv_h; + }; + + int16 _width; + int16 _height; + YUVBufs _iv_frame[2]; + YUVBufs *_cur_frame; + YUVBufs *_ref_frame; + + byte *_ModPred; + uint16 *_corrector_type; + + Graphics::PaletteLUT *_palLUT; + + DitherAlgorithm _dither; + Graphics::SierraLight *_ditherSL; + + struct BlitState { + uint32 curX, curY; + uint16 widthY, widthUV; + uint16 heightY, heightUV; + uint16 uwidthUV, uwidthOut; + uint16 uheightUV, uheightOut; + uint16 scaleWYUV, scaleWYOut; + uint16 scaleHYUV, scaleHYOut; + uint16 lineWidthOut, lineHeightOut; + byte *bufY, *bufU, *bufV, *bufOut; + }; + + void buildModPred(); + void allocFrames(); + + void decodeChunk(byte *cur, byte *ref, int width, int height, + const byte *buf1, uint32 fflags2, const byte *hdr, + const byte *buf2, int min_width_160); + + void blitFrame(BlitState &s); + + void blitLine(BlitState &s); + void blitLineDither(BlitState &s); +}; + +} // End of namespace Graphics + +#endif // GRAPHICS_VIDEO_INDEO3_H + +#endif // USE_INDEO3 diff --git a/gui/about.cpp b/gui/about.cpp index 3caab084ca..ea326eb71e 100644 --- a/gui/about.cpp +++ b/gui/about.cpp @@ -82,10 +82,31 @@ AboutDialog::AboutDialog() : Dialog(10, 20, 300, 174), _scrollPos(0), _scrollTime(0), _willClose(false) { - reflowLayout(); - int i; + const int screenW = g_system->getOverlayWidth(); + const int screenH = g_system->getOverlayHeight(); + + _xOff = g_gui.xmlEval()->getVar("Globals.About.XOffset", 5); + _yOff = g_gui.xmlEval()->getVar("Globals.About.YOffset", 5); + int outerBorder = g_gui.xmlEval()->getVar("Globals.About.OuterBorder"); + + _w = screenW - 2 * outerBorder; + _h = screenH - 2 * outerBorder; + + _lineHeight = g_gui.getFontHeight() + 3; + + // Heuristic to compute 'optimal' dialog width + int maxW = _w - 2*_xOff; + _w = 0; + for (i = 0; i < ARRAYSIZE(credits); i++) { + int tmp = g_gui.getStringWidth(credits[i] + 5); + if (_w < tmp && tmp <= maxW) { + _w = tmp; + } + } + _w += 2*_xOff; + for (i = 0; i < 1; i++) _lines.push_back(""); @@ -134,6 +155,10 @@ AboutDialog::AboutDialog() for (i = 0; i < ARRAYSIZE(credits); i++) addLine(credits[i]); + + // Center the dialog + _x = (screenW - _w) / 2; + _y = (screenH - _h) / 2; } void AboutDialog::addLine(const char *str) { @@ -273,7 +298,6 @@ void AboutDialog::handleKeyUp(Common::KeyState state) { void AboutDialog::reflowLayout() { Dialog::reflowLayout(); - int i; const int screenW = g_system->getOverlayWidth(); const int screenH = g_system->getOverlayHeight(); @@ -289,7 +313,7 @@ void AboutDialog::reflowLayout() { // Heuristic to compute 'optimal' dialog width int maxW = _w - 2*_xOff; _w = 0; - for (i = 0; i < ARRAYSIZE(credits); i++) { + for (int i = 0; i < ARRAYSIZE(credits); i++) { int tmp = g_gui.getStringWidth(credits[i] + 5); if (_w < tmp && tmp <= maxW) { _w = tmp; @@ -297,7 +321,8 @@ void AboutDialog::reflowLayout() { } _w += 2*_xOff; - // Center the dialog + _lineHeight = g_gui.getFontHeight() + 3; + _x = (screenW - _w) / 2; _y = (screenH - _h) / 2; } diff --git a/gui/editable.cpp b/gui/editable.cpp index 755e34e380..92faad5bb3 100644 --- a/gui/editable.cpp +++ b/gui/editable.cpp @@ -65,7 +65,7 @@ void EditableWidget::setEditString(const String &str) { // TODO: We probably should filter the input string here, // e.g. using tryInsertChar. _editString = str; - _caretPos = 0; + _caretPos = _editString.size(); } bool EditableWidget::tryInsertChar(byte c, int pos) { diff --git a/gui/launcher.cpp b/gui/launcher.cpp index d50e7ce578..bc5debd9cd 100644 --- a/gui/launcher.cpp +++ b/gui/launcher.cpp @@ -733,7 +733,7 @@ void LauncherDialog::addGame() { // ...so let's determine a list of candidates, games that // could be contained in the specified directory. GameList candidates(EngineMan.detectGames(files)); - + int idx; if (candidates.empty()) { // No game was found in the specified directory @@ -874,7 +874,12 @@ void LauncherDialog::loadGame(int item) { gameId = _domains[item]; const EnginePlugin *plugin = 0; + +#if defined(NEW_PLUGIN_DESIGN_FIRST_REFINEMENT) && defined(DYNAMIC_MODULES) + EngineMan.findGameOnePlugAtATime(gameId, &plugin); +#else EngineMan.findGame(gameId, &plugin); +#endif String target = _domains[item]; target.toLowercase(); diff --git a/gui/themes/scummclassic/classic_layout.stx b/gui/themes/scummclassic/classic_layout.stx index 74b8bf4b2c..78941003ef 100644 --- a/gui/themes/scummclassic/classic_layout.stx +++ b/gui/themes/scummclassic/classic_layout.stx @@ -635,10 +635,6 @@ width = '150' height = 'Globals.Button.Height' /> - <widget name = 'Help' - width = '150' - height = 'Globals.Button.Height' - /> <widget name = 'About' width = '150' height = 'Globals.Button.Height' @@ -655,7 +651,36 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'Resume' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Load' + type = 'Button' + /> + <widget name = 'Save' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Options' + type = 'Button' + /> + <widget name = 'Help' + type = 'Button' + /> + <widget name = 'About' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Quit' + type = 'Button' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <layout type = 'horizontal' padding = '0, 0, 0, 0'> <layout type = 'vertical' padding = '0, 0, 0, 0' center = 'true'> diff --git a/gui/themes/scummclassic/classic_layout_lowres.stx b/gui/themes/scummclassic/classic_layout_lowres.stx index 65083f4bce..e7cc7b6279 100644 --- a/gui/themes/scummclassic/classic_layout_lowres.stx +++ b/gui/themes/scummclassic/classic_layout_lowres.stx @@ -620,7 +620,7 @@ </dialog> <dialog name = 'GlobalMenu' overlays = 'screen_center'> - <layout type = 'vertical' padding = '2, 2, 4, 6' center = 'true' spacing='6'> + <layout type = 'vertical' padding = '8, 8, 4, 6' center = 'true'> <widget name = 'Title' width = '160' height = '4' @@ -643,10 +643,6 @@ width = '120' height = '12' /> - <widget name = 'Help' - width = '120' - height = '12' - /> <widget name = 'About' width = '120' height = '12' @@ -667,7 +663,43 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'Resume' + width = 'Globals.Button.Width' + height = '14' + /> + <space size = '2'/> + <widget name = 'Load' + width = 'Globals.Button.Width' + height = '14' + /> + <widget name = 'Save' + width = 'Globals.Button.Width' + height = '14' + /> + <space size = '2'/> + <widget name = 'Options' + width = 'Globals.Button.Width' + height = '14' + /> + <widget name = 'Help' + width = 'Globals.Button.Width' + height = '14' + /> + <widget name = 'About' + width = 'Globals.Button.Width' + height = '14' + /> + <space size = '2'/> + <widget name = 'Quit' + width = 'Globals.Button.Width' + height = '14' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'> <widget name = 'vcMusicText' diff --git a/gui/themes/scummmodern/scummmodern_layout.stx b/gui/themes/scummmodern/scummmodern_layout.stx index 484dd15429..3464619cf3 100644 --- a/gui/themes/scummmodern/scummmodern_layout.stx +++ b/gui/themes/scummmodern/scummmodern_layout.stx @@ -650,10 +650,6 @@ width = '150' height = 'Globals.Button.Height' /> - <widget name = 'Help' - width = '150' - height = 'Globals.Button.Height' - /> <widget name = 'About' width = '150' height = 'Globals.Button.Height' @@ -670,7 +666,36 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '8, 8, 8, 8'> + <widget name = 'Resume' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Load' + type = 'Button' + /> + <widget name = 'Save' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Options' + type = 'Button' + /> + <widget name = 'Help' + type = 'Button' + /> + <widget name = 'About' + type = 'Button' + /> + <space size = '15'/> + <widget name = 'Quit' + type = 'Button' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8' spacing = '8'> <layout type = 'horizontal' padding = '0, 0, 0, 0'> <layout type = 'vertical' padding = '0, 0, 0, 0' spacing = '8' center = 'true'> diff --git a/gui/themes/scummmodern/scummmodern_layout_lowres.stx b/gui/themes/scummmodern/scummmodern_layout_lowres.stx index 5998c50b60..f869956300 100644 --- a/gui/themes/scummmodern/scummmodern_layout_lowres.stx +++ b/gui/themes/scummmodern/scummmodern_layout_lowres.stx @@ -618,7 +618,7 @@ </dialog> <dialog name = 'GlobalMenu' overlays = 'screen_center'> - <layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true' spacing='2'> + <layout type = 'vertical' padding = '4, 4, 4, 4' center = 'true'> <widget name = 'Title' width = '160' height = 'Globals.Line.Height' @@ -641,10 +641,6 @@ width = '120' height = 'Globals.Button.Height' /> - <widget name = 'Help' - width = '120' - height = 'Globals.Button.Height' - /> <widget name = 'About' width = '120' height = 'Globals.Button.Height' @@ -665,7 +661,36 @@ </layout> </dialog> - <dialog name = 'GlobalConfig' overlays = 'screen_center'> + <dialog name = 'ScummMain' overlays = 'screen_center'> + <layout type = 'vertical' padding = '4, 4, 4, 4'> + <widget name = 'Resume' + type = 'Button' + /> + <space size = '8'/> + <widget name = 'Load' + type = 'Button' + /> + <widget name = 'Save' + type = 'Button' + /> + <space size = '8'/> + <widget name = 'Options' + type = 'Button' + /> + <widget name = 'Help' + type = 'Button' + /> + <widget name = 'About' + type = 'Button' + /> + <space size = '8'/> + <widget name = 'Quit' + type = 'Button' + /> + </layout> + </dialog> + + <dialog name = 'ScummConfig' overlays = 'screen_center'> <layout type = 'vertical' padding = '8, 8, 8, 8'> <layout type = 'horizontal' padding = '0, 0, 0, 0' spacing = '6' center = 'true'> <widget name = 'vcMusicText' @@ -44,9 +44,7 @@ bundle: scummvm-static cp $(srcdir)/icons/scummvm.icns $(bundle_name)/Contents/Resources/ cp $(DIST_FILES_DOCS) $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/Contents/Resources/ -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/Contents/Resources/ -endif $(srcdir)/tools/credits.pl --rtf > $(bundle_name)/Contents/Resources/Credits.rtf chmod 644 $(bundle_name)/Contents/Resources/* cp scummvm-static $(bundle_name)/Contents/MacOS/scummvm @@ -58,9 +56,7 @@ iphonebundle: iphone cp $(srcdir)/dists/iphone/Info.plist $(bundle_name)/ cp $(DIST_FILES_DOCS) $(bundle_name)/ cp $(DIST_FILES_THEMES) $(bundle_name)/ -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(bundle_name)/ -endif $(STRIP) scummvm ldid -S scummvm chmod 755 scummvm @@ -161,9 +157,7 @@ win32dist: $(EXECUTABLE) mkdir -p $(WIN32PATH) $(STRIP) $(EXECUTABLE) -o $(WIN32PATH)/$(EXECUTABLE) cp $(DIST_FILES_THEMES) $(WIN32PATH) -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(WIN32PATH) -endif cp $(srcdir)/AUTHORS $(WIN32PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(WIN32PATH)/COPYING.txt cp $(srcdir)/COPYING.LGPL $(WIN32PATH)/COPYING.LGPL.txt @@ -184,9 +178,7 @@ aos4dist: $(EXECUTABLE) $(STRIP) $(EXECUTABLE) -o $(AOS4PATH)/$(EXECUTABLE) cp icons/scummvm.info $(AOS4PATH)/$(EXECUTABLE).info cp $(DIST_FILES_THEMES) $(AOS4PATH)/themes/ -ifdef DIST_FILES_ENGINEDATA cp $(DIST_FILES_ENGINEDATA) $(AOS4PATH)/extras/ -endif cp $(srcdir)/AUTHORS $(AOS4PATH)/AUTHORS.txt cp $(srcdir)/COPYING $(AOS4PATH)/COPYING.txt cp $(srcdir)/COPYING.LGPL $(AOS4PATH)/COPYING.LGPL.txt diff --git a/sound/decoders/adpcm.cpp b/sound/decoders/adpcm.cpp index c8a907d13e..7a85bc24d5 100644 --- a/sound/decoders/adpcm.cpp +++ b/sound/decoders/adpcm.cpp @@ -724,10 +724,6 @@ int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) { } RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, typesADPCM type, int rate, int channels, uint32 blockAlign) { - // If size is 0, report the entire size of the stream - if (!size) - size = stream->size(); - switch (type) { case kADPCMOki: return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); diff --git a/sound/mods/rjp1.cpp b/sound/mods/rjp1.cpp index be376d61a4..fc1b49e9e9 100644 --- a/sound/mods/rjp1.cpp +++ b/sound/mods/rjp1.cpp @@ -422,7 +422,7 @@ void Rjp1::setupNote(Rjp1Channel *channel, int16 period) { channel->envelopeMode = 4; channel->data = channel->waveData; channel->pos = READ_BE_UINT16(note + 16); - channel->len = channel->pos + READ_BE_UINT16(note + 18); + channel->len = READ_BE_UINT16(note + 18); channel->setupNewNote = true; } } diff --git a/test/common/str.h b/test/common/str.h index 6581c37cdb..16fb0859db 100644 --- a/test/common/str.h +++ b/test/common/str.h @@ -118,30 +118,6 @@ class StringTestSuite : public CxxTest::TestSuite TS_ASSERT_EQUALS(foo3, "fooasdkadklasdjklasdjlkasjdlkasjdklasjdlkjasdasd""fooasdkadklasdjklasdjlkasjdlkasjdklasjdlkjasdasd"); } - void test_refCount5() { - // using external storage - Common::String foo1("HelloHelloHelloHelloAndHi"); - Common::String foo2(foo1); - - for (Common::String::iterator i = foo2.begin(); i != foo2.end(); ++i) - *i = 'h'; - - TS_ASSERT_EQUALS(foo1, "HelloHelloHelloHelloAndHi"); - TS_ASSERT_EQUALS(foo2, "hhhhhhhhhhhhhhhhhhhhhhhhh"); - } - - void test_refCount6() { - // using internal storage - Common::String foo1("Hello"); - Common::String foo2(foo1); - - for (Common::String::iterator i = foo2.begin(); i != foo2.end(); ++i) - *i = 'h'; - - TS_ASSERT_EQUALS(foo1, "Hello"); - TS_ASSERT_EQUALS(foo2, "hhhhh"); - } - void test_self_asignment() { Common::String foo1("12345678901234567890123456789012"); foo1 = foo1.c_str() + 2; diff --git a/tools/create_msvc/create_msvc.cpp b/tools/create_msvc/create_msvc.cpp index 1c395b01aa..fcae638c50 100644 --- a/tools/create_msvc/create_msvc.cpp +++ b/tools/create_msvc/create_msvc.cpp @@ -512,6 +512,8 @@ int main(int argc, char *argv[]) { // 4103 (alignment changed after including header, may be due to missing #pragma pack(pop)) // used by pack-start / pack-end // + // 4121 (alignment of a member was sensitive to packing) + // // 4127 (conditional expression is constant) // used in a lot of engines // @@ -565,6 +567,7 @@ int main(int argc, char *argv[]) { projectWarnings["lure"] = "4189;4355"; projectWarnings["kyra"] = "4355"; projectWarnings["m4"] = "4355"; + projectWarnings["mohawk"] = "4121"; ProjectProvider *provider = NULL; diff --git a/tools/md5table.c b/tools/md5table.c index 7d76b7541d..cb2959ed88 100644 --- a/tools/md5table.c +++ b/tools/md5table.c @@ -222,7 +222,7 @@ int main(int argc, char *argv[]) const int entrySize = 256; int numEntries = 0, maxEntries = 1; - char *entriesBuffer = (char *)malloc(maxEntries * entrySize); + char *entriesBuffer = malloc(maxEntries * entrySize); typedef enum { kCPPOutput, @@ -295,7 +295,7 @@ int main(int argc, char *argv[]) } else if (entry.md5) { if (numEntries >= maxEntries) { maxEntries *= 2; - entriesBuffer = (char *)realloc(entriesBuffer, maxEntries * entrySize); + entriesBuffer = realloc(entriesBuffer, maxEntries * entrySize); } if (0 == strcmp(entry.variant, "-")) entry.variant = ""; diff --git a/tools/module.mk b/tools/module.mk index 2c62e427ea..5248454382 100644 --- a/tools/module.mk +++ b/tools/module.mk @@ -36,15 +36,15 @@ clean-tools: tools/convbdf$(EXEEXT): $(srcdir)/tools/convbdf.c $(QUIET)$(MKDIR) tools/$(DEPDIR) - $(QUIET_LINK)$(LD) $(CFLAGS) -Wall -o $@ $< + $(QUIET_LINK)$(CC) $(CFLAGS) -Wall -o $@ $< tools/md5table$(EXEEXT): $(srcdir)/tools/md5table.c $(QUIET)$(MKDIR) tools/$(DEPDIR) - $(QUIET_LINK)$(LD) $(CFLAGS) -Wall -o $@ $< + $(QUIET_LINK)$(CC) $(CFLAGS) -Wall -o $@ $< tools/make-scumm-fontdata$(EXEEXT): $(srcdir)/tools/make-scumm-fontdata.c $(QUIET)$(MKDIR) tools/$(DEPDIR) - $(QUIET_LINK)$(LD) $(CFLAGS) -Wall -o $@ $< + $(QUIET_LINK)$(CC) $(CFLAGS) -Wall -o $@ $< # # Rules to explicitly rebuild the credits / MD5 tables. diff --git a/tools/scumm-md5.txt b/tools/scumm-md5.txt index c3e48d6c5a..b379bc837e 100644 --- a/tools/scumm-md5.txt +++ b/tools/scumm-md5.txt @@ -591,7 +591,6 @@ catalog Humongous Interactive Catalog airport Let's Explore the Airport with Buzzy d6334a5a9b61afe18c368540fdf522ca -1 en Mac - - - Joachim Eberhard 07433205acdca3bc553d0e731588b35f -1 en Windows - - - Kirben - 3e861421f494711bc6f619d4aba60285 93231 ru Windows - - - sev 7ea2da67ebabea4ac20cee9f4f9d2934 -1 en Mac - Demo - khalek 8ffd618a776a4c0d8922bb28b09f8ce8 -1 en Windows - Demo - khalek @@ -603,7 +602,6 @@ farm Let's Explore the Farm with Buzzy a5c5388da9bf0e6662fdca8813a79d13 86962 en Windows - - - George Kormendi a85856675429fe88051744f755b72f93 -1 en Windows - - - Kirben a2386da005672cbd5136f4f27a626c5f 87061 nl Windows - - - George Kormendi - 5dda73606533d66a4c3f4f9ea6e842af 87061 ru Windows - - - sev 39fd6db10d0222d817025c4d3346e3b4 -1 en Mac - Demo - Joachim Eberhard bf8b52fdd9a69c67f34e8e9fec72661c -1 en Windows HE 71 Demo - khalek, sev @@ -715,7 +713,6 @@ puttmoon Putt-Putt Goes to the Moon 697c9b7c55a05d8199c48b48e379d2c8 -1 he DOS - - - sev 9dc02577bf50d4cfaf3de3fbac06fbe2 -1 en Mac - - - khalek 9c92eeaf517a31b7221ec2546ab669fd -1 en Windows HE 70 - - khalek - 3c4c471342bd95505a42334367d8f127 12161 ru Windows HE 70 - - sev aa6a91b7f6f119d1b7b1f2a4c9e24d59 6233 en DOS - Demo - 4af4a6b248103c1fe9edef619677f540 -1 en Mac - Demo - khalek |