TWINE: new lba1 script decompiler

This commit is contained in:
Martin Gerhardy
2023-01-11 19:08:12 +01:00
parent 8c276d0445
commit 2caf8ea37d
12 changed files with 1878 additions and 17 deletions
+1
View File
@@ -15,6 +15,7 @@
/deprince
/descumm
/desword2
/detwine
/extract_hadesch_img
/extract_mohawk
/extract_mps
+43
View File
@@ -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})
+3
View File
@@ -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)
+11 -17
View File
@@ -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))
######################################################################
+21
View File
@@ -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
+83
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
/* TwinE Script disassembler */
#include <stdio.h>
#include <string.h>
#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 <variant> <index> <scene.hqr>\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);
}
+280
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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
+98
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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
File diff suppressed because it is too large Load Diff
+29
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TWINE_LBA1_H
#define TWINE_LBA1_H
#include "common/scummsys.h"
int decompileLBA1(const uint8 *data, int size);
#endif
+39
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#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;
}
+29
View File
@@ -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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TWINE_LBA2_H
#define TWINE_LBA2_H
#include "common/scummsys.h"
int decompileLBA2(const uint8 *data, int size);
#endif