mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
663 lines
13 KiB
C++
663 lines
13 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/>.
|
|
*
|
|
*/
|
|
|
|
#include "glk/glulx/glulx.h"
|
|
|
|
namespace Glk {
|
|
namespace Glulx {
|
|
|
|
/**
|
|
* Git passes along function arguments in reverse order. To make our lives more interesting
|
|
*/
|
|
#ifdef ARGS_REVERSED
|
|
#define ARG(argv, argc, ix) (argv[(argc-1)-ix])
|
|
#else
|
|
#define ARG(argv, argc, ix) (argv[ix])
|
|
#endif
|
|
|
|
/**
|
|
* Any function can be called with any number of arguments. This macro lets us snarf a given argument,
|
|
* or zero if it wasn't supplied.
|
|
*/
|
|
#define ARG_IF_GIVEN(argv, argc, ix) ((argc > ix) ? (ARG(argv, argc, ix)) : 0)
|
|
|
|
acceleration_func Glulx::accel_find_func(uint index) {
|
|
switch (index) {
|
|
case 0:
|
|
return nullptr; // 0 always means no acceleration
|
|
case 1:
|
|
return &Glulx::func_1_z__region;
|
|
case 2:
|
|
return &Glulx::func_2_cp__tab;
|
|
case 3:
|
|
return &Glulx::func_3_ra__pr;
|
|
case 4:
|
|
return &Glulx::func_4_rl__pr;
|
|
case 5:
|
|
return &Glulx::func_5_oc__cl;
|
|
case 6:
|
|
return &Glulx::func_6_rv__pr;
|
|
case 7:
|
|
return &Glulx::func_7_op__pr;
|
|
case 8:
|
|
return &Glulx::func_8_cp__tab;
|
|
case 9:
|
|
return &Glulx::func_9_ra__pr;
|
|
case 10:
|
|
return &Glulx::func_10_rl__pr;
|
|
case 11:
|
|
return &Glulx::func_11_oc__cl;
|
|
case 12:
|
|
return &Glulx::func_12_rv__pr;
|
|
case 13:
|
|
return &Glulx::func_13_op__pr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
acceleration_func Glulx::accel_get_func(uint addr) {
|
|
int bucknum;
|
|
accelentry_t *ptr;
|
|
|
|
if (!accelentries)
|
|
return nullptr;
|
|
|
|
bucknum = (addr % ACCEL_HASH_SIZE);
|
|
for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
|
|
if (ptr->addr == addr)
|
|
return ptr->func;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void Glulx::accel_iterate_funcs(void (*func)(uint index, uint addr)) {
|
|
int bucknum;
|
|
accelentry_t *ptr;
|
|
|
|
if (!accelentries)
|
|
return;
|
|
|
|
for (bucknum = 0; bucknum < ACCEL_HASH_SIZE; bucknum++) {
|
|
for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
|
|
if (ptr->func) {
|
|
func(ptr->index, ptr->addr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Glulx::accel_set_func(uint index, uint addr) {
|
|
int bucknum;
|
|
accelentry_t *ptr;
|
|
int functype;
|
|
acceleration_func new_func = nullptr;
|
|
|
|
/* Check the Glulx type identifier byte. */
|
|
functype = Mem1(addr);
|
|
if (functype != 0xC0 && functype != 0xC1) {
|
|
fatal_error_i("Attempt to accelerate non-function.", addr);
|
|
}
|
|
|
|
if (!accelentries) {
|
|
accelentries = (accelentry_t **)glulx_malloc(ACCEL_HASH_SIZE
|
|
* sizeof(accelentry_t *));
|
|
if (!accelentries)
|
|
fatal_error("Cannot malloc acceleration table.");
|
|
for (bucknum = 0; bucknum < ACCEL_HASH_SIZE; bucknum++)
|
|
accelentries[bucknum] = nullptr;
|
|
}
|
|
|
|
new_func = accel_find_func(index);
|
|
/* Might be nullptr, if the index is zero or not recognized. */
|
|
|
|
bucknum = (addr % ACCEL_HASH_SIZE);
|
|
for (ptr = accelentries[bucknum]; ptr; ptr = ptr->next) {
|
|
if (ptr->addr == addr)
|
|
break;
|
|
}
|
|
if (!ptr) {
|
|
if (!new_func) {
|
|
return; /* no need for a new entry */
|
|
}
|
|
ptr = (accelentry_t *)glulx_malloc(sizeof(accelentry_t));
|
|
if (!ptr)
|
|
fatal_error("Cannot malloc acceleration entry.");
|
|
ptr->addr = addr;
|
|
ptr->index = 0;
|
|
ptr->func = nullptr;
|
|
ptr->next = accelentries[bucknum];
|
|
accelentries[bucknum] = ptr;
|
|
}
|
|
|
|
ptr->index = index;
|
|
ptr->func = new_func;
|
|
}
|
|
|
|
void Glulx::accel_set_param(uint index, uint val) {
|
|
switch (index) {
|
|
case 0:
|
|
classes_table = val;
|
|
break;
|
|
case 1:
|
|
indiv_prop_start = val;
|
|
break;
|
|
case 2:
|
|
class_metaclass = val;
|
|
break;
|
|
case 3:
|
|
object_metaclass = val;
|
|
break;
|
|
case 4:
|
|
routine_metaclass = val;
|
|
break;
|
|
case 5:
|
|
string_metaclass = val;
|
|
break;
|
|
case 6:
|
|
self = val;
|
|
break;
|
|
case 7:
|
|
num_attr_bytes = val;
|
|
break;
|
|
case 8:
|
|
cpv__start = val;
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint Glulx::accel_get_param_count() const {
|
|
return 9;
|
|
}
|
|
|
|
uint Glulx::accel_get_param(uint index) const {
|
|
switch (index) {
|
|
case 0:
|
|
return classes_table;
|
|
case 1:
|
|
return indiv_prop_start;
|
|
case 2:
|
|
return class_metaclass;
|
|
case 3:
|
|
return object_metaclass;
|
|
case 4:
|
|
return routine_metaclass;
|
|
case 5:
|
|
return string_metaclass;
|
|
case 6:
|
|
return self;
|
|
case 7:
|
|
return num_attr_bytes;
|
|
case 8:
|
|
return cpv__start;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void Glulx::accel_error(const char *msg) {
|
|
glk_put_char('\n');
|
|
glk_put_string(msg);
|
|
glk_put_char('\n');
|
|
}
|
|
|
|
int Glulx::obj_in_class(uint obj) {
|
|
// This checks whether obj is contained in Class, not whether it is a member of Class
|
|
return (Mem4(obj + 13 + num_attr_bytes) == class_metaclass);
|
|
}
|
|
|
|
uint Glulx::get_prop(uint obj, uint id) {
|
|
uint cla = 0;
|
|
uint prop;
|
|
uint call_argv[2];
|
|
|
|
if (id & 0xFFFF0000) {
|
|
cla = Mem4(classes_table + ((id & 0xFFFF) * 4));
|
|
ARG(call_argv, 2, 0) = obj;
|
|
ARG(call_argv, 2, 1) = cla;
|
|
if (func_5_oc__cl(2, call_argv) == 0)
|
|
return 0;
|
|
|
|
id >>= 16;
|
|
obj = cla;
|
|
}
|
|
|
|
ARG(call_argv, 2, 0) = obj;
|
|
ARG(call_argv, 2, 1) = id;
|
|
prop = func_2_cp__tab(2, call_argv);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
if (obj_in_class(obj) && (cla == 0)) {
|
|
if ((id < indiv_prop_start) || (id >= indiv_prop_start + 8))
|
|
return 0;
|
|
}
|
|
|
|
if (Mem4(self) != obj) {
|
|
if (Mem1(prop + 9) & 1)
|
|
return 0;
|
|
}
|
|
return prop;
|
|
}
|
|
|
|
uint Glulx::get_prop_new(uint obj, uint id) {
|
|
uint cla = 0;
|
|
uint prop;
|
|
uint call_argv[2];
|
|
|
|
if (id & 0xFFFF0000) {
|
|
cla = Mem4(classes_table + ((id & 0xFFFF) * 4));
|
|
ARG(call_argv, 2, 0) = obj;
|
|
ARG(call_argv, 2, 1) = cla;
|
|
if (func_11_oc__cl(2, call_argv) == 0)
|
|
return 0;
|
|
|
|
id >>= 16;
|
|
obj = cla;
|
|
}
|
|
|
|
ARG(call_argv, 2, 0) = obj;
|
|
ARG(call_argv, 2, 1) = id;
|
|
prop = func_8_cp__tab(2, call_argv);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
if (obj_in_class(obj) && (cla == 0)) {
|
|
if ((id < indiv_prop_start) || (id >= indiv_prop_start + 8))
|
|
return 0;
|
|
}
|
|
|
|
if (Mem4(self) != obj) {
|
|
if (Mem1(prop + 9) & 1)
|
|
return 0;
|
|
}
|
|
return prop;
|
|
}
|
|
|
|
uint Glulx::func_1_z__region(uint argc, uint *argv) {
|
|
uint addr;
|
|
uint tb;
|
|
|
|
if (argc < 1)
|
|
return 0;
|
|
|
|
addr = ARG(argv, argc, 0);
|
|
if (addr < 36)
|
|
return 0;
|
|
if (addr >= endmem)
|
|
return 0;
|
|
|
|
tb = Mem1(addr);
|
|
if (tb >= 0xE0) {
|
|
return 3;
|
|
}
|
|
if (tb >= 0xC0) {
|
|
return 2;
|
|
}
|
|
if (tb >= 0x70 && tb <= 0x7F && addr >= ramstart) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint Glulx::func_2_cp__tab(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint otab, max;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
if (func_1_z__region(1, &obj) != 1) {
|
|
accel_error("[** Programming error: tried to find the \".\" of (something) **]");
|
|
return 0;
|
|
}
|
|
|
|
otab = Mem4(obj + 16);
|
|
if (!otab)
|
|
return 0;
|
|
|
|
max = Mem4(otab);
|
|
otab += 4;
|
|
/* @binarysearch id 2 otab 10 max 0 0 res; */
|
|
return binary_search(id, 2, otab, 10, max, 0, 0);
|
|
}
|
|
|
|
uint Glulx::func_3_ra__pr(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint prop;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
prop = get_prop(obj, id);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
return Mem4(prop + 4);
|
|
}
|
|
|
|
uint Glulx::func_4_rl__pr(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint prop;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
prop = get_prop(obj, id);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
return 4 * Mem2(prop + 2);
|
|
}
|
|
|
|
uint Glulx::func_5_oc__cl(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint cla;
|
|
uint zr, prop, inlist, inlistlen, jx;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
cla = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
zr = func_1_z__region(1, &obj);
|
|
if (zr == 3)
|
|
return (cla == string_metaclass) ? 1 : 0;
|
|
if (zr == 2)
|
|
return (cla == routine_metaclass) ? 1 : 0;
|
|
if (zr != 1)
|
|
return 0;
|
|
|
|
if (cla == class_metaclass) {
|
|
if (obj_in_class(obj))
|
|
return 1;
|
|
if (obj == class_metaclass)
|
|
return 1;
|
|
if (obj == string_metaclass)
|
|
return 1;
|
|
if (obj == routine_metaclass)
|
|
return 1;
|
|
if (obj == object_metaclass)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (cla == object_metaclass) {
|
|
if (obj_in_class(obj))
|
|
return 0;
|
|
if (obj == class_metaclass)
|
|
return 0;
|
|
if (obj == string_metaclass)
|
|
return 0;
|
|
if (obj == routine_metaclass)
|
|
return 0;
|
|
if (obj == object_metaclass)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
if ((cla == string_metaclass) || (cla == routine_metaclass))
|
|
return 0;
|
|
|
|
if (!obj_in_class(cla)) {
|
|
accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
|
|
return 0;
|
|
}
|
|
|
|
prop = get_prop(obj, 2);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
inlist = Mem4(prop + 4);
|
|
if (inlist == 0)
|
|
return 0;
|
|
|
|
inlistlen = Mem2(prop + 2);
|
|
for (jx = 0; jx < inlistlen; jx++) {
|
|
if (Mem4(inlist + (4 * jx)) == cla)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint Glulx::func_6_rv__pr(uint argc, uint *argv) {
|
|
uint id;
|
|
uint addr;
|
|
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
addr = func_3_ra__pr(argc, argv);
|
|
|
|
if (addr == 0) {
|
|
if ((id > 0) && (id < indiv_prop_start))
|
|
return Mem4(cpv__start + (4 * id));
|
|
|
|
accel_error("[** Programming error: tried to read (something) **]");
|
|
return 0;
|
|
}
|
|
|
|
return Mem4(addr);
|
|
}
|
|
|
|
uint Glulx::func_7_op__pr(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint zr;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
zr = func_1_z__region(1, &obj);
|
|
if (zr == 3) {
|
|
/* print is INDIV_PROP_START+6 */
|
|
if (id == indiv_prop_start + 6)
|
|
return 1;
|
|
/* print_to_array is INDIV_PROP_START+7 */
|
|
if (id == indiv_prop_start + 7)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (zr == 2) {
|
|
/* call is INDIV_PROP_START+5 */
|
|
return ((id == indiv_prop_start + 5) ? 1 : 0);
|
|
}
|
|
if (zr != 1)
|
|
return 0;
|
|
|
|
if ((id >= indiv_prop_start) && (id < indiv_prop_start + 8)) {
|
|
if (obj_in_class(obj))
|
|
return 1;
|
|
}
|
|
|
|
return ((func_3_ra__pr(argc, argv)) ? 1 : 0);
|
|
}
|
|
|
|
uint Glulx::func_8_cp__tab(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint otab, max;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
if (func_1_z__region(1, &obj) != 1) {
|
|
accel_error("[** Programming error: tried to find the \".\" of (something) **]");
|
|
return 0;
|
|
}
|
|
|
|
otab = Mem4(obj + 4 * (3 + (int)(num_attr_bytes / 4)));
|
|
if (!otab)
|
|
return 0;
|
|
|
|
max = Mem4(otab);
|
|
otab += 4;
|
|
/* @binarysearch id 2 otab 10 max 0 0 res; */
|
|
return binary_search(id, 2, otab, 10, max, 0, 0);
|
|
}
|
|
|
|
uint Glulx::func_9_ra__pr(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint prop;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
prop = get_prop_new(obj, id);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
return Mem4(prop + 4);
|
|
}
|
|
|
|
uint Glulx::func_10_rl__pr(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint prop;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
prop = get_prop_new(obj, id);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
return 4 * Mem2(prop + 2);
|
|
}
|
|
|
|
uint Glulx::func_11_oc__cl(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint cla;
|
|
uint zr, prop, inlist, inlistlen, jx;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
cla = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
zr = func_1_z__region(1, &obj);
|
|
if (zr == 3)
|
|
return (cla == string_metaclass) ? 1 : 0;
|
|
if (zr == 2)
|
|
return (cla == routine_metaclass) ? 1 : 0;
|
|
if (zr != 1)
|
|
return 0;
|
|
|
|
if (cla == class_metaclass) {
|
|
if (obj_in_class(obj))
|
|
return 1;
|
|
if (obj == class_metaclass)
|
|
return 1;
|
|
if (obj == string_metaclass)
|
|
return 1;
|
|
if (obj == routine_metaclass)
|
|
return 1;
|
|
if (obj == object_metaclass)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (cla == object_metaclass) {
|
|
if (obj_in_class(obj))
|
|
return 0;
|
|
if (obj == class_metaclass)
|
|
return 0;
|
|
if (obj == string_metaclass)
|
|
return 0;
|
|
if (obj == routine_metaclass)
|
|
return 0;
|
|
if (obj == object_metaclass)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
if ((cla == string_metaclass) || (cla == routine_metaclass))
|
|
return 0;
|
|
|
|
if (!obj_in_class(cla)) {
|
|
accel_error("[** Programming error: tried to apply 'ofclass' with non-class **]");
|
|
return 0;
|
|
}
|
|
|
|
prop = get_prop_new(obj, 2);
|
|
if (prop == 0)
|
|
return 0;
|
|
|
|
inlist = Mem4(prop + 4);
|
|
if (inlist == 0)
|
|
return 0;
|
|
|
|
inlistlen = Mem2(prop + 2);
|
|
for (jx = 0; jx < inlistlen; jx++) {
|
|
if (Mem4(inlist + (4 * jx)) == cla)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint Glulx::func_12_rv__pr(uint argc, uint *argv) {
|
|
uint id;
|
|
uint addr;
|
|
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
addr = func_9_ra__pr(argc, argv);
|
|
|
|
if (addr == 0) {
|
|
if ((id > 0) && (id < indiv_prop_start))
|
|
return Mem4(cpv__start + (4 * id));
|
|
|
|
accel_error("[** Programming error: tried to read (something) **]");
|
|
return 0;
|
|
}
|
|
|
|
return Mem4(addr);
|
|
}
|
|
|
|
uint Glulx::func_13_op__pr(uint argc, uint *argv) {
|
|
uint obj;
|
|
uint id;
|
|
uint zr;
|
|
|
|
obj = ARG_IF_GIVEN(argv, argc, 0);
|
|
id = ARG_IF_GIVEN(argv, argc, 1);
|
|
|
|
zr = func_1_z__region(1, &obj);
|
|
if (zr == 3) {
|
|
/* print is INDIV_PROP_START+6 */
|
|
if (id == indiv_prop_start + 6)
|
|
return 1;
|
|
/* print_to_array is INDIV_PROP_START+7 */
|
|
if (id == indiv_prop_start + 7)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (zr == 2) {
|
|
/* call is INDIV_PROP_START+5 */
|
|
return ((id == indiv_prop_start + 5) ? 1 : 0);
|
|
}
|
|
if (zr != 1)
|
|
return 0;
|
|
|
|
if ((id >= indiv_prop_start) && (id < indiv_prop_start + 8)) {
|
|
if (obj_in_class(obj))
|
|
return 1;
|
|
}
|
|
|
|
return ((func_9_ra__pr(argc, argv)) ? 1 : 0);
|
|
}
|
|
|
|
} // End of namespace Glulx
|
|
} // End of namespace Glk
|