diff --git a/.gitignore b/.gitignore index 8ab20711..ab28e83b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ /deprince /descumm /desword2 +/detwine /extract_hadesch_img /extract_mohawk /extract_mps diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..cd066a90 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.19) +project(scummvm-tools CXX) +include(CheckCXXCompilerFlag) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +if (WIN32) + add_definitions(-DWIN32) +else() + add_definitions(-DPOSIX) +endif() + +find_package(SDL REQUIRED) + +function(add_tool NAME) + add_executable(${NAME} ${ARGN}) + target_include_directories(${NAME} PUBLIC .) + target_link_libraries(${NAME} SDL::SDL) +endfunction() + +set(COMMON_SRC + common/file.cpp + common/hashmap.cpp + common/md5.cpp + common/memorypool.cpp + common/str.cpp + common/stream.cpp + common/util.cpp + sound/adpcm.cpp + sound/audiostream.cpp + sound/voc.cpp + sound/wave.cpp +) + +set(DETWINE_SRC + ${COMMON_SRC} + engines/twine/detwine.cpp + engines/twine/lba1.cpp + engines/twine/lba2.cpp + engines/twine/hqr.cpp +) +add_tool(detwine ${DETWINE_SRC}) diff --git a/Makefile b/Makefile index 78c4fb66..047c4042 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,7 @@ endif $(STRIP) deprince$(EXEEXT) -o $(WIN32PATH)/tools/deprince$(EXEEXT) $(STRIP) descumm$(EXEEXT) -o $(WIN32PATH)/tools/descumm$(EXEEXT) $(STRIP) desword2$(EXEEXT) -o $(WIN32PATH)/tools/desword2$(EXEEXT) + $(STRIP) detwine$(EXEEXT) -o $(WIN32PATH)/tools/detwine$(EXEEXT) $(STRIP) extract_mohawk$(EXEEXT) -o $(WIN32PATH)/tools/extract_mohawk$(EXEEXT) $(STRIP) gob_loadcalc$(EXEEXT) -o $(WIN32PATH)/tools/gob_loadcalc$(EXEEXT) $(STRIP) grim_animb2txt$(EXEEXT) -o $(WIN32PATH)/tools/grim_animb2txt$(EXEEXT) @@ -168,6 +169,7 @@ endif $(STRIP) deprince$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/deprince$(EXEEXT) $(STRIP) descumm$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/descumm$(EXEEXT) $(STRIP) desword2$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/desword2$(EXEEXT) + $(STRIP) detwine$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/detwine$(EXEEXT) $(STRIP) extract_hadesch$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/extract_hadesch$(EXEEXT) $(STRIP) extract_lokalizator$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/extract_lokalizator$(EXEEXT) $(STRIP) extract_mohawk$(EXEEXT) -o $(srcdir)/$(WIN32BUILD)/extract_mohawk$(EXEEXT) @@ -305,6 +307,7 @@ endif $(STRIP) deprince$(EXEEXT) -o $(AMIGAOSPATH)/deprince$(EXEEXT) $(STRIP) descumm$(EXEEXT) -o $(AMIGAOSPATH)/descumm$(EXEEXT) $(STRIP) desword2$(EXEEXT) -o $(AMIGAOSPATH)/desword2$(EXEEXT) + $(STRIP) detwine$(EXEEXT) -o $(AMIGAOSPATH)/detwine$(EXEEXT) $(STRIP) extract_mohawk$(EXEEXT) -o $(AMIGAOSPATH)/extract_mohawk$(EXEEXT) $(STRIP) extract_ngi$(EXEEXT) -o $(AMIGAOSPATH)/extract_ngi$(EXEEXT) $(STRIP) gob_loadcalc$(EXEEXT) -o $(AMIGAOSPATH)/gob_loadcalc$(EXEEXT) diff --git a/Makefile.common b/Makefile.common index 0cd11e01..af5e53d7 100644 --- a/Makefile.common +++ b/Makefile.common @@ -29,6 +29,7 @@ PROGRAMS = \ deprince \ descumm \ desword2 \ + detwine \ gob_loadcalc \ extract_gob_cdi \ extract_mohawk \ @@ -129,23 +130,6 @@ GRIM_LUA := \ decine_OBJS := engines/cine/decine.o -degob_OBJS := \ - engines/gob/degob.o \ - engines/gob/degob_script.o \ - engines/gob/degob_script_v1.o \ - engines/gob/degob_script_v2.o \ - engines/gob/degob_script_v3.o \ - engines/gob/degob_script_v4.o \ - engines/gob/degob_script_v5.o \ - engines/gob/degob_script_v6.o \ - engines/gob/degob_script_v7.o \ - engines/gob/degob_script_bargon.o \ - engines/gob/degob_script_fascin.o \ - engines/gob/degob_script_littlered.o \ - tool.o \ - version.o \ - $(UTILS) - dekyra_OBJS := \ engines/kyra/dekyra.o \ engines/kyra/dekyra_v1.o \ @@ -215,6 +199,15 @@ degob_OBJS := \ version.o \ $(UTILS) +detwine_OBJS := \ + engines/twine/detwine.o \ + engines/twine/hqr.o \ + engines/twine/lba1.o \ + engines/twine/lba2.o \ + tool.o \ + version.o \ + $(UTILS) + gob_loadcalc_OBJS := \ engines/gob/gob_loadcalc.o @@ -457,6 +450,7 @@ version.o: $(filter-out version.o,$(scummvm-tools-cli_OBJS)) ifdef USE_WXWIDGETS version.o: $(filter-out version.o,$(scummvm-tools_OBJS)) endif +version.o: $(filter-out version.o,$(detwine_OBJS)) ###################################################################### diff --git a/common/scummsys.h b/common/scummsys.h index 53c50dec..dcf7c8c6 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -209,6 +209,9 @@ typedef unsigned long int uint32; typedef signed long int int32; + typedef signed long long int64; + typedef unsigned long long uint64; + #elif defined(_WIN32_WCE) #define scumm_stricmp stricmp @@ -267,6 +270,19 @@ #endif #endif +#ifndef HAVE_CONFIG_H + typedef unsigned char byte; + typedef unsigned char uint8; + typedef signed char int8; + typedef unsigned short uint16; + typedef signed short int16; + typedef unsigned int uint32; + typedef signed int int32; + typedef unsigned int uint; + typedef signed long long int64; + typedef unsigned long long uint64; +#endif + // You need to set this manually if necessary // #define SCUMM_NEED_ALIGNMENT @@ -325,6 +341,9 @@ typedef unsigned long int uint32; typedef signed long int int32; + typedef signed long long int64; + typedef unsigned long long uint64; + #elif defined(__PLAYSTATION2__) #define scumm_stricmp strcasecmp @@ -464,6 +483,8 @@ typedef unsigned int uint32; typedef signed int int32; typedef unsigned int uint; + typedef signed long long int64; + typedef unsigned long long uint64; #endif diff --git a/engines/twine/detwine.cpp b/engines/twine/detwine.cpp new file mode 100644 index 00000000..c000701e --- /dev/null +++ b/engines/twine/detwine.cpp @@ -0,0 +1,83 @@ +/* ScummVM Tools + * + * ScummVM Tools 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 3 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, see . + * + */ + +/* TwinE Script disassembler */ + +#include +#include + +#include "common/util.h" +#include "common/memstream.h" +#include "engines/twine/hqr.h" +#include "engines/twine/lba1.h" +#include "engines/twine/lba2.h" + +static void printHelp(const char *bin) { + printf("Usage: %s \n\n", bin); + printf("The disassembled script will be written to stdout.\n\n"); + printf("Supported variants:\n"); + printf(" lba1 - Little Big Adventure 1\n"); + printf(" lba2 - Little Big Adventure 2\n"); + printf("\n"); +} + +static int getVariant(const char *verStr) { + if (!scumm_stricmp(verStr, "lba1")) { + return 1; + } else if (!scumm_stricmp(verStr, "lba2")) { + return 2; + } + return -1; +} + +int main(int argc, char **argv) { + if ((argc < 3) || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { + printHelp(argv[0]); + return -1; + } + + int variant = getVariant(argv[1]); + if (variant == -1) { + printHelp(argv[0]); + return -1; + } + + int index = atoi(argv[2]); + const char *sceneHqr = "scene.hqr"; + if (argc >= 4) { + sceneHqr = argv[3]; + } + const Common::Filename fn(sceneHqr); + uint8 *data = nullptr; + int size = 0; + if (fn.exists()) { + size = TwinE::HQR::getAllocEntry(&data, fn, index); + } + if (data == nullptr || size == 0) { + fprintf(stderr, "Failed to load index %i from %s", index, fn.getFullName().c_str()); + return 127; + } + + if (variant == 1) { + return decompileLBA1(data, size); + } + return decompileLBA2(data, size); +} diff --git a/engines/twine/hqr.cpp b/engines/twine/hqr.cpp new file mode 100644 index 00000000..13b7e411 --- /dev/null +++ b/engines/twine/hqr.cpp @@ -0,0 +1,280 @@ +/* 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 3 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, see . + * + */ + +#include "engines/twine/hqr.h" +#include "common/file.h" +#include "common/util.h" +#include "common/substream.h" +#include "common/memstream.h" + +namespace TwinE { + +namespace HQR { + +/** + * Decompress entry based in Yaz0r and Zink decompression code + * @param dst destination pointer where will be the decompressed entry + * @param compBuf compressed data pointer + * @param compSize @p compBuf buffer size + * @param decompsize real file size after decompression + * @param mode compression mode used + */ +static void decompressEntry(uint8 *dst, const uint8 *compBuf, uint32 compSize, int32 decompsize, int32 mode) { + Common::MemoryReadStream stream(compBuf, compSize); + do { + uint8 b = stream.readByte(); + for (int32 d = 0; d < 8; d++) { + int32 length; + if (!(b & (1 << d))) { + const uint16 offset = stream.readUint16LE(); + length = (offset & 0x0F) + (mode + 1); + const uint8 *ptr = dst - (offset >> 4) - 1; + for (int32 i = 0; i < length; i++) { + *(dst++) = *(ptr++); + } + } else { + length = 1; + *(dst++) = stream.readByte(); + } + decompsize -= length; + if (decompsize <= 0) { + return; + } + } + } while (decompsize); +} + +/** + * Get a HQR entry pointer + * @param filename HQR file name + * @param index entry index to extract + * @return entry real size + */ +static int voxEntrySize(const Common::Filename &filename, int32 index, int32 hiddenIndex) { + Common::File file; + file.open(filename, "r"); + if (!file.isOpen()) { + warning("HQR: Could not open %s", filename.getFullPath().c_str()); + return 0; + } + + uint32 headerSize = file.readUint32LE(); + if ((uint32)index >= headerSize / 4) { + warning("HQR: Invalid entry index"); + return 0; + } + + file.seek(index * 4, SEEK_SET); + uint32 offsetToData = file.readUint32LE(); + + file.seek(offsetToData, SEEK_SET); + uint32 realSize = file.readUint32LE(); + uint32 compSize = file.readUint32LE(); + + // exist hidden entries + for (int32 i = 0; i < hiddenIndex; i++) { + file.seek(offsetToData + compSize + 10, SEEK_SET); // hidden entry + offsetToData = offsetToData + compSize + 10; // current hidden offset + + realSize = file.readUint32LE(); + compSize = file.readUint32LE(); + } + + return realSize; +} + +int32 getEntry(uint8 *ptr, const Common::Filename &filename, int32 index) { + if (!ptr) { + return 0; + } + + Common::File file; + file.open(filename, "r"); + if (!file.isOpen()) { + warning("HQR: Could not open %s", filename.getFullPath().c_str()); + return 0; + } + + uint32 headerSize = file.readUint32LE(); + + if ((uint32)index >= headerSize / 4) { + warning("HQR: Invalid entry index"); + return 0; + } + + file.seek(index * 4, SEEK_SET); + uint32 offsetToData = file.readUint32LE(); + + file.seek(offsetToData, SEEK_SET); + uint32 realSize = file.readUint32LE(); + uint32 compSize = file.readUint32LE(); + uint16 mode = file.readUint16LE(); + + // uncompressed + if (mode == 0) { + file.read_throwsOnError(ptr, realSize); + } + // compressed: modes (1 & 2) + else if (mode == 1 || mode == 2) { + uint8 *compDataPtr = (uint8 *)malloc(compSize); + file.read_throwsOnError(compDataPtr, compSize); + decompressEntry(ptr, compDataPtr, compSize, realSize, mode); + free(compDataPtr); + } + + return realSize; +} + +int32 entrySize(const Common::Filename &filename, int32 index) { + Common::File file; + file.open(filename, "r"); + if (!file.isOpen()) { + warning("HQR: Could not open %s", filename.getFullPath().c_str()); + return 0; + } + + uint32 headerSize = file.readUint32LE(); + if ((uint32)index >= headerSize / 4) { + warning("HQR: Invalid entry index"); + return 0; + } + + file.seek(index * 4, SEEK_SET); + uint32 offsetToData = file.readUint32LE(); + + file.seek(offsetToData, SEEK_SET); + uint32 realSize = file.readUint32LE(); + + return realSize; +} + +int32 numEntries(const Common::Filename &filename) { + Common::File file; + file.open(filename, "r"); + if (!file.isOpen()) { + warning("HQR: Could not open %s", filename.getFullPath().c_str()); + return 0; + } + + uint32 headerSize = file.readUint32LE(); + return ((int)headerSize / 4) - 1; +} + +Common::SeekableReadStream *makeReadStream(const Common::Filename &filename, int index) { + uint8 *data = nullptr; + const int32 size = getAllocEntry(&data, filename, index); + if (size == 0) { + return nullptr; + } + return new Common::MemoryReadStream(data, size, DisposeAfterUse::YES); +} + +int32 getAllocEntry(uint8 **ptr, const Common::Filename &filename, int32 index) { + if (*ptr) { + free(*ptr); + } + const int32 size = entrySize(filename, index); + if (size <= 0) { + *ptr = nullptr; + warning("HQR: failed to get entry for index %i from file: %s", index, filename.getFullPath().c_str()); + return 0; + } + *ptr = (uint8 *)malloc(size * sizeof(uint8)); + if (!*ptr) { + warning("HQR: unable to allocate entry memory"); + return 0; + } + const int32 entrySize = getEntry(*ptr, filename, index); + assert(entrySize == size); + return entrySize; +} + +int32 getVoxEntry(uint8 *ptr, const Common::Filename &filename, int32 index, int32 hiddenIndex) { + if (!ptr) { + return 0; + } + Common::File file; + file.open(filename, "r"); + if (!file.isOpen()) { + warning("HQR: Could not open %s", filename.getFullPath().c_str()); + return 0; + } + + uint32 headerSize = file.readUint32LE(); + + if ((uint32)index >= headerSize / 4) { + warning("HQR: Invalid entry index"); + return 0; + } + + file.seek(index * 4, SEEK_SET); + uint32 offsetToData = file.readUint32LE(); + + file.seek(offsetToData, SEEK_SET); + uint32 realSize = file.readUint32LE(); + uint32 compSize = file.readUint32LE(); + uint16 mode = file.readSint16LE(); + + // exist hidden entries + for (int32 i = 0; i < hiddenIndex; i++) { + file.seek(offsetToData + compSize + 10, SEEK_SET); // hidden entry + offsetToData = offsetToData + compSize + 10; // current hidden offset + + realSize = file.readUint32LE(); + compSize = file.readUint32LE(); + mode = file.readUint16LE(); + } + + // uncompressed + if (mode == 0) { + file.read_throwsOnError(ptr, realSize); + } + // compressed: modes (1 & 2) + else if (mode == 1 || mode == 2) { + uint8 *compDataPtr = (uint8 *)malloc(compSize); + file.read_throwsOnError(compDataPtr, compSize); + decompressEntry(ptr, compDataPtr, compSize, realSize, mode); + free(compDataPtr); + } + + return realSize; +} + +int32 getAllocVoxEntry(uint8 **ptr, const Common::Filename &filename, int32 index, int32 hiddenIndex) { + const int32 size = voxEntrySize(filename, index, hiddenIndex); + if (size == 0) { + warning("HQR: vox entry with 0 size found for index: %d", index); + return 0; + } + + *ptr = (uint8 *)malloc(size * sizeof(uint8)); + if (!*ptr) { + warning("HQR: unable to allocate entry memory of size %d for index: %d", size, index); + return 0; + } + const int32 entrySize = getVoxEntry(*ptr, filename, index, hiddenIndex); + assert(entrySize == size); + return entrySize; +} + +} // namespace HQR + +} // namespace TwinE diff --git a/engines/twine/hqr.h b/engines/twine/hqr.h new file mode 100644 index 00000000..8b103402 --- /dev/null +++ b/engines/twine/hqr.h @@ -0,0 +1,98 @@ +/* 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 3 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, see . + * + */ + +#ifndef TWINE_HQR_H +#define TWINE_HQR_H + +#include "common/stream.h" +#include "common/file.h" + +namespace TwinE { + +class TwinEEngine; + +/** + * High Quality Resource + * + * https://web.archive.org/web/20181218233826/http://lbafileinfo.kazekr.net/index.php?title=High_quality_resource + */ +namespace HQR { + + +/** + * Get a HQR entry pointer + * @param ptr pointer to save the entry + * @param filename HQR file name + * @param index entry index to extract + * @return entry real size + */ +int32 getEntry(uint8 *ptr, const Common::Filename &filename, int32 index); + +/** + * Get a HQR entry pointer + * @param filename HQR file name + * @param index entry index to extract + * @return entry real size + */ +int32 entrySize(const Common::Filename &filename, int32 index); + +/** + * Get a HQR total number of entries + * @param filename HQR file name + * @return total number of entries + */ +int32 numEntries(const Common::Filename &filename); + +/** + * Get a HQR entry pointer with memory allocation + * @param ptr pointer to save the entry. This pointer is automatically freed and therefore must be initialized + * to @c nullptr on the first run. + * @param filename HQR file name + * @param index entry index to extract + * @return entry real size + */ +int32 getAllocEntry(uint8 **ptr, const Common::Filename &filename, int32 index); + +/** + * Get a HQR entry pointer + * @param ptr pointer to save the entry + * @param filename HQR file name + * @param index entry index to extract + * @return entry real size + */ +int32 getVoxEntry(uint8 *ptr, const Common::Filename &filename, int32 index, int32 hiddenIndex); +/** + * Get a HQR entry pointer with memory allocation + * @param ptr pointer to save the entry. This pointer is automatically freed and therefore must be initialized + * to @c nullptr on the first run. + * @param filename HQR file name + * @param index entry index to extract + * @return entry real size + */ +int32 getAllocVoxEntry(uint8 **ptr, const Common::Filename &filename, int32 index, int32 hiddenIndex); + +Common::SeekableReadStream *makeReadStream(const Common::Filename &filename, int index); + +} // namespace HQR + +} // namespace TwinE + +#endif diff --git a/engines/twine/lba1.cpp b/engines/twine/lba1.cpp new file mode 100644 index 00000000..d48d44fe --- /dev/null +++ b/engines/twine/lba1.cpp @@ -0,0 +1,1241 @@ +/* ScummVM Tools + * + * ScummVM Tools 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 3 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, see . + * + */ + +#include "engines/twine/lba1.h" +#include +#include + +#include "common/array.h" +#include "common/util.h" +#include "common/memstream.h" + +struct ScriptContext { + Common::MemoryReadStream stream; + Common::Array offsets; + int level; + int comportmentId; + bool comportement; +}; + +typedef void ScriptFunc(ScriptContext &ctx); + +struct ScriptFunction { + const char *name; + ScriptFunc *function; +}; + +#define MAPFUNC(name, func) \ + { name, func } + +static const int initialLevel = 2; +static const int indentWidth = 2; + +static void mEND(ScriptContext &ctx) { + printf("%*sEND\n", ctx.level, " "); +} + +static void mNOP(ScriptContext &ctx) { + printf("%*sNOP\n", ctx.level, " "); +} + +static void mBODY(ScriptContext &ctx) { + printf("%*sBODY %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mANIM(ScriptContext &ctx) { + printf("%*sANIM %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mGOTO_POINT(ScriptContext &ctx) { + printf("%*sGOTO_POINT %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mWAIT_ANIM(ScriptContext &ctx) { + printf("%*sWAIT_ANIM\n", ctx.level, " "); +} + +static void mLOOP(ScriptContext &ctx) { + printf("%*sLOOP\n", ctx.level, " "); +} + +static void mANGLE(ScriptContext &ctx) { + printf("%*sANGLE %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mPOS_POINT(ScriptContext &ctx) { + printf("%*sPOS_POINT %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mLABEL(ScriptContext &ctx) { + ctx.level = initialLevel; + printf("%*sLABEL %i\n", ctx.level, " ", (int)ctx.stream.readByte()); + ctx.level += indentWidth; +} + +static void mGOTO(ScriptContext &ctx) { + printf("%*sGOTO_SYM_POINT %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mSTOP(ScriptContext &ctx) { + printf("%*sSTOP\n", ctx.level, " "); +} + +static void mGOTO_SYM_POINT(ScriptContext &ctx) { + printf("%*sGOTO_SYM_POINT %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mWAIT_NUM_ANIM(ScriptContext &ctx) { + const int32 animRepeats = ctx.stream.readByte(); + const int32 animPos = ctx.stream.readByte(); + printf("%*sWAIT_NUM_ANIM %i %i\n", ctx.level, " ", animRepeats, animPos); +} + +static void mSAMPLE(ScriptContext &ctx) { + printf("%*sSAMPLE %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mGOTO_POINT_3D(ScriptContext &ctx) { + printf("%*sGOTO_POINT_3D %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mSPEED(ScriptContext &ctx) { + printf("%*sSPEED %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mBACKGROUND(ScriptContext &ctx) { + printf("%*sBACKGROUND %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void mWAIT_NUM_SECOND(ScriptContext &ctx) { + printf("%*sWAIT_NUM_SECOND %i %i\n", ctx.level, " ", (int)ctx.stream.readByte(), ctx.stream.readSint32LE()); +} + +static void mNO_BODY(ScriptContext &ctx) { + printf("%*sNO_BODY\n", ctx.level, " "); +} + +static void mBETA(ScriptContext &ctx) { + printf("%*sBETA %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mOPEN_LEFT(ScriptContext &ctx) { + printf("%*sOPEN_LEFT %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mOPEN_RIGHT(ScriptContext &ctx) { + printf("%*sOPEN_RIGHT %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mOPEN_UP(ScriptContext &ctx) { + printf("%*sOPEN_UP %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mOPEN_DOWN(ScriptContext &ctx) { + printf("%*sOPEN_DOWN %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mCLOSE(ScriptContext &ctx) { + printf("%*sCLOSE\n", ctx.level, " "); +} + +static void mWAIT_DOOR(ScriptContext &ctx) { + printf("%*sWAIT_DOOR\n", ctx.level, " "); +} + +static void mSAMPLE_RND(ScriptContext &ctx) { + printf("%*sSAMPLE_RND %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mSAMPLE_ALWAYS(ScriptContext &ctx) { + printf("%*sSAMPLE_ALWAYS %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mSAMPLE_STOP(ScriptContext &ctx) { + printf("%*sSAMPLE_STOP %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mPLAY_FLA(ScriptContext &ctx) { + int strIdx = 0; + char movie[64]; + do { + const byte c = ctx.stream.readByte(); + movie[strIdx++] = c; + if (c == '\0') { + break; + } + if (strIdx >= ARRAYSIZE(movie)) { + error("Max string size exceeded for fla name"); + } + } while (true); + + printf("%*sPLAY_FLA %s\n", ctx.level, " ", movie); +} + +static void mREPEAT_SAMPLE(ScriptContext &ctx) { + printf("%*sREPEAT_SAMPLE %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mSIMPLE_SAMPLE(ScriptContext &ctx) { + printf("%*sSIMPLE_SAMPLE %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mFACE_HERO(ScriptContext &ctx) { + printf("%*sFACE_HERO %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void mANGLE_RND(ScriptContext &ctx) { + const int16 val1 = ctx.stream.readSint16LE(); + const int16 val2 = ctx.stream.readSint16LE(); + printf("%*sANGLE_RND %i %i\n", ctx.level, " ", (int)val1, (int)val2); +} + +static const ScriptFunction moveScriptFunctions[] = { + /*0x00*/ MAPFUNC("END", mEND), + /*0x01*/ MAPFUNC("NOP", mNOP), + /*0x02*/ MAPFUNC("BODY", mBODY), + /*0x03*/ MAPFUNC("ANIM", mANIM), + /*0x04*/ MAPFUNC("GOTO_POINT", mGOTO_POINT), + /*0x05*/ MAPFUNC("WAIT_ANIM", mWAIT_ANIM), + /*0x06*/ MAPFUNC("LOOP", mLOOP), + /*0x07*/ MAPFUNC("ANGLE", mANGLE), + /*0x08*/ MAPFUNC("POS_POINT", mPOS_POINT), + /*0x09*/ MAPFUNC("LABEL", mLABEL), + /*0x0A*/ MAPFUNC("GOTO", mGOTO), + /*0x0B*/ MAPFUNC("STOP", mSTOP), + /*0x0C*/ MAPFUNC("GOTO_SYM_POINT", mGOTO_SYM_POINT), + /*0x0D*/ MAPFUNC("WAIT_NUM_ANIM", mWAIT_NUM_ANIM), + /*0x0E*/ MAPFUNC("SAMPLE", mSAMPLE), + /*0x0F*/ MAPFUNC("GOTO_POINT_3D", mGOTO_POINT_3D), + /*0x10*/ MAPFUNC("SPEED", mSPEED), + /*0x11*/ MAPFUNC("BACKGROUND", mBACKGROUND), + /*0x12*/ MAPFUNC("WAIT_NUM_SECOND", mWAIT_NUM_SECOND), + /*0x13*/ MAPFUNC("NO_BODY", mNO_BODY), + /*0x14*/ MAPFUNC("BETA", mBETA), + /*0x15*/ MAPFUNC("OPEN_LEFT", mOPEN_LEFT), + /*0x16*/ MAPFUNC("OPEN_RIGHT", mOPEN_RIGHT), + /*0x17*/ MAPFUNC("OPEN_UP", mOPEN_UP), + /*0x18*/ MAPFUNC("OPEN_DOWN", mOPEN_DOWN), + /*0x19*/ MAPFUNC("CLOSE", mCLOSE), + /*0x1A*/ MAPFUNC("WAIT_DOOR", mWAIT_DOOR), + /*0x1B*/ MAPFUNC("SAMPLE_RND", mSAMPLE_RND), + /*0x1C*/ MAPFUNC("SAMPLE_ALWAYS", mSAMPLE_ALWAYS), + /*0x1D*/ MAPFUNC("SAMPLE_STOP", mSAMPLE_STOP), + /*0x1E*/ MAPFUNC("PLAY_FLA", mPLAY_FLA), + /*0x1F*/ MAPFUNC("REPEAT_SAMPLE", mREPEAT_SAMPLE), + /*0x20*/ MAPFUNC("SIMPLE_SAMPLE", mSIMPLE_SAMPLE), + /*0x21*/ MAPFUNC("FACE_HERO", mFACE_HERO), + /*0x22*/ MAPFUNC("ANGLE_RND", mANGLE_RND) +}; + + +/** Script condition operators */ +static const char *LifeScriptOperators[] = { + "==", + ">", + "<", + ">=", + "<=", + "!=" +}; + +/** Script condition command opcodes */ +enum LifeScriptConditions { + /*0x00*/ kcCOL = 0, /*= ARRAYSIZE(LifeScriptOperators)) { + error("Invalid operator %i", (int)operatorCode); + } + printf(" %s ", LifeScriptOperators[operatorCode]); + int32 conditionValue; + if (valueSize == 1) { + conditionValue = ctx.stream.readByte(); + } else if (valueSize == 2) { + conditionValue = ctx.stream.readSint16LE(); + } else { + error("Unknown operator value size %d", valueSize); + } + printf("%i", conditionValue); +} + +static void lEMPTY(ScriptContext &ctx) { + printf("%*sEMPTY\n", ctx.level, " "); +} + +static void lEND(ScriptContext &ctx) { + printf("%*sEND\n", ctx.level, " "); + ctx.level -= indentWidth; +} + +static void lNOP(ScriptContext &ctx) { + printf("%*sNOP\n", ctx.level, " "); +} + +static void lOFFSET(ScriptContext &ctx) { + const int16 offset = ctx.stream.readSint16LE(); + printf("%*sOFFSET %i\n", ctx.level, " ", (int)offset); +} + +static void lSNIF(ScriptContext &ctx) { + printf("%*sSWITCH_NO_IF ", ctx.level, " "); + const int32 valueSize = processLifeConditions(ctx); + processLifeOperators(ctx, valueSize); + const int16 offset = ctx.stream.readSint16LE(); + ctx.offsets.push_back(offset); + printf(" (offset %i)\n", offset); + ctx.level += indentWidth; +} + +static void lNEVERIF(ScriptContext &ctx) { + printf("%*sNEVER_IF ", ctx.level, " "); + const int32 valueSize = processLifeConditions(ctx); + processLifeOperators(ctx, valueSize); + const int16 offset = ctx.stream.readSint16LE(); + ctx.offsets.push_back(offset); + printf(" (offset %i)\n", offset); + ctx.level += indentWidth; +} + +static void lOR_IF(ScriptContext &ctx) { + printf("%*sOR_IF ", ctx.level, " "); + const int32 valueSize = processLifeConditions(ctx); + processLifeOperators(ctx, valueSize); + const int16 offset = ctx.stream.readSint16LE(); + printf(" (offset %i)\n", offset); +} + +static void lNO_IF(ScriptContext &ctx) { + printf("%*sNO_IF\n", ctx.level, " "); + ctx.level += indentWidth; +} + +static void lIF(ScriptContext &ctx) { + printf("%*sIF ", ctx.level, " "); + const int32 valueSize = processLifeConditions(ctx); + processLifeOperators(ctx, valueSize); + const int16 offset = ctx.stream.readSint16LE(); + ctx.offsets.push_back(offset); + printf(" (offset %i)\n", offset); + ctx.level += indentWidth; +} + +static void lSWIF(ScriptContext &ctx) { + printf("%*sSWITCH_IF ", ctx.level, " "); + const int32 valueSize = processLifeConditions(ctx); + processLifeOperators(ctx, valueSize); + const int16 offset = ctx.stream.readSint16LE(); + ctx.offsets.push_back(offset); + printf(" (offset %i)\n", offset); + ctx.level += indentWidth; +} + +static void lONEIF(ScriptContext &ctx) { + printf("%*sONEIF ", ctx.level, " "); + const int32 valueSize = processLifeConditions(ctx); + processLifeOperators(ctx, valueSize); + const int16 offset = ctx.stream.readSint16LE(); + ctx.offsets.push_back(offset); + printf(" (offset %i)\n", offset); + ctx.level += indentWidth; +} + +static void lELSE(ScriptContext &ctx) { + const int16 offset = ctx.stream.readSint16LE(); + ctx.offsets.push_back(ctx.stream.pos()); + ctx.level -= indentWidth; + printf("%*sELSE (offset %i)\n", ctx.level, " ", offset); + ctx.level += indentWidth; +} + +static void lLABEL(ScriptContext &ctx) { + printf("%*sLABEL %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lRETURN(ScriptContext &ctx) { + printf("%*sRETURN\n", ctx.level, " "); + ctx.level -= indentWidth; +} + +static void lBODY(ScriptContext &ctx) { + const int32 bodyIdx = ctx.stream.readByte(); + printf("%*sBODY %i\n", ctx.level, " ", (int)bodyIdx); +} + +static void lBODY_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 otherBodyIdx = ctx.stream.readByte(); + printf("%*sBODY_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)otherBodyIdx); +} + +static void lANIM(ScriptContext &ctx) { + const int32 animIdx = ctx.stream.readByte(); + printf("%*sANIM %i\n", ctx.level, " ", (int)animIdx); +} + +static void lANIM_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 otherAnimIdx = ctx.stream.readByte(); + printf("%*sANIM_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)otherAnimIdx); +} + +static void lSET_LIFE(ScriptContext &ctx) { + const int16 offset = ctx.stream.readSint16LE(); + printf("%*sSET_LIFE %i\n", ctx.level, " ", (int)offset); +} + +static void lSET_LIFE_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int16 offset = ctx.stream.readSint16LE(); + printf("%*sSET_LIFE_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)offset); +} + +static void lSET_TRACK(ScriptContext &ctx) { + const int16 offset = ctx.stream.readSint16LE(); + printf("%*sSET_TRACK %i\n", ctx.level, " ", (int)offset); +} + +static void lSET_TRACK_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int16 offset = ctx.stream.readSint16LE(); + printf("%*sSET_TRACK_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)offset); +} + +static void lMESSAGE(ScriptContext &ctx) { + printf("%*sMESSAGE %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void lFALLABLE(ScriptContext &ctx) { + printf("%*sFALLABLE %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lSET_DIRMODE(ScriptContext &ctx) { + const int32 controlMode = ctx.stream.readByte(); + if (controlMode == 2) { // kFollow + printf("%*sSET_DIRMODE %i %i\n", ctx.level, " ", (int)controlMode, (int)ctx.stream.readByte()); + } else { + printf("%*sSET_DIRMODE %i\n", ctx.level, " ", (int)controlMode); + } +} + +static void lSET_DIRMODE_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 controlMode = ctx.stream.readByte(); + + if (controlMode == 2) { + int32 followedActor = ctx.stream.readByte(); + printf("%*sSET_DIRMODE_OBJ %i %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)controlMode, (int)followedActor); + } else { + printf("%*sSET_DIRMODE_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)controlMode); + } +} + +static void lCAM_FOLLOW(ScriptContext &ctx) { + printf("%*sCAM_FOLLOW %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lSET_BEHAVIOUR(ScriptContext &ctx) { + const int32 behavior = ctx.stream.readByte(); + printf("%*sSET_BEHAVIOUR %i\n", ctx.level, " ", (int)behavior); +} + +static void lSET_FLAG_CUBE(ScriptContext &ctx) { + const int32 flagIdx = ctx.stream.readByte(); + const int32 flagValue = ctx.stream.readByte(); + printf("%*sSET_FLAG_CUBE %i %i\n", ctx.level, " ", (int)flagIdx, (int)flagValue); +} + +static void lCOMPORTEMENT(ScriptContext &ctx) { + printf("%*sCOMPORTEMENT %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lSET_COMPORTEMENT(ScriptContext &ctx) { + printf("%*sSET_COMPORTEMENT %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void lSET_COMPORTEMENT_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int16 pos = ctx.stream.readSint16LE(); + printf("%*sSET_COMPORTEMENT_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)pos); +} + +static void lEND_COMPORTEMENT(ScriptContext &ctx) { + ctx.level -= indentWidth; + printf("%*sEND_COMPORTEMENT\n", ctx.level, " "); + ctx.comportement = false; +} + +static void lSET_FLAG_GAME(ScriptContext &ctx) { + const uint8 flagIdx = ctx.stream.readByte(); + const uint8 flagValue = ctx.stream.readByte(); + printf("%*sSET_FLAG_GAME %i %i\n", ctx.level, " ", (int)flagIdx, (int)flagValue); +} + +static void lKILL_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + printf("%*slKILL_OBJ %i\n", ctx.level, " ", (int)otherActorIdx); +} + +static void lSUICIDE(ScriptContext &ctx) { + printf("%*sSUICIDE\n", ctx.level, " "); +} + +static void lUSE_ONE_LITTLE_KEY(ScriptContext &ctx) { + printf("%*sUSE_ONE_LITTLE_KEY\n", ctx.level, " "); +} + +static void lGIVE_GOLD_PIECES(ScriptContext &ctx) { + const int16 kashes = ctx.stream.readSint16LE(); + printf("%*sGIVE_GOLD_PIECES %i\n", ctx.level, " ", (int)kashes); +} + +static void lEND_LIFE(ScriptContext &ctx) { + printf("%*sEND_LIFE\n", ctx.level, " "); +} + +static void lSTOP_L_TRACK(ScriptContext &ctx) { + printf("%*sSTOP_L_TRACK\n", ctx.level, " "); +} + +static void lRESTORE_L_TRACK(ScriptContext &ctx) { + printf("%*sRESTORE_L_TRACK\n", ctx.level, " "); +} + +static void lMESSAGE_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 int32x = ctx.stream.readSint16LE(); + printf("%*sMESSAGE_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)int32x); +} + +static void lINC_CHAPTER(ScriptContext &ctx) { + printf("%*sINC_CHAPTER\n", ctx.level, " "); +} + +static void lFOUND_OBJECT(ScriptContext &ctx) { + printf("%*sFOUND_OBJECT %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lSET_DOOR_LEFT(ScriptContext &ctx) { + const int32 distance = ctx.stream.readSint16LE(); + printf("%*sSET_DOOR_LEFT %i\n", ctx.level, " ", (int)distance); +} + +static void lSET_DOOR_RIGHT(ScriptContext &ctx) { + const int32 distance = ctx.stream.readSint16LE(); + printf("%*sSET_DOOR_RIGHT %i\n", ctx.level, " ", (int)distance); +} + +static void lSET_DOOR_UP(ScriptContext &ctx) { + const int32 distance = ctx.stream.readSint16LE(); + printf("%*sSET_DOOR_UP %i\n", ctx.level, " ", (int)distance); +} + +static void lSET_DOOR_DOWN(ScriptContext &ctx) { + const int32 distance = ctx.stream.readSint16LE(); + printf("%*sSET_DOOR_DOWN %i\n", ctx.level, " ", (int)distance); +} + +static void lGIVE_BONUS(ScriptContext &ctx) { + const int32 flag = ctx.stream.readByte(); + printf("%*sGIVE_BONUS %i\n", ctx.level, " ", (int)flag); +} + +static void lCHANGE_CUBE(ScriptContext &ctx) { + const int32 sceneIdx = ctx.stream.readByte(); + printf("%*sCHANGE_CUBE %i\n", ctx.level, " ", (int)sceneIdx); +} + +static void lOBJ_COL(ScriptContext &ctx) { + const int32 collision = ctx.stream.readByte(); + printf("%*sOBJ_COL %i\n", ctx.level, " ", (int)collision); +} + +static void lBRICK_COL(ScriptContext &ctx) { + const int32 collision = ctx.stream.readByte(); + printf("%*sBRICK_COL %i\n", ctx.level, " ", (int)collision); +} + +static void lINVISIBLE(ScriptContext &ctx) { + printf("%*sINVISIBLE %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lZOOM(ScriptContext &ctx) { + const int zoomScreen = ctx.stream.readByte(); + printf("%*sZOOM %i\n", ctx.level, " ", zoomScreen); +} + +static void lPOS_POINT(ScriptContext &ctx) { + const int32 trackIdx = ctx.stream.readByte(); + printf("%*sPOS_POINT %i\n", ctx.level, " ", (int)trackIdx); +} + +static void lSET_MAGIC_LEVEL(ScriptContext &ctx) { + printf("%*sSET_MAGIC_LEVEL %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lSUB_MAGIC_POINT(ScriptContext &ctx) { + const int16 magicPoints = (int16)ctx.stream.readByte(); + printf("%*sSET_MAGIC_POINT %i\n", ctx.level, " ", (int)magicPoints); +} + +static void lSET_LIFE_POINT_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 lifeValue = ctx.stream.readByte(); + printf("%*sSET_LIFE_POINT_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)lifeValue); +} + +static void lSUB_LIFE_POINT_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 lifeValue = ctx.stream.readByte(); + printf("%*sSUB_LIFE_POINT_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)lifeValue); +} + +static void lHIT_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 strengthOfHit = ctx.stream.readByte(); + printf("%*sHIT_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)strengthOfHit); +} + +static void lPLAY_FLA(ScriptContext &ctx) { + int strIdx = 0; + char movie[64]; + do { + const byte c = ctx.stream.readByte(); + movie[strIdx++] = c; + if (c == '\0') { + break; + } + if (strIdx >= ARRAYSIZE(movie)) { + error("Max string size exceeded for fla name"); + } + } while (true); + printf("%*sPLAY_FLA %s\n", ctx.level, " ", movie); +} + +static void lPLAY_MIDI(ScriptContext &ctx) { + const int32 midiIdx = ctx.stream.readByte(); + printf("%*sPLAY_MIDI %i\n", ctx.level, " ", (int)midiIdx); +} + +static void lINC_CLOVER_BOX(ScriptContext &ctx) { + printf("%*sINC_CLOVER_BOX\n", ctx.level, " "); +} + +static void lSET_USED_INVENTORY(ScriptContext &ctx) { + const int32 item = ctx.stream.readByte(); + printf("%*sSET_USED_INVENTORY %i\n", ctx.level, " ", (int)item); +} + +static void lADD_CHOICE(ScriptContext &ctx) { + const int32 choiceIdx = ctx.stream.readSint16LE(); + printf("%*sADD_CHOICE %i\n", ctx.level, " ", (int)choiceIdx); +} + +static void lASK_CHOICE(ScriptContext &ctx) { + const int32 choiceIdx = ctx.stream.readSint16LE(); + printf("%*sASK_CHOICE %i\n", ctx.level, " ", (int)choiceIdx); +} + +static void lBIG_MESSAGE(ScriptContext &ctx) { + const int32 int32x = ctx.stream.readSint16LE(); + printf("%*sBIG_MESSAGE %i\n", ctx.level, " ", (int)int32x); +} + +static void lINIT_PINGOUIN(ScriptContext &ctx) { + const int16 penguinActor = ctx.stream.readByte(); + printf("%*sINIT_PINGOUIN %i\n", ctx.level, " ", (int)penguinActor); +} + +static void lSET_HOLO_POS(ScriptContext &ctx) { + const int32 location = ctx.stream.readByte(); + printf("%*sSET_HOLO_POS %i\n", ctx.level, " ", (int)location); +} + +static void lCLR_HOLO_POS(ScriptContext &ctx) { + const int32 location = ctx.stream.readByte(); + printf("%*sCLR_HOLO_POS %i\n", ctx.level, " ", (int)location); +} + +static void lADD_FUEL(ScriptContext &ctx) { + const int16 value = ctx.stream.readByte(); + printf("%*sADD_FUEL %i\n", ctx.level, " ", (int)value); +} + +static void lSUB_FUEL(ScriptContext &ctx) { + const int16 value = ctx.stream.readByte(); + printf("%*sSUB_FUEL %i\n", ctx.level, " ", (int)value); +} + +static void lSET_GRM(ScriptContext &ctx) { + printf("%*sSET_GRM %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lSAY_MESSAGE(ScriptContext &ctx) { + const int32 textEntry = ctx.stream.readSint16LE(); + printf("%*sSAY_MESSAGE %i\n", ctx.level, " ", (int)textEntry); +} + +static void lSAY_MESSAGE_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 textEntry = ctx.stream.readSint16LE(); + printf("%*sSAY_MESSAGE_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)textEntry); +} + +static void lFULL_POINT(ScriptContext &ctx) { + printf("%*sFULL_POINT\n", ctx.level, " "); +} + +static void lBETA(ScriptContext &ctx) { + const int32 newAngle = ctx.stream.readSint16LE(); + printf("%*sBETA %i\n", ctx.level, " ", (int)newAngle); +} + +static void lGRM_OFF(ScriptContext &ctx) { + printf("%*sGRM_OFF\n", ctx.level, " "); +} + +static void lFADE_PAL_RED(ScriptContext &ctx) { + printf("%*sFADE_PAL_RED\n", ctx.level, " "); +} + +static void lFADE_ALARM_RED(ScriptContext &ctx) { + printf("%*sFADE_ALARM_RED\n", ctx.level, " "); +} + +static void lFADE_ALARM_PAL(ScriptContext &ctx) { + printf("%*sFADE_ALARM_PAL\n", ctx.level, " "); +} + +static void lFADE_RED_PAL(ScriptContext &ctx) { + printf("%*sFADE_RED_PAL\n", ctx.level, " "); +} + +static void lFADE_RED_ALARM(ScriptContext &ctx) { + printf("%*sFADE_RED_ALARM\n", ctx.level, " "); +} + +static void lFADE_PAL_ALARM(ScriptContext &ctx) { + printf("%*sFADE_PAL_ALARM\n", ctx.level, " "); +} + +static void lEXPLODE_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + printf("%*sEXPLODE_OBJ %i\n", ctx.level, " ", (int)otherActorIdx); +} + +static void lBUBBLE_ON(ScriptContext &ctx) { + printf("%*sBUBBLE_ON\n", ctx.level, " "); +} + +static void lBUBBLE_OFF(ScriptContext &ctx) { + printf("%*sBUBBLE_OFF\n", ctx.level, " "); +} + +static void lASK_CHOICE_OBJ(ScriptContext &ctx) { + const int32 otherActorIdx = ctx.stream.readByte(); + const int32 choiceIdx = ctx.stream.readSint16LE(); + printf("%*sASK_CHOICE_OBJ %i %i\n", ctx.level, " ", (int)otherActorIdx, (int)choiceIdx); +} + +static void lSET_DARK_PAL(ScriptContext &ctx) { + printf("%*sSET_DARK_PAL\n", ctx.level, " "); +} + +static void lSET_NORMAL_PAL(ScriptContext &ctx) { + printf("%*sSET_NORMAL_PAL\n", ctx.level, " "); +} + +static void lMESSAGE_SENDELL(ScriptContext &ctx) { + printf("%*sMESSAGE_SENDELL\n", ctx.level, " "); +} + +static void lANIM_SET(ScriptContext &ctx) { + printf("%*sANIM_SET %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lHOLOMAP_TRAJ(ScriptContext &ctx) { + printf("%*sHOLOMAP_TRAJ %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lGAME_OVER(ScriptContext &ctx) { + printf("%*sGAME_OVER\n", ctx.level, " "); +} + +static void lTHE_END(ScriptContext &ctx) { + printf("%*sTHE_END\n", ctx.level, " "); +} + +static void lMIDI_OFF(ScriptContext &ctx) { + printf("%*sMIDI_OFF\n", ctx.level, " "); +} + +static void lPLAY_CD_TRACK(ScriptContext &ctx) { + printf("%*sPLAY_CD_TRACK %i\n", ctx.level, " ", (int)ctx.stream.readByte()); +} + +static void lPROJ_ISO(ScriptContext &ctx) { + printf("%*sPROJ_ISO\n", ctx.level, " "); +} + +static void lPROJ_3D(ScriptContext &ctx) { + printf("%*sPROJ_3D\n", ctx.level, " "); +} + +static void lTEXT(ScriptContext &ctx) { + printf("%*sTEXT %i\n", ctx.level, " ", (int)ctx.stream.readSint16LE()); +} + +static void lCLEAR_TEXT(ScriptContext &ctx) { + printf("%*sCLEAR_TEXT\n", ctx.level, " "); +} + +static void lBRUTAL_EXIT(ScriptContext &ctx) { + printf("%*sBRUTAL_EXIT\n", ctx.level, " "); +} + +static const ScriptFunction lifeScriptFunctions[] = { + /*0x00*/ MAPFUNC("END", lEND), + /*0x01*/ MAPFUNC("NOP", lNOP), + /*0x02*/ MAPFUNC("SNIF", lSNIF), + /*0x03*/ MAPFUNC("OFFSET", lOFFSET), + /*0x04*/ MAPFUNC("NEVERIF", lNEVERIF), + /*0x05*/ MAPFUNC("", lEMPTY), // unused + /*0x06*/ MAPFUNC("NO_IF", lNO_IF), + /*0x07*/ MAPFUNC("", lEMPTY), // unused + /*0x08*/ MAPFUNC("", lEMPTY), // unused + /*0x09*/ MAPFUNC("", lEMPTY), // unused + /*0x0A*/ MAPFUNC("LABEL", lLABEL), + /*0x0B*/ MAPFUNC("RETURN", lRETURN), + /*0x0C*/ MAPFUNC("IF", lIF), + /*0x0D*/ MAPFUNC("SWIF", lSWIF), + /*0x0E*/ MAPFUNC("ONEIF", lONEIF), + /*0x0F*/ MAPFUNC("ELSE", lELSE), + /*0x10*/ MAPFUNC("ENDIF", lEMPTY), + /*0x11*/ MAPFUNC("BODY", lBODY), + /*0x12*/ MAPFUNC("BODY_OBJ", lBODY_OBJ), + /*0x13*/ MAPFUNC("ANIM", lANIM), + /*0x14*/ MAPFUNC("ANIM_OBJ", lANIM_OBJ), + /*0x15*/ MAPFUNC("SET_LIFE", lSET_LIFE), + /*0x16*/ MAPFUNC("SET_LIFE_OBJ", lSET_LIFE_OBJ), + /*0x17*/ MAPFUNC("SET_TRACK", lSET_TRACK), + /*0x18*/ MAPFUNC("SET_TRACK_OBJ", lSET_TRACK_OBJ), + /*0x19*/ MAPFUNC("MESSAGE", lMESSAGE), + /*0x1A*/ MAPFUNC("FALLABLE", lFALLABLE), + /*0x1B*/ MAPFUNC("SET_DIRMODE", lSET_DIRMODE), + /*0x1C*/ MAPFUNC("SET_DIRMODE_OBJ", lSET_DIRMODE_OBJ), + /*0x1D*/ MAPFUNC("CAM_FOLLOW", lCAM_FOLLOW), + /*0x1E*/ MAPFUNC("SET_BEHAVIOUR", lSET_BEHAVIOUR), + /*0x1F*/ MAPFUNC("SET_FLAG_CUBE", lSET_FLAG_CUBE), + /*0x20*/ MAPFUNC("COMPORTEMENT", lCOMPORTEMENT), + /*0x21*/ MAPFUNC("SET_COMPORTEMENT", lSET_COMPORTEMENT), + /*0x22*/ MAPFUNC("SET_COMPORTEMENT_OBJ", lSET_COMPORTEMENT_OBJ), + /*0x23*/ MAPFUNC("END_COMPORTEMENT", lEND_COMPORTEMENT), + /*0x24*/ MAPFUNC("SET_FLAG_GAME", lSET_FLAG_GAME), + /*0x25*/ MAPFUNC("KILL_OBJ", lKILL_OBJ), + /*0x26*/ MAPFUNC("SUICIDE", lSUICIDE), + /*0x27*/ MAPFUNC("USE_ONE_LITTLE_KEY", lUSE_ONE_LITTLE_KEY), + /*0x28*/ MAPFUNC("GIVE_GOLD_PIECES", lGIVE_GOLD_PIECES), + /*0x29*/ MAPFUNC("END_LIFE", lEND_LIFE), + /*0x2A*/ MAPFUNC("STOP_L_TRACK", lSTOP_L_TRACK), + /*0x2B*/ MAPFUNC("RESTORE_L_TRACK", lRESTORE_L_TRACK), + /*0x2C*/ MAPFUNC("MESSAGE_OBJ", lMESSAGE_OBJ), + /*0x2D*/ MAPFUNC("INC_CHAPTER", lINC_CHAPTER), + /*0x2E*/ MAPFUNC("FOUND_OBJECT", lFOUND_OBJECT), + /*0x2F*/ MAPFUNC("SET_DOOR_LEFT", lSET_DOOR_LEFT), + /*0x30*/ MAPFUNC("SET_DOOR_RIGHT", lSET_DOOR_RIGHT), + /*0x31*/ MAPFUNC("SET_DOOR_UP", lSET_DOOR_UP), + /*0x32*/ MAPFUNC("SET_DOOR_DOWN", lSET_DOOR_DOWN), + /*0x33*/ MAPFUNC("GIVE_BONUS", lGIVE_BONUS), + /*0x34*/ MAPFUNC("CHANGE_CUBE", lCHANGE_CUBE), + /*0x35*/ MAPFUNC("OBJ_COL", lOBJ_COL), + /*0x36*/ MAPFUNC("BRICK_COL", lBRICK_COL), + /*0x37*/ MAPFUNC("OR_IF", lOR_IF), + /*0x38*/ MAPFUNC("INVISIBLE", lINVISIBLE), + /*0x39*/ MAPFUNC("ZOOM", lZOOM), + /*0x3A*/ MAPFUNC("POS_POINT", lPOS_POINT), + /*0x3B*/ MAPFUNC("SET_MAGIC_LEVEL", lSET_MAGIC_LEVEL), + /*0x3C*/ MAPFUNC("SUB_MAGIC_POINT", lSUB_MAGIC_POINT), + /*0x3D*/ MAPFUNC("SET_LIFE_POINT_OBJ", lSET_LIFE_POINT_OBJ), + /*0x3E*/ MAPFUNC("SUB_LIFE_POINT_OBJ", lSUB_LIFE_POINT_OBJ), + /*0x3F*/ MAPFUNC("HIT_OBJ", lHIT_OBJ), + /*0x40*/ MAPFUNC("PLAY_FLA", lPLAY_FLA), + /*0x41*/ MAPFUNC("PLAY_MIDI", lPLAY_MIDI), + /*0x42*/ MAPFUNC("INC_CLOVER_BOX", lINC_CLOVER_BOX), + /*0x43*/ MAPFUNC("SET_USED_INVENTORY", lSET_USED_INVENTORY), + /*0x44*/ MAPFUNC("ADD_CHOICE", lADD_CHOICE), + /*0x45*/ MAPFUNC("ASK_CHOICE", lASK_CHOICE), + /*0x46*/ MAPFUNC("BIG_MESSAGE", lBIG_MESSAGE), + /*0x47*/ MAPFUNC("INIT_PINGOUIN", lINIT_PINGOUIN), + /*0x48*/ MAPFUNC("SET_HOLO_POS", lSET_HOLO_POS), + /*0x49*/ MAPFUNC("CLR_HOLO_POS", lCLR_HOLO_POS), + /*0x4A*/ MAPFUNC("ADD_FUEL", lADD_FUEL), + /*0x4B*/ MAPFUNC("SUB_FUEL", lSUB_FUEL), + /*0x4C*/ MAPFUNC("SET_GRM", lSET_GRM), + /*0x4D*/ MAPFUNC("SAY_MESSAGE", lSAY_MESSAGE), + /*0x4E*/ MAPFUNC("SAY_MESSAGE_OBJ", lSAY_MESSAGE_OBJ), + /*0x4F*/ MAPFUNC("FULL_POINT", lFULL_POINT), + /*0x50*/ MAPFUNC("BETA", lBETA), + /*0x51*/ MAPFUNC("GRM_OFF", lGRM_OFF), + /*0x52*/ MAPFUNC("FADE_PAL_RED", lFADE_PAL_RED), + /*0x53*/ MAPFUNC("FADE_ALARM_RED", lFADE_ALARM_RED), + /*0x54*/ MAPFUNC("FADE_ALARM_PAL", lFADE_ALARM_PAL), + /*0x55*/ MAPFUNC("FADE_RED_PAL", lFADE_RED_PAL), + /*0x56*/ MAPFUNC("FADE_RED_ALARM", lFADE_RED_ALARM), + /*0x57*/ MAPFUNC("FADE_PAL_ALARM", lFADE_PAL_ALARM), + /*0x58*/ MAPFUNC("EXPLODE_OBJ", lEXPLODE_OBJ), + /*0x59*/ MAPFUNC("BUBBLE_ON", lBUBBLE_ON), + /*0x5A*/ MAPFUNC("BUBBLE_OFF", lBUBBLE_OFF), + /*0x5B*/ MAPFUNC("ASK_CHOICE_OBJ", lASK_CHOICE_OBJ), + /*0x5C*/ MAPFUNC("SET_DARK_PAL", lSET_DARK_PAL), + /*0x5D*/ MAPFUNC("SET_NORMAL_PAL", lSET_NORMAL_PAL), + /*0x5E*/ MAPFUNC("MESSAGE_SENDELL", lMESSAGE_SENDELL), + /*0x5F*/ MAPFUNC("ANIM_SET", lANIM_SET), + /*0x60*/ MAPFUNC("HOLOMAP_TRAJ", lHOLOMAP_TRAJ), + /*0x61*/ MAPFUNC("GAME_OVER", lGAME_OVER), + /*0x62*/ MAPFUNC("THE_END", lTHE_END), + /*0x63*/ MAPFUNC("MIDI_OFF", lMIDI_OFF), + /*0x64*/ MAPFUNC("PLAY_CD_TRACK", lPLAY_CD_TRACK), + /*0x65*/ MAPFUNC("PROJ_ISO", lPROJ_ISO), + /*0x66*/ MAPFUNC("PROJ_3D", lPROJ_3D), + /*0x67*/ MAPFUNC("TEXT", lTEXT), + /*0x68*/ MAPFUNC("CLEAR_TEXT", lCLEAR_TEXT), + /*0x69*/ MAPFUNC("BRUTAL_EXIT", lBRUTAL_EXIT) +}; + +static int decompileLBA1MoveScript(int actorIdx, const uint8 *data, int16 size) { + Common::MemoryReadStream stream(data, size); + ScriptContext ctx{stream, {}, initialLevel, 0}; + + printf("Actor %i\n", actorIdx); + + while (ctx.stream.pos() < ctx.stream.size()) { + const byte scriptOpcode = ctx.stream.readByte(); + if (scriptOpcode < ARRAYSIZE(moveScriptFunctions)) { + moveScriptFunctions[scriptOpcode].function(ctx); + } else { + fprintf(stderr, "Actor %d with wrong offset/opcode - Offset: %d/%d (opcode: %u)", actorIdx, (int)ctx.stream.pos() - 1, (int)ctx.stream.size(), scriptOpcode); + return 1; + } + }; + + return 0; +} + +static int decompileLBA1LifeScript(int actorIdx, const uint8 *data, int16 size) { + Common::MemoryReadStream stream(data, size); + ScriptContext ctx{stream, {}, initialLevel, 0, true}; + + printf("Actor %i\n", actorIdx); + + printf("%*sCOMPORTMENT main\n", ctx.level, " "); + ctx.level += indentWidth; + while (ctx.stream.pos() < ctx.stream.size()) { + const byte scriptOpcode = ctx.stream.readByte(); + if (scriptOpcode < ARRAYSIZE(lifeScriptFunctions)) { + if (scriptOpcode && !ctx.comportement) { + ++ctx.comportmentId; + printf("%*sCOMPORTEMENT %i\n", ctx.level, " ", ctx.comportmentId); + ctx.level += indentWidth; + ctx.comportement = true; + } + lifeScriptFunctions[scriptOpcode].function(ctx); + while (!ctx.offsets.empty()) { + if (ctx.stream.pos() == ctx.offsets.back()) { + ctx.level -= indentWidth; + printf("%*sENDIF\n", ctx.level, " "); + ctx.offsets.pop_back(); + } else { + break; + } + } + } else { + fprintf(stderr, "Actor %d with wrong offset/opcode - Offset: %d/%d (opcode: %u)", actorIdx, (int)ctx.stream.pos() - 1, (int)ctx.stream.size(), scriptOpcode); + return 1; + } + }; + + return 0; +} + +int decompileLBA1(const uint8 *data, int size) { + Common::MemoryReadStream stream(data, size); + uint8_t sceneTextBank = stream.readByte(); + uint8_t currentGameOverScene = stream.readByte(); + stream.skip(4); + int16 _alphaLight = (int16)stream.readUint16LE(); + int16 _betaLight = (int16)stream.readUint16LE(); + + uint16 sampleAmbiance[4]; + uint16 sampleRepeat[4]; + uint16 sampleRound[4]; + + for (int i = 0; i < 4; ++i) { + sampleAmbiance[i] = stream.readUint16LE(); + sampleRepeat[i] = stream.readUint16LE(); + sampleRound[i] = stream.readUint16LE(); + } + + uint16 sampleMinDelay = stream.readUint16LE(); + uint16 sampleMinDelayRnd = stream.readUint16LE(); + + uint8 sceneMusic = stream.readByte(); + + int16 sceneHeroPosx = (int16)stream.readUint16LE(); + int16 sceneHeroPosy = (int16)stream.readUint16LE(); + int16 sceneHeroPosz = (int16)stream.readUint16LE(); + + int16 moveScriptSize = (int16)stream.readUint16LE(); + const uint8 *moveScript = data + stream.pos(); + stream.skip(moveScriptSize); + decompileLBA1MoveScript(0, moveScript, moveScriptSize); + + int16 lifeScriptSize = (int16)stream.readUint16LE(); + const uint8 *lifeScript = data + stream.pos(); + stream.skip(lifeScriptSize); + decompileLBA1LifeScript(0, lifeScript, lifeScriptSize); + + int16 sceneNumActors = (int16)stream.readUint16LE(); + int cnt = 1; + for (int32 a = 1; a < sceneNumActors; a++, cnt++) { + uint16 staticflags = stream.readUint16LE(); + uint16 body = stream.readUint16LE(); + uint8 genBody = stream.readByte(); + uint8 genAnim = stream.readByte(); + int16 sprite = stream.readSint16LE(); + int16 posx = stream.readSint16LE(); + int16 posy = stream.readSint16LE(); + int16 posz = stream.readSint16LE(); + uint8 strengthOfHit = stream.readByte(); + uint16 bonusflags = stream.readUint16LE(); + int16 beta = stream.readSint16LE(); + int16 speed = stream.readSint16LE(); + uint16 controlMode = stream.readUint16LE(); + int16 cropLeft = stream.readSint16LE(); + int16 cropTop = stream.readSint16LE(); + int16 cropRight = stream.readSint16LE(); + int16 cropBottom = stream.readSint16LE(); + uint8 bonusAmount = stream.readByte(); + uint8 talkColor = stream.readByte(); + uint8 armor = stream.readByte(); + uint8 lifePoints = stream.readByte(); + + moveScriptSize = (int16)stream.readUint16LE(); + moveScript = data + stream.pos(); + stream.skip(moveScriptSize); + decompileLBA1MoveScript(a, moveScript, moveScriptSize); + + lifeScriptSize = (int16)stream.readUint16LE(); + lifeScript = data + stream.pos(); + stream.skip(lifeScriptSize); + decompileLBA1LifeScript(a, lifeScript, lifeScriptSize); + } + + int16 sceneNumZones = stream.readSint16LE(); + for (int16 i = 0; i < sceneNumZones; i++) { + int16 zoneminsx = stream.readSint16LE(); + int16 zoneminsy = stream.readSint16LE(); + int16 zoneminsz = stream.readSint16LE(); + + int16 zonemaxsx = stream.readSint16LE(); + int16 zonemaxsy = stream.readSint16LE(); + int16 zonemaxsz = stream.readSint16LE(); + + uint16 zonetype = stream.readUint16LE(); + int16 zonenum = stream.readSint16LE(); + + int16 info0 = stream.readSint16LE(); + int16 info1 = stream.readSint16LE(); + int16 info2 = stream.readSint16LE(); + int16 info3 = stream.readSint16LE(); + } + + uint16 sceneNumTracks = stream.readUint16LE(); + for (uint16 i = 0; i < sceneNumTracks; i++) { + int16 pointx = stream.readSint16LE(); + int16 pointy = stream.readSint16LE(); + int16 pointz = stream.readSint16LE(); + } + + if (stream.err()) { + return 1; + } + return 0; +} diff --git a/engines/twine/lba1.h b/engines/twine/lba1.h new file mode 100644 index 00000000..e50ef31f --- /dev/null +++ b/engines/twine/lba1.h @@ -0,0 +1,29 @@ +/* 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 3 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, see . + * + */ + +#ifndef TWINE_LBA1_H +#define TWINE_LBA1_H + +#include "common/scummsys.h" + +int decompileLBA1(const uint8 *data, int size); + +#endif diff --git a/engines/twine/lba2.cpp b/engines/twine/lba2.cpp new file mode 100644 index 00000000..1b10b4b3 --- /dev/null +++ b/engines/twine/lba2.cpp @@ -0,0 +1,39 @@ +/* ScummVM Tools + * + * ScummVM Tools 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 3 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, see . + * + */ + +#include "engines/twine/lba2.h" +#include "common/memstream.h" + +// static int decompileLBA2MoveScript(int actor, const uint8 *moveScript, int16 moveScriptSize) { +// return 0; +// } + +// static int decompileLBA2LifeScript(int actor, const uint8 *moveScript, int16 moveScriptSize) { +// return 0; +// } + +int decompileLBA2(const uint8 *data, int size) { + Common::MemoryReadStream stream(data, size); + if (stream.err()) { + return 1; + } + return 0; +} diff --git a/engines/twine/lba2.h b/engines/twine/lba2.h new file mode 100644 index 00000000..7bf39076 --- /dev/null +++ b/engines/twine/lba2.h @@ -0,0 +1,29 @@ +/* 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 3 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, see . + * + */ + +#ifndef TWINE_LBA2_H +#define TWINE_LBA2_H + +#include "common/scummsys.h" + +int decompileLBA2(const uint8 *data, int size); + +#endif