[Scummvm-git-logs] scummvm-tools master -> d06a5562375ce5d27ad6846df1310c566a215856
mgerhardy
noreply at scummvm.org
Wed Jun 3 16:31:17 UTC 2026
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm-tools' repo located at https://api.github.com/repos/scummvm/scummvm-tools .
Summary:
c184c4f8ab MACS2: renamed some opcodes to match names in ghidra
88af2bbcef MACS2: updated the decompiler to also support json output and unified with the imgui decompiler in scummvm itself
fadaad3fbf MASC2: started to export json scene data, too
d06a556237 MACS2: this is a shadowmap
Commit: c184c4f8abe5ee8ddf61afd145f243b3638cf15e
https://github.com/scummvm/scummvm-tools/commit/c184c4f8abe5ee8ddf61afd145f243b3638cf15e
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T17:41:31+02:00
Commit Message:
MACS2: renamed some opcodes to match names in ghidra
Changed paths:
engines/macs2/demacs2.cpp
diff --git a/engines/macs2/demacs2.cpp b/engines/macs2/demacs2.cpp
index 099ec06e..96cc2bca 100644
--- a/engines/macs2/demacs2.cpp
+++ b/engines/macs2/demacs2.cpp
@@ -21,10 +21,10 @@
/* MACS2 Script decompiler */
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
#include <vector>
#include <string>
@@ -337,11 +337,11 @@ static void disassemble() {
printf("subtract %s, %s\n", a.c_str(), b.c_str());
break;
}
- case 0x26: { // setAnimSlot
+ case 0x26: { // loadSpecialAnim
std::string obj = formatValue();
- formatValue(); // unused
+ std::string decodeFlag = formatValue(); // decode/enable flag (runtime +0x182)
uint8_t animIdx = readByte();
- printf("setAnimSlot obj=%s anim=%u\n", obj.c_str(), animIdx);
+ printf("loadSpecialAnim obj=%s decode=%s anim=%u\n", obj.c_str(), decodeFlag.c_str(), animIdx);
break;
}
case 0x27: { // setMaxAnimFrame
@@ -403,9 +403,9 @@ static void disassemble() {
printf("nop30\n");
break;
}
- case 0x31: { // setMusicVolume
+ case 0x31: { // setVolume
std::string vol = formatValue();
- printf("setMusicVolume %s\n", vol.c_str());
+ printf("setVolume %s\n", vol.c_str());
break;
}
case 0x32: { // setClickable
Commit: 88af2bbcef51c400c3c6f85e0c62be4a994e170b
https://github.com/scummvm/scummvm-tools/commit/88af2bbcef51c400c3c6f85e0c62be4a994e170b
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T17:45:00+02:00
Commit Message:
MACS2: updated the decompiler to also support json output and unified with the imgui decompiler in scummvm itself
Changed paths:
engines/macs2/demacs2.cpp
diff --git a/engines/macs2/demacs2.cpp b/engines/macs2/demacs2.cpp
index 96cc2bca..8898d093 100644
--- a/engines/macs2/demacs2.cpp
+++ b/engines/macs2/demacs2.cpp
@@ -21,19 +21,20 @@
/* MACS2 Script decompiler */
-#include <cstdio>
#include <cstdint>
+#include <cstdio>
#include <cstdlib>
#include <cstring>
-#include <vector>
#include <string>
+#include <vector>
static uint8_t *scriptData = nullptr;
static uint32_t scriptSize = 0;
static uint32_t pos = 0;
static uint8_t readByte() {
- if (pos >= scriptSize) return 0;
+ if (pos >= scriptSize)
+ return 0;
return scriptData[pos++];
}
@@ -43,6 +44,41 @@ static uint16_t readWord() {
return (uint16_t)hi << 8 | lo;
}
+static const struct {
+ uint16_t id;
+ const char *name;
+} kSpecialNames[] = {
+ {0x01, "interactedUse"},
+ {0x02, "interactedLook"},
+ {0x03, "interactedTalk"},
+ {0x04, "areaAtActor"},
+ {0x0B, "isRepeatRun"},
+ {0x0D, "dialogueResult"},
+ {0x23, "pathWalkable"},
+ {0x24, "actorX"},
+ {0x25, "actorY"},
+ {0x26, "isSceneInit"},
+ {0x27, "areaRepeatRun"},
+ {0x28, "invCheck"},
+ {0x29, "animBlobRange"},
+ {0x2A, "invCombine"},
+ {0x2B, "invAction"},
+ {0x2C, "interactedMap"},
+ {0x2D, "curScene"},
+ {0x2E, "const2"},
+ {0x2F, "prevScene"},
+ {0x30, "musicActive"},
+ {0x31, "soundActive"},
+};
+
+static const char *getSpecialName(uint16_t val) {
+ for (const auto &s : kSpecialNames) {
+ if (s.id == val)
+ return s.name;
+ }
+ return nullptr;
+}
+
static std::string formatValue() {
uint8_t type = readByte();
uint16_t val = readWord();
@@ -51,531 +87,682 @@ static std::string formatValue() {
snprintf(buf, sizeof(buf), "%u", val);
return buf;
} else if (type == 0xFF) {
- switch (val) {
- case 0x01: return "interactedUse";
- case 0x02: return "interactedLook";
- case 0x03: return "interactedTalk";
- case 0x04: return "areaAtActor";
- case 0x0B: return "isRepeatRun";
- case 0x0D: return "dialogueResult";
- case 0x23: return "pathWalkable";
- case 0x24: return "actorX";
- case 0x25: return "actorY";
- case 0x26: return "isSceneInit";
- case 0x28: return "invCheck";
- case 0x2A: return "invCombine";
- case 0x2B: return "invAction";
- case 0x2D: return "curScene";
- case 0x2F: return "prevScene";
- default:
+ const char *name = getSpecialName(val);
+ if (name)
+ return name;
+ if (val >= 0x0E && val <= 0x22) {
char buf[32];
- snprintf(buf, sizeof(buf), "special[0x%02x]", val);
+ snprintf(buf, sizeof(buf), "%u", val - 0x0D);
return buf;
}
+ char buf[32];
+ snprintf(buf, sizeof(buf), "special[0x%02x]", val);
+ return buf;
}
char buf[32];
snprintf(buf, sizeof(buf), "var[%u]", val);
return buf;
}
+static const char *getOpcodeName(uint8_t opcode) {
+ switch (opcode) {
+ case 0x01:
+ return "setVar";
+ case 0x02:
+ return "setVarOr";
+ case 0x03:
+ return "ifFalse";
+ case 0x04:
+ return "ifTrue";
+ case 0x05:
+ return "compare";
+ case 0x06:
+ return "ifInteraction";
+ case 0x07:
+ return "endIf";
+ case 0x08:
+ return "else";
+ case 0x09:
+ return "nop09";
+ case 0x0A:
+ return "printStringLeft";
+ case 0x0B:
+ return "moveObject";
+ case 0x0C:
+ return "changeScene";
+ case 0x0D:
+ return "showDialogue";
+ case 0x0E:
+ return "changeAnimation";
+ case 0x0F:
+ return "frameWait";
+ case 0x10:
+ return "walkToPosition";
+ case 0x11:
+ return "waitForWalk";
+ case 0x12:
+ return "setPathfinding";
+ case 0x13:
+ return "skipUntil14";
+ case 0x14:
+ return "skipWord";
+ case 0x15:
+ return "clearDialogueChoices";
+ case 0x16:
+ return "addDialogueChoice";
+ case 0x17:
+ return "showDialogueChoice";
+ case 0x18:
+ return "dismissPanel";
+ case 0x19:
+ return "walkToAndPickup";
+ case 0x1A:
+ return "setPickupFrames";
+ case 0x1B:
+ return "setupObject";
+ case 0x1C:
+ return "setSkippable";
+ case 0x1D:
+ return "clearSkippable";
+ case 0x1E:
+ return "playAnimation";
+ case 0x1F:
+ return "pathWalkable = testPathfinding";
+ case 0x20:
+ return "setYOffset";
+ case 0x21:
+ return "setMotion";
+ case 0x22:
+ return "setOrientation";
+ case 0x23:
+ return "moveToPosition";
+ case 0x24:
+ return "addValues";
+ case 0x25:
+ return "subValues";
+ case 0x26:
+ return "loadSpecialAnim";
+ case 0x27:
+ return "setDirection";
+ case 0x28:
+ return "stopAnimation";
+ case 0x29:
+ return "openInventory";
+ case 0x2A:
+ return "loadObjectAnim";
+ case 0x2B:
+ return "checkObjectData";
+ case 0x2C:
+ return "invCheck = checkInventory";
+ case 0x2D:
+ return "setSnapToTarget";
+ case 0x2E:
+ return "animRangeTest = testSceneAnimFrame";
+ case 0x2F:
+ return "animRangeTest = testObjectAnimFrame";
+ case 0x30:
+ return "printStringRight";
+ case 0x31:
+ return "setVolume";
+ case 0x32:
+ return "setObjectClickable";
+ case 0x33:
+ return "setObjectVisible";
+ case 0x34:
+ return "setHotspotOverride";
+ case 0x35:
+ return "setObjectBounds";
+ case 0x36:
+ return "dismissAllPanels";
+ case 0x37:
+ return "resetToSceneScript";
+ case 0x38:
+ return "loadOverlayFont";
+ case 0x39:
+ return "endOverlayText";
+ case 0x3A:
+ return "addOverlayTextEntry";
+ case 0x3B:
+ return "clearOverlayText";
+ case 0x3C:
+ return "fadeToBlack";
+ case 0x3D:
+ return "fadeFromBlack";
+ case 0x3E:
+ return "loadPcmSound";
+ case 0x3F:
+ return "freePcmSound";
+ case 0x40:
+ return "playPcmSound";
+ case 0x41:
+ return "waitForSound";
+ case 0x42:
+ return "stopPcmSound";
+ case 0x43:
+ return "loadMusicSlot";
+ case 0x44:
+ return "playMusicSlot";
+ case 0x45:
+ return "stopMusicSlot";
+ case 0x46:
+ return "freeMusicSlot";
+ case 0x47:
+ return "waitForMusic";
+ case 0x48:
+ return "getObjectX";
+ case 0x49:
+ return "getObjectY";
+ case 0x4A:
+ return "getObjectField8";
+ case 0x4B:
+ return "getObjectOrientation";
+ case 0x4C:
+ return "clearActorInventory";
+ case 0x4D:
+ return "setPathfindingRemap";
+ case 0x4E:
+ return "waitForAdlib";
+ default:
+ return "???";
+ }
+}
+
static const char *cmpOpName(uint8_t op) {
switch (op) {
- case 0x01: return "==";
- case 0x02: return "!=";
- case 0x03: return "<";
- case 0x04: return ">";
- case 0x05: return "<=";
- case 0x06: return ">=";
- default: return "?";
+ case 0x01:
+ return "==";
+ case 0x02:
+ return "!=";
+ case 0x03:
+ return "<";
+ case 0x04:
+ return ">";
+ case 0x05:
+ return "<=";
+ case 0x06:
+ return ">=";
+ default:
+ return "?";
}
}
-static void disassemble() {
+struct DecompiledLine {
+ uint32_t offset;
+ uint8_t opcode;
+ int indent;
+ std::string params;
+};
+
+static std::string decodeParams(uint8_t opcode, uint32_t endPos, int &indent) {
+ std::string result;
+
+ char buf[256];
+ switch (opcode) {
+ case 0x01:
+ case 0x02: {
+ readByte();
+ uint16_t varIdx = readWord();
+ std::string val = formatValue();
+ snprintf(buf, sizeof(buf), " var[%u] = %s", varIdx, val.c_str());
+ result = buf;
+ break;
+ }
+ case 0x03:
+ case 0x04: {
+ std::string val = formatValue();
+ snprintf(buf, sizeof(buf), " (%s)", val.c_str());
+ result = buf;
+ indent++;
+ break;
+ }
+ case 0x05: {
+ uint8_t cmpOp = readByte();
+ std::string a = formatValue();
+ std::string b = formatValue();
+ snprintf(buf, sizeof(buf), " (%s %s %s)", a.c_str(), cmpOpName(cmpOp), b.c_str());
+ result = buf;
+ indent++;
+ break;
+ }
+ case 0x06: {
+ uint8_t subOp = readByte();
+ std::string i = formatValue();
+ std::string a = formatValue();
+ std::string b = formatValue();
+ snprintf(buf, sizeof(buf), " %s(%s, %s, %s)", subOp == 2 ? "NOT " : "", i.c_str(), a.c_str(), b.c_str());
+ result = buf;
+ indent++;
+ break;
+ }
+ case 0x08: {
+ indent++;
+ break;
+ }
+ case 0x0A: {
+ std::string x = formatValue();
+ std::string y = formatValue();
+ uint16_t strOffset = readWord();
+ uint16_t numLines = readWord();
+ snprintf(buf, sizeof(buf), " pos=(%s,%s) str=%u lines=%u", x.c_str(), y.c_str(), strOffset, numLines);
+ result = buf;
+ break;
+ }
+ case 0x0B: {
+ std::string obj = formatValue();
+ std::string scene = formatValue();
+ std::string x = formatValue();
+ std::string y = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s scene=%s pos=(%s,%s)", obj.c_str(), scene.c_str(), x.c_str(), y.c_str());
+ result = buf;
+ break;
+ }
+ case 0x0C: {
+ std::string scene = formatValue();
+ std::string mode = formatValue();
+ std::string speed = formatValue();
+ snprintf(buf, sizeof(buf), " scene=%s mode=%s speed=%s", scene.c_str(), mode.c_str(), speed.c_str());
+ result = buf;
+ break;
+ }
+ case 0x0D: {
+ std::string obj = formatValue();
+ std::string x = formatValue();
+ std::string y = formatValue();
+ std::string side = formatValue();
+ uint16_t strOffset = readWord();
+ uint16_t numLines = readWord();
+ snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s) side=%s str=%u lines=%u",
+ obj.c_str(), x.c_str(), y.c_str(), side.c_str(), strOffset, numLines);
+ result = buf;
+ break;
+ }
+ case 0x0F:
+ case 0x31:
+ case 0x3C:
+ case 0x3D: {
+ std::string val = formatValue();
+ result = " " + val;
+ break;
+ }
+ case 0x10: {
+ std::string obj = formatValue();
+ std::string x = formatValue();
+ std::string y = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s)", obj.c_str(), x.c_str(), y.c_str());
+ result = buf;
+ break;
+ }
+ case 0x11:
+ case 0x47: {
+ std::string obj = formatValue();
+ result = " obj=" + obj;
+ break;
+ }
+ case 0x12: {
+ std::string area = formatValue();
+ std::string active = formatValue();
+ std::string val = formatValue();
+ snprintf(buf, sizeof(buf), " area=%s active=%s val=%s", area.c_str(), active.c_str(), val.c_str());
+ result = buf;
+ break;
+ }
+ case 0x14: {
+ uint16_t w = readWord();
+ snprintf(buf, sizeof(buf), " 0x%04x", w);
+ result = buf;
+ break;
+ }
+ case 0x16: {
+ std::string idx = formatValue();
+ uint16_t strOffset = readWord();
+ uint16_t numLines = readWord();
+ snprintf(buf, sizeof(buf), " idx=%s str=%u lines=%u", idx.c_str(), strOffset, numLines);
+ result = buf;
+ break;
+ }
+ case 0x17: {
+ std::string obj = formatValue();
+ std::string x = formatValue();
+ std::string y = formatValue();
+ std::string side = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s) side=%s", obj.c_str(), x.c_str(), y.c_str(), side.c_str());
+ result = buf;
+ break;
+ }
+ case 0x19: {
+ std::string actor = formatValue();
+ std::string obj = formatValue();
+ snprintf(buf, sizeof(buf), " actor=%s obj=%s", actor.c_str(), obj.c_str());
+ result = buf;
+ break;
+ }
+ case 0x1A: {
+ std::string obj = formatValue();
+ std::string start = formatValue();
+ std::string end = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s start=%s end=%s", obj.c_str(), start.c_str(), end.c_str());
+ result = buf;
+ break;
+ }
+ case 0x1B: {
+ std::string obj = formatValue();
+ std::string slot = formatValue();
+ std::string speed = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s slot=%s speed=%s", obj.c_str(), slot.c_str(), speed.c_str());
+ result = buf;
+ break;
+ }
+ case 0x1E: {
+ std::string obj = formatValue();
+ std::string slot = formatValue();
+ std::string frame = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s slot=%s frame=%s", obj.c_str(), slot.c_str(), frame.c_str());
+ result = buf;
+ break;
+ }
+ case 0x1F: {
+ std::string obj = formatValue();
+ std::string x = formatValue();
+ std::string y = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s)", obj.c_str(), x.c_str(), y.c_str());
+ result = buf;
+ break;
+ }
+ case 0x20: {
+ std::string obj = formatValue();
+ std::string offset = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s offset=%s", obj.c_str(), offset.c_str());
+ result = buf;
+ break;
+ }
+ case 0x21: {
+ std::string obj = formatValue();
+ std::string target = formatValue();
+ std::string delta = formatValue();
+ std::string dist = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s target=%s delta=%s dist=%s", obj.c_str(), target.c_str(), delta.c_str(), dist.c_str());
+ result = buf;
+ break;
+ }
+ case 0x22: {
+ std::string obj = formatValue();
+ std::string anim = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s anim=%s", obj.c_str(), anim.c_str());
+ result = buf;
+ break;
+ }
+ case 0x23: {
+ std::string obj = formatValue();
+ std::string x = formatValue();
+ std::string y = formatValue();
+ std::string voff = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s pos=(%s,%s) voff=%s", obj.c_str(), x.c_str(), y.c_str(), voff.c_str());
+ result = buf;
+ break;
+ }
+ case 0x24:
+ case 0x25: {
+ std::string a = formatValue();
+ std::string b = formatValue();
+ const char *op = (opcode == 0x24) ? "+" : "-";
+ snprintf(buf, sizeof(buf), " %s = %s %s %s", a.c_str(), a.c_str(), op, b.c_str());
+ result = buf;
+ break;
+ }
+ case 0x26: {
+ std::string obj = formatValue();
+ std::string decodeFlag = formatValue();
+ uint8_t animIdx = readByte();
+ snprintf(buf, sizeof(buf), " obj=%s decode=%s anim=%u", obj.c_str(), decodeFlag.c_str(), animIdx);
+ result = buf;
+ break;
+ }
+ case 0x27: {
+ std::string obj = formatValue();
+ std::string maxFrame = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s maxFrame=%s", obj.c_str(), maxFrame.c_str());
+ result = buf;
+ break;
+ }
+ case 0x29: {
+ std::string obj = formatValue();
+ result = " obj=" + obj;
+ break;
+ }
+ case 0x2A: {
+ std::string obj = formatValue();
+ std::string slot = formatValue();
+ std::string decode = formatValue();
+ uint8_t idx = readByte();
+ snprintf(buf, sizeof(buf), " obj=%s slot=%s decode=%s idx=%u", obj.c_str(), slot.c_str(), decode.c_str(), idx);
+ result = buf;
+ break;
+ }
+ case 0x2B: {
+ std::string obj = formatValue();
+ result = " obj=" + obj;
+ break;
+ }
+ case 0x2C:
+ case 0x32:
+ case 0x33:
+ case 0x2D: {
+ std::string obj = formatValue();
+ std::string val = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s val=%s", obj.c_str(), val.c_str());
+ result = buf;
+ break;
+ }
+ case 0x2E: {
+ std::string anim = formatValue();
+ std::string lo = formatValue();
+ std::string hi = formatValue();
+ snprintf(buf, sizeof(buf), " anim=%s range=[%s,%s]", anim.c_str(), lo.c_str(), hi.c_str());
+ result = buf;
+ break;
+ }
+ case 0x2F: {
+ std::string obj = formatValue();
+ std::string slot = formatValue();
+ std::string lo = formatValue();
+ std::string hi = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s slot=%s range=[%s,%s]", obj.c_str(), slot.c_str(), lo.c_str(), hi.c_str());
+ result = buf;
+ break;
+ }
+ case 0x34:
+ case 0x4D: {
+ std::string a = formatValue();
+ std::string b = formatValue();
+ snprintf(buf, sizeof(buf), " %s -> %s", a.c_str(), b.c_str());
+ result = buf;
+ break;
+ }
+ case 0x35: {
+ std::string obj = formatValue();
+ std::string parent = formatValue();
+ std::string a = formatValue();
+ std::string b = formatValue();
+ std::string c = formatValue();
+ snprintf(buf, sizeof(buf), " obj=%s parent=%s (%s,%s,%s)", obj.c_str(), parent.c_str(), a.c_str(), b.c_str(), c.c_str());
+ result = buf;
+ break;
+ }
+ case 0x38:
+ case 0x3E: {
+ uint8_t resIdx = readByte();
+ snprintf(buf, sizeof(buf), " res=%u", resIdx);
+ result = buf;
+ break;
+ }
+ case 0x3A: {
+ std::string x = formatValue();
+ std::string y = formatValue();
+ std::string align = formatValue();
+ uint16_t strOffset = readWord();
+ uint16_t entryType = readWord();
+ snprintf(buf, sizeof(buf), " pos=(%s,%s) align=%s str=%u type=%u", x.c_str(), y.c_str(), align.c_str(), strOffset, entryType);
+ result = buf;
+ break;
+ }
+ case 0x43: {
+ std::string slot = formatValue();
+ uint8_t resIdx = readByte();
+ snprintf(buf, sizeof(buf), " slot=%s res=%u", slot.c_str(), resIdx);
+ result = buf;
+ break;
+ }
+ case 0x44:
+ case 0x45: {
+ std::string slot = formatValue();
+ std::string a = formatValue();
+ std::string b = formatValue();
+ snprintf(buf, sizeof(buf), " slot=%s %s %s", slot.c_str(), a.c_str(), b.c_str());
+ result = buf;
+ break;
+ }
+ case 0x46: {
+ std::string slot = formatValue();
+ result = " slot=" + slot;
+ break;
+ }
+ case 0x48:
+ case 0x49:
+ case 0x4A:
+ case 0x4B: {
+ std::string obj = formatValue();
+ if (pos + 3 <= endPos) {
+ readByte();
+ uint16_t varIdx = readWord();
+ snprintf(buf, sizeof(buf), " var[%u] = obj=%s", varIdx, obj.c_str());
+ result = buf;
+ } else {
+ result = " obj=" + obj;
+ }
+ break;
+ }
+ case 0x07:
+ case 0x09:
+ case 0x0E:
+ case 0x13:
+ case 0x15:
+ case 0x18:
+ case 0x1C:
+ case 0x1D:
+ case 0x28:
+ case 0x30:
+ case 0x36:
+ case 0x37:
+ case 0x39:
+ case 0x3B:
+ case 0x3F:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x4C:
+ case 0x4E:
+ break;
+ default: {
+ uint8_t length = (uint8_t)(endPos - pos);
+ if (length > 0 && length <= 30) {
+ result = " [";
+ uint32_t dataPos = pos;
+ for (uint8_t i = 0; i < length && dataPos < scriptSize; i++, dataPos++) {
+ if (i)
+ result += " ";
+ snprintf(buf, sizeof(buf), "%02x", scriptData[dataPos]);
+ result += buf;
+ }
+ result += "]";
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+static std::vector<DecompiledLine> decompileToLines() {
+ std::vector<DecompiledLine> lines;
int indent = 0;
while (pos < scriptSize - 1) {
uint32_t instrAddr = pos;
uint8_t opcode = readByte();
- if (opcode == 0x00) continue;
+ if (opcode == 0x00)
+ continue;
uint8_t length = readByte();
uint32_t endPos = pos + length;
- // Adjust indent for block-end opcodes
- if (opcode == 0x07 && indent > 0) indent--;
- if (opcode == 0x08 && indent > 0) indent--;
+ if (opcode == 0x07 && indent > 0)
+ indent--;
+ if (opcode == 0x08 && indent > 0)
+ indent--;
- for (int i = 0; i < indent; i++) printf(" ");
- printf("%04x: ", instrAddr);
+ DecompiledLine line;
+ line.offset = instrAddr;
+ line.opcode = opcode;
+ line.indent = indent;
+ line.params = decodeParams(opcode, endPos, indent);
+ lines.push_back(line);
- switch (opcode) {
- case 0x01: { // setVar
- readByte();
- uint16_t varIdx = readWord();
- std::string val = formatValue();
- printf("setVar var[%u] = %s\n", varIdx, val.c_str());
- break;
- }
- case 0x02: { // setVarOr
- readByte();
- uint16_t varIdx = readWord();
- std::string val = formatValue();
- printf("setVarOr var[%u] = %s\n", varIdx, val.c_str());
- break;
- }
- case 0x03: { // ifTrue
- std::string val = formatValue();
- printf("ifTrue (%s)\n", val.c_str());
- indent++;
- break;
- }
- case 0x04: { // ifFalse
- std::string val = formatValue();
- printf("ifFalse (%s)\n", val.c_str());
- indent++;
- break;
- }
- case 0x05: { // compare
- uint8_t cmpOp = readByte();
- std::string a = formatValue();
- std::string b = formatValue();
- printf("compare (%s %s %s)\n", a.c_str(), cmpOpName(cmpOp), b.c_str());
- indent++;
- break;
- }
- case 0x06: { // ifInteraction
- uint8_t subOp = readByte();
- std::string i = formatValue();
- std::string a = formatValue();
- std::string b = formatValue();
- printf("ifInteraction %s(%s, %s, %s)\n", subOp == 2 ? "NOT " : "", i.c_str(), a.c_str(), b.c_str());
- indent++;
- break;
- }
- case 0x07: { // endIf
- printf("endIf\n");
- break;
- }
- case 0x08: { // else
- printf("else\n");
- indent++;
- break;
- }
- case 0x0A: { // printString
- std::string x = formatValue();
- std::string y = formatValue();
- uint16_t strOffset = readWord();
- uint16_t numLines = readWord();
- printf("printString pos=(%s, %s) strOffset=%u numLines=%u\n", x.c_str(), y.c_str(), strOffset, numLines);
- break;
- }
- case 0x0B: { // moveObject
- std::string obj = formatValue();
- std::string scene = formatValue();
- std::string x = formatValue();
- std::string y = formatValue();
- printf("moveObject obj=%s scene=%s pos=(%s, %s)\n", obj.c_str(), scene.c_str(), x.c_str(), y.c_str());
- break;
- }
- case 0x0C: { // changeScene
- std::string scene = formatValue();
- std::string mode = formatValue();
- std::string speed = formatValue();
- printf("changeScene scene=%s mode=%s speed=%s\n", scene.c_str(), mode.c_str(), speed.c_str());
- break;
- }
- case 0x0D: { // showDialogue
- std::string obj = formatValue();
- std::string x = formatValue();
- std::string y = formatValue();
- std::string side = formatValue();
- uint16_t strOffset = readWord();
- uint16_t numLines = readWord();
- printf("showDialogue obj=%s pos=(%s, %s) side=%s strOffset=%u numLines=%u\n",
- obj.c_str(), x.c_str(), y.c_str(), side.c_str(), strOffset, numLines);
- break;
- }
- case 0x0E: { // changeAnim
- printf("changeAnim\n");
- break;
- }
- case 0x0F: { // frameWait
- std::string val = formatValue();
- printf("frameWait %s\n", val.c_str());
- break;
- }
- case 0x10: { // walkTo
- std::string obj = formatValue();
- std::string x = formatValue();
- std::string y = formatValue();
- printf("walkTo obj=%s pos=(%s, %s)\n", obj.c_str(), x.c_str(), y.c_str());
- break;
- }
- case 0x11: { // waitForWalk
- std::string obj = formatValue();
- printf("waitForWalk obj=%s\n", obj.c_str());
- break;
- }
- case 0x12: { // setPathOverride
- std::string area = formatValue();
- std::string active = formatValue();
- std::string val = formatValue();
- printf("setPathOverride area=%s active=%s val=%s\n", area.c_str(), active.c_str(), val.c_str());
- break;
- }
- case 0x13: { // loadAnim
- printf("loadAnim\n");
- break;
- }
- case 0x14: { // skipWord
- uint16_t w = readWord();
- printf("skipWord 0x%04x\n", w);
- break;
- }
- case 0x15: { // clearDialogueChoices
- printf("clearDialogueChoices\n");
- break;
- }
- case 0x16: { // addDialogueChoice
- std::string idx = formatValue();
- uint16_t strOffset = readWord();
- uint16_t numLines = readWord();
- printf("addDialogueChoice idx=%s strOffset=%u numLines=%u\n", idx.c_str(), strOffset, numLines);
- break;
- }
- case 0x17: { // showDialogueChoice
- std::string obj = formatValue();
- std::string x = formatValue();
- std::string y = formatValue();
- std::string side = formatValue();
- printf("showDialogueChoice obj=%s pos=(%s, %s) side=%s\n", obj.c_str(), x.c_str(), y.c_str(), side.c_str());
- break;
- }
- case 0x18: { // dismissPanel
- printf("dismissPanel\n");
- break;
- }
- case 0x19: { // walkToAndPickup
- std::string actor = formatValue();
- std::string obj = formatValue();
- printf("walkToAndPickup actor=%s obj=%s\n", actor.c_str(), obj.c_str());
- break;
- }
- case 0x1A: { // setPickupFrames
- std::string obj = formatValue();
- std::string start = formatValue();
- std::string end = formatValue();
- printf("setPickupFrames obj=%s start=%s end=%s\n", obj.c_str(), start.c_str(), end.c_str());
- break;
- }
- case 0x1B: { // setAnimSpeed
- std::string obj = formatValue();
- std::string slot = formatValue();
- std::string speed = formatValue();
- printf("setAnimSpeed obj=%s slot=%s speed=%s\n", obj.c_str(), slot.c_str(), speed.c_str());
- break;
- }
- case 0x1C: { // setSkippable
- printf("setSkippable\n");
- break;
- }
- case 0x1D: { // clearSkippable
- printf("clearSkippable\n");
- break;
- }
- case 0x1E: { // loadAnimBlob
- std::string obj = formatValue();
- std::string slot = formatValue();
- std::string frame = formatValue();
- printf("loadAnimBlob obj=%s slot=%s frame=%s\n", obj.c_str(), slot.c_str(), frame.c_str());
- break;
- }
- case 0x1F: { // setPosition
- std::string obj = formatValue();
- std::string x = formatValue();
- std::string y = formatValue();
- printf("setPosition obj=%s pos=(%s, %s)\n", obj.c_str(), x.c_str(), y.c_str());
- break;
- }
- case 0x20: { // setVerticalOffset
- std::string obj = formatValue();
- std::string offset = formatValue();
- printf("setVerticalOffset obj=%s offset=%s\n", obj.c_str(), offset.c_str());
- break;
- }
- case 0x21: { // setMotion
- std::string obj = formatValue();
- std::string target = formatValue();
- std::string delta = formatValue();
- std::string dist = formatValue();
- printf("setMotion obj=%s target=%s delta=%s dist=%s\n", obj.c_str(), target.c_str(), delta.c_str(), dist.c_str());
- break;
- }
- case 0x22: { // setAnimIndex
- std::string obj = formatValue();
- std::string anim = formatValue();
- printf("setAnimIndex obj=%s anim=%s\n", obj.c_str(), anim.c_str());
- break;
- }
- case 0x23: { // moveToPosition
- std::string obj = formatValue();
- std::string x = formatValue();
- std::string y = formatValue();
- std::string voff = formatValue();
- printf("moveToPosition obj=%s pos=(%s, %s) voff=%s\n", obj.c_str(), x.c_str(), y.c_str(), voff.c_str());
- break;
- }
- case 0x24: { // add
- std::string a = formatValue();
- std::string b = formatValue();
- printf("add %s, %s\n", a.c_str(), b.c_str());
- break;
- }
- case 0x25: { // subtract
- std::string a = formatValue();
- std::string b = formatValue();
- printf("subtract %s, %s\n", a.c_str(), b.c_str());
- break;
- }
- case 0x26: { // loadSpecialAnim
- std::string obj = formatValue();
- std::string decodeFlag = formatValue(); // decode/enable flag (runtime +0x182)
- uint8_t animIdx = readByte();
- printf("loadSpecialAnim obj=%s decode=%s anim=%u\n", obj.c_str(), decodeFlag.c_str(), animIdx);
- break;
- }
- case 0x27: { // setMaxAnimFrame
- std::string obj = formatValue();
- std::string maxFrame = formatValue();
- printf("setMaxAnimFrame obj=%s maxFrame=%s\n", obj.c_str(), maxFrame.c_str());
- break;
- }
- case 0x28: { // nop28
- printf("nop28\n");
- break;
- }
- case 0x29: { // loadSong
- std::string obj = formatValue();
- printf("loadSong obj=%s\n", obj.c_str());
- break;
- }
- case 0x2A: { // loadAnimFromScene
- std::string obj = formatValue();
- std::string slot = formatValue();
- std::string decode = formatValue();
- uint8_t idx = readByte();
- printf("loadAnimFromScene obj=%s slot=%s decode=%s idx=%u\n", obj.c_str(), slot.c_str(), decode.c_str(), idx);
- break;
- }
- case 0x2B: { // setShading
- std::string obj = formatValue();
- printf("setShading obj=%s\n", obj.c_str());
- break;
- }
- case 0x2C: { // setParent
- std::string obj = formatValue();
- std::string val = formatValue();
- printf("setParent obj=%s val=%s\n", obj.c_str(), val.c_str());
- break;
- }
- case 0x2D: { // setScaling
- std::string obj = formatValue();
- std::string val = formatValue();
- printf("setScaling obj=%s val=%s\n", obj.c_str(), val.c_str());
- break;
- }
- case 0x2E: { // checkAnimRange
- std::string anim = formatValue();
- std::string lo = formatValue();
- std::string hi = formatValue();
- printf("checkAnimRange anim=%s range=[%s, %s]\n", anim.c_str(), lo.c_str(), hi.c_str());
- break;
- }
- case 0x2F: { // checkBlobRange
- std::string obj = formatValue();
- std::string slot = formatValue();
- std::string lo = formatValue();
- std::string hi = formatValue();
- printf("checkBlobRange obj=%s slot=%s range=[%s, %s]\n", obj.c_str(), slot.c_str(), lo.c_str(), hi.c_str());
- break;
- }
- case 0x30: { // nop30
- printf("nop30\n");
- break;
- }
- case 0x31: { // setVolume
- std::string vol = formatValue();
- printf("setVolume %s\n", vol.c_str());
- break;
- }
- case 0x32: { // setClickable
- std::string obj = formatValue();
- std::string val = formatValue();
- printf("setClickable obj=%s val=%s\n", obj.c_str(), val.c_str());
- break;
- }
- case 0x33: { // setVisible
- std::string obj = formatValue();
- std::string val = formatValue();
- printf("setVisible obj=%s val=%s\n", obj.c_str(), val.c_str());
- break;
- }
- case 0x34: { // setHotspotRemap
- std::string a = formatValue();
- std::string b = formatValue();
- printf("setHotspotRemap %s -> %s\n", a.c_str(), b.c_str());
- break;
- }
- case 0x35: { // setBoundsAttach
- std::string obj = formatValue();
- std::string parent = formatValue();
- std::string a = formatValue();
- std::string b = formatValue();
- std::string c = formatValue();
- printf("setBoundsAttach obj=%s parent=%s (%s, %s, %s)\n",
- obj.c_str(), parent.c_str(), a.c_str(), b.c_str(), c.c_str());
- break;
- }
- case 0x36: { // dismissAllPanels
- printf("dismissAllPanels\n");
- break;
- }
- case 0x37: { // resetScript
- printf("resetScript\n");
- break;
- }
- case 0x38: { // loadOverlayFont
- uint8_t resIdx = readByte();
- printf("loadOverlayFont res=%u\n", resIdx);
- break;
- }
- case 0x39: { // endOverlayText
- printf("endOverlayText\n");
- break;
- }
- case 0x3A: { // addOverlayEntry
- std::string x = formatValue();
- std::string y = formatValue();
- std::string align = formatValue();
- uint16_t strOffset = readWord();
- uint16_t entryType = readWord();
- printf("addOverlayEntry pos=(%s, %s) align=%s str=%u type=%u\n",
- x.c_str(), y.c_str(), align.c_str(), strOffset, entryType);
- break;
- }
- case 0x3B: { // clearOverlayEntries
- printf("clearOverlayEntries\n");
- break;
- }
- case 0x3C: { // fadeToBlack
- std::string speed = formatValue();
- printf("fadeToBlack %s\n", speed.c_str());
- break;
- }
- case 0x3D: { // fadeFromBlack
- std::string speed = formatValue();
- printf("fadeFromBlack %s\n", speed.c_str());
- break;
- }
- case 0x3E: { // loadSoundRes
- uint8_t resIdx = readByte();
- printf("loadSoundRes res=%u\n", resIdx);
- break;
- }
- case 0x3F: { // clearSound
- printf("clearSound\n");
- break;
- }
- case 0x40: { // playSound
- printf("playSound\n");
- break;
- }
- case 0x41: { // waitForSound
- printf("waitForSound\n");
- break;
- }
- case 0x42: { // stopSound
- printf("stopSound\n");
- break;
- }
- case 0x43: { // loadMusicSlot
- std::string slot = formatValue();
- uint8_t resIdx = readByte();
- printf("loadMusicSlot slot=%s res=%u\n", slot.c_str(), resIdx);
- break;
- }
- case 0x44: { // playMusic
- std::string slot = formatValue();
- std::string a = formatValue();
- std::string b = formatValue();
- printf("playMusic slot=%s %s %s\n", slot.c_str(), a.c_str(), b.c_str());
- break;
- }
- case 0x45: { // stopMusic
- std::string slot = formatValue();
- std::string a = formatValue();
- std::string b = formatValue();
- printf("stopMusic slot=%s %s %s\n", slot.c_str(), a.c_str(), b.c_str());
- break;
- }
- case 0x46: { // freeMusic
- std::string slot = formatValue();
- printf("freeMusic slot=%s\n", slot.c_str());
- break;
- }
- case 0x47: { // waitForMusic
- printf("waitForMusic\n");
- break;
- }
- case 0x48: { // getObjectX
- std::string obj = formatValue();
- printf("getObjectX obj=%s\n", obj.c_str());
- break;
- }
- case 0x49: { // getObjectY
- std::string obj = formatValue();
- printf("getObjectY obj=%s\n", obj.c_str());
- break;
- }
- case 0x4A: { // getObjectField
- std::string obj = formatValue();
- printf("getObjectField obj=%s\n", obj.c_str());
- break;
- }
- case 0x4B: { // getObjectOrient
- std::string obj = formatValue();
- printf("getObjectOrient obj=%s\n", obj.c_str());
+ if (pos != endPos)
+ pos = endPos;
+ }
+ return lines;
+}
+
+static void disassemble() {
+ auto lines = decompileToLines();
+ for (const auto &l : lines) {
+ for (int i = 0; i < l.indent; i++)
+ printf(" ");
+ printf("%04x: %s%s\n", l.offset, getOpcodeName(l.opcode), l.params.c_str());
+ }
+}
+
+static std::string jsonEscape(const std::string &s) {
+ std::string out;
+ for (char c : s) {
+ switch (c) {
+ case '"':
+ out += "\\\"";
break;
- }
- case 0x4C: { // clearActorItems
- printf("clearActorItems\n");
+ case '\\':
+ out += "\\\\";
break;
- }
- case 0x4D: { // setAreaRemap
- std::string a = formatValue();
- std::string b = formatValue();
- printf("setAreaRemap %s -> %s\n", a.c_str(), b.c_str());
+ case '\n':
+ out += "\\n";
break;
- }
- case 0x4E: { // waitForAdlib
- printf("waitForAdlib\n");
+ case '\t':
+ out += "\\t";
break;
- }
- default: {
- printf("??? opcode=0x%02x len=%u\n", opcode, length);
+ default:
+ out += c;
break;
}
- }
+ }
+ return out;
+}
- if (pos != endPos) {
- pos = endPos;
- }
+static void disassembleJson(int sceneIndex) {
+ auto lines = decompileToLines();
+ printf(" {\"scene\": %d, \"size\": %u, \"instructions\": [\n", sceneIndex, scriptSize);
+ for (size_t i = 0; i < lines.size(); i++) {
+ const auto &l = lines[i];
+ std::string text = std::string(getOpcodeName(l.opcode)) + l.params;
+ printf(" {\"offset\": %u, \"opcode\": %u, \"name\": \"%s\", \"indent\": %d, \"text\": \"%s\"}%s\n",
+ l.offset, l.opcode, jsonEscape(getOpcodeName(l.opcode)).c_str(),
+ l.indent, jsonEscape(text).c_str(),
+ (i + 1 < lines.size()) ? "," : "");
}
+ printf(" ]}");
}
static void printHelp(const char *bin) {
printf("MACS2 Script Decompiler\n\n");
- printf("Usage: %s <game_data_file> [scene_index]\n\n", bin);
+ printf("Usage: %s [--json] <game_data_file> [scene_index]\n\n", bin);
+ printf(" --json - Output as JSON\n");
printf(" game_data_file - The main game data file\n");
printf(" scene_index - Scene number to decompile (1-based)\n");
printf(" If omitted, decompiles all scenes\n\n");
@@ -587,17 +774,24 @@ static bool loadSceneScript(FILE *f, uint16_t sceneIndex) {
fseek(f, offset, SEEK_SET);
uint32_t sceneDataOffset;
- if (fread(&sceneDataOffset, 4, 1, f) != 1) return false;
+ if (fread(&sceneDataOffset, 4, 1, f) != 1)
+ return false;
+
+ if (sceneDataOffset == 0)
+ return false;
fseek(f, sceneDataOffset + 0x80, SEEK_SET);
uint16_t size;
- if (fread(&size, 2, 1, f) != 1) return false;
+ if (fread(&size, 2, 1, f) != 1)
+ return false;
- if (size == 0) return false;
+ if (size == 0)
+ return false;
scriptData = (uint8_t *)malloc(size);
- if (!scriptData) return false;
+ if (!scriptData)
+ return false;
if (fread(scriptData, 1, size, f) != size) {
free(scriptData);
@@ -616,53 +810,71 @@ int main(int argc, char **argv) {
return 1;
}
- FILE *f = fopen(argv[1], "rb");
+ bool jsonMode = false;
+ int argIdx = 1;
+ if (!strcmp(argv[argIdx], "--json")) {
+ jsonMode = true;
+ argIdx++;
+ }
+
+ if (argIdx >= argc) {
+ printHelp(argv[0]);
+ return 1;
+ }
+
+ FILE *f = fopen(argv[argIdx], "rb");
if (!f) {
- fprintf(stderr, "Error: Cannot open file '%s'\n", argv[1]);
+ fprintf(stderr, "Error: Cannot open file '%s'\n", argv[argIdx]);
return 1;
}
+ argIdx++;
int startScene = -1;
int endScene = -1;
- if (argc >= 3) {
- startScene = atoi(argv[2]);
+ if (argIdx < argc) {
+ startScene = atoi(argv[argIdx]);
endScene = startScene;
} else {
startScene = 1;
endScene = 512;
}
- printf("; MACS2 Script Runtime Variables (type 0xFF, read-only)\n");
- printf("; These are computed by the engine at read-time, not stored in script data.\n");
- printf("; interactedUse (FF:01) Object ID interacted with via Use/UseInventory cursor\n");
- printf("; interactedLook (FF:02) Object ID interacted with via Look cursor\n");
- printf("; interactedTalk (FF:03) Object ID interacted with via Talk cursor\n");
- printf("; areaAtActor (FF:04) Area ID at protagonist's current position\n");
- printf("; isRepeatRun (FF:0B) 1 during repeat/object script pass, 0 otherwise\n");
- printf("; dialogueResult (FF:0D) Index of last chosen dialogue option\n");
- printf("; pathWalkable (FF:23) 1 if last setPosition path test succeeded\n");
- printf("; actorX (FF:24) Protagonist X position\n");
- printf("; actorY (FF:25) Protagonist Y position\n");
- printf("; isSceneInit (FF:26) 1 during scene initialization pass\n");
- printf("; invCheck (FF:28) 1 if last setParent inventory check matched\n");
- printf("; invCombine (FF:2A) 1 if inventory combine pending (no UI open)\n");
- printf("; invAction (FF:2B) 1 if inventory action pending (no UI open)\n");
- printf("; curScene (FF:2D) Current scene index\n");
- printf("; prevScene (FF:2F) Previous scene index\n");
- printf(";\n");
- printf("; Script variables: var[N] (read/write, all zeroed on scene load)\n");
- printf("; Object IDs in moveObject/etc.: raw value - 0x400 = object index\n");
- printf(";\n\n");
-
- for (int scene = startScene; scene <= endScene; scene++) {
- if (loadSceneScript(f, (uint16_t)scene)) {
- printf("=== Scene %d (size: %u bytes) ===\n", scene, scriptSize);
- disassemble();
- printf("\n");
- free(scriptData);
- scriptData = nullptr;
- scriptSize = 0;
+ if (jsonMode) {
+ printf("{\"scenes\": [\n");
+ bool first = true;
+ for (int scene = startScene; scene <= endScene; scene++) {
+ if (loadSceneScript(f, (uint16_t)scene)) {
+ if (!first)
+ printf(",\n");
+ first = false;
+ disassembleJson(scene);
+ free(scriptData);
+ scriptData = nullptr;
+ scriptSize = 0;
+ }
+ }
+ printf("\n]}\n");
+ } else {
+ printf("; MACS2 Script Runtime Variables (type 0xFF, read-only)\n");
+ printf("; These are computed by the engine at read-time, not stored in script data.\n");
+ for (const auto &s : kSpecialNames) {
+ printf("; %-18s (FF:%02x)\n", s.name, s.id);
+ }
+ printf(";\n");
+ printf("; Script variables: var[N] (read/write, all zeroed on scene load)\n");
+ printf("; Object IDs in moveObject/etc.: raw value - 0x400 = object index\n");
+ printf(";\n\n");
+
+ for (int scene = startScene; scene <= endScene; scene++) {
+ if (loadSceneScript(f, (uint16_t)scene)) {
+ printf("=== Scene %d (size: %u bytes) ===\n", scene, scriptSize);
+ disassemble();
+ printf("\n");
+ free(scriptData);
+ scriptData = nullptr;
+ scriptSize = 0;
+ }
}
}
Commit: fadaad3fbf935207e9f37a2865ac07a00f0e43bf
https://github.com/scummvm/scummvm-tools/commit/fadaad3fbf935207e9f37a2865ac07a00f0e43bf
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-05-31T18:08:08+02:00
Commit Message:
MASC2: started to export json scene data, too
Changed paths:
engines/macs2/extract_macs2.cpp
diff --git a/engines/macs2/extract_macs2.cpp b/engines/macs2/extract_macs2.cpp
index 5a6c3f0d..dd10941e 100644
--- a/engines/macs2/extract_macs2.cpp
+++ b/engines/macs2/extract_macs2.cpp
@@ -21,8 +21,8 @@
/* MACS2 Resource Extractor - extracts images, sounds, music, and strings */
-#include <stdio.h>
#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
@@ -68,7 +68,8 @@ static bool decodeRLEImage(uint32_t offset, uint8_t *pixels) {
fseek(resFile, offset, SEEK_SET);
for (int y = 0; y < 200; y++) {
uint16_t length = readU16(resFile);
- if (length == 0 || length > 960) return false;
+ if (length == 0 || length > 960)
+ return false;
std::vector<uint8_t> rowData(length);
fread(rowData.data(), 1, length, resFile);
int remaining = 320;
@@ -81,7 +82,8 @@ static bool decodeRLEImage(uint32_t offset, uint8_t *pixels) {
pixels[y * 320 + x++] = val;
remaining--;
} else {
- if (ptr + 2 > end) break;
+ if (ptr + 2 > end)
+ break;
uint8_t runLen = *ptr++;
uint8_t runVal = *ptr++;
for (int i = 0; i < runLen && remaining > 0; i++) {
@@ -97,7 +99,10 @@ static bool decodeRLEImage(uint32_t offset, uint8_t *pixels) {
// Write a BMP file (8-bit indexed)
static void writeBMP(const char *path, const uint8_t *pixels, const uint8_t *palette) {
FILE *f = fopen(path, "wb");
- if (!f) { fprintf(stderr, "Cannot write %s\n", path); return; }
+ if (!f) {
+ fprintf(stderr, "Cannot write %s\n", path);
+ return;
+ }
// BMP header
uint32_t imageSize = 320 * 200;
@@ -106,21 +111,27 @@ static void writeBMP(const char *path, const uint8_t *pixels, const uint8_t *pal
uint32_t fileSize = dataOffset + imageSize;
// File header
- fputc('B', f); fputc('M', f);
+ fputc('B', f);
+ fputc('M', f);
uint8_t hdr[12] = {};
- hdr[0] = fileSize & 0xFF; hdr[1] = (fileSize >> 8) & 0xFF;
- hdr[2] = (fileSize >> 16) & 0xFF; hdr[3] = (fileSize >> 24) & 0xFF;
- hdr[8] = dataOffset & 0xFF; hdr[9] = (dataOffset >> 8) & 0xFF;
- hdr[10] = (dataOffset >> 16) & 0xFF; hdr[11] = (dataOffset >> 24) & 0xFF;
+ hdr[0] = fileSize & 0xFF;
+ hdr[1] = (fileSize >> 8) & 0xFF;
+ hdr[2] = (fileSize >> 16) & 0xFF;
+ hdr[3] = (fileSize >> 24) & 0xFF;
+ hdr[8] = dataOffset & 0xFF;
+ hdr[9] = (dataOffset >> 8) & 0xFF;
+ hdr[10] = (dataOffset >> 16) & 0xFF;
+ hdr[11] = (dataOffset >> 24) & 0xFF;
fwrite(hdr, 1, 12, f);
// DIB header (BITMAPINFOHEADER)
uint8_t dib[40] = {};
dib[0] = 40; // header size
- dib[4] = 0x40; dib[5] = 0x01; // width = 320
+ dib[4] = 0x40;
+ dib[5] = 0x01; // width = 320
dib[8] = 0xC8; // height = 200 (positive = bottom-up)
- dib[12] = 1; // planes
- dib[14] = 8; // bpp
+ dib[12] = 1; // planes
+ dib[14] = 8; // bpp
fwrite(dib, 1, 40, f);
// Palette (BGRA)
@@ -141,13 +152,18 @@ static void writeBMP(const char *path, const uint8_t *pixels, const uint8_t *pal
printf(" Wrote %s\n", path);
}
+// Forward declaration
+static bool decodeRLEMap(FILE *f, uint8_t *pixels);
+
// Extract background image for a scene
static void extractImage(uint16_t sceneIndex, const char *outDir) {
uint32_t bgOffset = getSceneBgOffset(sceneIndex);
- if (bgOffset == 0) return;
+ if (bgOffset == 0)
+ return;
uint8_t pixels[320 * 200];
- if (!decodeRLEImage(bgOffset, pixels)) return;
+ if (!decodeRLEImage(bgOffset, pixels))
+ return;
// Palette follows immediately after the image
uint8_t palette[768];
@@ -156,12 +172,26 @@ static void extractImage(uint16_t sceneIndex, const char *outDir) {
char path[512];
snprintf(path, sizeof(path), "%s/scene_%03d.bmp", outDir, sceneIndex);
writeBMP(path, pixels, palette);
+
+ // Export the 4 maps as BMP using the scene's own palette (same as imgui debugger)
+ fseek(resFile, 256, SEEK_CUR); // shading table
+ fseek(resFile, 3, SEEK_CUR); // 3 unknown bytes
+
+ const char *mapNames[] = {"depth", "pathfinding", "unknown", "hotspot"};
+ for (int m = 0; m < 4; m++) {
+ uint8_t mapPixels[320 * 200];
+ if (!decodeRLEMap(resFile, mapPixels))
+ break;
+ snprintf(path, sizeof(path), "%s/scene_%03d_%s.bmp", outDir, sceneIndex, mapNames[m]);
+ writeBMP(path, mapPixels, palette);
+ }
}
// Extract indexed resources (sounds/music) for a scene
static void extractResources(uint16_t sceneIndex, const char *outDir, const char *prefix) {
uint32_t dataOffset = getSceneDataOffset(sceneIndex);
- if (dataOffset == 0) return;
+ if (dataOffset == 0)
+ return;
fseek(resFile, dataOffset, SEEK_SET);
uint32_t resourceTable[32]; // 0x80 / 4 = 32 entries
@@ -170,10 +200,12 @@ static void extractResources(uint16_t sceneIndex, const char *outDir, const char
}
for (int i = 0; i < 32; i++) {
- if (resourceTable[i] == 0) continue;
+ if (resourceTable[i] == 0)
+ continue;
fseek(resFile, resourceTable[i], SEEK_SET);
uint32_t size = readU32(resFile);
- if (size == 0 || size > 0x100000) continue;
+ if (size == 0 || size > 0x100000)
+ continue;
std::vector<uint8_t> data(size);
fread(data.data(), 1, size, resFile);
@@ -204,11 +236,13 @@ static std::string decryptString(const uint8_t *data, uint16_t length) {
// Extract strings for a scene
static void extractStrings(uint16_t sceneIndex, const char *outDir) {
uint32_t strOffset = getSceneStringsOffset(sceneIndex);
- if (strOffset == 0) return;
+ if (strOffset == 0)
+ return;
fseek(resFile, strOffset, SEEK_SET);
uint16_t totalSize = readU16(resFile);
- if (totalSize == 0) return;
+ if (totalSize == 0)
+ return;
std::vector<uint8_t> strData(totalSize);
fread(strData.data(), 1, totalSize, resFile);
@@ -216,7 +250,8 @@ static void extractStrings(uint16_t sceneIndex, const char *outDir) {
char path[512];
snprintf(path, sizeof(path), "%s/strings_scene%03d.txt", outDir, sceneIndex);
FILE *out = fopen(path, "w");
- if (!out) return;
+ if (!out)
+ return;
fprintf(out, "; Scene %d strings\n", sceneIndex);
fprintf(out, "; Total data size: %u bytes\n\n", totalSize);
@@ -227,7 +262,8 @@ static void extractStrings(uint16_t sceneIndex, const char *outDir) {
while (pos + 2 <= totalSize) {
uint16_t len = strData[pos] | (strData[pos + 1] << 8);
pos += 2;
- if (len == 0 || pos + len > totalSize) break;
+ if (len == 0 || pos + len > totalSize)
+ break;
std::string decoded = decryptString(strData.data() + pos, len);
fprintf(out, "[%d] (offset=%u) %s\n", stringIndex, (unsigned)(pos - 2), decoded.c_str());
pos += len;
@@ -246,14 +282,238 @@ static void mkdirp(const char *path) {
#endif
}
+// Skip an RLE-compressed 320x200 image in the file (advance past it without decoding)
+static void skipRLEImage(FILE *f) {
+ for (int y = 0; y < 200; y++) {
+ uint16_t length = readU16(f);
+ fseek(f, length, SEEK_CUR);
+ }
+}
+
+// Decode an RLE 320x200 image into a buffer
+static bool decodeRLEMap(FILE *f, uint8_t *pixels) {
+ for (int y = 0; y < 200; y++) {
+ uint16_t length = readU16(f);
+ if (length == 0 || length > 960)
+ return false;
+ std::vector<uint8_t> rowData(length);
+ fread(rowData.data(), 1, length, f);
+ int remaining = 320;
+ int x = 0;
+ uint8_t *ptr = rowData.data();
+ uint8_t *end = ptr + length;
+ while (remaining > 0 && ptr < end) {
+ uint8_t val = *ptr++;
+ if (val != 0xF0) {
+ pixels[y * 320 + x++] = val;
+ remaining--;
+ } else {
+ if (ptr + 2 > end)
+ break;
+ uint8_t runLen = *ptr++;
+ uint8_t runVal = *ptr++;
+ for (int i = 0; i < runLen && remaining > 0; i++) {
+ pixels[y * 320 + x++] = runVal;
+ remaining--;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+static void writeRawFile(const char *path, const uint8_t *data, size_t size) {
+ FILE *f = fopen(path, "wb");
+ if (f) {
+ fwrite(data, 1, size, f);
+ fclose(f);
+ }
+}
+
+// Extract comprehensive scene data as JSON + binary map files
+static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
+ uint32_t bgOffset = getSceneBgOffset(sceneIndex);
+ if (bgOffset == 0)
+ return;
+
+ uint32_t dataOffset = getSceneDataOffset(sceneIndex);
+ uint32_t stringsOffset = getSceneStringsOffset(sceneIndex);
+
+ fseek(resFile, bgOffset, SEEK_SET);
+ skipRLEImage(resFile); // background image
+
+ // Palette (768 bytes, 6-bit VGA)
+ uint8_t palette[768];
+ fread(palette, 1, 768, resFile);
+
+ // Shading table (256 bytes)
+ uint8_t shadingTable[256];
+ fread(shadingTable, 1, 256, resFile);
+
+ // 3 unknown bytes
+ uint8_t unknownBytes[3];
+ fread(unknownBytes, 1, 3, resFile);
+
+ // 4 RLE maps (each 320x200)
+ uint8_t depthMap[320 * 200];
+ uint8_t pathfindingMap[320 * 200];
+ uint8_t unknownMap[320 * 200];
+ uint8_t hotspotMap[320 * 200];
+ decodeRLEMap(resFile, depthMap);
+ decodeRLEMap(resFile, pathfindingMap);
+ decodeRLEMap(resFile, unknownMap);
+ decodeRLEMap(resFile, hotspotMap);
+
+ // Write map binary files
+ char path[512];
+ snprintf(path, sizeof(path), "%s/scene%03d_depth.bin", outDir, sceneIndex);
+ writeRawFile(path, depthMap, sizeof(depthMap));
+ snprintf(path, sizeof(path), "%s/scene%03d_pathfinding.bin", outDir, sceneIndex);
+ writeRawFile(path, pathfindingMap, sizeof(pathfindingMap));
+ snprintf(path, sizeof(path), "%s/scene%03d_unknown.bin", outDir, sceneIndex);
+ writeRawFile(path, unknownMap, sizeof(unknownMap));
+ snprintf(path, sizeof(path), "%s/scene%03d_hotspot.bin", outDir, sceneIndex);
+ writeRawFile(path, hotspotMap, sizeof(hotspotMap));
+
+ // Pathfinding nodes: 16 entries à 10 bytes
+ struct PathNode {
+ uint16_t x, y;
+ uint8_t adj[4];
+ uint16_t numConnections;
+ } nodes[16];
+ for (int i = 0; i < 16; i++) {
+ nodes[i].x = readU16(resFile);
+ nodes[i].y = readU16(resFile);
+ fread(nodes[i].adj, 1, 4, resFile);
+ nodes[i].numConnections = readU16(resFile);
+ }
+
+ uint16_t numHotspots = readU16(resFile);
+
+ // Hotspot color table: 16 uint16 entries (maps pixel value to object ID)
+ uint16_t hotspotColors[16];
+ for (int i = 0; i < 16; i++)
+ hotspotColors[i] = readU16(resFile);
+
+ // Resource offsets from data block (32 dwords = special anim file offsets)
+ uint32_t resourceOffsets[32] = {};
+ uint32_t mapImageOffset = 0;
+ if (dataOffset != 0) {
+ fseek(resFile, dataOffset, SEEK_SET);
+ for (int i = 0; i < 32; i++)
+ resourceOffsets[i] = readU32(resFile);
+ // Map image offset at data block +0x3C0
+ fseek(resFile, dataOffset + 0x3C0, SEEK_SET);
+ mapImageOffset = readU32(resFile);
+ }
+
+ // Script size
+ uint16_t scriptSize = 0;
+ if (dataOffset != 0) {
+ fseek(resFile, dataOffset + 0x80, SEEK_SET);
+ scriptSize = readU16(resFile);
+ }
+
+ // Strings size
+ uint16_t stringsSize = 0;
+ if (stringsOffset != 0) {
+ fseek(resFile, stringsOffset, SEEK_SET);
+ stringsSize = readU16(resFile);
+ }
+
+ // Write JSON
+ snprintf(path, sizeof(path), "%s/scenedata_%03d.json", outDir, sceneIndex);
+ FILE *out = fopen(path, "w");
+ if (!out)
+ return;
+
+ fprintf(out, "{\n");
+ fprintf(out, " \"scene\": %d,\n", sceneIndex);
+ fprintf(out, " \"offsets\": {\n");
+ fprintf(out, " \"background\": %u,\n", bgOffset);
+ fprintf(out, " \"data\": %u,\n", dataOffset);
+ fprintf(out, " \"strings\": %u,\n", stringsOffset);
+ fprintf(out, " \"mapImage\": %u\n", mapImageOffset);
+ fprintf(out, " },\n");
+ fprintf(out, " \"scriptSize\": %u,\n", scriptSize);
+ fprintf(out, " \"stringsSize\": %u,\n", stringsSize);
+
+ // Palette
+ fprintf(out, " \"palette\": [");
+ for (int i = 0; i < 256; i++) {
+ if (i > 0)
+ fprintf(out, ",");
+ if (i % 8 == 0)
+ fprintf(out, "\n ");
+ fprintf(out, "[%u,%u,%u]", palette[i * 3], palette[i * 3 + 1], palette[i * 3 + 2]);
+ }
+ fprintf(out, "\n ],\n");
+
+ // Shading table
+ fprintf(out, " \"shadingTable\": [");
+ for (int i = 0; i < 256; i++) {
+ if (i > 0)
+ fprintf(out, ",");
+ if (i % 32 == 0)
+ fprintf(out, "\n ");
+ fprintf(out, "%u", shadingTable[i]);
+ }
+ fprintf(out, "\n ],\n");
+
+ fprintf(out, " \"unknownBytes\": [%u, %u, %u],\n", unknownBytes[0], unknownBytes[1], unknownBytes[2]);
+
+ // Pathfinding nodes
+ fprintf(out, " \"pathfindingNodes\": [\n");
+ for (int i = 0; i < 16; i++) {
+ fprintf(out, " {\"index\": %d, \"x\": %u, \"y\": %u, \"adjacent\": [", i, nodes[i].x, nodes[i].y);
+ for (uint16_t j = 0; j < nodes[i].numConnections && j < 4; j++) {
+ if (j > 0)
+ fprintf(out, ", ");
+ fprintf(out, "%u", nodes[i].adj[j]);
+ }
+ fprintf(out, "]}%s\n", i < 15 ? "," : "");
+ }
+ fprintf(out, " ],\n");
+
+ fprintf(out, " \"numHotspots\": %u,\n", numHotspots);
+ fprintf(out, " \"hotspotColorTable\": [");
+ for (int i = 0; i < 16; i++) {
+ if (i > 0)
+ fprintf(out, ", ");
+ fprintf(out, "%u", hotspotColors[i]);
+ }
+ fprintf(out, "],\n");
+
+ // Resource offsets (special animation file offsets)
+ fprintf(out, " \"resourceOffsets\": [");
+ for (int i = 0; i < 32; i++) {
+ if (i > 0)
+ fprintf(out, ", ");
+ fprintf(out, "%u", resourceOffsets[i]);
+ }
+ fprintf(out, "],\n");
+
+ // Map file references
+ fprintf(out, " \"maps\": {\n");
+ fprintf(out, " \"depth\": \"scene%03d_depth.bin\",\n", sceneIndex);
+ fprintf(out, " \"pathfinding\": \"scene%03d_pathfinding.bin\",\n", sceneIndex);
+ fprintf(out, " \"unknown\": \"scene%03d_unknown.bin\",\n", sceneIndex);
+ fprintf(out, " \"hotspot\": \"scene%03d_hotspot.bin\"\n", sceneIndex);
+ fprintf(out, " }\n");
+ fprintf(out, "}\n");
+ fclose(out);
+ printf(" Wrote %s + 4 map files\n", path);
+}
+
static void printHelp(const char *bin) {
printf("MACS2 Resource Extractor\n\n");
printf("Usage: %s <mode> <game_data_file> <output_dir> [scene_index]\n\n", bin);
printf("Modes:\n");
- printf(" images - Extract background images as BMP files\n");
- printf(" sounds - Extract sound/music resource blobs\n");
- printf(" strings - Extract and decrypt text strings\n");
- printf(" all - Extract everything\n");
+ printf(" images - Extract background images as BMP files\n");
+ printf(" sounds - Extract sound/music resource blobs\n");
+ printf(" strings - Extract and decrypt text strings\n");
+ printf(" scenedata - Extract scene metadata as JSON (pathfinding, hotspots, walk params)\n");
+ printf(" all - Extract everything\n");
printf("\n");
printf("If scene_index is omitted, extracts from all scenes.\n");
}
@@ -284,6 +544,7 @@ int main(int argc, char **argv) {
bool doImages = !strcmp(mode, "images") || !strcmp(mode, "all");
bool doSounds = !strcmp(mode, "sounds") || !strcmp(mode, "all");
bool doStrings = !strcmp(mode, "strings") || !strcmp(mode, "all");
+ bool doSceneData = !strcmp(mode, "scenedata") || !strcmp(mode, "all");
for (int scene = startScene; scene <= endScene; scene++) {
bool hasData = false;
@@ -291,7 +552,10 @@ int main(int argc, char **argv) {
if (doImages) {
uint32_t bgOff = getSceneBgOffset(scene);
if (bgOff != 0) {
- if (!hasData) { printf("Scene %d:\n", scene); hasData = true; }
+ if (!hasData) {
+ printf("Scene %d:\n", scene);
+ hasData = true;
+ }
extractImage(scene, outDir);
}
}
@@ -299,7 +563,10 @@ int main(int argc, char **argv) {
if (doSounds) {
uint32_t dataOff = getSceneDataOffset(scene);
if (dataOff != 0) {
- if (!hasData) { printf("Scene %d:\n", scene); hasData = true; }
+ if (!hasData) {
+ printf("Scene %d:\n", scene);
+ hasData = true;
+ }
extractResources(scene, outDir, "res");
}
}
@@ -307,10 +574,24 @@ int main(int argc, char **argv) {
if (doStrings) {
uint32_t strOff = getSceneStringsOffset(scene);
if (strOff != 0) {
- if (!hasData) { printf("Scene %d:\n", scene); hasData = true; }
+ if (!hasData) {
+ printf("Scene %d:\n", scene);
+ hasData = true;
+ }
extractStrings(scene, outDir);
}
}
+
+ if (doSceneData) {
+ uint32_t bgOff = getSceneBgOffset(scene);
+ if (bgOff != 0) {
+ if (!hasData) {
+ printf("Scene %d:\n", scene);
+ hasData = true;
+ }
+ extractSceneData(scene, outDir);
+ }
+ }
}
fclose(resFile);
Commit: d06a5562375ce5d27ad6846df1310c566a215856
https://github.com/scummvm/scummvm-tools/commit/d06a5562375ce5d27ad6846df1310c566a215856
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-06-03T18:30:41+02:00
Commit Message:
MACS2: this is a shadowmap
Changed paths:
engines/macs2/extract_macs2.cpp
diff --git a/engines/macs2/extract_macs2.cpp b/engines/macs2/extract_macs2.cpp
index dd10941e..9a9b4a1b 100644
--- a/engines/macs2/extract_macs2.cpp
+++ b/engines/macs2/extract_macs2.cpp
@@ -177,7 +177,7 @@ static void extractImage(uint16_t sceneIndex, const char *outDir) {
fseek(resFile, 256, SEEK_CUR); // shading table
fseek(resFile, 3, SEEK_CUR); // 3 unknown bytes
- const char *mapNames[] = {"depth", "pathfinding", "unknown", "hotspot"};
+ const char *mapNames[] = {"depth", "pathfinding", "shadow", "hotspot"};
for (int m = 0; m < 4; m++) {
uint8_t mapPixels[320 * 200];
if (!decodeRLEMap(resFile, mapPixels))
@@ -357,11 +357,11 @@ static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
// 4 RLE maps (each 320x200)
uint8_t depthMap[320 * 200];
uint8_t pathfindingMap[320 * 200];
- uint8_t unknownMap[320 * 200];
+ uint8_t shadowMap[320 * 200];
uint8_t hotspotMap[320 * 200];
decodeRLEMap(resFile, depthMap);
decodeRLEMap(resFile, pathfindingMap);
- decodeRLEMap(resFile, unknownMap);
+ decodeRLEMap(resFile, shadowMap);
decodeRLEMap(resFile, hotspotMap);
// Write map binary files
@@ -370,8 +370,8 @@ static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
writeRawFile(path, depthMap, sizeof(depthMap));
snprintf(path, sizeof(path), "%s/scene%03d_pathfinding.bin", outDir, sceneIndex);
writeRawFile(path, pathfindingMap, sizeof(pathfindingMap));
- snprintf(path, sizeof(path), "%s/scene%03d_unknown.bin", outDir, sceneIndex);
- writeRawFile(path, unknownMap, sizeof(unknownMap));
+ snprintf(path, sizeof(path), "%s/scene%03d_shadow.bin", outDir, sceneIndex);
+ writeRawFile(path, shadowMap, sizeof(shadowMap));
snprintf(path, sizeof(path), "%s/scene%03d_hotspot.bin", outDir, sceneIndex);
writeRawFile(path, hotspotMap, sizeof(hotspotMap));
@@ -497,7 +497,7 @@ static void extractSceneData(uint16_t sceneIndex, const char *outDir) {
fprintf(out, " \"maps\": {\n");
fprintf(out, " \"depth\": \"scene%03d_depth.bin\",\n", sceneIndex);
fprintf(out, " \"pathfinding\": \"scene%03d_pathfinding.bin\",\n", sceneIndex);
- fprintf(out, " \"unknown\": \"scene%03d_unknown.bin\",\n", sceneIndex);
+ fprintf(out, " \"shadow\": \"scene%03d_shadow.bin\",\n", sceneIndex);
fprintf(out, " \"hotspot\": \"scene%03d_hotspot.bin\"\n", sceneIndex);
fprintf(out, " }\n");
fprintf(out, "}\n");
More information about the Scummvm-git-logs
mailing list