Files
scummvm/engines/scumm/he/script_v72he.cpp
T

2223 lines
54 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef ENABLE_HE
#include "common/config-manager.h"
#include "common/savefile.h"
#include "common/system.h"
#include "scumm/actor.h"
#include "scumm/charset.h"
#include "scumm/dialogs.h"
#include "scumm/file.h"
#include "scumm/he/intern_he.h"
#include "scumm/he/localizer.h"
#include "scumm/object.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
namespace Scumm {
#define OPCODE(i, x) _opcodes[i]._OPCODE(ScummEngine_v72he, x)
void ScummEngine_v72he::setupOpcodes() {
ScummEngine_v71he::setupOpcodes();
OPCODE(0x02, o72_pushDWord);
OPCODE(0x04, o72_getScriptString);
_opcodes[0x0a].setProc(0, 0);
OPCODE(0x1b, o72_isAnyOf);
_opcodes[0x42].setProc(0, 0);
_opcodes[0x46].setProc(0, 0);
_opcodes[0x4a].setProc(0, 0);
_opcodes[0x4e].setProc(0, 0);
OPCODE(0x50, o72_resetCutscene);
OPCODE(0x52, o72_findObjectWithClassOf);
OPCODE(0x54, o72_getObjectImageX);
OPCODE(0x55, o72_getObjectImageY);
OPCODE(0x56, o72_captureWizImage);
OPCODE(0x58, o72_getTimer);
OPCODE(0x59, o72_setTimer);
OPCODE(0x5a, o72_getSoundPosition);
OPCODE(0x5e, o72_startScript);
OPCODE(0x60, o72_startObject);
OPCODE(0x61, o72_drawObject);
OPCODE(0x62, o72_printWizImage);
OPCODE(0x63, o72_getArrayDimSize);
OPCODE(0x64, o72_getNumFreeArrays);
_opcodes[0x97].setProc(0, 0); // was: o6_setObjectName
OPCODE(0x9c, o72_roomOps);
OPCODE(0x9d, o72_actorOps);
OPCODE(0x9e, o72_verbOps);
OPCODE(0xa0, o72_findObject);
OPCODE(0xa4, o72_arrayOps);
OPCODE(0xae, o72_systemOps);
OPCODE(0xba, o72_talkActor);
OPCODE(0xbb, o72_talkEgo);
OPCODE(0xbc, o72_dimArray);
OPCODE(0xc0, o72_dim2dimArray);
OPCODE(0xc1, o72_traceStatus);
OPCODE(0xc8, o72_kernelGetFunctions);
OPCODE(0xce, o72_drawWizImage);
OPCODE(0xcf, o72_debugInput);
OPCODE(0xd5, o72_jumpToScript);
OPCODE(0xda, o72_openFile);
OPCODE(0xdb, o72_readFile);
OPCODE(0xdc, o72_writeFile);
OPCODE(0xdd, o72_findAllObjects);
OPCODE(0xde, o72_deleteFile);
OPCODE(0xdf, o72_rename);
OPCODE(0xe1, o72_getPixel);
OPCODE(0xe3, o72_pickVarRandom);
OPCODE(0xea, o72_redimArray);
OPCODE(0xf3, o72_readINI);
OPCODE(0xf4, o72_writeINI);
OPCODE(0xf8, o72_getResourceSize);
OPCODE(0xf9, o72_createDirectory);
OPCODE(0xfa, o72_setSystemMessage);
}
static const int arrayDataSizes[] = { 0, 1, 4, 8, 8, 16, 32 };
byte *ScummEngine_v72he::defineArray(int array, int type, int dim2start, int dim2end,
int dim1start, int dim1end, bool newArray, int *newid) {
int id;
int size;
ArrayHeader *ah;
assert(dim2start >= 0 && dim2start <= dim2end);
assert(dim1start >= 0 && dim1start <= dim1end);
assert(0 <= type && type <= 6);
if (type == kBitArray || type == kNibbleArray)
type = kByteArray;
if (!newArray)
nukeArray(array);
id = findFreeArrayId();
if (newid != NULL)
*newid = id;
debug(9, "defineArray (array %d, dim2start %d, dim2end %d dim1start %d dim1end %d", id, dim2start, dim2end, dim1start, dim1end);
if (!newArray && (array & 0x80000000)) {
error("Can't define bit variable as array pointer");
}
size = arrayDataSizes[type];
if (_game.heversion >= 80)
id |= 0x33539000;
if (!newArray)
writeVar(array, id);
if (_game.heversion >= 80)
id &= ~0x33539000;
size *= dim2end - dim2start + 1;
size *= dim1end - dim1start + 1;
size >>= 3;
ah = (ArrayHeader *)_res->createResource(rtString, id, size + sizeof(ArrayHeader));
ah->type = TO_LE_32(type);
ah->dim1start = TO_LE_32(dim1start);
ah->dim1end = TO_LE_32(dim1end);
ah->dim2start = TO_LE_32(dim2start);
ah->dim2end = TO_LE_32(dim2end);
return ah->data;
}
int ScummEngine_v72he::readArray(int array, int idx2, int idx1) {
debug(9, "readArray (array %d, idx2 %d, idx1 %d)", readVar(array), idx2, idx1);
if (readVar(array) == 0)
error("readArray: Reference to zeroed array pointer");
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
if (!ah)
error("readArray: invalid array %d (%d)", array, readVar(array));
if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
error("readArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
}
#if defined(USE_ENET) && defined(USE_LIBCURL)
if (ConfMan.getBool("enable_competitive_mods")) {
// Mod for Backyard Baseball 2001 online competitive play: allow baserunners to be
// turned around while they're jogging to the next base on a pop-up.
// The game checks if the runner is forced to the next base (i.e. there's a runner on the base behind them),
// and if they are then basepath clicks to turn them around have no effect.
// Here we return 0 (false) under certain conditions, so these clicks now have the desired effect.
if (_game.id == GID_BASEBALL2001 &&
_currentRoom == 3 && vm.slot[_currentScript].number == 2076 && // This is the script that handles basepath clicks
readVar(399) == 1 && // This checks that we're playing online
readVar(0x8000 + 11) == 1 && // The ball is a pop-up
readVar(291) < 2 && // Less than two outs
// This is the array of baserunner status info, and the value in position 8 specifies whether the runner is forced
array == 295 && idx1 == 8) {
int runnerIdx = readVar(342);
if (readArray(array, runnerIdx, 6) == 1 && readArray(array, runnerIdx, 7) == 1) {
// Bugfix: if runner is going forward to 1st base, return 1 so they can't turn around
return 1;
} else {
return 0;
}
}
}
#endif
const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
(idx2 - FROM_LE_32(ah->dim2start)) + (idx1 - FROM_LE_32(ah->dim1start));
switch (FROM_LE_32(ah->type)) {
case kByteArray:
case kStringArray:
return ah->data[offset];
case kIntArray:
return (int16)READ_LE_UINT16(ah->data + offset * 2);
case kDwordArray:
return (int32)READ_LE_UINT32(ah->data + offset * 4);
default:
break;
}
return 0;
}
void ScummEngine_v72he::writeArray(int array, int idx2, int idx1, int value) {
debug(9, "writeArray (array %d, idx2 %d, idx1 %d, value %d)", readVar(array), idx2, idx1, value);
if (readVar(array) == 0)
error("writeArray: Reference to zeroed array pointer");
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
if (!ah)
error("writeArray: Invalid array (%d) reference", readVar(array));
if (idx2 < (int)FROM_LE_32(ah->dim2start) || idx2 > (int)FROM_LE_32(ah->dim2end) ||
idx1 < (int)FROM_LE_32(ah->dim1start) || idx1 > (int)FROM_LE_32(ah->dim1end)) {
error("writeArray: array %d out of bounds: [%d, %d] exceeds [%d..%d, %d..%d]",
array, idx1, idx2, FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end),
FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end));
}
const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
(idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1;
switch (FROM_LE_32(ah->type)) {
case kByteArray:
case kStringArray:
ah->data[offset] = value;
break;
case kIntArray:
WRITE_LE_UINT16(ah->data + offset * 2, value);
break;
case kDwordArray:
WRITE_LE_UINT32(ah->data + offset * 4, value);
break;
default:
break;
}
}
int ScummEngine_v72he::setupStringArray(int size) {
writeVar(0, 0);
defineArray(0, kStringArray, 0, 0, 0, size + 1);
writeArray(0, 0, 0, 0);
return readVar(0);
}
int ScummEngine_v72he::setupStringArrayFromString(const char *cStr) {
// this is PUI_ScummStringArrayFromCString() found in PUSERMAC.cpp
// I can see how its done up there in setupStringArray()
// yet I'd note that 'SCUMMVAR_user_reserved' var was used instead of 0
// and strlen(), not strlen() + 1 was used
// plus, this function actually copies the string, not just 'sets up' the array
writeVar(0, 0);
int len = strlen(cStr) + 1;
byte *ptr = defineArray(0, kStringArray, 0, 0, 0, len);
if (ptr != nullptr)
Common::strlcpy((char*)ptr, cStr, len);
return readVar(0);
}
void ScummEngine_v72he::readArrayFromIndexFile() {
int num;
int a, b, c;
while ((num = _fileHandle->readUint16LE()) != 0) {
a = _fileHandle->readUint16LE();
b = _fileHandle->readUint16LE();
c = _fileHandle->readUint16LE();
if (c == 1)
defineArray(num, kBitArray, 0, a, 0, b);
else
defineArray(num, kDwordArray, 0, a, 0, b);
}
}
void ScummEngine_v72he::copyScriptString(byte *dst, int dstSize) {
byte string[1024];
byte chr;
int pos = 0;
int array = pop();
if (array == -1) {
if (_stringLength == 1)
error("String stack underflow");
_stringLength -= 2;
while ((chr = _stringBuffer[_stringLength]) != 0) {
string[pos] = chr;
pos++;
if (pos > dstSize)
error("String too long to pop");
_stringLength--;
}
string[pos] = 0;
_stringLength++;
// Reverse string
int len = resStrLen(string);
while (len--)
*dst++ = string[len];
} else {
writeVar(0, array);
while ((chr = readArray(0, 0, pos)) != 0) {
*dst++ = chr;
pos++;
}
}
*dst = 0;
}
void ScummEngine_v72he::decodeScriptString(byte *dst, bool scriptString) {
const byte *src;
int args[31];
int num, len, val;
byte chr, string[1024];
byte *dst0 = dst;
memset(args, 0, sizeof(args));
memset(string, 0, sizeof(string));
// Get stack list, plus one
num = pop();
for (int i = num; i >= 0; i--)
args[i] = pop();
// Get string
if (scriptString) {
len = resStrLen(_scriptPointer) + 1;
memcpy(string, _scriptPointer, len);
_scriptPointer += len;
} else {
copyScriptString(string, sizeof(string));
len = resStrLen(string) + 1;
}
if (_localizer) {
strncpy((char *) string, _localizer->translate((char *) string).c_str(), sizeof(string) - 1);
}
// Decode string
num = 0;
val = 0;
while (len--) {
chr = string[num++];
if (chr == '%') {
chr = string[num++];
switch (chr) {
case 'b':
//dst += Common::sprintf_s((char *)dst, "%b", args[val++]);
break;
case 'c':
*dst++ = args[val++];
break;
case 'd':
dst += Common::sprintf_s((char *)dst, sizeof(string) - (dst - dst0), "%d", args[val++]);
break;
case 's':
src = getStringAddress(args[val++]);
if (src) {
while (*src != 0)
*dst++ = *src++;
}
break;
case 'x':
dst += Common::sprintf_s((char *)dst, sizeof(string) - (dst - dst0), "%x", args[val++]);
break;
default:
*dst++ = '%';
num--;
break;
}
} else {
*dst++ = chr;
}
}
*dst = 0;
if (_localizer) {
strncpy((char *) dst0, _localizer->translate((char *) dst0).c_str(), sizeof(string) - 1);
}
}
int ScummEngine_v72he::findObject(int x, int y, int num, int *args) {
int b, cls, i, result;
for (i = 1; i < _numLocalObjects; i++) {
result = 0;
if ((_objs[i].obj_nr < 1) || getClass(_objs[i].obj_nr, kObjectClassUntouchable))
continue;
// Check polygon bounds
if (_wiz->polygonDefined(_objs[i].obj_nr)) {
if (_wiz->polygonHit(_objs[i].obj_nr, x, y))
result = _objs[i].obj_nr;
else if (VAR_POLYGONS_ONLY != 0xFF && VAR(VAR_POLYGONS_ONLY))
continue;
}
if (!result) {
// Check object bounds
if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x &&
_objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y)
result = _objs[i].obj_nr;
}
if (result) {
if (!num)
return result;
// Check object class
cls = args[0];
b = getClass(_objs[i].obj_nr, cls);
if ((cls & 0x80 && b) || (!(cls & 0x80) && !b))
return result;
}
}
return 0;
}
void ScummEngine_v72he::o72_pushDWord() {
push(fetchScriptDWordSigned());
}
void ScummEngine_v72he::o72_getScriptString() {
byte chr;
while ((chr = fetchScriptByte()) != 0) {
_stringBuffer[_stringLength] = chr;
_stringLength++;
if (_stringLength >= 4096)
error("String stack overflow");
}
_stringBuffer[_stringLength] = 0;
_stringLength++;
}
void ScummEngine_v72he::o72_isAnyOf() {
int args[128];
int num, value;
num = getStackList(args, ARRAYSIZE(args));
value = pop();
for (int i = 0; i < num; i++) {
if (args[i] == value) {
push(1);
return;
}
}
push(0);
}
void ScummEngine_v72he::o72_resetCutscene() {
int idx;
idx = vm.cutSceneStackPointer;
vm.cutSceneStackPointer = 0;
vm.cutScenePtr[idx] = 0;
vm.cutSceneScript[idx] = 0;
VAR(VAR_OVERRIDE) = 0;
}
void ScummEngine_v72he::o72_findObjectWithClassOf() {
int args[16], num;
num = getStackList(args, ARRAYSIZE(args));
int y = pop();
int x = pop();
int r = findObject(x, y, num, args);
push(r);
}
void ScummEngine_v72he::o72_getObjectImageX() {
int object = pop();
int objnum = getObjectIndex(object);
if (objnum == -1) {
push(0);
return;
}
push(_objs[objnum].x_pos / 8);
}
void ScummEngine_v72he::o72_getObjectImageY() {
int object = pop();
int objnum = getObjectIndex(object);
if (objnum == -1) {
push(0);
return;
}
push(_objs[objnum].y_pos / 8);
}
void ScummEngine_v72he::o72_captureWizImage() {
Common::Rect grab;
grab.bottom = pop() + 1;
grab.right = pop() + 1;
grab.top = pop();
grab.left = pop();
_wiz->captureWizImage(pop(), grab, false, true);
}
void ScummEngine_v72he::o72_getTimer() {
int timer = pop();
byte cmd = fetchScriptByte();
if (cmd == 10 || cmd == 50) {
push(getHETimer(timer));
} else {
push(0);
}
}
void ScummEngine_v72he::o72_setTimer() {
int timer = pop();
byte cmd = fetchScriptByte();
if (cmd == 158 || cmd == 61) {
setHETimer(timer);
} else {
error("TIMER command %d?", cmd);
}
}
void ScummEngine_v72he::o72_getSoundPosition() {
int snd = pop();
push(((SoundHE *)_sound)->getSoundPosition(snd));
}
void ScummEngine_v72he::o72_startScript() {
int args[25];
int script;
byte flags;
getStackList(args, ARRAYSIZE(args));
script = pop();
flags = fetchScriptByte();
// HACK: The credits script in Russian HE99 version of Freddi Fish 3
// uses null strings, causing various errors, so skip it.
if (_game.id == GID_FREDDI3 && _game.heversion == 99 && _language == Common::RU_RUS &&
_currentRoom == 40 && script == 2057) {
return;
}
runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
}
void ScummEngine_v72he::o72_startObject() {
int args[25];
int script, entryp;
byte flags;
getStackList(args, ARRAYSIZE(args));
entryp = pop();
script = pop();
flags = fetchScriptByte();
runObjectScript(script, entryp, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
}
void ScummEngine_v72he::o72_drawObject() {
byte subOp = fetchScriptByte();
int state, y, x;
switch (subOp) {
case SO_AT_IMAGE:
state = pop();
y = pop();
x = pop();
break;
case SO_IMAGE:
state = pop();
if (state == 0)
state = 1;
y = x = -100;
break;
case ScummEngine_v6::SubOpType::SO_AT:
state = 1;
y = pop();
x = pop();
break;
default:
error("o72_drawObject: default case %d", subOp);
}
int object = pop();
int objnum = getObjectIndex(object);
if (objnum == -1)
return;
if (y != -100 && x != -100) {
_objs[objnum].x_pos = x * 8;
_objs[objnum].y_pos = y * 8;
}
if (state != -1) {
addObjectToDrawQue(objnum);
putState(object, state);
}
}
void ScummEngine_v72he::o72_printWizImage() {
WizImage wi;
wi.resNum = pop();
wi.x1 = wi.y1 = 0;
wi.state = 0;
wi.flags = kWIFPrint;
_wiz->displayWizImage(&wi);
}
void ScummEngine_v72he::o72_getArrayDimSize() {
byte subOp = fetchScriptByte();
int32 val1, val2;
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(fetchScriptWord()));
if (!ah) {
push(0);
return;
}
switch (subOp) {
case SO_NONE:
case SO_NIBBLE:
val1 = FROM_LE_32(ah->dim1end);
val2 = FROM_LE_32(ah->dim1start);
push(val1 - val2 + 1);
break;
case SO_BIT:
val1 = FROM_LE_32(ah->dim2end);
val2 = FROM_LE_32(ah->dim2start);
push(val1 - val2 + 1);
break;
case SO_BYTE:
push(FROM_LE_32(ah->dim1start));
break;
case SO_INT:
push(FROM_LE_32(ah->dim1end));
break;
case SO_DWORD:
push(FROM_LE_32(ah->dim2start));
break;
case SO_STRING:
push(FROM_LE_32(ah->dim2end));
break;
default:
error("o72_getArrayDimSize: default case %d", subOp);
}
}
void ScummEngine_v72he::o72_getNumFreeArrays() {
const ResourceManager::ResTypeData &rtd = _res->_types[rtString];
int i, num = 0;
for (i = 1; i < _numArray; i++) {
if (!rtd[i]._address)
num++;
}
push(num);
}
void ScummEngine_v72he::o72_roomOps() {
int a, b, c, d, e;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_ROOM_SCROLL:
b = pop();
a = pop();
if (a < (_screenWidth / 2))
a = (_screenWidth / 2);
if (b < (_screenWidth / 2))
b = (_screenWidth / 2);
if (a > _roomWidth - (_screenWidth / 2))
a = _roomWidth - (_screenWidth / 2);
if (b > _roomWidth - (_screenWidth / 2))
b = _roomWidth - (_screenWidth / 2);
VAR(VAR_CAMERA_MIN_X) = a;
VAR(VAR_CAMERA_MAX_X) = b;
break;
case SO_ROOM_SCREEN:
b = pop();
a = pop();
initScreens(a, _screenHeight);
break;
case SO_ROOM_PALETTE:
d = pop();
c = pop();
b = pop();
a = pop();
setPalColor(d, a, b, c);
break;
case SO_ROOM_INTENSITY:
c = pop();
b = pop();
a = pop();
darkenPalette(a, a, a, b, c);
break;
case SO_ROOM_SAVEGAME:
_saveTemporaryState = true;
_saveLoadSlot = pop();
_saveLoadFlag = pop();
break;
case SO_ROOM_FADE:
// Defaults to 1 but doesn't use fade effects
a = pop();
break;
case SO_RGB_ROOM_INTENSITY:
e = pop();
d = pop();
c = pop();
b = pop();
a = pop();
darkenPalette(a, b, c, d, e);
break;
case SO_ROOM_NEW_PALETTE:
a = pop();
setCurrentPalette(a);
break;
case SO_ROOM_COPY_PALETTE:
a = pop();
b = pop();
copyPalColor(a, b);
break;
case SO_ROOM_SAVEGAME_BY_NAME:
byte buffer[256];
copyScriptString((byte *)buffer, sizeof(buffer));
_saveLoadFileName = (char *)buffer;
debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName.c_str());
_saveLoadFlag = pop();
_saveLoadSlot = 255;
_saveTemporaryState = true;
break;
case SO_OBJECT_ORDER:
b = pop();
a = pop();
swapObjects(a, b);
break;
case SO_ROOM_PALETTE_IN_ROOM:
b = pop();
a = pop();
setRoomPalette(a, b);
break;
default:
error("o72_roomOps: default case %d", subOp);
}
}
void ScummEngine_v72he::o72_actorOps() {
ActorHE *a;
int i, j, k;
int args[32];
byte string[256];
byte subOp = fetchScriptByte();
if (subOp == SO_ACTOR_INIT) {
_curActor = pop();
return;
}
a = (ActorHE *)derefActorSafe(_curActor, "o72_actorOps");
if (!a)
return;
switch (subOp) {
case SO_CONDITION: // (HE 80+)
k = getStackList(args, ARRAYSIZE(args));
for (i = 0; i < k; ++i) {
a->setUserCondition(args[i] & 0x7F, args[i] & 0x80);
}
break;
case SO_TALK_CONDITION: // (HE 80+)
k = pop();
if (k == 0)
k = _rnd.getRandomNumberRng(1, 10);
a->_heNoTalkAnimation = 1;
a->setTalkCondition(k);
break;
case SO_PRIORITY: // (HE 90+)
a->_layer = pop();
a->_needRedraw = true;
break;
case SO_ACTOR_DEFAULT_CLIPPED:
_actorClipOverride.bottom = pop();
_actorClipOverride.right = pop();
_actorClipOverride.top = pop();
_actorClipOverride.left = pop();
adjustRect(_actorClipOverride);
break;
case ScummEngine_v6::SubOpType::SO_AT: // (HE 98+)
j = pop();
i = pop();
a->putActor(i, j);
break;
case SO_CLIPPED: // (HE 99+)
a->_clipOverride.bottom = pop();
a->_clipOverride.right = pop();
a->_clipOverride.top = pop();
a->_clipOverride.left = pop();
adjustRect(a->_clipOverride);
break;
case SO_ERASE: // // (HE 90+)
k = pop();
a->setHEFlag(1, k);
break;
case SO_COSTUME:
a->setActorCostume(pop());
break;
case SO_STEP_DIST:
j = pop();
i = pop();
a->setActorWalkSpeed(i, j);
break;
case SO_SOUND:
k = getStackList(args, ARRAYSIZE(args));
for (i = 0; i < k; i++)
a->_sound[i] = args[i];
break;
case SO_WALK_ANIMATION:
a->_walkFrame = pop();
break;
case SO_TALK_ANIMATION:
a->_talkStopFrame = pop();
a->_talkStartFrame = pop();
break;
case SO_STAND_ANIMATION:
a->_standFrame = pop();
break;
case SO_ANIMATION:
// dummy case in scumm6
pop();
pop();
pop();
break;
case SO_DEFAULT:
a->initActor(0);
break;
case SO_ELEVATION:
a->setElevation(pop());
break;
case SO_ANIMATION_DEFAULT:
a->_initFrame = 1;
a->_walkFrame = 2;
a->_standFrame = 3;
a->_talkStartFrame = 4;
a->_talkStopFrame = 5;
break;
case SO_PALETTE:
j = pop();
i = pop();
assertRange(0, i, 255, "palette slot");
a->remapActorPaletteColor(i, j);
a->_needRedraw = true;
break;
case SO_TALK_COLOR:
a->_talkColor = pop();
// WORKAROUND bug #13730: defined subtitles color 16 is very dark and hard to read on the dark background.
// we change it to brighter color to ease reading.
if (_game.id == GID_FREDDI4 && _game.heversion == 98 && _currentRoom == 43 && a->_talkColor == 16 && enhancementEnabled(kEnhSubFmtCntChanges))
a->_talkColor = 200;
break;
case SO_ACTOR_NAME:
copyScriptString(string, sizeof(string));
loadPtrToResource(rtActorName, a->_number, string);
break;
case SO_INIT_ANIMATION:
a->_initFrame = pop();
break;
case SO_ACTOR_WIDTH:
a->_width = pop();
break;
case SO_SCALE:
i = pop();
a->setScale(i, i);
break;
case SO_NEVER_ZCLIP:
a->_forceClip = 0;
break;
case SO_ALWAYS_ZCLIP:
a->_forceClip = pop();
break;
case SO_IGNORE_BOXES:
a->_ignoreBoxes = 1;
a->_forceClip = 0;
if (a->isInCurrentRoom())
a->putActor();
break;
case SO_FOLLOW_BOXES:
a->_ignoreBoxes = 0;
a->_forceClip = 0;
if (a->isInCurrentRoom())
a->putActor();
break;
case SO_ANIMATION_SPEED:
a->setAnimSpeed(pop());
break;
case SO_SHADOW:
a->_heXmapNum = pop();
a->_needRedraw = true;
break;
case SO_TEXT_OFFSET:
a->_talkPosY = pop();
a->_talkPosX = pop();
break;
case SO_CHARSET_SET: // (HE 72+)
a->_charset = pop();
break;
case SO_ROOM_PALETTE: // (HE 99+)
a->_hePaletteNum = pop();
a->_needRedraw = true;
break;
case SO_ACTOR_VARIABLE:
i = pop();
a->setAnimVar(pop(), i);
break;
case SO_ACTOR_IGNORE_TURNS_ON:
a->_ignoreTurns = true;
break;
case SO_ACTOR_IGNORE_TURNS_OFF:
a->_ignoreTurns = false;
break;
case SO_NEW:
a->initActor(2);
break;
case SO_BACKGROUND_ON:
a->drawActorToBackBuf(a->getPos().x, a->getPos().y);
break;
case SO_BACKGROUND_OFF:
a->_drawToBackBuf = false;
a->_needRedraw = true;
a->_needBgReset = true;
break;
case SO_TALKIE:
{
copyScriptString(string, sizeof(string));
int slot = pop();
int len = resStrLen(string) + 1;
memcpy(a->_heTalkQueue[slot].sentence, string, len);
a->_heTalkQueue[slot].posX = a->_talkPosX;
a->_heTalkQueue[slot].posY = a->_talkPosY;
a->_heTalkQueue[slot].color = a->_talkColor;
break;
}
default:
error("o72_actorOps: default case %d", subOp);
}
}
void ScummEngine_v72he::o72_verbOps() {
int slot, a, b;
VerbSlot *vs;
byte name[200];
byte subOp = fetchScriptByte();
if (subOp == SO_VERB_INIT) {
_curVerb = pop();
_curVerbSlot = getVerbSlot(_curVerb, 0);
assertRange(0, _curVerbSlot, _numVerbs - 1, "new verb slot");
return;
}
vs = &_verbs[_curVerbSlot];
slot = _curVerbSlot;
switch (subOp) {
case SO_VERB_IMAGE:
a = pop();
if (_curVerbSlot) {
setVerbObject(_roomResource, a, slot);
vs->type = kImageVerbType;
vs->imgindex = a;
}
break;
case SO_VERB_NAME:
copyScriptString(name, sizeof(name));
loadPtrToResource(rtVerb, slot, name);
vs->type = kTextVerbType;
vs->imgindex = 0;
break;
case SO_VERB_COLOR:
vs->color = pop();
break;
case SO_VERB_HICOLOR:
vs->hicolor = pop();
break;
case SO_VERB_AT:
vs->curRect.top = pop();
vs->curRect.left = pop();
break;
case SO_VERB_ON:
vs->curmode = 1;
break;
case SO_VERB_OFF:
vs->curmode = 0;
break;
case SO_VERB_DELETE:
slot = getVerbSlot(pop(), 0);
killVerb(slot);
break;
case SO_VERB_NEW:
slot = getVerbSlot(_curVerb, 0);
if (slot == 0) {
for (slot = 1; slot < _numVerbs; slot++) {
if (_verbs[slot].verbid == 0)
break;
}
if (slot == _numVerbs)
error("Too many verbs");
_curVerbSlot = slot;
}
vs = &_verbs[slot];
vs->verbid = _curVerb;
vs->color = 2;
vs->hicolor = 0;
vs->dimcolor = 8;
vs->type = kTextVerbType;
vs->charset_nr = _string[0]._default.charset;
vs->curmode = 0;
vs->saveid = 0;
vs->key = 0;
vs->center = 0;
vs->imgindex = 0;
break;
case SO_VERB_DIMCOLOR:
vs->dimcolor = pop();
break;
case SO_VERB_DIM:
vs->curmode = 2;
break;
case SO_VERB_KEY:
vs->key = pop();
break;
case SO_VERB_CENTER:
vs->center = 1;
break;
case SO_VERB_NAME_STR:
a = pop();
if (a == 0) {
loadPtrToResource(rtVerb, slot, (const byte *)"");
} else {
loadPtrToResource(rtVerb, slot, getStringAddress(a));
}
vs->type = kTextVerbType;
vs->imgindex = 0;
break;
case SO_VERB_IMAGE_IN_ROOM:
b = pop();
a = pop();
if (slot && a != vs->imgindex) {
setVerbObject(b, a, slot);
vs->type = kImageVerbType;
vs->imgindex = a;
}
break;
case SO_VERB_BAKCOLOR:
vs->bkcolor = pop();
break;
case 255:
drawVerb(slot, 0);
verbMouseOver(0);
break;
default:
error("o72_verbops: default case %d", subOp);
}
}
void ScummEngine_v72he::o72_findObject() {
int y = pop();
int x = pop();
int r = findObject(x, y, 0, 0);
push(r);
}
void ScummEngine_v72he::o72_arrayOps() {
byte *data;
byte string[1024];
int dim1end, dim1start, dim2end, dim2start;
int id, len, b, c, list[128];
int offs, tmp, tmp2;
uint tmp3, type;
byte subOp = fetchScriptByte();
int array = fetchScriptWord();
debug(9,"o72_arrayOps: array %d case %d", array, subOp);
switch (subOp) {
case SO_STRING:
copyScriptString(string, sizeof(string));
len = resStrLen(string);
data = defineArray(array, kStringArray, 0, 0, 0, len);
memcpy(data, string, len);
break;
case SO_COMPLEX_ARRAY_ASSIGNMENT:
len = getStackList(list, ARRAYSIZE(list));
dim1end = pop();
dim1start = pop();
dim2end = pop();
dim2start = pop();
id = readVar(array);
if (id == 0) {
defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
}
checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
tmp2 = 0;
while (dim2start <= dim2end) {
tmp = dim1start;
while (tmp <= dim1end) {
writeArray(array, dim2start, tmp, list[tmp2++]);
if (tmp2 == len)
tmp2 = 0;
tmp++;
}
dim2start++;
}
break;
case SO_COMPLEX_ARRAY_COPY_OPERATION:
{
int a2_dim1end = pop();
int a2_dim1start = pop();
int a2_dim2end = pop();
int a2_dim2start = pop();
int array2 = fetchScriptWord();
int a1_dim1end = pop();
int a1_dim1start = pop();
int a1_dim2end = pop();
int a1_dim2start = pop();
if (a1_dim1end - a1_dim1start != a2_dim1end - a2_dim1start || a2_dim2end - a2_dim2start != a1_dim2end - a1_dim2start) {
error("Source and dest ranges size are mismatched");
}
copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
}
break;
case SO_RANGE_ARRAY_ASSIGNMENT:
b = pop();
c = pop();
dim1end = pop();
dim1start = pop();
dim2end = pop();
dim2start = pop();
id = readVar(array);
if (id == 0) {
defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
}
checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end);
offs = (b >= c) ? 1 : -1;
tmp2 = c;
tmp3 = ABS(c - b) + 1;
while (dim2start <= dim2end) {
tmp = dim1start;
while (tmp <= dim1end) {
writeArray(array, dim2start, tmp, tmp2);
if (--tmp3 == 0) {
tmp2 = c;
tmp3 = ABS(c - b) + 1;
} else {
tmp2 += offs;
}
tmp++;
}
dim2start++;
}
break;
case SO_COMPLEX_ARRAY_MATH_OPERATION:
{
// Used by script 84 (Send end of play info) in Backyard Football during online play.
int array2 = fetchScriptWord();
int array1 = fetchScriptWord();
type = pop();
int a1_dim1end = pop();
int a1_dim1start = pop();
int a1_dim2end = pop();
int a1_dim2start = pop();
int a2_dim1end = pop();
int a2_dim1start = pop();
int a2_dim2end = pop();
int a2_dim2start = pop();
dim1end = pop();
dim1start = pop();
dim2end = pop();
dim2start = pop();
debug(0, "Complex: %d = %d[%d to %d][%d to %d] %c %d[%d to %d][%d to %d]", array,
array1, a1_dim1start, a1_dim2end, a1_dim1start, a1_dim2end,
" +-&|^"[type],
array2, a2_dim1start, a2_dim2end, a2_dim1start, a2_dim2end);
int a12_num = a1_dim2end - a1_dim2start + 1;
int a11_num = a1_dim1end - a1_dim1start + 1;
int a22_num = a2_dim2end - a2_dim2start + 1;
int a21_num = a2_dim1end - a2_dim1start + 1;
int d12_num = dim2end - dim2start + 1;
int d11_num = dim1end - dim1start + 1;
id = readVar(array);
if (id == 0) {
defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end);
}
if (a12_num != a22_num || a12_num != d12_num || a11_num != a21_num || a11_num != d11_num) {
error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num);
}
for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start, ++dim2start) {
int a2dim1 = a2_dim1start;
int a1dim1 = a1_dim1start;
int dim1 = dim1start;
for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1, ++dim1) {
int val1 = readArray(array1, a1_dim2start, a1dim1);
int val2 = readArray(array2, a2_dim2start, a2dim1);
int res;
switch (type) {
case 1: // Addition
res = val2 + val1;
break;
case 2: // Subtraction
res = val2 - val1;
break;
case 3: // Binary AND
res = val2 & val1;
break;
case 4: // Binary OR
res = val2 | val1;
break;
case 5: // Binary XOR
res = val2 ^ val1;
break;
default:
error("o72_arrayOps: case 138 unknown type %d)", type);
}
writeArray(array, dim2start, dim1, res);
}
}
break;
}
case SO_FORMATTED_STRING: // SO_FORMATTED_STRING
decodeScriptString(string);
len = resStrLen(string);
data = defineArray(array, kStringArray, 0, 0, 0, len);
memcpy(data, string, len);
break;
case SO_ASSIGN_INT_LIST:
b = pop();
c = pop();
id = readVar(array);
if (id == 0) {
defineArray(array, kDwordArray, 0, 0, 0, b + c - 1);
}
while (c--) {
writeArray(array, 0, b + c, pop());
}
break;
case SO_ASSIGN_2DIM_LIST:
len = getStackList(list, ARRAYSIZE(list));
id = readVar(array);
if (id == 0)
error("Must DIM a two dimensional array before assigning");
c = pop();
while (--len >= 0) {
writeArray(array, c, len, list[len]);
}
break;
default:
error("o72_arrayOps: default case %d (array %d)", subOp, array);
}
}
void ScummEngine_v72he::o72_systemOps() {
byte string[1024];
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_FLUSH_OBJECT_DRAW_QUE: // HE80+
clearDrawObjectQueue();
break;
case SO_UPDATE_SCREEN: // HE80+
restoreBackgroundHE(Common::Rect(_screenWidth, _screenHeight));
updatePalette();
break;
case SO_RESTART:
restart();
break;
case SO_QUIT:
// Confirm shutdown
confirmExitDialog();
break;
case SO_QUIT_QUIT:
quitGame();
break;
case SO_START_SYSTEM_STRING:
copyScriptString(string, sizeof(string));
debug(0, "Start executable (%s)", string);
break;
case SO_RESTART_STRING:
copyScriptString(string, sizeof(string));
debug(0, "Start game (%s)", string);
break;
default:
error("o72_systemOps invalid case %d", subOp);
}
}
void ScummEngine_v72he::o72_talkActor() {
Actor *a;
int act = pop();
_string[0].loadDefault();
// A value of 225 can occur when examining the gold in the mine of pajama, after mining the gold.
// This is a script bug, the script should set the subtitle color, not actor number.
// This script bug was fixed in the updated version of pajama.
if (act == 225) {
_string[0].color = act;
} else {
_actorToPrintStrFor = act;
if (_actorToPrintStrFor != 0xFF) {
a = derefActor(_actorToPrintStrFor, "o72_talkActor");
_string[0].color = a->_talkColor;
}
}
actorTalk(_scriptPointer);
_scriptPointer += resStrLen(_scriptPointer) + 1;
}
void ScummEngine_v72he::o72_talkEgo() {
push(VAR(VAR_EGO));
o72_talkActor();
}
void ScummEngine_v72he::o72_dimArray() {
int data;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_BIT:
data = kBitArray;
break;
case SO_NIBBLE:
data = kNibbleArray;
break;
case SO_BYTE:
data = kByteArray;
break;
case SO_INT:
data = kIntArray;
break;
case SO_DWORD:
data = kDwordArray;
break;
case SO_STRING:
data = kStringArray;
break;
case SO_UNDIM_ARRAY:
nukeArray(fetchScriptWord());
return;
default:
error("o72_dimArray: default case %d", subOp);
}
defineArray(fetchScriptWord(), data, 0, 0, 0, pop());
}
void ScummEngine_v72he::o72_dim2dimArray() {
int data, dim1end, dim2end;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_BIT:
data = kBitArray;
break;
case SO_NIBBLE:
data = kNibbleArray;
break;
case SO_BYTE:
data = kByteArray;
break;
case SO_INT:
data = kIntArray;
break;
case SO_DWORD:
data = kDwordArray;
break;
case SO_STRING:
data = kStringArray;
break;
default:
error("o72_dim2dimArray: default case %d", subOp);
}
dim1end = pop();
dim2end = pop();
defineArray(fetchScriptWord(), data, 0, dim2end, 0, dim1end);
}
void ScummEngine_v72he::o72_traceStatus() {
byte string[80];
copyScriptString(string, sizeof(string));
pop();
}
void ScummEngine_v72he::o72_kernelGetFunctions() {
int args[29];
byte *data;
getStackList(args, ARRAYSIZE(args));
switch (args[0]) {
case 1:
writeVar(0, 0);
data = defineArray(0, kByteArray, 0, 0, 0, virtScreenSave(0, args[1], args[2], args[3], args[4]));
virtScreenSave(data, args[1], args[2], args[3], args[4]);
push(readVar(0));
break;
default:
error("o72_kernelGetFunctions: default case %d", args[0]);
}
}
void ScummEngine_v72he::o72_drawWizImage() {
WizImage wi;
wi.flags = pop();
wi.y1 = pop();
wi.x1 = pop();
wi.resNum = pop();
wi.state = 0;
_wiz->displayWizImage(&wi);
}
void ScummEngine_v72he::debugInput(byte* string) {
byte *debugInputString;
DebugInputDialog dialog(this, (char *)string);
runDialog(dialog);
while (!dialog.done) {
parseEvents();
dialog.handleKeyDown(_keyPressed);
}
writeVar(0, 0);
debugInputString = defineArray(0, kStringArray, 0, 0, 0, dialog.buffer.size());
memcpy(debugInputString, dialog.buffer.c_str(), dialog.buffer.size());
push(readVar(0));
}
void ScummEngine_v72he::o72_debugInput() {
byte string[255];
copyScriptString(string, sizeof(string));
debugInput(string);
}
void ScummEngine_v72he::o72_jumpToScript() {
int args[25];
int script;
byte flags;
getStackList(args, ARRAYSIZE(args));
script = pop();
flags = fetchScriptByte();
stopObjectCode();
runScript(script, (flags == 199 || flags == 200), (flags == 195 || flags == 200), args);
}
void ScummEngine_v72he::o72_openFile() {
int mode, slot, i;
byte buffer[256];
mode = pop();
copyScriptString(buffer, sizeof(buffer));
debug(1, "Trying to open file '%s'", (char *)buffer);
slot = -1;
for (i = 1; i < 17; i++) {
if (_hInFileTable[i] == 0 && _hOutFileTable[i] == 0) {
slot = i;
break;
}
}
if (slot != -1) {
switch (mode) {
case 1: // Read mode
_hInFileTable[slot] = openFileForReading(buffer);
break;
case 2: // Write mode
if (!strchr((char *)buffer, '/')) {
_hOutFileTable[slot] = openSaveFileForWriting(buffer);
}
break;
case 6: // Append mode
if (!strchr((char *)buffer, '/'))
_hOutFileTable[slot] = openSaveFileForAppending(buffer);
break;
default:
error("o72_openFile(): wrong open file mode %d", mode);
}
if (_hInFileTable[slot] == 0 && _hOutFileTable[slot] == 0)
slot = -1;
}
debug(1, "o72_openFile: slot %d, mode %d", slot, mode);
push(slot);
}
int ScummEngine_v72he::readFileToArray(int slot, int32 size) {
writeVar(0, 0);
byte *data = defineArray(0, kByteArray, 0, 0, 0, size);
if (slot != -1) {
_hInFileTable[slot]->read(data, size + 1);
}
return readVar(0);
}
void ScummEngine_v72he::o72_readFile() {
int slot, val;
int32 size;
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_BYTE:
slot = pop();
assert(_hInFileTable[slot]);
val = _hInFileTable[slot]->readByte();
push(val);
break;
case SO_INT:
slot = pop();
assert(_hInFileTable[slot]);
val = _hInFileTable[slot]->readUint16LE();
push(val);
break;
case SO_DWORD:
slot = pop();
assert(_hInFileTable[slot]);
val = _hInFileTable[slot]->readUint32LE();
push(val);
break;
case SO_ARRAY:
fetchScriptByte();
size = pop();
slot = pop();
assert(_hInFileTable[slot]);
val = readFileToArray(slot, size);
push(val);
break;
default:
error("o72_readFile: default case %d", subOp);
}
}
void ScummEngine_v72he::writeFileFromArray(int slot, int32 resID) {
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, resID);
int32 size = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
if (slot != -1) {
_hOutFileTable[slot]->write(ah->data, size);
}
}
void ScummEngine_v72he::getStringFromArray(int arrayNumber, char *buffer, int maxLength) {
// I'm not really sure it belongs here and not some other version
// this is ARRAY_GetStringFromArray() from ARRAYS.cpp of SPUTM
// this function makes a C-string out of <arrayNumber> contents
VAR(0) = arrayNumber; // it was 0 in original code, but I've seen ScummVM Moonbase code which uses VAR_U32_ARRAY_UNK
int i, ch;
for (i = 0; i < maxLength; ++i) {
if (!(ch = readArray(0, 0, i))) {
break;
}
buffer[i] = ch;
}
buffer[i] = 0;
}
void ScummEngine_v72he::o72_writeFile() {
int32 resID = pop();
int slot = pop();
byte subOp = fetchScriptByte();
assert(_hOutFileTable[slot]);
switch (subOp) {
case SO_BYTE:
_hOutFileTable[slot]->writeByte(resID);
break;
case SO_INT:
_hOutFileTable[slot]->writeUint16LE(resID);
break;
case SO_DWORD:
_hOutFileTable[slot]->writeUint32LE(resID);
break;
case SO_ARRAY:
fetchScriptByte();
writeFileFromArray(slot, resID);
break;
default:
error("o72_writeFile: default case %d", subOp);
}
}
void ScummEngine_v72he::o72_findAllObjects() {
int room = pop();
int i;
if (room != _currentRoom)
error("o72_findAllObjects: current room is not %d", room);
writeVar(0, 0);
defineArray(0, kDwordArray, 0, 0, 0, _numLocalObjects);
writeArray(0, 0, 0, _numLocalObjects);
for (i = 1; i < _numLocalObjects; i++) {
writeArray(0, 0, i, _objs[i].obj_nr);
}
push(readVar(0));
}
void ScummEngine_v72he::o72_deleteFile() {
byte buffer[256];
copyScriptString(buffer, sizeof(buffer));
debug(1, "o72_deleteFile(%s)", (char *)buffer);
deleteSaveFile(buffer);
}
void ScummEngine_v72he::o72_rename() {
byte buffer1[100],buffer2[100];
copyScriptString(buffer1, sizeof(buffer1));
copyScriptString(buffer2, sizeof(buffer2));
debug(1, "o72_rename(%s to %s)", (char *)buffer2, (char *)buffer1);
renameSaveFile(buffer2, buffer1);
}
void ScummEngine_v72he::o72_getPixel() {
uint16 area;
int y = pop();
int x = pop();
byte subOp = fetchScriptByte();
VirtScreen *vs = findVirtScreen(y);
if (vs == NULL || x > _screenWidth - 1 || x < 0) {
push(-1);
return;
}
switch (subOp) {
case ScummEngine_v100he::SO_BACKGROUND_ON: // HE 100
case SO_BACKGROUND_ON:
if (_game.features & GF_16BIT_COLOR)
area = READ_UINT16(vs->getBackPixels(x, y - vs->topline));
else
area = *vs->getBackPixels(x, y - vs->topline);
break;
case ScummEngine_v100he::SO_BACKGROUND_OFF: // HE 100
case SO_BACKGROUND_OFF:
if (_game.features & GF_16BIT_COLOR)
area = READ_UINT16(vs->getPixels(x, y - vs->topline));
else
area = *vs->getPixels(x, y - vs->topline);
break;
default:
error("o72_getPixel: default case %d", subOp);
}
push(area);
}
void ScummEngine_v72he::o72_pickVarRandom() {
int num;
int args[100];
int32 dim1end;
num = getStackList(args, ARRAYSIZE(args));
int value = fetchScriptWord();
if (readVar(value) == 0) {
defineArray(value, kDwordArray, 0, 0, 0, num);
if (num > 0) {
int16 counter = 0;
do {
writeArray(value, 0, counter + 1, args[counter]);
} while (++counter < num);
}
shuffleArray(value, 1, num);
writeArray(value, 0, 0, 2);
push(readArray(value, 0, 1));
return;
}
num = readArray(value, 0, 0);
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(value));
dim1end = FROM_LE_32(ah->dim1end);
if (dim1end < num) {
int32 var_2 = readArray(value, 0, num - 1);
shuffleArray(value, 1, dim1end);
if (readArray(value, 0, 1) == var_2) {
num = 2;
} else {
num = 1;
}
}
writeArray(value, 0, 0, num + 1);
push(readArray(value, 0, num));
}
void ScummEngine_v72he::o72_redimArray() {
int newX, newY;
newY = pop();
newX = pop();
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_BYTE:
redimArray(fetchScriptWord(), 0, newX, 0, newY, kByteArray);
break;
case SO_INT:
redimArray(fetchScriptWord(), 0, newX, 0, newY, kIntArray);
break;
case SO_DWORD:
redimArray(fetchScriptWord(), 0, newX, 0, newY, kDwordArray);
break;
default:
error("o72_redimArray: default type %d", subOp);
}
}
void ScummEngine_v72he::redimArray(int arrayId, int newDim2start, int newDim2end,
int newDim1start, int newDim1end, int type) {
int newSize, oldSize;
if (readVar(arrayId) == 0)
error("redimArray: Reference to zeroed array pointer");
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(arrayId));
if (!ah)
error("redimArray: Invalid array (%d) reference", readVar(arrayId));
newSize = arrayDataSizes[type];
oldSize = arrayDataSizes[FROM_LE_32(ah->type)];
newSize *= (newDim1end - newDim1start + 1) * (newDim2end - newDim2start + 1);
oldSize *= (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) *
(FROM_LE_32(ah->dim2end) - FROM_LE_32(ah->dim2start) + 1);
newSize >>= 3;
oldSize >>= 3;
if (newSize != oldSize)
error("redimArray: array %d redim mismatch", readVar(arrayId));
ah->type = TO_LE_32(type);
ah->dim1start = TO_LE_32(newDim1start);
ah->dim1end = TO_LE_32(newDim1end);
ah->dim2start = TO_LE_32(newDim2start);
ah->dim2end = TO_LE_32(newDim2end);
}
void ScummEngine_v72he::checkArrayLimits(int array, int dim2start, int dim2end, int dim1start, int dim1end) {
if (dim1end < dim1start) {
error("Across max %d smaller than min %d", dim1end, dim1start);
}
if (dim2end < dim2start) {
error("Down max %d smaller than min %d", dim2end, dim2start);
}
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array));
assert(ah);
if ((int)FROM_LE_32(ah->dim2start) > dim2start || (int)FROM_LE_32(ah->dim2end) < dim2end || (int)FROM_LE_32(ah->dim1start) > dim1start || (int)FROM_LE_32(ah->dim1end) < dim1end) {
error("Invalid array access (%d,%d,%d,%d) limit (%d,%d,%d,%d)", dim2start, dim2end, dim1start, dim1end, FROM_LE_32(ah->dim2start), FROM_LE_32(ah->dim2end), FROM_LE_32(ah->dim1start), FROM_LE_32(ah->dim1end));
}
}
void ScummEngine_v72he::copyArray(int array1, int a1_dim2start, int a1_dim2end, int a1_dim1start, int a1_dim1end,
int array2, int a2_dim2start, int a2_dim2end, int a2_dim1start, int a2_dim1end)
{
byte *dst, *src;
int dstPitch, srcPitch;
int rowSize;
checkArrayLimits(array1, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end);
checkArrayLimits(array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end);
int a12_num = a1_dim2end - a1_dim2start + 1;
int a11_num = a1_dim1end - a1_dim1start + 1;
int a22_num = a2_dim2end - a2_dim2start + 1;
int a21_num = a2_dim1end - a2_dim1start + 1;
if (a22_num != a12_num || a21_num != a11_num) {
error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num);
}
if (array1 != array2) {
ArrayHeader *ah1 = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
assert(ah1);
ArrayHeader *ah2 = (ArrayHeader *)getResourceAddress(rtString, readVar(array2));
assert(ah2);
if (FROM_LE_32(ah1->type) == FROM_LE_32(ah2->type)) {
copyArrayHelper(ah1, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
copyArrayHelper(ah2, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
memcpy(dst, src, rowSize);
dst += dstPitch;
src += srcPitch;
}
} else {
for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start) {
int a2dim1 = a2_dim1start;
int a1dim1 = a1_dim1start;
for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1) {
int val = readArray(array2, a2_dim2start, a2dim1);
writeArray(array1, a1_dim2start, a1dim1, val);
}
}
}
} else {
if (a2_dim2start != a1_dim2start || a2_dim1start != a1_dim1start) {
ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array1));
assert(ah);
if (a2_dim2start > a1_dim2start) {
copyArrayHelper(ah, a1_dim2start, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
copyArrayHelper(ah, a2_dim2start, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
} else {
// start at the end, so we copy backwards (in case the indices overlap)
copyArrayHelper(ah, a1_dim2end, a1_dim1start, a1_dim1end, &dst, &dstPitch, &rowSize);
copyArrayHelper(ah, a2_dim2end, a2_dim1start, a2_dim1end, &src, &srcPitch, &rowSize);
dstPitch = -dstPitch;
srcPitch = -srcPitch;
}
for (; a1_dim2start <= a1_dim2end; ++a1_dim2start) {
memcpy(dst, src, rowSize);
dst += dstPitch;
src += srcPitch;
}
}
}
}
void ScummEngine_v72he::copyArrayHelper(ArrayHeader *ah, int idx2, int idx1, int len1, byte **data, int *size, int *num) {
const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1;
const int offset = pitch * (idx2 - FROM_LE_32(ah->dim2start)) + idx1 - FROM_LE_32(ah->dim1start);
switch (FROM_LE_32(ah->type)) {
case kByteArray:
case kStringArray:
*num = len1 - idx1 + 1;
*size = pitch;
*data = ah->data + offset;
break;
case kIntArray:
*num = (len1 - idx1) * 2 + 2;
*size = pitch * 2;
*data = ah->data + offset * 2;
break;
case kDwordArray:
*num = (len1 - idx1) * 4 + 4;
*size = pitch * 4;
*data = ah->data + offset * 4;
break;
default:
error("Invalid array type %d", FROM_LE_32(ah->type));
}
}
void ScummEngine_v72he::o72_readINI() {
byte option[128];
byte *data;
copyScriptString(option, sizeof(option));
byte subOp = fetchScriptByte();
switch (subOp) {
case ScummEngine_v100he::SO_DWORD: // HE 100
case SO_DWORD: // number
if (!strcmp((char *)option, "DisablePrinting") || !strcmp((char *)option, "NoPrinting")) {
push(1);
} else if (!strcmp((char *)option, "DisableMaiaUpdates")) {
// WORKAROUND: Override the update checks.
// This gets checked in Baseball 2001 and will check for
// updates before connecting to the servers, since we
// don't support game updates and such updates appears to
// be lost, skip through the whole thing. This disables
// the update button on the login screen as well.
push(1);
} else if (!strcmp((char *)option, "InternetConnect")) {
// WORKAROUND: In the patched version of Backyard
// Football 2002, they added an option to join a game
// over the internet by manually inputing an IP address.
// Our network implementation does not support this currently,
// and we have our own way of connecting to joins over the
// Internet anyways, so force disable it.
push(0);
} else if (!strcmp((char *)option, "TextOn")) {
push(ConfMan.getBool("subtitles"));
} else if (!strcmp((char *)option, "Disk") && (_game.id == GID_BIRTHDAYRED || _game.id == GID_BIRTHDAYYELLOW)) {
// WORKAROUND: Override the disk detection
// This removes the reliance on having the binary file around (which is
// very bad for the Mac version) just for the scripts to figure out if
// we're running Yellow or Red
if (_game.id == GID_BIRTHDAYRED)
push(4);
else
push(2);
} else {
push(ConfMan.getInt((char *)option));
}
break;
case ScummEngine_v100he::SO_STRING: // HE 100
case SO_STRING: // string
writeVar(0, 0);
if (!strcmp((char *)option, "HE3File")) {
Common::String fileName = generateFilename(-3).toString('/');
int len = resStrLen((const byte *)fileName.c_str());
data = defineArray(0, kStringArray, 0, 0, 0, len);
memcpy(data, fileName.c_str(), len);
} else if (!strcmp((char *)option, "GameResourcePath") || !strcmp((char *)option, "SaveGamePath")) {
// We set SaveGamePath in order to detect where it used
// in convertFilePath and to avoid warning about invalid
// path in Macintosh verisons.
data = defineArray(0, kStringArray, 0, 0, 0, 2);
if (_game.platform == Common::kPlatformMacintosh)
memcpy(data, (const char *)"*:", 2);
else
memcpy(data, (const char *)"*\\", 2);
} else {
const char *entry = (ConfMan.get((char *)option).c_str());
int len = resStrLen((const byte *)entry);
data = defineArray(0, kStringArray, 0, 0, 0, len);
memcpy(data, entry, len);
}
push(readVar(0));
break;
default:
error("o72_readINI: default type %d", subOp);
}
debug(1, "o72_readINI: Option %s", option);
}
void ScummEngine_v72he::o72_writeINI() {
int value;
byte option[256], string[1024];
byte subOp = fetchScriptByte();
switch (subOp) {
case ScummEngine_v100he::SO_DWORD: // HE 100
case SO_DWORD: // number
value = pop();
copyScriptString(option, sizeof(option));
debug(1, "o72_writeINI: Option %s Value %d", option, value);
ConfMan.setInt((char *)option, value);
break;
case ScummEngine_v100he::SO_STRING: // HE 100
case SO_STRING: // string
copyScriptString(string, sizeof(string));
copyScriptString(option, sizeof(option));
debug(1, "o72_writeINI: Option %s String %s", option, string);
// Filter out useless setting
if (!strcmp((char *)option, "HETest"))
return;
// Filter out confusing subtitle setting
if (!strcmp((char *)option, "TextOn"))
return;
// Filter out confusing path settings
if (!strcmp((char *)option, "DownLoadPath") || !strcmp((char *)option, "GameResourcePath") || !strcmp((char *)option, "SaveGamePath"))
return;
ConfMan.set((char *)option, (char *)string);
break;
default:
error("o72_writeINI: default type %d", subOp);
}
ConfMan.flushToDisk();
}
void ScummEngine_v72he::o72_getResourceSize() {
const byte *ptr;
int size;
ResType type;
int resid = pop();
if (_game.heversion == 72) {
push(getSoundResourceSize(resid));
return;
}
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_SOUND_SIZE:
push(getSoundResourceSize(resid));
return;
case SO_ROOM_SIZE:
type = rtRoomImage;
break;
case SO_IMAGE_SIZE:
type = rtImage;
break;
case SO_COSTUME_SIZE:
type = rtCostume;
break;
case SO_SCRIPT_SIZE:
type = rtScript;
break;
default:
error("o72_getResourceSize: default type %d", subOp);
}
ptr = getResourceAddress(type, resid);
assert(ptr);
size = READ_BE_UINT32(ptr + 4) - 8;
push(size);
}
void ScummEngine_v72he::o72_createDirectory() {
byte directoryName[255];
copyScriptString(directoryName, sizeof(directoryName));
debug(1,"o72_createDirectory: %s", directoryName);
}
void ScummEngine_v72he::o72_setSystemMessage() {
byte name[1024];
copyScriptString(name, sizeof(name));
byte subOp = fetchScriptByte();
switch (subOp) {
case SO_PAUSE_MESSAGE:
debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
break;
case SO_PAUSE_TITLE: // Set Version
debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
break;
case SO_PAUSE_OPTION:
debug(1,"o72_setSystemMessage: (%d) %s", subOp, name);
break;
case SO_TITLE_BAR: // Set Window Caption
// TODO: The 'name' string can contain non-ASCII data. This can lead to
// problems, because the encoding used for "name" is not clear.
//
// Possible fixes/workarounds:
// - Simply stop using this. It's a rather unimportant "feature" anyway.
// - Try to translate the text to UTF-32.
//_system->setWindowCaption(Common::U32String((const char *)name));
break;
default:
error("o72_setSystemMessage: default case %d", subOp);
}
}
void ScummEngine_v72he::decodeParseString(int m, int n) {
Actor *a;
int i, colors, size;
int args[31];
byte name[1024];
byte b = fetchScriptByte();
switch (b) {
case ScummEngine_v6::SubOpType::SO_AT:
_string[m].ypos = pop();
_string[m].xpos = pop();
_string[m].overhead = false;
break;
case SO_COLOR:
_string[m].color = pop();
break;
case SO_CLIPPED:
_string[m].right = pop();
break;
case SO_CENTER:
_string[m].center = true;
_string[m].overhead = false;
break;
case SO_LEFT:
_string[m].center = false;
_string[m].overhead = false;
break;
case SO_OVERHEAD:
_string[m].overhead = true;
_string[m].no_talk_anim = false;
break;
case SO_MUMBLE:
_string[m].no_talk_anim = true;
break;
case SO_TEXTSTRING:
printString(m, _scriptPointer);
_scriptPointer += resStrLen(_scriptPointer) + 1;
break;
case SO_FORMATTED_STRING:
decodeScriptString(name, true);
printString(m, name);
break;
case SO_TALKIE:
{
byte *dataPtr = getResourceAddress(rtTalkie, pop());
byte *text = findWrappedBlock(MKTAG('T','E','X','T'), dataPtr, 0, 0);
size = getResourceDataSize(text);
memcpy(name, text, size);
printString(m, name);
}
break;
case SO_COLOR_LIST:
colors = pop();
if (colors == 1) {
_string[m].color = pop();
// WORKAROUND bug #13730: defined subtitles color 16 is very dark and hard to read on the dark background.
// we change it to brighter color to ease reading.
if (_game.id == GID_FREDDI4 && _game.heversion == 98 && _currentRoom == 43 && _string[m].color == 16 && enhancementEnabled(kEnhSubFmtCntChanges))
_string[m].color = 200;
} else {
push(colors);
getStackList(args, ARRAYSIZE(args));
for (i = 0; i < 16; i++)
_charsetColorMap[i] = _charsetData[_string[m]._default.charset][i] = (unsigned char)args[i];
_string[m].color = _charsetColorMap[0];
}
break;
case SO_BASEOP:
_string[m].loadDefault();
if (n) {
_actorToPrintStrFor = pop();
if (_actorToPrintStrFor != 0xFF) {
a = derefActor(_actorToPrintStrFor, "decodeParseString");
_string[m].color = a->_talkColor;
}
}
break;
case SO_END:
_string[m].saveDefault();
break;
default:
error("decodeParseString: default case 0x%x", b);
}
}
} // End of namespace Scumm
#endif // ENABLE_HE