/* 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$ * */ #ifdef WIN32 # include # include # undef ARRAYSIZE #endif #include "sci/engine/gc.h" #include "sci/include/sciresource.h" #include "sci/include/engine.h" #include "sci/include/gfx_operations.h" #include "sci/engine/kernel_types.h" /* New kernel functions */ reg_t kStrLen(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetFarText(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kReadNumber(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kStrCat(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kStrCmp(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetSynonyms(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kLock(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kPalette(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNumCels(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNumLoops(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDrawCel(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCoordPri(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kPriCoord(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kShakeScreen(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetCursor(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kMoveCursor(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kShow(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kPicNotValid(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kOnControl(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDrawPic(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetPort(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetPort(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNewWindow(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDisposeWindow(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCelWide(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCelHigh(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetJump(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDirLoop(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDoAvoider(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetAngle(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetDistance(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kRandom(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAbs(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSqrt(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kTimesSin(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kTimesCos(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCosMult(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSinMult(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kTimesTan(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kTimesCot(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCosDiv(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSinDiv(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kValidPath(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFOpen(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFPuts(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFGets(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFClose(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kMapKeyToDir(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGlobalToLocal(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kLocalToGlobal(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kWait(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kRestartGame(struct _state *s, int funct_nr, int argc, reg_t *argv); #ifdef WIN32 reg_t kDeviceInfo_Win32(struct _state *s, int funct_nr, int argc, reg_t *argv); #else reg_t kDeviceInfo_Unix(struct _state *s, int funct_nr, int argc, reg_t *argv); #endif reg_t kGetEvent(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCheckFreeSpace(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFlushResources(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetSaveFiles(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetDebug(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCheckSaveGame(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSaveGame(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kRestoreGame(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFileIO(struct _state *s, int funct_nr, int argc, reg_t *argp); reg_t kGetTime(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kHaveMouse(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kJoystick(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGameIsRestarting(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetCWD(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSort(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kStrEnd(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kMemory(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAvoidPath(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kParse(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSaid(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kStrCpy(struct _state *s, int funct_nr, int argc, reg_t *argp); reg_t kStrAt(struct _state *s, int funct_nr, int argc, reg_t *argp); reg_t kEditControl(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDrawControl(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kHiliteControl(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kClone(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDisposeClone(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kCanBeHere(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetNowSeen(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kInitBresen(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDoBresen(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kBaseSetter(struct _state *s, int funct_nr, int argc, reg_t *argp); reg_t kAddToPic(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAnimate(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDisplay(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGraph(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFormat(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDoSound(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAddMenu(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kSetMenu(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetMenu(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDrawStatus(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDrawMenuBar(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kMenuSelect(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kLoad(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kUnLoad(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kScriptID(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDisposeScript(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kIsObject(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kRespondsTo(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNewList(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDisposeList(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNewNode(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFirstNode(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kLastNode(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kEmptyList(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNextNode(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kPrevNode(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kNodeValue(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAddAfter(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAddToFront(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kAddToEnd(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kFindKey(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDeleteKey(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kMemoryInfo(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kGetSaveDir(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kTextSize(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kIsItSkip(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kMessage(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t kDoAudio(struct _state *s, int funct_nr, int argc, reg_t *argv); reg_t k_Unknown(struct _state *s, int funct_nr, int argc, reg_t *argv); /* The Unknown/Unnamed kernel function */ reg_t kstub(struct _state *s, int funct_nr, int argc, reg_t *argv); /* for unimplemented kernel functions */ reg_t kNOP(struct _state *s, int funct_nr, int argc, reg_t *argv); /* for kernel functions that don't do anything */ reg_t kFsciEmu(struct _state *s, int funct_nr, int argc, reg_t *argv); /* Emulating "old" kernel functions on the heap */ #define SCI_MAPPED_UNKNOWN_KFUNCTIONS_NR 0x75 /* kfunct_mappers below doubles for unknown kfunctions */ static int sci_max_allowed_unknown_kernel_functions[] = { 0, 0x72, /* SCI0 */ 0x7b, /* SCI01/EGA */ 0x7b, /* SCI01/VGA */ 0x7b, /* SCI1/EARLY */ 0x7b, /* SCI1/LATE */ 0x7b, /* SCI1.1 */ 0x0, /* SCI32 */ }; #define DEFUN(nm, cname, sig) {KF_NEW, nm, {cname, sig, NULL}} #define NOFUN(nm) {KF_NONE, nm, {NULL, NULL, NULL}} sci_kernel_function_t kfunct_mappers[] = { /*00*/ DEFUN("Load", kLoad, "iii*"), /*01*/ DEFUN("UnLoad", kUnLoad, "i.*"), /*02*/ DEFUN("ScriptID", kScriptID, "Ioi*"), /*03*/ DEFUN("DisposeScript", kDisposeScript, "Oi"), /* Work around QfG1 bug */ /*04*/ DEFUN("Clone", kClone, "o"), /*05*/ DEFUN("DisposeClone", kDisposeClone, "o"), /*06*/ DEFUN("IsObject", kIsObject, "."), /*07*/ DEFUN("RespondsTo", kRespondsTo, ".i"), /*08*/ DEFUN("DrawPic", kDrawPic, "i*"), /*09*/ DEFUN("Show", kShow, "i"), /*0a*/ DEFUN("PicNotValid", kPicNotValid, "i*"), /*0b*/ DEFUN("Animate", kAnimate, "LI*"), /* More like (li?)? */ /*0c*/ DEFUN("SetNowSeen", kSetNowSeen, "oi*"), /* The second parameter is ignored */ /*0d*/ DEFUN("NumLoops", kNumLoops, "o"), /*0e*/ DEFUN("NumCels", kNumCels, "o"), /*0f*/ DEFUN("CelWide", kCelWide, "iOiOi"), /*10*/ DEFUN("CelHigh", kCelHigh, "iOiOi"), /*11*/ DEFUN("DrawCel", kDrawCel, "iiiiii*"), /*12*/ DEFUN("AddToPic", kAddToPic, "Il*"), /*13*/ DEFUN("NewWindow", kNewWindow, "iiiiZRi*"), /*14*/ DEFUN("GetPort", kGetPort, ""), /*15*/ DEFUN("SetPort", kSetPort, "ii*"), /*16*/ DEFUN("DisposeWindow", kDisposeWindow, "ii*"), /*17*/ DEFUN("DrawControl", kDrawControl, "o"), /*18*/ DEFUN("HiliteControl", kHiliteControl, "o"), /*19*/ DEFUN("EditControl", kEditControl, "ZoZo"), /*1a*/ DEFUN("TextSize", kTextSize, "rZrii*r*"), /*1b*/ DEFUN("Display", kDisplay, ".*"), /*1c*/ DEFUN("GetEvent", kGetEvent, "io"), /*1d*/ DEFUN("GlobalToLocal", kGlobalToLocal, "o"), /*1e*/ DEFUN("LocalToGlobal", kLocalToGlobal, "o"), /*1f*/ DEFUN("MapKeyToDir", kMapKeyToDir, "o"), /*20*/ DEFUN("DrawMenuBar", kDrawMenuBar, "i"), /*21*/ DEFUN("MenuSelect", kMenuSelect, "oi*"), /*22*/ DEFUN("AddMenu", kAddMenu, "rr"), /*23*/ DEFUN("DrawStatus", kDrawStatus, "Zri*"), /*24*/ DEFUN("Parse", kParse, "ro"), /*25*/ DEFUN("Said", kSaid, "Zr"), /*26*/ DEFUN("SetSynonyms", kSetSynonyms, "o"), /*27*/ DEFUN("HaveMouse", kHaveMouse, ""), /*28*/ DEFUN("SetCursor", kSetCursor, "i*"), /*28*/ DEFUN("MoveCursor", kMoveCursor, "ii*"), /*29*/ DEFUN("FOpen", kFOpen, "ri"), /*2a*/ DEFUN("FPuts", kFPuts, "ir"), /*2b*/ DEFUN("FGets", kFGets, "rii"), /*2c*/ DEFUN("FClose", kFClose, "i"), /*2d*/ DEFUN("SaveGame", kSaveGame, "rirr*"), /*2e*/ DEFUN("RestoreGame", kRestoreGame, "rir*"), /*2f*/ DEFUN("RestartGame", kRestartGame, ""), /*30*/ DEFUN("GameIsRestarting", kGameIsRestarting, "i*"), /*31*/ DEFUN("DoSound", kDoSound, "iIo*"), /*32*/ DEFUN("NewList", kNewList, ""), /*33*/ DEFUN("DisposeList", kDisposeList, "l"), /*34*/ DEFUN("NewNode", kNewNode, ".."), /*35*/ DEFUN("FirstNode", kFirstNode, "Zl"), /*36*/ DEFUN("LastNode", kLastNode, "l"), /*37*/ DEFUN("EmptyList", kEmptyList, "l"), /*38*/ DEFUN("NextNode", kNextNode, "n!"), /*39*/ DEFUN("PrevNode", kPrevNode, "n"), /*3a*/ DEFUN("NodeValue", kNodeValue, "Zn!"), /*3b*/ DEFUN("AddAfter", kAddAfter, "lnn"), /*3c*/ DEFUN("AddToFront", kAddToFront, "ln"), /*3d*/ DEFUN("AddToEnd", kAddToEnd, "ln"), /*3e*/ DEFUN("FindKey", kFindKey, "l."), /*3f*/ DEFUN("DeleteKey", kDeleteKey, "l."), /*40*/ DEFUN("Random", kRandom, "i*"), /*41*/ DEFUN("Abs", kAbs, "Oi"), /*42*/ DEFUN("Sqrt", kSqrt, "i"), /*43*/ DEFUN("GetAngle", kGetAngle, "iiii"), /*44*/ DEFUN("GetDistance", kGetDistance, "iiiii*"), /*45*/ DEFUN("Wait", kWait, "i"), /*46*/ DEFUN("GetTime", kGetTime, "i*"), /*47*/ DEFUN("StrEnd", kStrEnd, "r"), /*48*/ DEFUN("StrCat", kStrCat, "rr"), /*49*/ DEFUN("StrCmp", kStrCmp, "rri*"), /*4a*/ DEFUN("StrLen", kStrLen, "r"), /*4b*/ DEFUN("StrCpy", kStrCpy, "rri*"), /*4c*/ DEFUN("Format", kFormat, "r.*"), /*4d*/ DEFUN("GetFarText", kGetFarText, "iir"), /*4e*/ DEFUN("ReadNumber", kReadNumber, "r"), /*4f*/ DEFUN("BaseSetter", kBaseSetter, "o"), /*50*/ DEFUN("DirLoop", kDirLoop, "oi"), /*51*/ DEFUN("CanBeHere", kCanBeHere, "ol*"), /*51*/ DEFUN("CantBeHere", kCanBeHere, "ol*"), /*52*/ DEFUN("OnControl", kOnControl, "i*"), /*53*/ DEFUN("InitBresen", kInitBresen, "oi*"), /*54*/ DEFUN("DoBresen", kDoBresen, "o"), /*55*/ DEFUN("DoAvoider", kDoAvoider, "o"), /*56*/ DEFUN("SetJump", kSetJump, "oiii"), /*57*/ DEFUN("SetDebug", kSetDebug, "i*"), /*58*/ NOFUN("InspectObj"), /*59*/ NOFUN("ShowSends"), /*5a*/ NOFUN("ShowObjs"), /*5b*/ NOFUN("ShowFree"), /*5c*/ DEFUN("MemoryInfo", kMemoryInfo, "i"), /*5d*/ NOFUN("StackUsage"), /*5e*/ NOFUN("Profiler"), /*5f*/ DEFUN("GetMenu", kGetMenu, "i."), /*60*/ DEFUN("SetMenu", kSetMenu, "i.*"), /*61*/ DEFUN("GetSaveFiles", kGetSaveFiles, "rrr"), /*62*/ DEFUN("GetCWD", kGetCWD, "r"), /*63*/ DEFUN("CheckFreeSpace", kCheckFreeSpace, "r"), /*64*/ DEFUN("ValidPath", kValidPath, "r"), /*65*/ DEFUN("CoordPri", kCoordPri, "i"), /*66*/ DEFUN("StrAt", kStrAt, "rii*"), #ifdef WIN32 /*67*/ DEFUN("DeviceInfo", kDeviceInfo_Win32, "i.*"), #else /* !WIN32 */ /*67*/ DEFUN("DeviceInfo", kDeviceInfo_Unix, "i.*"), #endif /*68*/ DEFUN("GetSaveDir", kGetSaveDir, ""), /*69*/ DEFUN("CheckSaveGame", kCheckSaveGame, ".*"), /*6a*/ DEFUN("ShakeScreen", kShakeScreen, "ii*"), /*6b*/ DEFUN("FlushResources", kFlushResources, "i"), /*6c*/ DEFUN("TimesSin", kTimesSin, "ii"), /*6d*/ DEFUN("TimesCos", kTimesCos, "ii"), #if 0 /*6e*/ NOFUN(NULL), /*6f*/ NOFUN(NULL), #else /*6e*/ DEFUN("6e", kTimesSin, "ii"), /*6f*/ DEFUN("6f", kTimesCos, "ii"), #endif /*70*/ DEFUN("Graph", kGraph, ".*"), /*71*/ DEFUN("Joystick", kJoystick, ".*"), /*72*/ NOFUN(NULL), /*73*/ NOFUN(NULL), /* Experimental functions */ /*74*/ DEFUN("FileIO", kFileIO, "i.*"), /*(?)*/ DEFUN("Memory", kMemory, "i.*"), /*(?)*/ DEFUN("Sort", kSort, "ooo"), /*(?)*/ DEFUN("AvoidPath", kAvoidPath, "ii.*"), /*(?)*/ DEFUN("Lock", kLock, "iii*"), /*(?)*/ DEFUN("Palette", kPalette, "i*"), /*(?)*/ DEFUN("IsItSkip", kIsItSkip, "iiiii"), /* Non-experimental Functions without a fixed ID */ DEFUN("CosMult", kTimesCos, "ii"), DEFUN("SinMult", kTimesSin, "ii"), /*(?)*/ DEFUN("CosDiv", kCosDiv, "ii"), /*(?)*/ DEFUN("PriCoord", kPriCoord, "i"), /*(?)*/ DEFUN("SinDiv", kSinDiv, "ii"), /*(?)*/ DEFUN("TimesCot", kTimesCot, "ii"), /*(?)*/ DEFUN("TimesTan", kTimesTan, "ii"), DEFUN("Message", kMessage, ".*"), DEFUN("DoAudio", kDoAudio, ".*"), /* Special and NOP stuff */ {KF_NEW, NULL, {k_Unknown, NULL, NULL}}, {KF_TERMINATOR, NULL, {NULL, NULL, NULL}} /* Terminator */ }; static const char *argtype_description[] = {"Undetermined (WTF?)", "List", "Node", "Object", "Reference", "Arithmetic"}; /******************** Kernel Oops ********************/ int kernel_oops(state_t *s, const char *file, int line, const char *reason) { sciprintf("Kernel Oops in file %s, line %d: %s\n", file, line, reason); fprintf(stderr, "Kernel Oops in file %s, line %d: %s\n", file, line, reason); script_debug_flag = script_error_flag = 1; return 0; } /* Allocates a set amount of memory for a specified use and returns a handle to it. */ reg_t kalloc(state_t *s, const char *type, int space) { reg_t reg; sm_alloc_hunk_entry(&s->seg_manager, type, space, ®); SCIkdebug(SCIkMEM, "Allocated %d at hunk "PREG" (%s)\n", space, PRINT_REG(reg), type); return reg; } int has_kernel_function(state_t *s, const char *kname) { int i = 0; while (s->kernel_names[i]) { if (!strcmp(s->kernel_names[i], kname)) return 1; i++; } return 0; } /* Returns a pointer to the memory indicated by the specified handle */ byte * kmem(state_t *s, reg_t handle) { mem_obj_t *mobj = GET_SEGMENT(s->seg_manager, handle.segment, MEM_OBJ_HUNK); hunk_table_t *ht = &(mobj->data.hunks); if (!mobj || !ENTRY_IS_VALID(ht, handle.offset)) { SCIkwarn(SCIkERROR, "Error: kmem() with invalid handle\n"); return NULL; } return (byte *) ht->table[handle.offset].entry.mem; } /* Frees the specified handle. Returns 0 on success, 1 otherwise. */ int kfree(state_t *s, reg_t handle) { sm_free_hunk_entry(&s->seg_manager, handle); return 0; } /*****************************************/ /************* Kernel functions **********/ /*****************************************/ char *old_save_dir; reg_t kRestartGame(state_t *s, int funct_nr, int argc, reg_t *argv) { char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1); old_save_dir = strdup(deref_save_dir); s->restarting_flags |= SCI_GAME_IS_RESTARTING_NOW; s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED_AT_LEAST_ONCE; /* This appears to help */ s->execution_stack_pos = s->execution_stack_base; script_abort_flag = 1; /* Force vm to abort ASAP */ return NULL_REG; } /* kGameIsRestarting(): ** Returns the restarting_flag in acc */ reg_t kGameIsRestarting(state_t *s, int funct_nr, int argc, reg_t *argv) { char *deref_save_dir = (char*)kernel_dereference_bulk_pointer(s, s->save_dir_copy, 1); if (old_save_dir && deref_save_dir) { strcpy(deref_save_dir, old_save_dir); free(old_save_dir); old_save_dir = NULL; } s->r_acc = make_reg(0, (s->restarting_flags & SCI_GAME_WAS_RESTARTED)); if (argc) {/* Only happens during replay */ if (!UKPV(0)) /* Set restarting flag */ s->restarting_flags &= ~SCI_GAME_WAS_RESTARTED; } return s->r_acc; } reg_t kHaveMouse(state_t *s, int funct_nr, int argc, reg_t *argv) { return make_reg(0, -1); } reg_t kMemoryInfo(state_t *s, int funct_nr, int argc, reg_t *argv) { switch (argv[0].offset) { case 0: /* Total free heap memory */ case 1: /* Largest heap block available */ case 2: /* Largest available hunk memory block */ case 3: /* Total amount of hunk memory */ case 4: /* Amount of free DOS paragraphs- SCI01 */ return make_reg(0, 0x7fff); /* Must not be 0xffff, or some memory calculations will overflow */ default: SCIkwarn(SCIkWARNING, "Unknown MemoryInfo operation: %04x\n", argv[0].offset); } return NULL_REG; } reg_t k_Unknown(state_t *s, int funct_nr, int argc, reg_t *argv) { if (funct_nr >= SCI_MAPPED_UNKNOWN_KFUNCTIONS_NR) { SCIkwarn(SCIkSTUB, "Unhandled Unknown function %04x\n", funct_nr); return NULL_REG; } else switch (kfunct_mappers[funct_nr].type) { case KF_NEW: return kfunct_mappers[funct_nr].sig_pair.fun(s, funct_nr, argc, argv); case KF_NONE: default: SCIkwarn(SCIkSTUB, "Unhandled Unknown function %04x\n", funct_nr); return NULL_REG; } } reg_t kFlushResources(state_t *s, int funct_nr, int argc, reg_t *argv) { run_gc(s); SCIkdebug(SCIkROOM, "Entering room number %d\n", UKPV(0)); return s->r_acc; } reg_t kSetDebug(state_t *s, int funct_nr, int argc, reg_t *argv) { sciprintf("Debug mode activated\n"); script_debug_flag = 1; /* Enter debug mode */ _debug_seeking = _debug_step_running = 0; return s->r_acc; } #define _K_NEW_GETTIME_TICKS 0 #define _K_NEW_GETTIME_TIME_12HOUR 1 #define _K_NEW_GETTIME_TIME_24HOUR 2 #define _K_NEW_GETTIME_DATE 3 reg_t kGetTime(state_t *s, int funct_nr, int argc, reg_t *argv) { struct tm* loc_time; GTimeVal time_prec; time_t the_time; int retval = 0; /* Avoid spurious warning */ #if 0 /* Reset optimization flags: If this function is called, ** the game may be waiting for a timeout */ s->kernel_opt_flags &= ~(KERNEL_OPT_FLAG_GOT_EVENT | KERNEL_OPT_FLAG_GOT_2NDEVENT); #endif #ifdef WIN32 if (TIMERR_NOERROR != timeBeginPeriod(1)) { fprintf(stderr, "timeBeginPeriod(1) failed in kGetTime!\n"); } #endif /* WIN32 */ the_time = time(NULL); loc_time = localtime(&the_time); #ifdef WIN32 if (TIMERR_NOERROR != timeEndPeriod(1)) { fprintf(stderr, "timeEndPeriod(1) failed in kGetTime!\n"); } #endif /* WIN32 */ if (s->version < SCI_VERSION_FTU_NEW_GETTIME) { /* Use old semantics */ if (argc) { /* Get seconds since last am/pm switch */ retval = loc_time->tm_sec + loc_time->tm_min * 60 + (loc_time->tm_hour % 12) * 3600; SCIkdebug(SCIkTIME, "GetTime(timeofday) returns %d\n", retval); } else { /* Get time since game started */ sci_get_current_time(&time_prec); retval = ((time_prec.tv_usec - s->game_start_time.tv_usec) * 60 / 1000000) + (time_prec.tv_sec - s->game_start_time.tv_sec) * 60; SCIkdebug(SCIkTIME, "GetTime(elapsed) returns %d\n", retval); } } else { int mode = UKPV_OR_ALT(0, 0); /* The same strange method is still used for distinguishing mode 0 and the others. We assume that this is safe, though */ switch (mode) { case _K_NEW_GETTIME_TICKS : { sci_get_current_time(&time_prec); retval = ((time_prec.tv_usec - s->game_start_time.tv_usec) * 60 / 1000000) + (time_prec.tv_sec - s->game_start_time.tv_sec) * 60; SCIkdebug(SCIkTIME, "GetTime(elapsed) returns %d\n", retval); break; } case _K_NEW_GETTIME_TIME_12HOUR : { loc_time->tm_hour %= 12; retval = (loc_time->tm_min << 6) | (loc_time->tm_hour << 12) | (loc_time->tm_sec); SCIkdebug(SCIkTIME, "GetTime(12h) returns %d\n", retval); break; } case _K_NEW_GETTIME_TIME_24HOUR : { retval = (loc_time->tm_min << 5) | (loc_time->tm_sec >> 1) | (loc_time->tm_hour << 11); SCIkdebug(SCIkTIME, "GetTime(24h) returns %d\n", retval); break; } case _K_NEW_GETTIME_DATE : { retval = (loc_time->tm_mon << 5) | loc_time->tm_mday | (loc_time->tm_year << 9); SCIkdebug(SCIkTIME, "GetTime(date) returns %d\n", retval); break; } default: { SCIkdebug(SCIkWARNING, "Attempt to use unknown GetTime mode %d\n", mode); break; } } } return make_reg(0, retval); } #define K_MEMORY_ALLOCATE_CRITICAL 1 #define K_MEMORY_ALLOCATE_NONCRITICAL 2 #define K_MEMORY_FREE 3 #define K_MEMORY_MEMCPY 4 #define K_MEMORY_PEEK 5 #define K_MEMORY_POKE 6 reg_t kMemory(state_t *s, int funct_nr, int argc, reg_t *argv) { switch (UKPV(0)) { case K_MEMORY_ALLOCATE_CRITICAL : if (!sm_alloc_dynmem(&s->seg_manager, UKPV(1), "kMemory() critical", &s->r_acc)) { SCIkwarn(SCIkERROR, "Critical heap allocation failed\n"); script_error_flag = script_debug_flag = 1; } return s->r_acc; break; case K_MEMORY_ALLOCATE_NONCRITICAL : sm_alloc_dynmem(&s->seg_manager, UKPV(1), "kMemory() non-critical", &s->r_acc); break; case K_MEMORY_FREE : if (sm_free_dynmem(&s->seg_manager, argv[1])) { SCIkwarn(SCIkERROR, "Attempt to kMemory::free() non-dynmem pointer "PREG"!\n", PRINT_REG(argv[1])); } break; case K_MEMORY_MEMCPY : { int size = UKPV(3); byte *dest = kernel_dereference_bulk_pointer(s, argv[1], size); byte *src = kernel_dereference_bulk_pointer(s, argv[2], size); if (dest && src) memcpy(dest, src, size); else { SCIkdebug(SCIkWARNING, "Warning: Could not execute kMemory:memcpy of %d bytes:\n", size); if (!dest) { SCIkdebug(SCIkWARNING, " dest ptr ("PREG") invalid/memory region too small\n", PRINT_REG(argv[1])); } if (!src) { SCIkdebug(SCIkWARNING, " src ptr ("PREG") invalid/memory region too small\n", PRINT_REG(argv[2])); } } break; } case K_MEMORY_PEEK : { byte *ref = kernel_dereference_bulk_pointer(s, argv[1], 2); if (!ref) { SCIkdebug(SCIkERROR, "Attempt to poke invalid memory at "PREG"!\n", PRINT_REG(argv[1])); return s->r_acc; } if (s->seg_manager.heap[argv[1].segment]->type == MEM_OBJ_LOCALS) return *((reg_t *) ref); else return make_reg(0, getInt16(ref)); } break; case K_MEMORY_POKE : { byte *ref = kernel_dereference_bulk_pointer(s, argv[1], 2); if (!ref) { SCIkdebug(SCIkERROR, "Attempt to poke invalid memory at "PREG"!\n", PRINT_REG(argv[1])); return s->r_acc; } if (s->seg_manager.heap[argv[1].segment]->type == MEM_OBJ_LOCALS) *((reg_t *) ref) = argv[2]; else { if (argv[2].segment) { SCIkdebug(SCIkERROR, "Attempt to poke memory reference "PREG" to "PREG"!\n", PRINT_REG(argv[2]), PRINT_REG(argv[1])); return s->r_acc; putInt16(ref, argv[2].offset); } } return s->r_acc; break; } } return s->r_acc; } reg_t kstub(state_t *s, int funct_nr, int argc, reg_t *argv) { int i; SCIkwarn(SCIkWARNING, "Unimplemented syscall: %s[%x](", s->kernel_names[funct_nr], funct_nr); for (i = 0; i < argc; i++) { sciprintf(PREG, PRINT_REG(argv[i])); if (i + 1 < argc) sciprintf(", "); } sciprintf(")\n"); return NULL_REG; } reg_t kNOP(state_t *s, int funct_nr, int argc, reg_t *argv) { const char *problem = (const char*)(s->kfunct_table[funct_nr].orig_name ? "unmapped" : "NOP"); SCIkwarn(SCIkWARNING, "Warning: Kernel function 0x%02x invoked: %s", funct_nr, problem); if (s->kfunct_table[funct_nr].orig_name && strcmp(s->kfunct_table[funct_nr].orig_name, SCRIPT_UNKNOWN_FUNCTION_STRING)) { sciprintf(" (but its name is known to be %s)", s->kfunct_table[funct_nr].orig_name); } sciprintf("\n"); return NULL_REG; } void kernel_compile_signature(const char **s) { const char *src = *s; char *result; int ellipsis = 0; char v; int index = 0; if (!src) return; /* NULL signature: Nothing to do */ result = (char*)sci_malloc(strlen(*s) + 1); while (*src) { char c; v = 0; if (ellipsis) { sciprintf("INTERNAL ERROR when compiling kernel" " function signature '%s': non-terminal ellipsis\n", *s, *src); exit(1); } do { char cc; cc = c = *src++; if (c >= 'A' || c <= 'Z') cc = c | KSIG_SPEC_SUM_DONE; switch (cc) { case KSIG_SPEC_LIST: v |= KSIG_LIST; break; case KSIG_SPEC_NODE: v |= KSIG_NODE; break; case KSIG_SPEC_REF: v |= KSIG_REF; break; case KSIG_SPEC_OBJECT: v |= KSIG_OBJECT; break; case KSIG_SPEC_ARITHMETIC: v |= KSIG_ARITHMETIC; break; case KSIG_SPEC_NULL: v |= KSIG_NULL; break; case KSIG_SPEC_ANY: v |= KSIG_ANY; break; case KSIG_SPEC_ALLOW_INV: v |= KSIG_ALLOW_INV; break; case KSIG_SPEC_ELLIPSIS: v |= KSIG_ELLIPSIS; ellipsis = 1; break; default: { sciprintf("INTERNAL ERROR when compiling kernel" " function signature '%s': (%02x) not understood (aka" " '%c')\n", *s, c, c); exit(1); } } } while (*src && (*src == KSIG_SPEC_ALLOW_INV || *src == KSIG_SPEC_ELLIPSIS || (c < 'a' && c != KSIG_SPEC_ANY))); /* To handle sum types */ result[index++] = v; } result[index] = 0; *s = result; /* Write back */ } int script_map_kernel(state_t *s) { int functnr; int mapped = 0; int ignored = 0; int functions_nr = s->kernel_names_nr; int max_functions_nr = sci_max_allowed_unknown_kernel_functions[s->resmgr ->sci_version]; if (functions_nr < max_functions_nr) { sciprintf("Warning: SCI version believed to have %d kernel" " functions, but only %d reported-- filling up" " remaining %d\n", max_functions_nr, functions_nr, max_functions_nr - functions_nr); functions_nr = max_functions_nr; } s->kfunct_table = (kfunct_sig_pair_t*)sci_malloc(sizeof(kfunct_sig_pair_t) * functions_nr); s->kfunct_nr = functions_nr; for (functnr = 0; functnr < functions_nr; functnr++) { int seeker, found = -1; char *sought_name = NULL; if (functnr < s->kernel_names_nr) sought_name = s->kernel_names[functnr]; if (sought_name) for (seeker = 0; (found == -1) && kfunct_mappers[seeker].type != KF_TERMINATOR; seeker++) if (kfunct_mappers[seeker].name && strcmp(kfunct_mappers[seeker].name, sought_name) == 0) found = seeker; /* Found a kernel function with the same name! */ if (found == -1) { if (sought_name) { sciprintf("Warning: Kernel function %s[%x] unmapped\n", s->kernel_names[functnr], functnr); s->kfunct_table[functnr].fun = kNOP; } else { sciprintf("Warning: Flagging kernel function %x as unknown\n", functnr); s->kfunct_table[functnr].fun = k_Unknown; } s->kfunct_table[functnr].signature = NULL; s->kfunct_table[functnr].orig_name = sought_name; } else switch (kfunct_mappers[found].type) { case KF_OLD: sciprintf("Emulated kernel function found.\nThis shouldn't happen anymore."); return 1; case KF_NONE: s->kfunct_table[functnr].signature = NULL; ++ignored; break; case KF_NEW: s->kfunct_table[functnr] = kfunct_mappers[found].sig_pair; kernel_compile_signature(&(s->kfunct_table[functnr].signature)); ++mapped; break; } } /* for all functions requesting to be mapped */ sciprintf("Handled %d/%d kernel functions, mapping %d", mapped + ignored, s->kernel_names_nr, mapped); if (ignored) sciprintf(" and ignoring %d", ignored); sciprintf(".\n"); return 0; } void free_kfunct_tables(state_t *s) { free(s->kfunct_table); s->kfunct_table = NULL; } int determine_reg_type(state_t *s, reg_t reg, int allow_invalid) { mem_obj_t *mobj; if (!reg.segment) { if (!reg.offset) return KSIG_ARITHMETIC | KSIG_NULL; return KSIG_ARITHMETIC; } if ((reg.segment >= s->seg_manager.heap_size) || !s->seg_manager.heap[reg.segment]) return 0; /* Invalid */ mobj = s->seg_manager.heap[reg.segment]; switch (mobj->type) { case MEM_OBJ_SCRIPT: if (reg.offset <= mobj->data.script.buf_size && reg.offset >= -SCRIPT_OBJECT_MAGIC_OFFSET && RAW_IS_OBJECT(mobj->data.script.buf + reg.offset)) { int idx = RAW_GET_CLASS_INDEX(&(mobj->data.script), reg); if (idx >= 0 && idx < mobj->data.script.objects_nr) return KSIG_OBJECT; else return KSIG_REF; } else return KSIG_REF; case MEM_OBJ_CLONES: if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.clones), reg.offset)) return KSIG_OBJECT; else return KSIG_OBJECT | KSIG_INVALID; case MEM_OBJ_LOCALS: if (allow_invalid || reg.offset < mobj->data.locals.nr * sizeof(reg_t)) return KSIG_REF; else return KSIG_REF | KSIG_INVALID; case MEM_OBJ_STACK: if (allow_invalid || reg.offset < mobj->data.stack.nr * sizeof(reg_t)) return KSIG_REF; else return KSIG_REF | KSIG_INVALID; case MEM_OBJ_SYS_STRINGS: if (allow_invalid || (reg.offset < SYS_STRINGS_MAX && mobj->data.sys_strings.strings[reg.offset].name)) return KSIG_REF; else return KSIG_REF | KSIG_INVALID; case MEM_OBJ_LISTS: if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.lists), reg.offset)) return KSIG_LIST; else return KSIG_LIST | KSIG_INVALID; case MEM_OBJ_NODES: if (allow_invalid || ENTRY_IS_VALID(&(mobj->data.nodes), reg.offset)) return KSIG_NODE; else return KSIG_NODE | KSIG_INVALID; case MEM_OBJ_DYNMEM: if (allow_invalid || reg.offset < mobj->data.dynmem.size) return KSIG_REF; else return KSIG_REF | KSIG_INVALID; default: return 0; } } const char * kernel_argtype_description(int type) { type &= ~KSIG_INVALID; return argtype_description[sci_ffs(type)]; } int kernel_matches_signature(state_t *s, const char *sig, int argc, reg_t *argv) { if (!sig) return 1; while (*sig && argc) { if ((*sig & KSIG_ANY) != KSIG_ANY) { int type = determine_reg_type(s, *argv, *sig & KSIG_ALLOW_INV); if (!type) { sciprintf("[KERN] Could not determine type of ref "PREG";" " failing signature check\n", PRINT_REG(*argv)); return 0; } if (type & KSIG_INVALID) { sciprintf("[KERN] ref "PREG" was determined to be a %s, " "but the reference itself is invalid\n", PRINT_REG(*argv), kernel_argtype_description(type)); return 0; } if (!(type & *sig)) return 0; } if (!(*sig & KSIG_ELLIPSIS)) ++sig; ++argv; --argc; } if (argc) return 0; /* Too many arguments */ else return (*sig == 0 || (*sig & KSIG_ELLIPSIS)); } static inline void * _kernel_dereference_pointer(struct _state *s, reg_t pointer, int entries, int align) { int maxsize; void *retval = sm_dereference(&s->seg_manager, pointer, &maxsize); if (pointer.offset & (align - 1)) { SCIkdebug(SCIkERROR, "Unaligned pointer read: "PREG" expected with %d alignment!\n", PRINT_REG(pointer), align); return NULL; } if (entries > maxsize) { SCIkdebug(SCIkERROR, "Trying to dereference pointer "PREG" beyond end of segment!\n", PRINT_REG(pointer)); return NULL; } return retval; } byte * kernel_dereference_bulk_pointer(struct _state *s, reg_t pointer, int entries) { return (byte*)_kernel_dereference_pointer(s, pointer, entries, 1); } reg_t * kernel_dereference_reg_pointer(struct _state *s, reg_t pointer, int entries) { return (reg_t*)_kernel_dereference_pointer(s, pointer, entries, sizeof(reg_t)); }