mirror of
https://github.com/libretro/scummvm.git
synced 2025-03-02 08:19:19 +00:00
SCI: Refactored the reg_t related operations and comparisons
This refactoring reduces a lot of code duplication, allows for better control, makes the code more readable and allows us to remove a lot of now unneeded workarounds
This commit is contained in:
parent
d7422de7f7
commit
4e88d75eb6
@ -163,18 +163,6 @@ static bool validate_variable(reg_t *r, reg_t *stack_base, int type, int max, in
|
||||
|
||||
extern const char *opcodeNames[]; // from scriptdebug.cpp
|
||||
|
||||
static reg_t arithmetic_lookForWorkaround(const byte opcode, const SciWorkaroundEntry *workaroundList, reg_t value1, reg_t value2) {
|
||||
SciTrackOriginReply originReply;
|
||||
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, workaroundList, &originReply);
|
||||
if (solution.type == WORKAROUND_NONE)
|
||||
error("%s on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)",
|
||||
opcodeNames[opcode], PRINT_REG(value1), PRINT_REG(value2), originReply.objectName.c_str(),
|
||||
originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(),
|
||||
originReply.localCallOffset);
|
||||
assert(solution.type == WORKAROUND_FAKE);
|
||||
return make_reg(0, solution.value);
|
||||
}
|
||||
|
||||
static reg_t validate_read_var(reg_t *r, reg_t *stack_base, int type, int max, int index, reg_t default_value) {
|
||||
if (validate_variable(r, stack_base, type, max, index)) {
|
||||
if (r[index].segment == 0xffff) {
|
||||
@ -486,7 +474,7 @@ ExecStack *send_selector(EngineState *s, reg_t send_obj, reg_t work_obj, StackPt
|
||||
if (argNr == 1)
|
||||
debugN(" - ");
|
||||
reg_t curParam = argp[argNr];
|
||||
if (curParam.segment) {
|
||||
if (curParam.isPointer()) {
|
||||
debugN("[%04x:%04x] ", PRINT_REG(curParam));
|
||||
displaySize += 12;
|
||||
} else {
|
||||
@ -597,32 +585,6 @@ static ExecStack *add_exec_stack_entry(Common::List<ExecStack> &execStack, reg_t
|
||||
return &(execStack.back());
|
||||
}
|
||||
|
||||
static reg_t pointer_add(EngineState *s, reg_t base, int offset) {
|
||||
SegmentObj *mobj = s->_segMan->getSegmentObj(base.segment);
|
||||
|
||||
if (!mobj) {
|
||||
error("[VM] Error: Attempt to add %d to invalid pointer %04x:%04x", offset, PRINT_REG(base));
|
||||
return NULL_REG;
|
||||
}
|
||||
|
||||
switch (mobj->getType()) {
|
||||
|
||||
case SEG_TYPE_LOCALS:
|
||||
case SEG_TYPE_SCRIPT:
|
||||
case SEG_TYPE_STACK:
|
||||
case SEG_TYPE_DYNMEM:
|
||||
base.offset += offset;
|
||||
return base;
|
||||
default:
|
||||
// FIXME: Changed this to warning, because iceman does this during dancing with girl.
|
||||
// Investigate why that is so and either fix the underlying issue or implement a more
|
||||
// specialized workaround!
|
||||
warning("[VM] Error: Attempt to add %d to pointer %04x:%04x, type %d: Pointer arithmetics of this type unsupported", offset, PRINT_REG(base), mobj->getType());
|
||||
return NULL_REG;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void addKernelCallToExecStack(EngineState *s, int kernelCallNr, int argc, reg_t *argv) {
|
||||
// Add stack frame to indicate we're executing a callk.
|
||||
// This is useful in debugger backtraces if this
|
||||
@ -683,7 +645,7 @@ static void logKernelCall(const KernelFunction *kernelCall, const KernelSubFunct
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result.segment)
|
||||
if (result.isPointer())
|
||||
debugN(" = %04x:%04x\n", PRINT_REG(result));
|
||||
else
|
||||
debugN(" = %d\n", result.offset);
|
||||
@ -738,15 +700,15 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
|
||||
} else {
|
||||
// Sub-functions available, check signature and call that one directly
|
||||
if (argc < 1)
|
||||
error("[VM] k%s[%x]: no subfunction-id parameter given", kernelCall.name, kernelCallNr);
|
||||
if (argv[0].segment)
|
||||
error("[VM] k%s[%x]: given subfunction-id is actually a pointer", kernelCall.name, kernelCallNr);
|
||||
error("[VM] k%s[%x]: no subfunction ID parameter given", kernelCall.name, kernelCallNr);
|
||||
if (argv[0].isPointer())
|
||||
error("[VM] k%s[%x]: given subfunction ID is actually a pointer", kernelCall.name, kernelCallNr);
|
||||
const uint16 subId = argv[0].toUint16();
|
||||
// Skip over subfunction-id
|
||||
argc--;
|
||||
argv++;
|
||||
if (subId >= kernelCall.subFunctionCount)
|
||||
error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
|
||||
error("[VM] k%s: subfunction ID %d requested, but not available", kernelCall.name, subId);
|
||||
const KernelSubFunction &kernelSubCall = kernelCall.subFunctions[subId];
|
||||
if (kernelSubCall.signature && !kernel->signatureMatch(kernelSubCall.signature, argc, argv)) {
|
||||
// Signature mismatch
|
||||
@ -779,7 +741,7 @@ static void callKernelFunc(EngineState *s, int kernelCallNr, int argc) {
|
||||
}
|
||||
}
|
||||
if (!kernelSubCall.function)
|
||||
error("[VM] k%s: subfunction-id %d requested, but not available", kernelCall.name, subId);
|
||||
error("[VM] k%s: subfunction ID %d requested, but not available", kernelCall.name, subId);
|
||||
addKernelCallToExecStack(s, kernelCallNr, argc, argv);
|
||||
s->r_acc = kernelSubCall.function(s, argc, argv);
|
||||
|
||||
@ -980,114 +942,38 @@ void run_vm(EngineState *s) {
|
||||
byte extOpcode;
|
||||
s->xs->addr.pc.offset += readPMachineInstruction(scr->getBuf() + s->xs->addr.pc.offset, extOpcode, opparams);
|
||||
const byte opcode = extOpcode >> 1;
|
||||
//debug("%s", opcodeNames[opcode]);
|
||||
|
||||
switch (opcode) {
|
||||
|
||||
case op_bnot: { // 0x00 (00)
|
||||
case op_bnot: // 0x00 (00)
|
||||
// Binary not
|
||||
int16 value = s->r_acc.toSint16();
|
||||
if (s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, 0xffff ^ value);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG);
|
||||
s->r_acc = make_reg(0, 0xffff ^ s->r_acc.requireUint16());
|
||||
break;
|
||||
}
|
||||
|
||||
case op_add: // 0x01 (01)
|
||||
r_temp = POP32();
|
||||
|
||||
// Happens in SQ1, room 28, when throwing the water at Orat
|
||||
if (s->r_acc.segment == 0xFFFF) {
|
||||
// WORKAROUND: init uninitialized variable to 0
|
||||
warning("op_add: attempt to write to uninitialized variable");
|
||||
s->r_acc = NULL_REG;
|
||||
}
|
||||
|
||||
if (r_temp.segment || s->r_acc.segment) {
|
||||
reg_t r_ptr = NULL_REG;
|
||||
int offset;
|
||||
// Pointer arithmetics!
|
||||
if (s->r_acc.segment) {
|
||||
if (r_temp.segment) {
|
||||
error("Attempt to add two pointers, stack=%04x:%04x and acc=%04x:%04x",
|
||||
PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
||||
offset = 0;
|
||||
} else {
|
||||
r_ptr = s->r_acc;
|
||||
offset = r_temp.offset;
|
||||
}
|
||||
} else {
|
||||
r_ptr = r_temp;
|
||||
offset = s->r_acc.offset;
|
||||
}
|
||||
|
||||
s->r_acc = pointer_add(s, r_ptr, offset);
|
||||
|
||||
} else
|
||||
s->r_acc = make_reg(0, r_temp.offset + s->r_acc.offset);
|
||||
s->r_acc = POP32() + s->r_acc;
|
||||
break;
|
||||
|
||||
case op_sub: // 0x02 (02)
|
||||
r_temp = POP32();
|
||||
if (r_temp.segment != s->r_acc.segment) {
|
||||
reg_t r_ptr = NULL_REG;
|
||||
int offset;
|
||||
// Pointer arithmetics!
|
||||
if (s->r_acc.segment) {
|
||||
if (r_temp.segment) {
|
||||
error("Attempt to subtract two pointers, stack=%04x:%04x and acc=%04x:%04x",
|
||||
PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
||||
offset = 0;
|
||||
} else {
|
||||
r_ptr = s->r_acc;
|
||||
offset = r_temp.offset;
|
||||
}
|
||||
} else {
|
||||
r_ptr = r_temp;
|
||||
offset = s->r_acc.offset;
|
||||
}
|
||||
|
||||
s->r_acc = pointer_add(s, r_ptr, -offset);
|
||||
|
||||
} else {
|
||||
// We can subtract numbers, or pointers with the same segment,
|
||||
// an operation which will yield a number like in C
|
||||
s->r_acc = make_reg(0, r_temp.offset - s->r_acc.offset);
|
||||
}
|
||||
s->r_acc = POP32() - s->r_acc;
|
||||
break;
|
||||
|
||||
case op_mul: { // 0x03 (03)
|
||||
r_temp = POP32();
|
||||
int16 value1 = s->r_acc.toSint16();
|
||||
int16 value2 = r_temp.toSint16();
|
||||
if (s->r_acc.isNumber() && r_temp.isNumber())
|
||||
s->r_acc = make_reg(0, value1 * value2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeMulWorkarounds, s->r_acc, r_temp);
|
||||
case op_mul: // 0x03 (03)
|
||||
s->r_acc = POP32() * s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_div: { // 0x04 (04)
|
||||
r_temp = POP32();
|
||||
int16 divisor = s->r_acc.toSint16();
|
||||
int16 dividend = r_temp.toSint16();
|
||||
if (s->r_acc.isNumber() && r_temp.isNumber())
|
||||
s->r_acc = make_reg(0, (divisor != 0 ? dividend / divisor : 0));
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDivWorkarounds, s->r_acc, r_temp);
|
||||
case op_div: // 0x04 (04)
|
||||
// we check for division by 0 inside the custom reg_t division operator
|
||||
s->r_acc = POP32() / s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_mod: { // 0x05 (05)
|
||||
r_temp = POP32();
|
||||
|
||||
if (getSciVersion() <= SCI_VERSION_0_LATE) {
|
||||
uint16 modulo = s->r_acc.toUint16();
|
||||
uint16 value = r_temp.toUint16();
|
||||
if (s->r_acc.isNumber() && r_temp.isNumber())
|
||||
s->r_acc = make_reg(0, (modulo != 0 ? value % modulo : 0));
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp);
|
||||
uint16 modulo = s->r_acc.requireUint16();
|
||||
uint16 value = POP32().requireUint16();
|
||||
uint16 result = (modulo != 0 ? value % modulo : 0);
|
||||
s->r_acc = make_reg(0, result);
|
||||
} else {
|
||||
// In Iceman (and perhaps from SCI0 0.000.685 onwards in general),
|
||||
// handling for negative numbers was added. Since Iceman doesn't
|
||||
@ -1095,86 +981,41 @@ void run_vm(EngineState *s) {
|
||||
// for simplicity's sake and use the new code for SCI01 and newer
|
||||
// games. Fixes the battlecruiser mini game in SQ5 (room 850),
|
||||
// bug #3035755
|
||||
int16 modulo = s->r_acc.toSint16();
|
||||
int16 value = r_temp.toSint16();
|
||||
int16 result;
|
||||
if (s->r_acc.isNumber() && r_temp.isNumber()) {
|
||||
modulo = ABS(modulo);
|
||||
result = (modulo != 0 ? value % modulo : 0);
|
||||
if (result < 0)
|
||||
result += modulo;
|
||||
s->r_acc = make_reg(0, result);
|
||||
} else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, r_temp);
|
||||
int16 modulo = ABS(s->r_acc.requireSint16());
|
||||
int16 value = POP32().requireSint16();
|
||||
int16 result = (modulo != 0 ? value % modulo : 0);
|
||||
if (result < 0)
|
||||
result += modulo;
|
||||
s->r_acc = make_reg(0, result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case op_shr: { // 0x06 (06)
|
||||
case op_shr: // 0x06 (06)
|
||||
// Shift right logical
|
||||
r_temp = POP32();
|
||||
uint16 value = r_temp.toUint16();
|
||||
uint16 shiftCount = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, value >> shiftCount);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
s->r_acc = POP32() >> s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_shl: { // 0x07 (07)
|
||||
case op_shl: // 0x07 (07)
|
||||
// Shift left logical
|
||||
r_temp = POP32();
|
||||
uint16 value = r_temp.toUint16();
|
||||
uint16 shiftCount = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, value << shiftCount);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
s->r_acc = POP32() << s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_xor: { // 0x08 (08)
|
||||
r_temp = POP32();
|
||||
uint16 value1 = r_temp.toUint16();
|
||||
uint16 value2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, value1 ^ value2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
case op_xor: // 0x08 (08)
|
||||
s->r_acc = POP32() ^ s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_and: { // 0x09 (09)
|
||||
r_temp = POP32();
|
||||
uint16 value1 = r_temp.toUint16();
|
||||
uint16 value2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, value1 & value2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeAndWorkarounds, r_temp, s->r_acc);
|
||||
case op_and: // 0x09 (09)
|
||||
s->r_acc = POP32() & s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_or: { // 0x0a (10)
|
||||
r_temp = POP32();
|
||||
uint16 value1 = r_temp.toUint16();
|
||||
uint16 value2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, value1 | value2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeOrWorkarounds, r_temp, s->r_acc);
|
||||
case op_or: // 0x0a (10)
|
||||
s->r_acc = POP32() | s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_neg: { // 0x0b (11)
|
||||
int16 value = s->r_acc.toSint16();
|
||||
if (s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, -value);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, s->r_acc, NULL_REG);
|
||||
case op_neg: // 0x0b (11)
|
||||
s->r_acc = make_reg(0, -s->r_acc.requireSint16());
|
||||
break;
|
||||
}
|
||||
|
||||
case op_not: // 0x0c (12)
|
||||
s->r_acc = make_reg(0, !(s->r_acc.offset || s->r_acc.segment));
|
||||
@ -1182,194 +1023,57 @@ void run_vm(EngineState *s) {
|
||||
break;
|
||||
|
||||
case op_eq_: // 0x0d (13)
|
||||
// ==
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
s->r_acc = make_reg(0, r_temp == s->r_acc);
|
||||
// Explicitly allow pointers to be compared
|
||||
s->r_acc = make_reg(0, POP32() == s->r_acc);
|
||||
break;
|
||||
|
||||
case op_ne_: // 0x0e (14)
|
||||
// !=
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
s->r_acc = make_reg(0, r_temp != s->r_acc);
|
||||
// Explicitly allow pointers to be compared
|
||||
s->r_acc = make_reg(0, POP32() != s->r_acc);
|
||||
break;
|
||||
|
||||
case op_gt_: // 0x0f (15)
|
||||
// >
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
if (r_temp.segment && s->r_acc.segment) {
|
||||
// Signed pointer comparison. We do unsigned comparison instead, as that is probably what was intended.
|
||||
if (r_temp.segment != s->r_acc.segment)
|
||||
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
|
||||
} else if (r_temp.segment && !s->r_acc.segment) {
|
||||
if (s->r_acc.offset >= 1000)
|
||||
error("[VM] op_gt: comparison between a pointer and number");
|
||||
// Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
|
||||
// Happens in SQ1, room 28, when throwing the water at Orat
|
||||
s->r_acc = make_reg(0, 1);
|
||||
} else {
|
||||
int16 compare1 = r_temp.toSint16();
|
||||
int16 compare2 = s->r_acc.toSint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 > compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32() > s->r_acc);
|
||||
break;
|
||||
|
||||
case op_ge_: // 0x10 (16)
|
||||
// >=
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
if (r_temp.segment && s->r_acc.segment) {
|
||||
if (r_temp.segment != s->r_acc.segment)
|
||||
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
|
||||
} else {
|
||||
int16 compare1 = r_temp.toSint16();
|
||||
int16 compare2 = s->r_acc.toSint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 >= compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeGeWorkarounds, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32() >= s->r_acc);
|
||||
break;
|
||||
|
||||
case op_lt_: // 0x11 (17)
|
||||
// <
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
if (r_temp.segment && s->r_acc.segment) {
|
||||
if (r_temp.segment != s->r_acc.segment)
|
||||
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
|
||||
} else if (r_temp.segment && !s->r_acc.segment) {
|
||||
if (s->r_acc.offset >= 1000)
|
||||
error("[VM] op_lt: comparison between a pointer and number");
|
||||
// Pseudo-WORKAROUND: Sierra allows any pointer <-> value comparison
|
||||
// Happens in SQ1, room 58, when giving id-card to robot
|
||||
s->r_acc = make_reg(0, 1);
|
||||
} else {
|
||||
int16 compare1 = r_temp.toSint16();
|
||||
int16 compare2 = s->r_acc.toSint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 < compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32() < s->r_acc);
|
||||
break;
|
||||
|
||||
case op_le_: // 0x12 (18)
|
||||
// <=
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
if (r_temp.segment && s->r_acc.segment) {
|
||||
if (r_temp.segment != s->r_acc.segment)
|
||||
warning("[VM] Comparing pointers in different segments (%04x:%04x vs. %04x:%04x)", PRINT_REG(r_temp), PRINT_REG(s->r_acc));
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
|
||||
} else {
|
||||
int16 compare1 = r_temp.toSint16();
|
||||
int16 compare2 = s->r_acc.toSint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 <= compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeLeWorkarounds, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32() <= s->r_acc);
|
||||
break;
|
||||
|
||||
case op_ugt_: // 0x13 (19)
|
||||
// > (unsigned)
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
|
||||
// SCI0/SCI1 scripts use this to check whether a
|
||||
// parameter is a pointer or a far text
|
||||
// reference. It is used e.g. by the standard library
|
||||
// Print function to distinguish two ways of calling it:
|
||||
//
|
||||
// (Print "foo") // Pointer to a string
|
||||
// (Print 420 5) // Reference to the fifth message in text resource 420
|
||||
|
||||
// It works because in those games, the maximum resource number is 999,
|
||||
// so any parameter value above that threshold must be a pointer.
|
||||
if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
|
||||
s->r_acc = make_reg(0, 1);
|
||||
else if (r_temp.segment && s->r_acc.segment)
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset > s->r_acc.offset);
|
||||
else {
|
||||
uint16 compare1 = r_temp.toUint16();
|
||||
uint16 compare2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 > compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32().gtU(s->r_acc));
|
||||
break;
|
||||
|
||||
case op_uge_: // 0x14 (20)
|
||||
// >= (unsigned)
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
|
||||
// See above
|
||||
if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
|
||||
s->r_acc = make_reg(0, 1);
|
||||
else if (r_temp.segment && s->r_acc.segment)
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset >= s->r_acc.offset);
|
||||
else {
|
||||
uint16 compare1 = r_temp.toUint16();
|
||||
uint16 compare2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 >= compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32().geU(s->r_acc));
|
||||
break;
|
||||
|
||||
case op_ult_: // 0x15 (21)
|
||||
// < (unsigned)
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
|
||||
// See above
|
||||
// PQ2 japanese compares pointers to 2000 to find out if its a pointer or a resourceid
|
||||
if (r_temp.segment && (s->r_acc == make_reg(0, 1000) || (s->r_acc == make_reg(0, 2000))))
|
||||
s->r_acc = NULL_REG;
|
||||
else if (r_temp.segment && s->r_acc.segment)
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset < s->r_acc.offset);
|
||||
else {
|
||||
uint16 compare1 = r_temp.toUint16();
|
||||
uint16 compare2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 < compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeUltWorkarounds, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32().ltU(s->r_acc));
|
||||
break;
|
||||
|
||||
case op_ule_: // 0x16 (22)
|
||||
// <= (unsigned)
|
||||
s->r_prev = s->r_acc;
|
||||
r_temp = POP32();
|
||||
|
||||
// See above
|
||||
if (r_temp.segment && (s->r_acc == make_reg(0, 1000)))
|
||||
s->r_acc = NULL_REG;
|
||||
else if (r_temp.segment && s->r_acc.segment)
|
||||
s->r_acc = make_reg(0, (r_temp.segment == s->r_acc.segment) && r_temp.offset <= s->r_acc.offset);
|
||||
else {
|
||||
uint16 compare1 = r_temp.toUint16();
|
||||
uint16 compare2 = s->r_acc.toUint16();
|
||||
if (r_temp.isNumber() && s->r_acc.isNumber())
|
||||
s->r_acc = make_reg(0, compare1 <= compare2);
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, r_temp, s->r_acc);
|
||||
}
|
||||
s->r_acc = make_reg(0, POP32().leU(s->r_acc));
|
||||
break;
|
||||
|
||||
case op_bt: // 0x17 (23)
|
||||
@ -1613,7 +1317,7 @@ void run_vm(EngineState *s) {
|
||||
// Send to any class
|
||||
r_temp = s->_segMan->getClassAddress(opparams[0], SCRIPT_GET_LOAD, s->xs->addr.pc);
|
||||
|
||||
if (!r_temp.segment)
|
||||
if (!r_temp.isPointer())
|
||||
error("[VM]: Invalid superclass in object");
|
||||
else {
|
||||
s_temp = s->xs->sp;
|
||||
@ -1696,87 +1400,29 @@ void run_vm(EngineState *s) {
|
||||
validate_property(s, obj, opparams[0]) = POP32();
|
||||
break;
|
||||
|
||||
case op_ipToa: { // 0x35 (53)
|
||||
// Increment Property and copy To Accumulator
|
||||
case op_ipToa: // 0x35 (53)
|
||||
case op_dpToa: // 0x36 (54)
|
||||
case op_ipTos: // 0x37 (55)
|
||||
case op_dpTos: // 0x38 (56)
|
||||
{
|
||||
// Increment/decrement a property and copy to accumulator,
|
||||
// or push to stack
|
||||
reg_t &opProperty = validate_property(s, obj, opparams[0]);
|
||||
uint16 valueProperty = opProperty.toUint16();
|
||||
if (opProperty.isNumber())
|
||||
s->r_acc = make_reg(0, valueProperty + 1);
|
||||
if (opcode & 1)
|
||||
opProperty += 1;
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG);
|
||||
opProperty = s->r_acc;
|
||||
break;
|
||||
}
|
||||
opProperty -= 1;
|
||||
|
||||
case op_dpToa: { // 0x36 (54)
|
||||
// Decrement Property and copy To Accumulator
|
||||
reg_t &opProperty = validate_property(s, obj, opparams[0]);
|
||||
uint16 valueProperty = opProperty.toUint16();
|
||||
if (opProperty.isNumber())
|
||||
s->r_acc = make_reg(0, valueProperty - 1);
|
||||
if (opcode == op_ipToa || opcode == op_dpToa)
|
||||
s->r_acc = opProperty;
|
||||
else
|
||||
s->r_acc = arithmetic_lookForWorkaround(opcode, opcodeDptoaWorkarounds, opProperty, NULL_REG);
|
||||
opProperty = s->r_acc;
|
||||
break;
|
||||
}
|
||||
|
||||
case op_ipTos: { // 0x37 (55)
|
||||
// Increment Property and push to Stack
|
||||
reg_t &opProperty = validate_property(s, obj, opparams[0]);
|
||||
uint16 valueProperty = opProperty.toUint16();
|
||||
if (opProperty.isNumber())
|
||||
valueProperty++;
|
||||
else
|
||||
valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset;
|
||||
opProperty = make_reg(0, valueProperty);
|
||||
PUSH(valueProperty);
|
||||
break;
|
||||
}
|
||||
|
||||
case op_dpTos: { // 0x38 (56)
|
||||
// Decrement Property and push to Stack
|
||||
reg_t &opProperty = validate_property(s, obj, opparams[0]);
|
||||
uint16 valueProperty = opProperty.toUint16();
|
||||
if (opProperty.isNumber())
|
||||
valueProperty--;
|
||||
else
|
||||
valueProperty = arithmetic_lookForWorkaround(opcode, NULL, opProperty, NULL_REG).offset;
|
||||
opProperty = make_reg(0, valueProperty);
|
||||
PUSH(valueProperty);
|
||||
PUSH32(opProperty);
|
||||
break;
|
||||
}
|
||||
|
||||
case op_lofsa: // 0x39 (57)
|
||||
// Load Offset to Accumulator
|
||||
s->r_acc.segment = s->xs->addr.pc.segment;
|
||||
|
||||
switch (g_sci->_features->detectLofsType()) {
|
||||
case SCI_VERSION_0_EARLY:
|
||||
s->r_acc.offset = s->xs->addr.pc.offset + opparams[0];
|
||||
break;
|
||||
case SCI_VERSION_1_MIDDLE:
|
||||
s->r_acc.offset = opparams[0];
|
||||
break;
|
||||
case SCI_VERSION_1_1:
|
||||
s->r_acc.offset = opparams[0] + local_script->getScriptSize();
|
||||
break;
|
||||
case SCI_VERSION_3:
|
||||
// In theory this can break if the variant with a one-byte argument is
|
||||
// used. For now, assume it doesn't happen.
|
||||
s->r_acc.offset = local_script->relocateOffsetSci3(s->xs->addr.pc.offset-2);
|
||||
break;
|
||||
default:
|
||||
error("Unknown lofs type");
|
||||
}
|
||||
|
||||
if (s->r_acc.offset >= scr->getBufSize()) {
|
||||
error("VM: lofsa operation overflowed: %04x:%04x beyond end"
|
||||
" of script (at %04x)", PRINT_REG(s->r_acc), scr->getBufSize());
|
||||
}
|
||||
break;
|
||||
|
||||
case op_lofss: // 0x3a (58)
|
||||
// Load Offset to Stack
|
||||
// Load offset to accumulator or push to stack
|
||||
r_temp.segment = s->xs->addr.pc.segment;
|
||||
|
||||
switch (g_sci->_features->detectLofsType()) {
|
||||
@ -1790,17 +1436,22 @@ void run_vm(EngineState *s) {
|
||||
r_temp.offset = opparams[0] + local_script->getScriptSize();
|
||||
break;
|
||||
case SCI_VERSION_3:
|
||||
r_temp.offset = opparams[0];
|
||||
// In theory this can break if the variant with a one-byte argument is
|
||||
// used. For now, assume it doesn't happen.
|
||||
r_temp.offset = local_script->relocateOffsetSci3(s->xs->addr.pc.offset-2);
|
||||
break;
|
||||
default:
|
||||
error("Unknown lofs type");
|
||||
}
|
||||
|
||||
if (r_temp.offset >= scr->getBufSize()) {
|
||||
error("VM: lofss operation overflowed: %04x:%04x beyond end"
|
||||
if (r_temp.offset >= scr->getBufSize())
|
||||
error("VM: lofsa/lofss operation overflowed: %04x:%04x beyond end"
|
||||
" of script (at %04x)", PRINT_REG(r_temp), scr->getBufSize());
|
||||
}
|
||||
PUSH32(r_temp);
|
||||
|
||||
if (opcode == op_lofsa)
|
||||
s->r_acc = r_temp;
|
||||
else
|
||||
PUSH32(r_temp);
|
||||
break;
|
||||
|
||||
case op_push0: // 0x3b (59)
|
||||
@ -1838,8 +1489,14 @@ void run_vm(EngineState *s) {
|
||||
case op_lat: // 0x42 (66)
|
||||
case op_lap: // 0x43 (67)
|
||||
// Load global, local, temp or param variable into the accumulator
|
||||
case op_lagi: // 0x48 (72)
|
||||
case op_lali: // 0x49 (73)
|
||||
case op_lati: // 0x4a (74)
|
||||
case op_lapi: // 0x4b (75)
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
var_number = opparams[0] + (opcode >= op_lagi ? s->r_acc.requireSint16() : 0);
|
||||
s->r_acc = READ_VAR(var_type, var_number);
|
||||
break;
|
||||
|
||||
@ -1848,48 +1505,32 @@ void run_vm(EngineState *s) {
|
||||
case op_lst: // 0x46 (70)
|
||||
case op_lsp: // 0x47 (71)
|
||||
// Load global, local, temp or param variable into the stack
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
PUSH32(READ_VAR(var_type, var_number));
|
||||
break;
|
||||
|
||||
case op_lagi: // 0x48 (72)
|
||||
case op_lali: // 0x49 (73)
|
||||
case op_lati: // 0x4a (74)
|
||||
case op_lapi: { // 0x4b (75)
|
||||
// Load global, local, temp or param variable into the accumulator,
|
||||
// using the accumulator as an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
int16 value = s->r_acc.toSint16();
|
||||
if (!s->r_acc.isNumber())
|
||||
value = arithmetic_lookForWorkaround(opcode, opcodeLaiWorkarounds, s->r_acc, NULL_REG).offset;
|
||||
var_number = opparams[0] + value;
|
||||
s->r_acc = READ_VAR(var_type, var_number);
|
||||
break;
|
||||
}
|
||||
|
||||
case op_lsgi: // 0x4c (76)
|
||||
case op_lsli: // 0x4d (77)
|
||||
case op_lsti: // 0x4e (78)
|
||||
case op_lspi: { // 0x4f (79)
|
||||
// Load global, local, temp or param variable into the stack,
|
||||
// using the accumulator as an additional index
|
||||
case op_lspi: // 0x4f (79)
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
int16 value = s->r_acc.toSint16();
|
||||
if (!s->r_acc.isNumber())
|
||||
value = arithmetic_lookForWorkaround(opcode, opcodeLsiWorkarounds, s->r_acc, NULL_REG).offset;
|
||||
var_number = opparams[0] + value;
|
||||
var_number = opparams[0] + (opcode >= op_lsgi ? s->r_acc.requireSint16() : 0);
|
||||
PUSH32(READ_VAR(var_type, var_number));
|
||||
break;
|
||||
}
|
||||
|
||||
case op_sag: // 0x50 (80)
|
||||
case op_sal: // 0x51 (81)
|
||||
case op_sat: // 0x52 (82)
|
||||
case op_sap: // 0x53 (83)
|
||||
// Save the accumulator into the global, local, temp or param variable
|
||||
case op_sagi: // 0x58 (88)
|
||||
case op_sali: // 0x59 (89)
|
||||
case op_sati: // 0x5a (90)
|
||||
case op_sapi: // 0x5b (91)
|
||||
// Save the accumulator into the global, local, temp or param variable,
|
||||
// using the accumulator as an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
var_number = opparams[0] + (opcode >= op_sagi ? s->r_acc.requireSint16() : 0);
|
||||
if (opcode >= op_sagi) // load the actual value to store in the accumulator
|
||||
s->r_acc = POP32();
|
||||
WRITE_VAR(var_type, var_number, s->r_acc);
|
||||
break;
|
||||
|
||||
@ -1898,35 +1539,14 @@ void run_vm(EngineState *s) {
|
||||
case op_sst: // 0x56 (86)
|
||||
case op_ssp: // 0x57 (87)
|
||||
// Save the stack into the global, local, temp or param variable
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
WRITE_VAR(var_type, var_number, POP32());
|
||||
break;
|
||||
|
||||
case op_sagi: // 0x58 (88)
|
||||
case op_sali: // 0x59 (89)
|
||||
case op_sati: // 0x5a (90)
|
||||
case op_sapi: // 0x5b (91)
|
||||
// Save the accumulator into the global, local, temp or param variable,
|
||||
// using the accumulator as an additional index
|
||||
|
||||
// Special semantics because it wouldn't really make a whole lot
|
||||
// of sense otherwise, with acc being used for two things
|
||||
// simultaneously...
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0] + s->r_acc.requireSint16();
|
||||
s->r_acc = POP32();
|
||||
WRITE_VAR(var_type, var_number, s->r_acc);
|
||||
break;
|
||||
|
||||
case op_ssgi: // 0x5c (92)
|
||||
case op_ssli: // 0x5d (93)
|
||||
case op_ssti: // 0x5e (94)
|
||||
case op_sspi: // 0x5f (95)
|
||||
// Save the stack into the global, local, temp or param variable,
|
||||
// using the accumulator as an additional index
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0] + s->r_acc.requireSint16();
|
||||
var_number = opparams[0] + (opcode >= op_ssgi ? s->r_acc.requireSint16() : 0);
|
||||
WRITE_VAR(var_type, var_number, POP32());
|
||||
break;
|
||||
|
||||
@ -1936,14 +1556,15 @@ void run_vm(EngineState *s) {
|
||||
case op_plusap: // 0x63 (99)
|
||||
// Increment the global, local, temp or param variable and save it
|
||||
// to the accumulator
|
||||
case op_plusagi: // 0x68 (104)
|
||||
case op_plusali: // 0x69 (105)
|
||||
case op_plusati: // 0x6a (106)
|
||||
case op_plusapi: // 0x6b (107)
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
s->r_acc = pointer_add(s, r_temp, 1);
|
||||
} else
|
||||
s->r_acc = make_reg(0, r_temp.offset + 1);
|
||||
var_number = opparams[0] + (opcode >= op_plusagi ? s->r_acc.requireSint16() : 0);
|
||||
s->r_acc = READ_VAR(var_type, var_number) + 1;
|
||||
WRITE_VAR(var_type, var_number, s->r_acc);
|
||||
break;
|
||||
|
||||
@ -1953,49 +1574,15 @@ void run_vm(EngineState *s) {
|
||||
case op_plussp: // 0x67 (103)
|
||||
// Increment the global, local, temp or param variable and save it
|
||||
// to the stack
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
r_temp = pointer_add(s, r_temp, 1);
|
||||
} else
|
||||
r_temp = make_reg(0, r_temp.offset + 1);
|
||||
PUSH32(r_temp);
|
||||
WRITE_VAR(var_type, var_number, r_temp);
|
||||
break;
|
||||
|
||||
case op_plusagi: // 0x68 (104)
|
||||
case op_plusali: // 0x69 (105)
|
||||
case op_plusati: // 0x6a (106)
|
||||
case op_plusapi: // 0x6b (107)
|
||||
// Increment the global, local, temp or param variable and save it
|
||||
// to the accumulator, using the accumulator as an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0] + s->r_acc.requireSint16();
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
s->r_acc = pointer_add(s, r_temp, 1);
|
||||
} else
|
||||
s->r_acc = make_reg(0, r_temp.offset + 1);
|
||||
WRITE_VAR(var_type, var_number, s->r_acc);
|
||||
break;
|
||||
|
||||
case op_plussgi: // 0x6c (108)
|
||||
case op_plussli: // 0x6d (109)
|
||||
case op_plussti: // 0x6e (110)
|
||||
case op_plusspi: // 0x6f (111)
|
||||
// Increment the global, local, temp or param variable and save it
|
||||
// to the stack, using the accumulator as an additional index
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0] + s->r_acc.requireSint16();
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
r_temp = pointer_add(s, r_temp, 1);
|
||||
} else
|
||||
r_temp = make_reg(0, r_temp.offset + 1);
|
||||
var_number = opparams[0] + (opcode >= op_plussgi ? s->r_acc.requireSint16() : 0);
|
||||
r_temp = READ_VAR(var_type, var_number) + 1;
|
||||
PUSH32(r_temp);
|
||||
WRITE_VAR(var_type, var_number, r_temp);
|
||||
break;
|
||||
@ -2006,14 +1593,15 @@ void run_vm(EngineState *s) {
|
||||
case op_minusap: // 0x73 (115)
|
||||
// Decrement the global, local, temp or param variable and save it
|
||||
// to the accumulator
|
||||
case op_minusagi: // 0x78 (120)
|
||||
case op_minusali: // 0x79 (121)
|
||||
case op_minusati: // 0x7a (122)
|
||||
case op_minusapi: // 0x7b (123)
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
s->r_acc = pointer_add(s, r_temp, -1);
|
||||
} else
|
||||
s->r_acc = make_reg(0, r_temp.offset - 1);
|
||||
var_number = opparams[0] + (opcode >= op_minusagi ? s->r_acc.requireSint16() : 0);
|
||||
s->r_acc = READ_VAR(var_type, var_number) - 1;
|
||||
WRITE_VAR(var_type, var_number, s->r_acc);
|
||||
break;
|
||||
|
||||
@ -2023,49 +1611,15 @@ void run_vm(EngineState *s) {
|
||||
case op_minussp: // 0x77 (119)
|
||||
// Decrement the global, local, temp or param variable and save it
|
||||
// to the stack
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0];
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
r_temp = pointer_add(s, r_temp, -1);
|
||||
} else
|
||||
r_temp = make_reg(0, r_temp.offset - 1);
|
||||
PUSH32(r_temp);
|
||||
WRITE_VAR(var_type, var_number, r_temp);
|
||||
break;
|
||||
|
||||
case op_minusagi: // 0x78 (120)
|
||||
case op_minusali: // 0x79 (121)
|
||||
case op_minusati: // 0x7a (122)
|
||||
case op_minusapi: // 0x7b (123)
|
||||
// Decrement the global, local, temp or param variable and save it
|
||||
// to the accumulator, using the accumulator as an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0] + s->r_acc.requireSint16();
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
s->r_acc = pointer_add(s, r_temp, -1);
|
||||
} else
|
||||
s->r_acc = make_reg(0, r_temp.offset - 1);
|
||||
WRITE_VAR(var_type, var_number, s->r_acc);
|
||||
break;
|
||||
|
||||
case op_minussgi: // 0x7c (124)
|
||||
case op_minussli: // 0x7d (125)
|
||||
case op_minussti: // 0x7e (126)
|
||||
case op_minusspi: // 0x7f (127)
|
||||
// Decrement the global, local, temp or param variable and save it
|
||||
// to the stack, using the accumulator as an additional index
|
||||
// Same as the 4 ones above, except that the accumulator is used as
|
||||
// an additional index
|
||||
var_type = opcode & 0x3; // Gets the variable type: g, l, t or p
|
||||
var_number = opparams[0] + s->r_acc.requireSint16();
|
||||
r_temp = READ_VAR(var_type, var_number);
|
||||
if (r_temp.segment) {
|
||||
// Pointer arithmetics!
|
||||
r_temp = pointer_add(s, r_temp, -1);
|
||||
} else
|
||||
r_temp = make_reg(0, r_temp.offset - 1);
|
||||
var_number = opparams[0] + (opcode >= op_minussgi ? s->r_acc.requireSint16() : 0);
|
||||
r_temp = READ_VAR(var_type, var_number) - 1;
|
||||
PUSH32(r_temp);
|
||||
WRITE_VAR(var_type, var_number, r_temp);
|
||||
break;
|
||||
|
247
engines/sci/engine/vm_types.cpp
Normal file
247
engines/sci/engine/vm_types.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $URL$
|
||||
* $Id$
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sci/sci.h"
|
||||
#include "sci/engine/state.h"
|
||||
#include "sci/engine/seg_manager.h"
|
||||
#include "sci/engine/vm_types.h"
|
||||
#include "sci/engine/workarounds.h"
|
||||
|
||||
namespace Sci {
|
||||
|
||||
extern const char *opcodeNames[]; // from scriptdebug.cpp
|
||||
|
||||
reg_t reg_t::lookForWorkaround(const reg_t right) const {
|
||||
SciTrackOriginReply originReply;
|
||||
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, arithmeticWorkarounds, &originReply);
|
||||
if (solution.type == WORKAROUND_NONE)
|
||||
error("arithmetic operation on non-integer (%04x:%04x, %04x:%04x) from method %s::%s (script %d, room %d, localCall %x)",
|
||||
PRINT_REG(*this), PRINT_REG(right), originReply.objectName.c_str(),
|
||||
originReply.methodName.c_str(), originReply.scriptNr, g_sci->getEngineState()->currentRoomNumber(),
|
||||
originReply.localCallOffset);
|
||||
assert(solution.type == WORKAROUND_FAKE);
|
||||
return make_reg(0, solution.value);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator+(const reg_t right) const {
|
||||
if (isPointer() && isInitialized()) {
|
||||
// Pointer arithmetics. Only some pointer types make sense here
|
||||
SegmentObj *mobj = g_sci->getEngineState()->_segMan->getSegmentObj(segment);
|
||||
|
||||
if (!mobj)
|
||||
error("[VM]: Attempt to add %d to invalid pointer %04x:%04x", right.offset, PRINT_REG(*this));
|
||||
|
||||
switch (mobj->getType()) {
|
||||
case SEG_TYPE_LOCALS:
|
||||
case SEG_TYPE_SCRIPT:
|
||||
case SEG_TYPE_STACK:
|
||||
case SEG_TYPE_DYNMEM:
|
||||
// Make sure that we are adding an offset to the pointer
|
||||
if (right.isPointer())
|
||||
return lookForWorkaround(right);
|
||||
return make_reg(segment, offset + right.toSint16());
|
||||
default:
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
} else if (isNumber() && isInitialized() && right.isPointer()) {
|
||||
// Adding a pointer to a number, flip the order
|
||||
return right + *this;
|
||||
} else {
|
||||
// Normal arithmetics. Make sure we're adding a number
|
||||
if (right.isPointer())
|
||||
return lookForWorkaround(right);
|
||||
// If the current variable is uninitialized, it'll be set
|
||||
// to zero in order to perform the operation. Such a case
|
||||
// happens in SQ1, room 28, when throwing the water at Orat.
|
||||
if (!isInitialized())
|
||||
return make_reg(0, right.toSint16());
|
||||
else if (!right.isInitialized())
|
||||
return *this;
|
||||
else
|
||||
return make_reg(0, toSint16() + right.toSint16());
|
||||
}
|
||||
}
|
||||
|
||||
reg_t reg_t::operator-(const reg_t right) const {
|
||||
if (segment == right.segment) {
|
||||
// We can subtract numbers, or pointers with the same segment,
|
||||
// an operation which will yield a number like in C
|
||||
return make_reg(0, toSint16() - right.toSint16());
|
||||
} else {
|
||||
return *this + make_reg(right.segment, -right.offset);
|
||||
}
|
||||
}
|
||||
|
||||
reg_t reg_t::operator*(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return make_reg(0, toSint16() * right.toSint16());
|
||||
else if (!isInitialized() || !right.isInitialized())
|
||||
return NULL_REG; // unitialized variables - always return 0
|
||||
else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator/(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber()) {
|
||||
if (right.isNull())
|
||||
return NULL_REG; // division by zero
|
||||
else
|
||||
return make_reg(0, toSint16() / right.toSint16());
|
||||
} else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator>>(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return make_reg(0, toUint16() >> right.toUint16());
|
||||
else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator<<(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return make_reg(0, toUint16() << right.toUint16());
|
||||
else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator+(int16 right) const {
|
||||
return *this + make_reg(0, right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator-(int16 right) const {
|
||||
return *this - make_reg(0, right);
|
||||
}
|
||||
|
||||
uint16 reg_t::requireUint16() const {
|
||||
if (isNumber())
|
||||
return toUint16();
|
||||
else
|
||||
return lookForWorkaround(NULL_REG).toUint16();
|
||||
}
|
||||
|
||||
int16 reg_t::requireSint16() const {
|
||||
if (isNumber())
|
||||
return toSint16();
|
||||
else
|
||||
return lookForWorkaround(NULL_REG).toSint16();
|
||||
}
|
||||
|
||||
reg_t reg_t::operator&(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return make_reg(0, toUint16() & right.toUint16());
|
||||
else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator|(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return make_reg(0, toUint16() | right.toUint16());
|
||||
else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
reg_t reg_t::operator^(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return make_reg(0, toUint16() ^ right.toUint16());
|
||||
else
|
||||
return lookForWorkaround(right);
|
||||
}
|
||||
|
||||
bool reg_t::operator>(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return toSint16() > right.toSint16();
|
||||
else if (isPointer() && segment == right.segment)
|
||||
return toUint16() > right.toUint16(); // pointer comparison
|
||||
else if (pointerComparisonWithInteger(right))
|
||||
return true;
|
||||
else if (right.pointerComparisonWithInteger(*this))
|
||||
return false;
|
||||
else
|
||||
return lookForWorkaround(right).toSint16();
|
||||
}
|
||||
|
||||
bool reg_t::operator<(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return toSint16() < right.toSint16();
|
||||
else if (isPointer() && segment == right.segment)
|
||||
return toUint16() < right.toUint16(); // pointer comparison
|
||||
else if (pointerComparisonWithInteger(right))
|
||||
return false;
|
||||
else if (right.pointerComparisonWithInteger(*this))
|
||||
return true;
|
||||
else
|
||||
return lookForWorkaround(right).toSint16();
|
||||
}
|
||||
|
||||
bool reg_t::gtU(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return toUint16() > right.toUint16();
|
||||
else if (isPointer() && segment == right.segment)
|
||||
return toUint16() > right.toUint16(); // pointer comparison
|
||||
else if (pointerComparisonWithInteger(right))
|
||||
return true;
|
||||
else if (right.pointerComparisonWithInteger(*this))
|
||||
return false;
|
||||
else
|
||||
return lookForWorkaround(right).toSint16();
|
||||
}
|
||||
|
||||
bool reg_t::ltU(const reg_t right) const {
|
||||
if (isNumber() && right.isNumber())
|
||||
return toUint16() < right.toUint16();
|
||||
else if (isPointer() && segment == right.segment)
|
||||
return toUint16() < right.toUint16(); // pointer comparison
|
||||
else if (pointerComparisonWithInteger(right))
|
||||
return false;
|
||||
else if (right.pointerComparisonWithInteger(*this))
|
||||
return true;
|
||||
else
|
||||
return lookForWorkaround(right).toSint16();
|
||||
}
|
||||
|
||||
bool reg_t::pointerComparisonWithInteger(const reg_t right) const {
|
||||
// SCI0/SCI1 scripts use this to check whether a
|
||||
// parameter is a pointer or a far text
|
||||
// reference. It is used e.g. by the standard library
|
||||
// Print function to distinguish two ways of calling it:
|
||||
//
|
||||
// (Print "foo") // Pointer to a string
|
||||
// (Print 420 5) // Reference to the fifth message in text resource 420
|
||||
|
||||
// It works because in those games, the maximum resource number is 999,
|
||||
// so any parameter value above that threshold must be a pointer.
|
||||
// PQ2 japanese compares pointers to 2000 to find out if its a pointer
|
||||
// or a resource ID.
|
||||
// There are cases where game scripts check for arbitrary numbers against
|
||||
// pointers, e.g.:
|
||||
// Hoyle 3, Pachisi, when any opponent is about to talk
|
||||
// SQ1, room 28, when throwing water at the Orat
|
||||
// SQ1, room 58, when giving the ID card to the robot
|
||||
// Thus we check for all integers <= 2000
|
||||
return (isPointer() && right.isNumber() && right.offset <= 2000 && getSciVersion() <= SCI_VERSION_1_LATE);
|
||||
}
|
||||
|
||||
} // End of namespace Sci
|
@ -41,14 +41,6 @@ struct reg_t {
|
||||
return !(offset || segment);
|
||||
}
|
||||
|
||||
bool operator==(const reg_t &x) const {
|
||||
return (offset == x.offset) && (segment == x.segment);
|
||||
}
|
||||
|
||||
bool operator!=(const reg_t &x) const {
|
||||
return (offset != x.offset) || (segment != x.segment);
|
||||
}
|
||||
|
||||
uint16 toUint16() const {
|
||||
return offset;
|
||||
}
|
||||
@ -57,31 +49,82 @@ struct reg_t {
|
||||
return (int16) offset;
|
||||
}
|
||||
|
||||
uint16 requireUint16() const {
|
||||
if (isNumber())
|
||||
return toUint16();
|
||||
else
|
||||
// The results of this are likely unpredictable... It most likely
|
||||
// means that a kernel function is returning something wrong. If
|
||||
// such an error occurs, we usually need to find the last kernel
|
||||
// function called and check its return value.
|
||||
error("[VM] Attempt to read unsigned arithmetic value from non-zero segment %04x. Offset: %04x", segment, offset);
|
||||
}
|
||||
|
||||
int16 requireSint16() const {
|
||||
if (isNumber())
|
||||
return toSint16();
|
||||
else
|
||||
// The results of this are likely unpredictable... It most likely
|
||||
// means that a kernel function is returning something wrong. If
|
||||
// such an error occurs, we usually need to find the last kernel
|
||||
// function called and check its return value.
|
||||
error("[VM] Attempt to read signed arithmetic value from non-zero segment %04x. Offset: %04x", segment, offset);
|
||||
}
|
||||
|
||||
bool isNumber() const {
|
||||
return !segment;
|
||||
}
|
||||
|
||||
bool isPointer() const {
|
||||
return segment != 0 && segment != 0xFFFF;
|
||||
}
|
||||
|
||||
uint16 requireUint16() const;
|
||||
int16 requireSint16() const;
|
||||
|
||||
bool isInitialized() const {
|
||||
return segment != 0xFFFF;
|
||||
}
|
||||
|
||||
// Comparison operators
|
||||
bool operator==(const reg_t &x) const {
|
||||
return (offset == x.offset) && (segment == x.segment);
|
||||
}
|
||||
|
||||
bool operator!=(const reg_t &x) const {
|
||||
return (offset != x.offset) || (segment != x.segment);
|
||||
}
|
||||
|
||||
bool operator>(const reg_t right) const;
|
||||
bool operator>=(const reg_t right) const {
|
||||
if (*this == right)
|
||||
return true;
|
||||
return *this > right;
|
||||
}
|
||||
bool operator<(const reg_t right) const;
|
||||
bool operator<=(const reg_t right) const {
|
||||
if (*this == right)
|
||||
return true;
|
||||
return *this < right;
|
||||
}
|
||||
|
||||
// Same as the normal operators, but perform unsigned
|
||||
// integer checking
|
||||
bool gtU(const reg_t right) const;
|
||||
bool geU(const reg_t right) const {
|
||||
if (*this == right)
|
||||
return true;
|
||||
return gtU(right);
|
||||
}
|
||||
bool ltU(const reg_t right) const;
|
||||
bool leU(const reg_t right) const {
|
||||
if (*this == right)
|
||||
return true;
|
||||
return ltU(right);
|
||||
}
|
||||
|
||||
bool pointerComparisonWithInteger(const reg_t right) const;
|
||||
|
||||
// Arithmetic operators
|
||||
reg_t operator+(const reg_t right) const;
|
||||
reg_t operator-(const reg_t right) const;
|
||||
reg_t operator*(const reg_t right) const;
|
||||
reg_t operator/(const reg_t right) const;
|
||||
reg_t operator>>(const reg_t right) const;
|
||||
reg_t operator<<(const reg_t right) const;
|
||||
|
||||
reg_t operator+(int16 right) const;
|
||||
reg_t operator-(int16 right) const;
|
||||
|
||||
void operator+=(const reg_t &right) { *this = *this + right; }
|
||||
void operator-=(const reg_t &right) { *this = *this - right; }
|
||||
void operator+=(int16 right) { *this = *this + right; }
|
||||
void operator-=(int16 right) { *this = *this - right; }
|
||||
|
||||
// Boolean operators
|
||||
reg_t operator&(const reg_t right) const;
|
||||
reg_t operator|(const reg_t right) const;
|
||||
reg_t operator^(const reg_t right) const;
|
||||
|
||||
reg_t lookForWorkaround(const reg_t right) const;
|
||||
};
|
||||
|
||||
static inline reg_t make_reg(SegmentId segment, uint16 offset) {
|
||||
|
@ -34,69 +34,17 @@
|
||||
namespace Sci {
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeDivWorkarounds[] = {
|
||||
{ GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // when entering inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call, index, workaround
|
||||
const SciWorkaroundEntry opcodeDptoaWorkarounds[] = {
|
||||
{ GID_LSL6, 360, 938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease
|
||||
{ GID_LSL6HIRES, 360,64938, 0, "ROsc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when looking through tile in the shower room initial cycles get set to an object instead of 2, we fix this by setting 1 after decrease
|
||||
{ GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // when going back to bridge the crew is goofing off, we get an object as cycle count
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeGeWorkarounds[] = {
|
||||
{ GID_HOYLE1, 5, 213, 0, "", "export 0", -1, 0, { WORKAROUND_FAKE, 1 } }, // happens sometimes during cribbage - bug #3038433
|
||||
{ GID_MOTHERGOOSE256, 4, 998, 0, "door", "setCel", -1, 0, { WORKAROUND_FAKE, 1 } }, // after giving the king his pipe back, listening to his song and leaving the castle - bug #3051475
|
||||
{ GID_PQ3, 31, 31, 0, "rm031", "init", -1, 0, { WORKAROUND_FAKE, 1 } }, // pq3 english: when exiting the car, while morales is making phonecalls - bug #3037565
|
||||
{ GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue.
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeLeWorkarounds[] = {
|
||||
{ GID_PEPPER, 370, 23, 0, "eastExitFeature", "onMe", -1, 0, { WORKAROUND_FAKE, 1 } }, // Pugh's office, when trying to use either the left or right exits, gets called on an integer and a pointer - bug #3040142
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeUltWorkarounds[] = {
|
||||
{ GID_HOYLE3, 400, 0, 1, "Character", "say", -1, 0, { WORKAROUND_FAKE, 0 } }, // While playing Pachisi, when any character starts to talk - bug #3038837
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeLaiWorkarounds[] = {
|
||||
{ GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", 0x20d, 0, { WORKAROUND_FAKE, 0 } }, // during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #3044734
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeLsiWorkarounds[] = {
|
||||
{ GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // when getting asked for your name by the astrologer bug #3039879
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeMulWorkarounds[] = {
|
||||
{ GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeAndWorkarounds[] = {
|
||||
{ GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // constantly during the game
|
||||
// ^^ TODO: which of the mother goose versions is affected by this? EGA? SCI1? SCI1.1?
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
// gameID, room,script,lvl, object-name, method-name, call,index, workaround
|
||||
const SciWorkaroundEntry opcodeOrWorkarounds[] = {
|
||||
{ GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464
|
||||
{ GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // when going north and reaching the castle (rooms 4 and 37) - bug #3038228
|
||||
const SciWorkaroundEntry arithmeticWorkarounds[] = {
|
||||
{ GID_CAMELOT, 92, 92, 0, "endingCartoon2", "changeState", 0x20d, 0, { WORKAROUND_FAKE, 0 } }, // op_lai: during the ending, sub gets called with no parameters, uses parameter 1 which is theGrail in this case - bug #3044734
|
||||
{ GID_ECOQUEST2, 100, 0, 0, "Rain", "points", 0xcc6, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when giving the papers to the customs officer, gets called against a pointer instead of a number - bug #3034464
|
||||
{ GID_FANMADE, 516, 983, 0, "Wander", "setTarget", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_mul: The Legend of the Lost Jewel Demo (fan made): called with object as second parameter when attacked by insects - bug #3038913
|
||||
{ GID_ICEMAN, 199, 977, 0, "Grooper", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_add: While dancing with the girl
|
||||
{ GID_MOTHERGOOSE256, -1, 999, 0, "Event", "new", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_and: constantly during the game (SCI1 version)
|
||||
{ GID_MOTHERGOOSE256, -1, 4, 0, "rm004", "doit", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_or: when going north and reaching the castle (rooms 4 and 37) - bug #3038228
|
||||
{ GID_MOTHERGOOSEHIRES,90, 90, 0, "newGameButton", "select", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_ge: MUMG Deluxe, when selecting "New Game" in the main menu. It tries to compare an integer with a list. Needs to return false for the game to continue.
|
||||
{ GID_QFG1VGA, 301, 928, 0, "Blink", "init", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_div: when entering inn, gets called with 1 parameter, but 2nd parameter is used for div which happens to be an object
|
||||
{ GID_QFG2, 200, 200, 0, "astro", "messages", -1, 0, { WORKAROUND_FAKE, 0 } }, // op_lsi: when getting asked for your name by the astrologer bug #3039879
|
||||
{ GID_SQ5, 200, 939, 0, "Osc", "cycleDone", -1, 0, { WORKAROUND_FAKE, 1 } }, // op_dpToa: when going back to bridge the crew is goofing off, we get an object as cycle count
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
@ -328,7 +276,7 @@ const SciWorkaroundEntry kGraphRestoreBox_workarounds[] = {
|
||||
{ GID_LSL6, -1, 86, 0, "LL6Inv", "show", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens when restoring, is called with hunk segment, but hunk is not allocated at that time
|
||||
// ^^ TODO: check, if this is really a script error or an issue with our restore code
|
||||
{ GID_LSL6, -1, 86, 0, "LL6Inv", "hide", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens during the game, gets called with 1 extra parameter
|
||||
{ GID_SQ5, 850, 850, 0, "quirksTurn", "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811
|
||||
{ GID_SQ5, 850, 850, 0, NULL, "changeState", -1, 0, { WORKAROUND_STILLCALL, 0 } }, // happens while playing Battle Cruiser (invalid segment) - bug #3056811
|
||||
SCI_WORKAROUNDENTRY_TERMINATOR
|
||||
};
|
||||
|
||||
|
@ -68,16 +68,7 @@ struct SciWorkaroundEntry {
|
||||
SciWorkaroundSolution newValue;
|
||||
};
|
||||
|
||||
extern const SciWorkaroundEntry opcodeDivWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeDptoaWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeGeWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeLeWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeUltWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeLaiWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeLsiWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeMulWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeAndWorkarounds[];
|
||||
extern const SciWorkaroundEntry opcodeOrWorkarounds[];
|
||||
extern const SciWorkaroundEntry arithmeticWorkarounds[];
|
||||
extern const SciWorkaroundEntry uninitializedReadWorkarounds[];
|
||||
extern const SciWorkaroundEntry kAbs_workarounds[];
|
||||
extern const SciWorkaroundEntry kCelHigh_workarounds[];
|
||||
|
@ -38,6 +38,7 @@ MODULE_OBJS := \
|
||||
engine/state.o \
|
||||
engine/static_selectors.o \
|
||||
engine/vm.o \
|
||||
engine/vm_types.o \
|
||||
engine/workarounds.o \
|
||||
graphics/animate.o \
|
||||
graphics/cache.o \
|
||||
|
Loading…
x
Reference in New Issue
Block a user