2004-04-12 21:40:49 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
|
|
|
* Copyright (C) 2004 The ScummVM project
|
|
|
|
*
|
|
|
|
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
|
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* $Header$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Scripting module: Script resource handling functions
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/saga.h"
|
|
|
|
#include "saga/yslib.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/gfx.h"
|
|
|
|
#include "saga/rscfile_mod.h"
|
|
|
|
#include "saga/game_mod.h"
|
2004-08-10 18:31:33 +00:00
|
|
|
#include "saga/console.h"
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/cvar_mod.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/script.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
namespace Saga {
|
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
static void CF_script_info(int argc, char *argv[], void *refCon);
|
|
|
|
static void CF_script_exec(int argc, char *argv[], void *refCon);
|
|
|
|
static void CF_script_togglestep(int argc, char *argv[], void *refCon);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
int Script::reg() {
|
|
|
|
CVAR_RegisterFunc(CF_script_info, "script_info", NULL, R_CVAR_NONE, 0, 0, this);
|
|
|
|
CVAR_RegisterFunc(CF_script_exec, "script_exec", "<Script number>", R_CVAR_NONE, 1, 1, this);
|
|
|
|
CVAR_RegisterFunc(CF_script_togglestep, "script_togglestep", NULL, R_CVAR_NONE, 0, 0, this);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
return R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Initializes the scripting module.
|
|
|
|
// Loads script resource look-up table, initializes script data system
|
2004-08-01 22:48:40 +00:00
|
|
|
Script::Script() {
|
2004-04-12 21:40:49 +00:00
|
|
|
R_RSCFILE_CONTEXT *s_lut_ctxt;
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *rsc_ptr;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t rsc_len;
|
2004-05-04 03:33:03 +00:00
|
|
|
int prevTell;
|
2004-04-12 21:40:49 +00:00
|
|
|
int result;
|
2004-05-04 03:33:03 +00:00
|
|
|
int i, j;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
//initialize member variables
|
2004-10-21 06:49:11 +00:00
|
|
|
_dbg_thread = 0;
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptContext = 0;
|
|
|
|
_voiceLUTPresent = false;
|
|
|
|
_scriptLUTEntryLen = 0;
|
|
|
|
_currentScript = 0;
|
|
|
|
_threadList = 0;
|
|
|
|
memset(_dataBuf, 0, sizeof(_dataBuf));
|
2004-08-01 22:48:40 +00:00
|
|
|
|
|
|
|
debug(0, "Initializing scripting subsystem");
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script resource file context
|
2004-08-01 23:47:19 +00:00
|
|
|
result = GAME_GetFileContext(&_scriptContext, R_GAME_SCRIPTFILE, 0);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (result != R_SUCCESS) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Couldn't get script file context");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script LUT resource
|
2004-04-12 21:40:49 +00:00
|
|
|
result = GAME_GetFileContext(&s_lut_ctxt, R_GAME_RESOURCEFILE, 0);
|
|
|
|
if (result != R_SUCCESS) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Couldn't get resource file context");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
result = RSC_LoadResource(s_lut_ctxt, ITE_SCRIPT_LUT, &rsc_ptr, &rsc_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (result != R_SUCCESS) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Error: Couldn't load script resource look-up table");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Create logical script LUT from resource
|
2004-04-12 21:40:49 +00:00
|
|
|
if (rsc_len % R_S_LUT_ENTRYLEN_ITECD == 0) {
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptLUTEntryLen = R_S_LUT_ENTRYLEN_ITECD;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else if (rsc_len % R_S_LUT_ENTRYLEN_ITEDISK == 0) {
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptLUTEntryLen = R_S_LUT_ENTRYLEN_ITEDISK;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Error: Invalid script lookup table length");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Calculate number of entries
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptLUTMax = rsc_len / _scriptLUTEntryLen;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate space for logical LUT
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptLUT = (R_SCRIPT_LUT_ENTRY *)malloc(_scriptLUTMax * sizeof(R_SCRIPT_LUT_ENTRY));
|
|
|
|
if (_scriptLUT == NULL) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Error: Couldn't allocate memory for script resource look-up table");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Convert LUT resource to logical LUT
|
2004-08-22 18:28:42 +00:00
|
|
|
MemoryReadStream scriptS(rsc_ptr, rsc_len);
|
2004-08-01 23:47:19 +00:00
|
|
|
for (i = 0; i < _scriptLUTMax; i++) {
|
2004-08-22 18:28:42 +00:00
|
|
|
prevTell = scriptS.pos();
|
|
|
|
_scriptLUT[i].script_rn = scriptS.readUint16LE();
|
|
|
|
_scriptLUT[i].diag_list_rn = scriptS.readUint16LE();
|
|
|
|
_scriptLUT[i].voice_lut_rn = scriptS.readUint16LE();
|
2004-08-01 23:47:19 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Skip the unused portion of the structure
|
2004-08-22 18:28:42 +00:00
|
|
|
for (j = scriptS.pos(); j < prevTell + _scriptLUTEntryLen; j++) {
|
|
|
|
if (scriptS.readByte() != 0)
|
2004-10-09 07:39:46 +00:00
|
|
|
warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", i, j);
|
2004-08-22 18:28:42 +00:00
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RSC_FreeResource(rsc_ptr);
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Any voice lookup table resources present?
|
2004-08-01 23:47:19 +00:00
|
|
|
for (i = 0; i < _scriptLUTMax; i++) {
|
|
|
|
if (_scriptLUT[i].voice_lut_rn) {
|
|
|
|
_voiceLUTPresent = true;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Initialize script submodules
|
2004-08-01 23:47:19 +00:00
|
|
|
_threadList = ys_dll_create();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-12 23:57:45 +00:00
|
|
|
setupScriptFuncList();
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
_initialized = true;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Shut down script module gracefully; free all allocated module resources
|
2004-08-01 22:48:40 +00:00
|
|
|
Script::~Script() {
|
2004-04-12 21:40:49 +00:00
|
|
|
YS_DL_NODE *thread_node;
|
|
|
|
R_SCRIPT_THREAD *thread;
|
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
if (!_initialized) {
|
|
|
|
error("Script not initialized");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Shutting down scripting subsystem.");
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Free script lookup table
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_scriptLUT);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Stop all threads and destroy them
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
for (thread_node = ys_dll_head(_threadList); thread_node != NULL;
|
2004-05-01 14:05:10 +00:00
|
|
|
thread_node = ys_dll_next(thread_node)) {
|
2004-04-12 21:40:49 +00:00
|
|
|
thread = (R_SCRIPT_THREAD *)ys_dll_get_data(thread_node);
|
2004-08-12 23:57:45 +00:00
|
|
|
SThreadDestroy(thread);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
_initialized = false;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Loads a script; including script bytecode and dialogue list
|
2004-08-01 22:48:40 +00:00
|
|
|
int Script::loadScript(int script_num) {
|
2004-04-12 21:40:49 +00:00
|
|
|
R_SCRIPTDATA *script_data;
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *bytecode_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t bytecode_len;
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 scriptl_rn;
|
|
|
|
byte *diagl_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t diagl_len;
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 diagl_rn;
|
|
|
|
byte *voicelut_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t voicelut_len;
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 voicelut_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
int result;
|
|
|
|
|
2004-08-27 01:32:10 +00:00
|
|
|
if (GAME_GetGameType() == GID_IHNM) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Validate script number
|
2004-08-01 23:47:19 +00:00
|
|
|
if ((script_num < 0) || (script_num > _scriptLUTMax)) {
|
2004-08-01 22:48:40 +00:00
|
|
|
warning("Script::loadScript(): Invalid script number");
|
2004-04-12 21:40:49 +00:00
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Release old script data if present
|
2004-08-01 22:48:40 +00:00
|
|
|
freeScript();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Initialize script data structure
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Loading script data for script #%d", script_num);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
script_data = (R_SCRIPTDATA *)malloc(sizeof *script_data);
|
|
|
|
if (script_data == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Memory allocation failed");
|
2004-04-12 21:40:49 +00:00
|
|
|
return R_MEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
script_data->loaded = 0;
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Initialize script pointers
|
2004-04-12 21:40:49 +00:00
|
|
|
script_data->diag = NULL;
|
|
|
|
script_data->bytecode = NULL;
|
|
|
|
script_data->voice = NULL;
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script bytecode
|
2004-08-01 23:47:19 +00:00
|
|
|
scriptl_rn = _scriptLUT[script_num].script_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
result = RSC_LoadResource(_scriptContext, scriptl_rn, &bytecode_p, &bytecode_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (result != R_SUCCESS) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error loading script bytecode resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
script_data->bytecode = loadBytecode(bytecode_p, bytecode_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
if (script_data->bytecode == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error interpreting script bytecode resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
|
|
|
RSC_FreeResource(bytecode_p);
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script dialogue list
|
2004-08-01 23:47:19 +00:00
|
|
|
diagl_rn = _scriptLUT[script_num].diag_list_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load dialogue list resource
|
2004-08-01 23:47:19 +00:00
|
|
|
result = RSC_LoadResource(_scriptContext, diagl_rn, &diagl_p, &diagl_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (result != R_SUCCESS) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error loading dialogue list resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
|
|
|
RSC_FreeResource(bytecode_p);
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Convert dialogue list resource to logical dialogue list
|
2004-08-01 22:48:40 +00:00
|
|
|
script_data->diag = loadDialogue(diagl_p, diagl_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (script_data->diag == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error interpreting dialogue list resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
|
|
|
RSC_FreeResource(bytecode_p);
|
|
|
|
RSC_FreeResource(diagl_p);
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load voice resource lookup table
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_voiceLUTPresent) {
|
|
|
|
voicelut_rn = _scriptLUT[script_num].voice_lut_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load voice LUT resource
|
2004-08-01 23:47:19 +00:00
|
|
|
result = RSC_LoadResource(_scriptContext, voicelut_rn, &voicelut_p, &voicelut_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (result != R_SUCCESS) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error loading voice LUT resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
|
|
|
RSC_FreeResource(bytecode_p);
|
|
|
|
RSC_FreeResource(diagl_p);
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Convert voice LUT resource to logical voice LUT
|
2004-08-01 22:48:40 +00:00
|
|
|
script_data->voice = loadVoiceLUT(voicelut_p, voicelut_len, script_data);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (script_data->voice == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error interpreting voice LUT resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
|
|
|
RSC_FreeResource(bytecode_p);
|
|
|
|
RSC_FreeResource(diagl_p);
|
|
|
|
RSC_FreeResource(voicelut_p);
|
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Finish initialization
|
2004-04-12 21:40:49 +00:00
|
|
|
script_data->loaded = 1;
|
2004-08-01 23:47:19 +00:00
|
|
|
_currentScript = script_data;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
return R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Frees all resources associated with current script.
|
2004-08-01 22:48:40 +00:00
|
|
|
int Script::freeScript() {
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_currentScript == NULL) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
if (!_currentScript->loaded) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return R_FAILURE;
|
|
|
|
}
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Releasing script data.");
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Finish initialization
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_currentScript->diag != NULL) {
|
|
|
|
free(_currentScript->diag->str);
|
|
|
|
free(_currentScript->diag->str_off);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_currentScript->diag);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_currentScript->bytecode != NULL) {
|
|
|
|
free(_currentScript->bytecode->entrypoints);
|
|
|
|
RSC_FreeResource(_currentScript->bytecode->bytecode_p);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_currentScript->bytecode);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_voiceLUTPresent) {
|
|
|
|
free(_currentScript->voice->voices);
|
|
|
|
free(_currentScript->voice);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_currentScript);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
_currentScript = NULL;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
return R_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Reads the entrypoint table from a script bytecode resource in memory.
|
|
|
|
// Returns NULL on failure.
|
2004-08-01 22:48:40 +00:00
|
|
|
R_SCRIPT_BYTECODE *Script::loadBytecode(byte *bytecode_p, size_t bytecode_len) {
|
2004-04-12 21:40:49 +00:00
|
|
|
R_PROC_TBLENTRY *bc_ep_tbl = NULL;
|
|
|
|
R_SCRIPT_BYTECODE *bc_new_data = NULL;
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
unsigned long n_entrypoints; // Number of entrypoints
|
|
|
|
size_t ep_tbl_offset; // Offset of bytecode entrypoint table
|
2004-04-12 21:40:49 +00:00
|
|
|
unsigned long i;
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Loading script bytecode...");
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-22 18:28:42 +00:00
|
|
|
MemoryReadStream scriptS(bytecode_p, bytecode_len);
|
2004-05-04 03:33:03 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// The first two uint32 values are the number of entrypoints, and the
|
|
|
|
// offset to the entrypoint table, respectively.
|
2004-08-22 18:28:42 +00:00
|
|
|
n_entrypoints = scriptS.readUint32LE();
|
|
|
|
ep_tbl_offset = scriptS.readUint32LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Check that the entrypoint table offset is valid.
|
|
|
|
if ((bytecode_len - ep_tbl_offset) < (n_entrypoints * R_SCRIPT_TBLENTRY_LEN)) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Invalid table offset");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n_entrypoints > R_SCRIPT_MAX) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Script limit exceeded");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate a new bytecode resource information structure and table of
|
|
|
|
// entrypoints
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
bc_new_data = (R_SCRIPT_BYTECODE *)malloc(sizeof *bc_new_data);
|
|
|
|
if (bc_new_data == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Memory allocation failure loading script bytecode");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bc_ep_tbl = (R_PROC_TBLENTRY *)malloc(n_entrypoints * sizeof *bc_ep_tbl);
|
|
|
|
if (bc_ep_tbl == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Memory allocation failure creating script entrypoint table");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(bc_new_data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Read in the entrypoint table
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-22 18:28:42 +00:00
|
|
|
while (scriptS.pos() < ep_tbl_offset)
|
|
|
|
scriptS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
for (i = 0; i < n_entrypoints; i++) {
|
2004-05-01 14:05:10 +00:00
|
|
|
// First uint16 is the offset of the entrypoint name from the start
|
|
|
|
// of the bytecode resource, second uint16 is the offset of the
|
|
|
|
// bytecode itself for said entrypoint
|
2004-08-22 18:28:42 +00:00
|
|
|
bc_ep_tbl[i].name_offset = scriptS.readUint16LE();
|
|
|
|
bc_ep_tbl[i].offset = scriptS.readUint16LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Perform a simple range check on offset values
|
|
|
|
if ((bc_ep_tbl[i].name_offset > bytecode_len) || (bc_ep_tbl[i].offset > bytecode_len)) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Invalid offset encountered in script entrypoint table");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(bc_new_data);
|
|
|
|
free(bc_ep_tbl);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
bc_new_data->bytecode_p = (byte *) bytecode_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
bc_new_data->bytecode_len = bytecode_len;
|
|
|
|
|
|
|
|
bc_new_data->n_entrypoints = n_entrypoints;
|
|
|
|
bc_new_data->entrypoints = bc_ep_tbl;
|
|
|
|
bc_new_data->ep_tbl_offset = ep_tbl_offset;
|
|
|
|
|
|
|
|
return bc_new_data;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Reads a logical dialogue list from a dialogue list resource in memory.
|
|
|
|
// Returns NULL on failure.
|
2004-08-01 22:48:40 +00:00
|
|
|
R_DIALOGUE_LIST *Script::loadDialogue(const byte *dialogue_p, size_t dialogue_len) {
|
2004-04-12 21:40:49 +00:00
|
|
|
R_DIALOGUE_LIST *dialogue_list;
|
2004-04-30 23:02:23 +00:00
|
|
|
uint16 n_dialogue;
|
|
|
|
uint16 i;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t offset;
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Loading dialogue list...");
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate dialogue list structure
|
2004-04-12 21:40:49 +00:00
|
|
|
dialogue_list = (R_DIALOGUE_LIST *)malloc(sizeof *dialogue_list);
|
|
|
|
if (dialogue_list == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-08-22 18:28:42 +00:00
|
|
|
MemoryReadStream scriptS(dialogue_p, dialogue_len);
|
2004-05-04 03:33:03 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// First uint16 is the offset of the first string
|
2004-08-22 18:28:42 +00:00
|
|
|
offset = scriptS.readUint16LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
if (offset > dialogue_len) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error, invalid string offset");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Calculate table length
|
2004-04-12 21:40:49 +00:00
|
|
|
n_dialogue = offset / 2;
|
|
|
|
dialogue_list->n_dialogue = n_dialogue;
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate table of string pointers
|
2004-04-25 14:42:14 +00:00
|
|
|
dialogue_list->str = (const char **)malloc(n_dialogue * sizeof(const char *));
|
2004-04-12 21:40:49 +00:00
|
|
|
if (dialogue_list->str == NULL) {
|
|
|
|
free(dialogue_list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate table of string offsets
|
2004-04-12 21:40:49 +00:00
|
|
|
dialogue_list->str_off = (size_t *)malloc(n_dialogue * sizeof(size_t));
|
|
|
|
if (dialogue_list->str_off == NULL) {
|
|
|
|
free(dialogue_list->str);
|
|
|
|
free(dialogue_list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Read in tables from dialogue list resource
|
2004-08-22 18:28:42 +00:00
|
|
|
scriptS.seek(0);
|
2004-04-12 21:40:49 +00:00
|
|
|
for (i = 0; i < n_dialogue; i++) {
|
2004-08-22 18:28:42 +00:00
|
|
|
offset = scriptS.readUint16LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
if (offset > dialogue_len) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error, invalid string offset");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(dialogue_list->str);
|
|
|
|
free(dialogue_list->str_off);
|
|
|
|
free(dialogue_list);
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-04-25 14:42:14 +00:00
|
|
|
dialogue_list->str[i] = (const char *)dialogue_p + offset;
|
2004-04-12 21:40:49 +00:00
|
|
|
dialogue_list->str_off[i] = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dialogue_list;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Reads a logical voice LUT from a voice LUT resource in memory.
|
|
|
|
// Returns NULL on failure.
|
2004-08-01 22:48:40 +00:00
|
|
|
R_VOICE_LUT *Script::loadVoiceLUT(const byte *voicelut_p, size_t voicelut_len, R_SCRIPTDATA *script) {
|
2004-04-12 21:40:49 +00:00
|
|
|
R_VOICE_LUT *voice_lut;
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
uint16 n_voices;
|
|
|
|
uint16 i;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
voice_lut = (R_VOICE_LUT *)malloc(sizeof *voice_lut);
|
|
|
|
if (voice_lut == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
n_voices = voicelut_len / 2;
|
|
|
|
if (n_voices != script->diag->n_dialogue) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error: Voice LUT entries do not match dialogue entries");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
voice_lut->voices = (int *)malloc(n_voices * sizeof *voice_lut->voices);
|
|
|
|
if (voice_lut->voices == NULL) {
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-08-22 18:28:42 +00:00
|
|
|
MemoryReadStream scriptS(voicelut_p, voicelut_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-04 03:33:03 +00:00
|
|
|
for (i = 0; i < n_voices; i++) {
|
2004-08-22 18:28:42 +00:00
|
|
|
voice_lut->voices[i] = scriptS.readUint16LE();
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return voice_lut;
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
void Script::scriptInfo(int argc, char *argv[]) {
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 n_entrypoints;
|
|
|
|
uint32 i;
|
2004-04-12 21:40:49 +00:00
|
|
|
char *name_ptr;
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (currentScript() == NULL) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (!currentScript()->loaded) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
n_entrypoints = currentScript()->bytecode->n_entrypoints;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-10 18:31:33 +00:00
|
|
|
_vm->_console->print("Current script contains %d entrypoints:", n_entrypoints);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
for (i = 0; i < n_entrypoints; i++) {
|
2004-08-11 00:27:43 +00:00
|
|
|
name_ptr = (char *)currentScript()->bytecode->bytecode_p +
|
|
|
|
currentScript()->bytecode->entrypoints[i].name_offset;
|
2004-08-10 18:31:33 +00:00
|
|
|
_vm->_console->print("%lu: %s", i, name_ptr);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
void Script::scriptExec(int argc, char *argv[]) {
|
2004-04-30 23:02:23 +00:00
|
|
|
uint16 ep_num;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ep_num = atoi(argv[0]);
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (_dbg_thread == NULL) {
|
2004-08-10 18:31:33 +00:00
|
|
|
_vm->_console->print("Creating debug thread...");
|
2004-08-12 23:57:45 +00:00
|
|
|
_dbg_thread = SThreadCreate();
|
2004-08-11 00:27:43 +00:00
|
|
|
if (_dbg_thread == NULL) {
|
2004-08-10 18:31:33 +00:00
|
|
|
_vm->_console->print("Thread creation failed.");
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (ep_num >= currentScript()->bytecode->n_entrypoints) {
|
2004-08-10 18:31:33 +00:00
|
|
|
_vm->_console->print("Invalid entrypoint.");
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-12 23:57:45 +00:00
|
|
|
SThreadExecute(_dbg_thread, ep_num);
|
2004-08-11 00:27:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CF_script_info(int argc, char *argv[], void *refCon) {
|
|
|
|
((Script *)refCon)->scriptInfo(argc, argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CF_script_exec(int argc, char *argv[], void *refCon) {
|
|
|
|
((Script *)refCon)->scriptExec(argc, argv);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-07-31 23:33:14 +00:00
|
|
|
void CF_script_togglestep(int argc, char *argv[], void *refCon) {
|
2004-08-01 22:48:40 +00:00
|
|
|
((Script *)refCon)->_dbg_singlestep = !((Script *)refCon)->_dbg_singlestep;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Saga
|