aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/cine_tools/Makefile6
-rw-r--r--tools/cine_tools/cine_decode.cpp864
-rw-r--r--tools/cine_tools/cine_unpack.cpp323
-rw-r--r--tools/cine_tools/cine_unpack.h87
-rw-r--r--tools/cine_tools/endian.h30
-rw-r--r--tools/cine_tools/typedefs.h14
6 files changed, 1324 insertions, 0 deletions
diff --git a/tools/cine_tools/Makefile b/tools/cine_tools/Makefile
new file mode 100644
index 0000000000..430743bb1e
--- /dev/null
+++ b/tools/cine_tools/Makefile
@@ -0,0 +1,6 @@
+# $URL$
+# $Id$
+
+all:
+ g++ cine_decode.cpp -o cine_decode
+ g++ cine_unpack.cpp -o cine_unpack
diff --git a/tools/cine_tools/cine_decode.cpp b/tools/cine_tools/cine_decode.cpp
new file mode 100644
index 0000000000..16411d25bf
--- /dev/null
+++ b/tools/cine_tools/cine_decode.cpp
@@ -0,0 +1,864 @@
+/* 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$
+ *
+ * This is a utility to decode Delphine's Cinematique engine's script files.
+ * The script files can be recognized from their extension .prc or .rel.
+ * Should at least work with Future Wars and Operation Stealth.
+ *
+ * Note that this isn't polished code so caveat emptor.
+ *
+ * TODO: Unify opcode namings between ScummVM's Cine engine and this utility.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "typedefs.h"
+#include "endian.h"
+
+static const uint8 *_baseScriptData, *_scriptData;
+
+static uint8 fetchScriptByte() {
+ return *_scriptData++;
+}
+
+static uint16 fetchScriptWord() {
+ uint16 w = READ_BE_UINT16(_scriptData);
+ _scriptData += 2;
+ return w;
+}
+
+static const char *fetchScriptString() {
+ const char *str = (const char *)_scriptData;
+ _scriptData += strlen(str) + 1;
+ return str;
+}
+
+//typedef void (*DecodeOpcode)();
+
+static const char *objectField[] = {
+ "<null>",
+ "x",
+ "y",
+ "mask",
+ "frame",
+ "costume",
+ "part"
+};
+
+static bool decodeOpcode(int isV2) {
+ uint8 opcode;
+ unsigned int addr = _scriptData - _baseScriptData;
+
+ opcode = fetchScriptByte();
+ if (opcode == 0) {
+ printf("[%04X] (FF) ", addr);
+ } else {
+ --opcode;
+ printf("[%04X] (%02X) ", addr, opcode);
+ switch (opcode) {
+ case 0x00: {
+ uint8 num = fetchScriptByte();
+ uint8 field = fetchScriptByte();
+ int16 value = fetchScriptWord();
+ if (field >= 7) return false;
+ printf("SET object[%d].%s, %d", num, objectField[field], value);
+ }
+ break;
+ case 0x01: {
+ uint8 num = fetchScriptByte();
+ uint8 field = fetchScriptByte();
+ uint8 var = fetchScriptByte();
+ if (field >= 7) return false;
+ printf("LOAD localvars[%d], object[%d].%s", var, num, objectField[field]);
+ }
+ break;
+ case 0x02: {
+ uint8 num = fetchScriptByte();
+ uint8 field = fetchScriptByte();
+ int16 value = fetchScriptWord();
+ if (field >= 7) return false;
+ printf("ADD object[%d].%s, %d", num, objectField[field], value);
+ }
+ break;
+ case 0x03: {
+ uint8 num = fetchScriptByte();
+ uint8 field = fetchScriptByte();
+ int16 value = fetchScriptWord();
+ if (field >= 7) return false;
+ printf("SUB object[%d].%s, %d", num, objectField[field], value);
+ }
+ break;
+ case 0x06: {
+ uint8 num = fetchScriptByte();
+ uint8 field = fetchScriptByte();
+ int16 value = fetchScriptWord();
+ if (field >= 7) return false;
+ printf("CMP object[%d].%s, %d", num, objectField[field], value);
+ }
+ break;
+ case 0x07: {
+ uint8 num = fetchScriptByte();
+ int16 x = fetchScriptWord();
+ int16 y = fetchScriptWord();
+ int16 mask = fetchScriptWord();
+ int16 frame = fetchScriptWord();
+ printf("SETUP object[%d] x=%d y=%d mask=%d frame=%d", num, x, y, mask, frame);
+ }
+ break;
+ case 0x08: {
+ uint8 num = fetchScriptByte();
+ int16 x = fetchScriptWord();
+ int16 y = fetchScriptWord();
+ int16 count = fetchScriptWord();
+ int16 index = fetchScriptWord();
+ printf("CHECK_COLLISION objects[%d] x=%d y=%d count=%d index=%d", num, x, y, count, index);
+ }
+ break;
+ case 0x09: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("SET localvars[%d], %d", var, value);
+ } else {
+ uint8 num = fetchScriptByte();
+ switch (type) {
+ case 1:
+ printf("localvars[%d] = localvars[%d]", var, num);
+ break;
+ case 2:
+ printf("localvars[%d] = globalvars[%d]", var, num);
+ break;
+ case 3:
+ printf("localvars[%d] = mouse.x", var);
+ break;
+ case 4:
+ printf("localvars[%d] = mouse.y", var);
+ break;
+ case 5:
+ printf("localvars[%d] = random(%d)", var, num);
+ break;
+ case 8:
+ printf("localvars[%d] = datas[%d].packed_size", var, num);
+ break;
+ case 9:
+ printf("localvars[%d] = datas[%d].unpacked_size", var, num);
+ break;
+ default:
+ return false;
+ break;
+ }
+ }
+ }
+ break;
+ case 0x0A: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("ADD localvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ printf("ADD localvars[%d], localvars[%d]", var, var2);
+ }
+ }
+ break;
+ case 0x0B: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("SUB localvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ printf("SUB localvars[%d], localvars[%d]", var, var2);
+ }
+ }
+ break;
+ case 0x0C: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("MUL localvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ printf("MUL localvars[%d], localvars[%d]", var, var2);
+ }
+ }
+ break;
+ case 0x0D: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("DIV localvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ printf("DIV localvars[%d], localvars[%d]", var, var2);
+ }
+ }
+ break;
+ case 0x0E: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("CMP localvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ if (type == 1) {
+ printf("CMP localvars[%d], localvars[%d]", var, var2);
+ } else if (type == 2) {
+ printf("CMP localvars[%d], globalvars[%d]", var, var2);
+ } else {
+ return false;
+ }
+ }
+ }
+ break;
+ case 0x0F: {
+ uint8 num = fetchScriptByte();
+ uint8 field = fetchScriptByte();
+ int16 var = fetchScriptByte();
+ if (field >= 7) return false;
+ printf("SET object[%d].%s, localvars[%d]", num, objectField[field], var);
+ }
+ break;
+ case 0x13: {
+ uint8 num = fetchScriptByte();
+ printf("ADD_OVERLAY object=%d, type=0", num);
+ }
+ break;
+ case 0x14: {
+ uint8 num = fetchScriptByte();
+ printf("REMOVE_OVERLAY object=%d, type=0", num);
+ }
+ break;
+ case 0x15: {
+ uint8 num = fetchScriptByte();
+ printf("ADD_OBJECT_TO_BACKGROUND_LIST objects[%d]", num);
+ }
+ break;
+ case 0x16: {
+ uint8 num = fetchScriptByte();
+ printf("ADD_OVERLAY object=%d, type=1", num);
+ }
+ break;
+ case 0x17: {
+ uint8 num = fetchScriptByte();
+ printf("REMOVE_OVERLAY object=%d, type=1", num);
+ }
+ break;
+ case 0x18: {
+ uint8 num = fetchScriptByte();
+ printf("ADD_OVERLAY object=%d, type=4", num);
+ }
+ break;
+ case 0x19: {
+ uint8 num = fetchScriptByte();
+ printf("REMOVE_OVERLAY object=%d, type=4", num);
+ }
+ break;
+ case 0x1A: {
+ uint8 num = fetchScriptByte();
+ printf("ADD_SPRITE_TO_BACKGROUND_LIST objects[%d], type=4", num);
+ }
+ break;
+ case 0x1B: {
+ printf("CLEAR_BACKGROUND_LIST");
+ }
+ break;
+ case 0x1D: {
+ uint8 num = fetchScriptByte();
+ printf("LABEL_%d:", num);
+ }
+ break;
+ case 0x1E: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP LABEL_%d", num);
+ }
+ break;
+ case 0x1F: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP G LABEL_%d", num);
+ }
+ break;
+ case 0x20: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP GEQ LABEL_%d", num);
+ }
+ break;
+ case 0x21: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP L LABEL_%d", num);
+ }
+ break;
+ case 0x22: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP LEQ LABEL_%d", num);
+ }
+ break;
+ case 0x23: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP EQ LABEL_%d", num);
+ }
+ break;
+ case 0x24: {
+ uint8 num = fetchScriptByte();
+ printf("JUMP NOT_EQ LABEL_%d", num);
+ }
+ break;
+ case 0x26: {
+ uint8 var = fetchScriptByte();
+ uint8 num = fetchScriptByte();
+ printf("--localvars[%d]; JUMP GEQ LABEL_%d", var, num);
+ }
+ break;
+ case 0x31: {
+ uint8 num = fetchScriptByte();
+ printf("START_GLOBAL_SCRIPT num=%d", num);
+ }
+ break;
+ case 0x32: {
+ uint8 num = fetchScriptByte();
+ printf("END_GLOBAL_SCRIPT num=%d", num);
+ }
+ break;
+ case 0x3B: {
+ const char *name = fetchScriptString();
+ printf("LOAD_ANIM name='%s'", name);
+ }
+ break;
+ case 0x3C: {
+ const char *name = fetchScriptString();
+ printf("LOAD_BACKGROUND_IMAGE name='%s'", name);
+ }
+ break;
+ case 0x3D: {
+ const char *name = fetchScriptString();
+ printf("LOAD_CT name='%s'", name);
+ }
+ break;
+ case 0x3F: {
+ const char *name = fetchScriptString();
+ printf("OPEN_RESOURCE_FILE name='%s'", name);
+ }
+ break;
+ case 0x40: {
+ printf("CLOSE_RESOURCE_FILE");
+ }
+ break;
+ case 0x41: {
+ static const char *dataType[] = { "prc", "rel", "obj", "msg" };
+ uint8 type = fetchScriptByte();
+ const char *name = fetchScriptString();
+ if (type >= 4) return false;
+ printf("SET_LOADING_DATA type='%s', name='%s'", dataType[type], name);
+ }
+ break;
+ case 0x42: {
+ printf("LOAD_PENDING_DATA");
+ }
+ break;
+ case 0x45: {
+ printf("FADE_IN");
+ }
+ break;
+ case 0x46: {
+ printf("FADE_OUT");
+ }
+ break;
+ case 0x47: {
+ uint8 start = fetchScriptByte();
+ uint8 count = fetchScriptByte();
+ int16 r = fetchScriptWord();
+ int16 g = fetchScriptWord();
+ int16 b = fetchScriptWord();
+ printf("TRANSFORM_PALETTE start=%d, count=%d, delta_red=%d, delta_green=%d, delta_blue=%d", start, count, r, g, b);
+ }
+ break;
+ case 0x49: {
+ uint8 color = fetchScriptByte();
+ printf("SET_MENU_COLOR_1 %d", color);
+ }
+ break;
+ case 0x4A: {
+ uint8 firstColor = fetchScriptByte();
+ uint8 endColor = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ printf("ROTATE_PALETTE first_color=%d, end_color=%d, type=%d", firstColor, endColor, type);
+ }
+ break;
+ case 0x4F: {
+ printf("BREAK");
+ }
+ break;
+ case 0x50: {
+ printf("END");
+ _baseScriptData = 0;
+ }
+ break;
+ case 0x51: {
+ uint8 num = fetchScriptByte();
+ int16 x = fetchScriptWord();
+ int16 y = fetchScriptWord();
+ int16 width = fetchScriptWord();
+ int16 color = fetchScriptWord();
+ printf("ADD_MESSAGE num=%d x=%d y=%d width=%d color=%d", num, x, y, width, color);
+ }
+ break;
+ case 0x52: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("SET globalvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ if (type == 1) {
+ printf("SET globalvars[%d], localvars[%d]", var, var2);
+ } else {
+ printf("SET globalvars[%d], globalvars[%d]", var, var2);
+ }
+ }
+ break;
+ }
+ case 0x53: {
+ uint8 var = fetchScriptByte();
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("CMP globalvars[%d], %d", var, value);
+ } else {
+ uint8 var2 = fetchScriptByte();
+ printf("CMP globalvars[%d], localvars[%d]", var, var2);
+ }
+ }
+ break;
+ case 0x59: {
+ const char *str = fetchScriptString();
+ printf("/* %s */", str);
+ }
+ break;
+ case 0x5A: {
+ uint8 start = fetchScriptByte();
+ uint8 count = fetchScriptByte();
+ printf("FREE_FRAMES start=%d, count=%d", start, count);
+ }
+ break;
+ case 0x5B: {
+ printf("UNLOAD_OVERLAYS");
+ }
+ break;
+ case 0x65: {
+ printf("CLEAR_ZONE_DATA");
+ }
+ break;
+ case 0x66: {
+ uint8 num = fetchScriptByte();
+ int16 value = fetchScriptWord();
+ printf("SET zones[%d], %d", num, value);
+ }
+ break;
+ case 0x68: {
+ uint8 color = fetchScriptByte();
+ printf("SET_MENU_COLOR_2 %d", color);
+ }
+ break;
+ case 0x69: {
+ printf("ENABLE_PLAYER_INPUT");
+ }
+ break;
+ case 0x6A: {
+ printf("DISABLE_PLAYER_INPUT");
+ }
+ break;
+ case 0x6B: {
+ uint8 num = fetchScriptByte();
+ printf("SET_CURRENT_DISK num=%d", num);
+ }
+ break;
+ case 0x6D: {
+ const char *name = fetchScriptString();
+ printf("LOAD_MUSIC name='%s'", name);
+ }
+ break;
+ case 0x6E: {
+ printf("PLAY_MUSIC");
+ }
+ break;
+ case 0x6F: {
+ printf("FADE_OUT_MUSIC");
+ }
+ break;
+ case 0x70: {
+ printf("STOP_MUSIC");
+ }
+ break;
+ case 0x77:
+ case 0x78: {
+ uint8 frame = fetchScriptByte();
+ uint8 channel = fetchScriptByte();
+ int16 freq = fetchScriptWord();
+ uint8 repeat = fetchScriptByte();
+ int16 volume = fetchScriptWord();
+ int16 size = fetchScriptWord();
+ printf("PLAY_SOUND frame=%d channel=%d freq=%d repeat=%d volume=%d size=%d", frame, channel, freq, repeat, volume, size);
+ }
+ break;
+ case 0x79: {
+ uint8 param = fetchScriptByte();
+ printf("DISABLE_SYSTEM_MENU flag=%d", param);
+ }
+ break;
+ case 0x7A: {
+ uint8 num = fetchScriptByte();
+ printf("ADD_OVERLAY object=%d, type=5", num);
+ }
+ break;
+ case 0x7B: {
+ uint8 num = fetchScriptByte();
+ printf("REMOVE_OVERLAY object=%d, type=5", num);
+ }
+ break;
+ case 0x7F:
+ assert(isV2); {
+ uint8 a = fetchScriptByte();
+ uint8 b = fetchScriptByte();
+ uint8 c = fetchScriptByte();
+ uint8 d = fetchScriptByte();
+ int16 e = fetchScriptWord();
+ int16 f = fetchScriptWord();
+ int16 g = fetchScriptWord();
+ printf("ADD_ANIM_SEQUENCE %d %d %d %d %d %d %d", a, b, c, d, e, f, g);
+ }
+ break;
+ case 0x80:
+ assert(isV2); {
+ uint8 a = fetchScriptByte();
+ uint8 b = fetchScriptByte();
+ printf("REMOVE_ANIM_SEQUENCE %d %d", a, b);
+ }
+ break;
+ case 0x82:
+ assert(isV2); {
+ uint8 a = fetchScriptByte();
+ uint8 b = fetchScriptByte();
+ int16 c = fetchScriptWord();
+ int16 d = fetchScriptWord();
+ uint8 e = fetchScriptByte();
+ printf("OP_0x82 %d %d %d %d %d", a, b, c, d, e);
+ }
+ break;
+ case 0x83:
+ assert(isV2); {
+ uint8 a = fetchScriptByte();
+ uint8 b = fetchScriptByte();
+ printf("IS_ANIM_SEQUENCE_PLAYING %d %d", a, b);
+ }
+ break;
+ case 0x84:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("JUMP G LABEL_%d", num);
+ }
+ break;
+ case 0x85:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("JUMP GEQ LABEL_%d", num);
+ }
+ break;
+ case 0x86:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("JUMP L LABEL_%d", num);
+ }
+ break;
+ case 0x88:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("JUMP EQ LABEL_%d", num);
+ }
+ break;
+ case 0x89:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("JUMP NOT_EQ LABEL_%d", num);
+ }
+ break;
+ case 0x8B:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("START_OBJECT_SCRIPT num=%d", num);
+ }
+ break;
+ case 0x8C:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("STOP_OBJECT_SCRIPT num=%d", num);
+ }
+ break;
+ case 0x8D:
+ assert(isV2); {
+ int16 a = fetchScriptWord();
+ int16 b = fetchScriptWord();
+ int16 c = fetchScriptWord();
+ int16 d = fetchScriptWord();
+ int16 e = fetchScriptWord();
+ int16 f = fetchScriptWord();
+ int16 g = fetchScriptWord();
+ int16 h = fetchScriptWord();
+ printf("IS_OBJECT_INTERSECTING_OBJECT %d %d %d %d %d %d %d %d", a, b, c, d, e, f, g, h);
+ }
+ break;
+ case 0x8E:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ const char *name = fetchScriptString();
+ printf("ADD_BACKGROUND name='%s' num=%d", name, num);
+ }
+ break;
+ case 0x8F:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("REMOVE_BACKGROUND num=%d", num);
+ }
+ break;
+ case 0x90:
+ assert(isV2); {
+ uint8 pos = fetchScriptByte();
+ const char *name = fetchScriptString();
+ printf("LOAD_DATA_ABSOLUTE name='%s' pos=%d", name, pos);
+ }
+ break;
+ case 0x91:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("SET_CURRENT_BACKGROUND num=%d", num);
+ }
+ break;
+ case 0x9A:
+ assert(isV2); {
+ uint8 a = fetchScriptByte();
+ printf("OP_0x9A %d", a);
+ }
+ break;
+ case 0x9D:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("SET_NEXT_BACKGROUND %d", num);
+ }
+ break;
+ case 0x9E:
+ assert(isV2); {
+ uint8 type = fetchScriptByte();
+ if (type == 0) {
+ int16 value = fetchScriptWord();
+ printf("SET_SCROLLING_BACKGROUND %d", value);
+ } else {
+ uint8 var = fetchScriptByte();
+ printf("SET_SCROLLING_BACKGROUND localvars[%d]", var);
+ }
+ }
+ break;
+ case 0xA0:
+ assert(isV2); {
+ int16 a = fetchScriptWord();
+ int16 b = fetchScriptWord();
+ printf("OP_0xA0 %d %d", a, b);
+ }
+ break;
+ case 0xA1:
+ assert(isV2); {
+ int16 a = fetchScriptWord();
+ int16 b = fetchScriptWord();
+ printf("OP_0xA1 %d %d", a, b);
+ }
+ break;
+ case 0xA2:
+ assert(isV2); {
+ int16 a = fetchScriptWord();
+ int16 b = fetchScriptWord();
+ printf("OP_0xA2 %d %d", a, b);
+ }
+ break;
+ case 0xA3:
+ assert(isV2); {
+ int16 a = fetchScriptWord();
+ int16 b = fetchScriptWord();
+ printf("OP_0xA3 %d %d", a, b);
+ }
+ break;
+ case 0xA4:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("ADD_OVERLAY object=%d, type=22", num);
+ }
+ break;
+ case 0xA5:
+ assert(isV2); {
+ uint8 num = fetchScriptByte();
+ printf("REMOVE_OVERLAY object=%d, type=22", num);
+ }
+ break;
+ default:
+ printf("Unhandled script opcode 0x%X", opcode);
+ return false;
+ }
+ }
+ printf("\n");
+ return true;
+}
+
+typedef void (*DecodeFile)(int isV2, const uint8 *data, int dataSize);
+
+/* .prc procedure */
+static void decodePrc(int isV2, const uint8 *data, int dataSize) {
+ int i, num;
+ struct {
+ uint16 size;
+ } hdr[256];
+
+ num = READ_BE_UINT16(data); data += 2;
+ if (num >= 256) {
+ printf("// Number of scripts (%d) in PRC-file is too large (i.e. >= 256)\n\n", num);
+ return;
+ }
+
+ for (i = 0; i < num; ++i) {
+ hdr[i].size = READ_BE_UINT16(data); data += 2;
+ }
+ for (i = 0; i < num; ++i) {
+ if (hdr[i].size != 0) {
+ printf("\n");
+ printf("// script.prc %d size 0x%X\n", i, hdr[i].size);
+ _baseScriptData = _scriptData = data; data += hdr[i].size;
+ bool status = true;
+ while (_baseScriptData && _scriptData < data && status) {
+ status = decodeOpcode(isV2);
+ }
+ if (!status) {
+ printf("// Error handling opcode\n\n");
+ return;
+ }
+ printf("\n");
+ }
+ }
+}
+
+/* .rel relation */
+static void decodeRel(int isV2, const uint8 *data, int dataSize) {
+ int i, num;
+ struct {
+ uint16 size;
+ uint16 param1;
+ uint16 index;
+ uint16 param2;
+ } hdr[256];
+
+ num = READ_BE_UINT16(data); data += 2;
+ if (num >= 256) {
+ printf("// Number of scripts (%d) in REL-file is too large (i.e. >= 256)\n\n", num);
+ return;
+ }
+ for (i = 0; i < num; ++i) {
+ hdr[i].size = READ_BE_UINT16(data); data += 2;
+ hdr[i].param1 = READ_BE_UINT16(data); data += 2;
+ hdr[i].index = READ_BE_UINT16(data); data += 2;
+ hdr[i].param2 = READ_BE_UINT16(data); data += 2;
+ }
+ for (i = 0; i < num; ++i) {
+ if (hdr[i].size != 0) {
+ printf("\n");
+ printf("// script.rel %d size 0x%X param1 %d index %d param2 %d\n", i, hdr[i].size, hdr[i].param1, hdr[i].index, hdr[i].param2);
+ _baseScriptData = _scriptData = data; data += hdr[i].size;
+ bool status = true;
+ while (_baseScriptData && _scriptData < data && status) {
+ status = decodeOpcode(isV2);
+ }
+ if (!status) {
+ printf("// Error handling opcode\n\n");
+ return;
+ }
+ printf("\n");
+ }
+ }
+}
+
+int showUsage() {
+ printf("USAGE: cine_decode [type] [version] [filename]\n" \
+ "type: -prc or -rel (Use -prc for *.prc-files, -rel for *.rel-files)\n" \
+ "version: -v1 or -v2 (Use -v1 for Future Wars, -v2 for Operation Stealth)\n" \
+ "filename: The name of the script file to decode\n");
+ return -1;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc == 4) {
+ FILE *fp;
+ uint8 *data;
+ int dataSize, isV2;
+ DecodeFile decodeFile;
+
+ if (strcmp(argv[1], "-prc") == 0) {
+ decodeFile = decodePrc;
+ } else if (strcmp(argv[1], "-rel") == 0) {
+ decodeFile = decodeRel;
+ } else {
+ return showUsage();
+ }
+
+ if (strcmp(argv[2], "-v1") == 0) {
+ isV2 = 0;
+ } else if (strcmp(argv[2], "-v2") == 0) {
+ isV2 = 1;
+ } else {
+ return showUsage();
+ }
+
+ fp = fopen(argv[3], "rb");
+ if (fp) {
+ fseek(fp, 0, SEEK_END);
+ dataSize = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ data = (uint8 *)malloc(dataSize);
+ if (data) {
+ fread(data, dataSize, 1, fp);
+ (*decodeFile)(isV2, data, dataSize);
+ free(data);
+ }
+ fclose(fp);
+ } else {
+ printf("Couldn't open script file '%s'\n", argv[3]);
+ return -1;
+ }
+ } else {
+ return showUsage();
+ }
+ return 0;
+}
diff --git a/tools/cine_tools/cine_unpack.cpp b/tools/cine_tools/cine_unpack.cpp
new file mode 100644
index 0000000000..14ac3df795
--- /dev/null
+++ b/tools/cine_tools/cine_unpack.cpp
@@ -0,0 +1,323 @@
+/* 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$
+ *
+ * This is a utility to unpack Delphine's Cinematique engine's archive files.
+ * Should at least work with Future Wars and Operation Stealth.
+ * Supports using Operation Stealth's 'vol.cnf' file as input for selecting
+ * which archive files to unpack.
+ *
+ * Note that this isn't polished code so caveat emptor.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "cine_unpack.h"
+#include "typedefs.h"
+#include "endian.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+uint32 CineUnpacker::readSource() {
+ if (_src < _srcBegin || _src + 4 > _srcEnd) {
+ _error = true;
+ return 0; // The source pointer is out of bounds, returning a default value
+ }
+ uint32 value = READ_BE_UINT32(_src);
+ _src -= 4;
+ return value;
+}
+
+uint CineUnpacker::rcr(bool inputCarry) {
+ uint outputCarry = (_chunk32b & 1);
+ _chunk32b >>= 1;
+ if (inputCarry) {
+ _chunk32b |= 0x80000000;
+ }
+ return outputCarry;
+}
+
+uint CineUnpacker::nextBit() {
+ uint carry = rcr(false);
+ // Normally if the chunk becomes zero then the carry is one as
+ // the end of chunk marker is always the last to be shifted out.
+ if (_chunk32b == 0) {
+ _chunk32b = readSource();
+ _crc ^= _chunk32b;
+ carry = rcr(true); // Put the end of chunk marker in the most significant bit
+ }
+ return carry;
+}
+
+uint CineUnpacker::getBits(uint numBits) {
+ uint c = 0;
+ while (numBits--) {
+ c <<= 1;
+ c |= nextBit();
+ }
+ return c;
+}
+
+void CineUnpacker::unpackRawBytes(uint numBytes) {
+ if (_dst >= _dstEnd || _dst - numBytes + 1 < _dstBegin) {
+ _error = true;
+ return; // Destination pointer is out of bounds for this operation
+ }
+ while (numBytes--) {
+ *_dst = (byte)getBits(8);
+ --_dst;
+ }
+}
+
+void CineUnpacker::copyRelocatedBytes(uint offset, uint numBytes) {
+ if (_dst + offset >= _dstEnd || _dst - numBytes + 1 < _dstBegin) {
+ _error = true;
+ return; // Destination pointer is out of bounds for this operation
+ }
+ while (numBytes--) {
+ *_dst = *(_dst + offset);
+ --_dst;
+ }
+}
+
+bool CineUnpacker::unpack(const byte *src, uint srcLen, byte *dst, uint dstLen) {
+ // Initialize variables used for detecting errors during unpacking
+ _error = false;
+ _srcBegin = src;
+ _srcEnd = src + srcLen;
+ _dstBegin = dst;
+ _dstEnd = dst + dstLen;
+
+ // Initialize other variables
+ _src = _srcBegin + srcLen - 4;
+ uint32 unpackedLength = readSource(); // Unpacked length in bytes
+ _dst = _dstBegin + unpackedLength - 1;
+ _crc = readSource();
+ _chunk32b = readSource();
+ _crc ^= _chunk32b;
+
+ while (_dst >= _dstBegin && !_error) {
+ /*
+ Bits => Action:
+ 0 0 => unpackRawBytes(3 bits + 1) i.e. unpackRawBytes(1..8)
+ 1 1 1 => unpackRawBytes(8 bits + 9) i.e. unpackRawBytes(9..264)
+ 0 1 => copyRelocatedBytes(8 bits, 2) i.e. copyRelocatedBytes(0..255, 2)
+ 1 0 0 => copyRelocatedBytes(9 bits, 3) i.e. copyRelocatedBytes(0..511, 3)
+ 1 0 1 => copyRelocatedBytes(10 bits, 4) i.e. copyRelocatedBytes(0..1023, 4)
+ 1 1 0 => copyRelocatedBytes(12 bits, 8 bits + 1) i.e. copyRelocatedBytes(0..4095, 1..256)
+ */
+ if (!nextBit()) { // 0...
+ if (!nextBit()) { // 0 0
+ uint numBytes = getBits(3) + 1;
+ unpackRawBytes(numBytes);
+ } else { // 0 1
+ uint numBytes = 2;
+ uint offset = getBits(8);
+ copyRelocatedBytes(offset, numBytes);
+ }
+ } else { // 1...
+ uint c = getBits(2);
+ if (c == 3) { // 1 1 1
+ uint numBytes = getBits(8) + 9;
+ unpackRawBytes(numBytes);
+ } else if (c < 2) { // 1 0 x
+ uint numBytes = c + 3;
+ uint offset = getBits(c + 9);
+ copyRelocatedBytes(offset, numBytes);
+ } else { // 1 1 0
+ uint numBytes = getBits(8) + 1;
+ uint offset = getBits(12);
+ copyRelocatedBytes(offset, numBytes);
+ }
+ }
+ }
+ return !_error && (_crc == 0);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+static void unpackFile(FILE *fp, const char *outDir) {
+ char filePath[512], fileName[15];
+
+ uint entryCount = freadUint16BE(fp); // How many entries?
+ uint entrySize = freadUint16BE(fp); // How many bytes per entry?
+ assert(entrySize == 0x1e);
+ while (entryCount--) {
+ fread(fileName, 14, 1, fp);
+ fileName[14] = '\0';
+ sprintf(filePath, "%s/%s", outDir, fileName);
+ FILE *fpOut = fopen(filePath, "wb");
+
+ uint32 offset = freadUint32BE(fp);
+ unsigned int packedSize = freadUint32BE(fp);
+ unsigned int unpackedSize = freadUint32BE(fp);
+ freadUint32BE(fp);
+ uint savedPos = ftell(fp);
+
+ if (!fpOut) {
+ printf("ERROR: unable to open '%s' for writing\n", filePath);
+ continue;
+ }
+ printf("unpacking '%s' ... ", filePath);
+
+ fseek(fp, offset, SEEK_SET);
+ assert(unpackedSize >= packedSize);
+ uint8 *data = (uint8 *)calloc(unpackedSize, 1);
+ uint8 *packedData = (uint8 *)calloc(packedSize, 1);
+ assert(data);
+ assert(packedData);
+ fread(packedData, packedSize, 1, fp);
+ bool status = true;
+ if (packedSize != unpackedSize) {
+ CineUnpacker cineUnpacker;
+ status = cineUnpacker.unpack(packedData, packedSize, data, unpackedSize);
+ } else {
+ memcpy(data, packedData, packedSize);
+ }
+ free(packedData);
+ fwrite(data, unpackedSize, 1, fpOut);
+ fclose(fpOut);
+ free(data);
+
+ if (!status) {
+ printf("CRC ERROR");
+ } else {
+ printf("ok");
+ }
+ printf(", packedSize %u unpackedSize %u\n", packedSize, unpackedSize);
+ fseek(fp, savedPos, SEEK_SET);
+ }
+}
+
+void fixVolCnfFileName(char *dst, const uint8 *src) {
+ char *ext, *end;
+
+ memcpy(dst, src, 8);
+ src += 8;
+ dst[8] = 0;
+ ext = strchr(dst, ' ');
+ if (!ext) {
+ ext = &dst[8];
+ }
+ if (*src == ' ') {
+ *ext = 0;
+ } else {
+ *ext++ = '.';
+ memcpy(ext, src, 3);
+ end = strchr(ext, ' ');
+ if (!end) {
+ end = &ext[3];
+ }
+ *end = 0;
+ }
+}
+
+void unpackAllResourceFiles(const char *filename, const char *outDir) {
+ FILE *fp = fopen(filename, "rb");
+ if (!fp) {
+ printf("Unable to open file '%s'\n", filename);
+ exit(1);
+ }
+
+ uint32 unpackedSize, packedSize;
+ {
+ char header[8];
+ fread(header, 8, 1, fp);
+ if (memcmp(header, "ABASECP", 7) == 0) {
+ unpackedSize = freadUint32BE(fp);
+ packedSize = freadUint32BE(fp);
+ } else {
+ fseek(fp, 0, SEEK_END);
+ unpackedSize = packedSize = ftell(fp); /* Get file size */
+ fseek(fp, 0, SEEK_SET);
+ }
+ }
+ assert(unpackedSize >= packedSize);
+ uint8 *buf = (uint8 *)calloc(unpackedSize, 1);
+ assert(buf);
+ fread(buf, packedSize, 1, fp);
+ fclose(fp);
+ if (packedSize != unpackedSize) {
+ CineUnpacker cineUnpacker;
+ if (!cineUnpacker.unpack(buf, packedSize, buf, unpackedSize)) {
+ printf("Error while unpacking 'vol.cnf' data");
+ exit(1);
+ }
+ }
+ uint resourceFilesCount = READ_BE_UINT16(&buf[0]);
+ uint entrySize = READ_BE_UINT16(&buf[2]);
+ printf("--- Unpacking all %d resource files from 'vol.cnf' (entrySize = %d):\n", resourceFilesCount, entrySize);
+ char resourceFileName[9];
+ for (unsigned int i = 0; i < resourceFilesCount; ++i) {
+ memcpy(resourceFileName, &buf[4 + i * entrySize], 8);
+ resourceFileName[8] = 0;
+ FILE *fpResFile = fopen(resourceFileName, "rb");
+ if (fpResFile) {
+ printf("--- Unpacking resource file %s:\n", resourceFileName);
+ unpackFile(fpResFile, outDir);
+ fclose(fpResFile);
+ } else {
+ printf("ERROR: Unable to open resource file %s\n", resourceFileName);
+ }
+ }
+
+ free(buf);
+}
+
+int showUsage() {
+ printf("USAGE: cine_unpack [input file] [output directory]\n" \
+ "Supports using Operation Stealth's 'vol.cnf' file as input.\n");
+ return -1;
+}
+
+int main(int argc, char *argv[]) {
+ int i;
+ char tmp[512];
+
+ if (argc == 3) {
+ strcpy(tmp, argv[1]);
+ for (i = 0; tmp[i] != 0; i++) {
+ tmp[i] = toupper(tmp[i]);
+ }
+ if (!strcmp(tmp, "VOL.CNF")) {
+ /* Unpack all archive files listed in 'vol.cnf' */
+ unpackAllResourceFiles(argv[1], argv[2]);
+ } else {
+ /* Unpack a single archive file */
+ FILE *fp = fopen(argv[1], "rb");
+ if (fp) {
+ unpackFile(fp, argv[2]);
+ fclose(fp);
+ } else {
+ printf("Couldn't open input file '%s'\n", argv[1]);
+ return -1;
+ }
+ }
+ } else {
+ return showUsage();
+ }
+ return 0;
+}
diff --git a/tools/cine_tools/cine_unpack.h b/tools/cine_tools/cine_unpack.h
new file mode 100644
index 0000000000..66a39a05af
--- /dev/null
+++ b/tools/cine_tools/cine_unpack.h
@@ -0,0 +1,87 @@
+#ifndef CINE_UNPACK_H
+#define CINE_UNPACK_H
+
+#include "typedefs.h"
+
+/**
+ * A LZ77 style decompressor for Delphine's data files
+ * used in at least Future Wars and Operation Stealth.
+ * @note Works backwards in the source and destination buffers.
+ * @note Can work with source and destination in the same buffer if there's space.
+ */
+class CineUnpacker {
+public:
+ /**
+ * Unpacks packed data from the source buffer to the destination buffer.
+ * @warning Do NOT call this on data that is not packed.
+ * @note Source and destination buffer pointers can be the same as long as there's space for the unpacked data.
+ * @param src Pointer to the source buffer.
+ * @param srcLen Length of the source buffer.
+ * @param dst Pointer to the destination buffer.
+ * @param dstLen Length of the destination buffer.
+ * @return True if no errors were detected in the source data and unpacking was successful, otherwise false.
+ */
+ bool unpack(const byte *src, uint srcLen, byte *dst, uint dstLen);
+private:
+ /**
+ * Reads an unsigned big endian 32-bit integer from the source stream and goes backwards 4 bytes.
+ * @return If the operation is valid, an unsigned big endian 32-bit integer read from the source stream.
+ * @return If the operation is invalid, zero.
+ * @note Sets internal error state if the read operation would be out of source bounds.
+ */
+ uint32 readSource();
+
+ /**
+ * Shifts the current internal 32-bit chunk to the right by one.
+ * Puts input carry into internal chunk's topmost (i.e. leftmost) bit.
+ * @return The least significant bit that was shifted out from the chunk.
+ */
+ uint rcr(bool inputCarry);
+
+ /**
+ * Get the next bit from the source stream.
+ * @note Changes the bit position in the source stream.
+ * @return The next bit from the source stream.
+ */
+ uint nextBit();
+
+ /**
+ * Get bits from the source stream.
+ * @note Changes the bit position in the source stream.
+ * @param numBits Number of bits to read from the source stream.
+ * @return Integer value consisting of the bits read from the source stream (In range [0, (2 ** numBits) - 1]).
+ * @return Later the bit was read from the source, the less significant it is in the return value.
+ */
+ uint getBits(uint numBits);
+
+ /**
+ * Copy raw bytes from the input stream and write them to the destination stream.
+ * This is used when no adequately long match is found in the sliding window.
+ * @note Sets internal error state if the operation would be out of bounds.
+ * @param numBytes Amount of bytes to copy from the input stream
+ */
+ void unpackRawBytes(uint numBytes);
+
+ /**
+ * Copy bytes from the sliding window in the destination buffer.
+ * This is used when a match of two bytes or longer is found.
+ * @note Sets internal error state if the operation would be out of bounds.
+ * @param offset Offset in the sliding window
+ * @param numBytes Amount of bytes to copy
+ */
+ void copyRelocatedBytes(uint offset, uint numBytes);
+private:
+ uint32 _crc; //!< Error-detecting code (This should be zero after successful unpacking)
+ uint32 _chunk32b; //!< The current internal 32-bit chunk of source data
+ byte *_dst; //!< Pointer to the current position in the destination buffer
+ const byte *_src; //!< Pointer to the current position in the source buffer
+
+ // These are used for detecting errors (e.g. out of bounds issues) during unpacking
+ bool _error; //!< Did an error occur during unpacking?
+ const byte *_srcBegin; //!< Source buffer's beginning
+ const byte *_srcEnd; //!< Source buffer's end
+ byte *_dstBegin; //!< Destination buffer's beginning
+ byte *_dstEnd; //!< Destination buffer's end
+};
+
+#endif
diff --git a/tools/cine_tools/endian.h b/tools/cine_tools/endian.h
new file mode 100644
index 0000000000..8c4471557e
--- /dev/null
+++ b/tools/cine_tools/endian.h
@@ -0,0 +1,30 @@
+#ifndef CINE_ENDIAN_H
+#define CINE_ENDIAN_H
+
+#include <stdio.h> // fread etc
+#include "typedefs.h" // uint8 etc
+
+/* NOTE: These big endian reading functions assume we're on a little endian machine. */
+static uint16 READ_BE_UINT16(const void *ptr) {
+ const uint8 *b = (const uint8 *)ptr;
+ return (b[0] << 8) | b[1];
+}
+
+static uint32 READ_BE_UINT32(const void *ptr) {
+ const uint8 *b = (const uint8 *)ptr;
+ return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+}
+
+static uint16 freadUint16BE(FILE *fp) {
+ char data[2];
+ fread(data, 2, 1, fp);
+ return READ_BE_UINT16(data);
+}
+
+static uint32 freadUint32BE(FILE *fp) {
+ char data[4];
+ fread(data, 4, 1, fp);
+ return READ_BE_UINT32(data);
+}
+
+#endif
diff --git a/tools/cine_tools/typedefs.h b/tools/cine_tools/typedefs.h
new file mode 100644
index 0000000000..03de18d41a
--- /dev/null
+++ b/tools/cine_tools/typedefs.h
@@ -0,0 +1,14 @@
+#ifndef CINE_TYPEDEFS_H
+#define CINE_TYPEDEFS_H
+
+typedef unsigned char uint8;
+typedef signed char int8;
+typedef unsigned short uint16;
+typedef signed short int16;
+typedef unsigned long uint32;
+typedef signed long int32;
+
+typedef unsigned int uint;
+typedef unsigned char byte;
+
+#endif