aboutsummaryrefslogtreecommitdiff
path: root/saga/script.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2004-04-12 21:40:49 +0000
committerEugene Sandulenko2004-04-12 21:40:49 +0000
commitf3d340fb0ce72b9db59b8c701153bc82b595f75e (patch)
treebf250cf3a1e6aee35c7f40d766994b0c2c188e5c /saga/script.cpp
parent0a0a0c7b0609d8774cef849e7511e7b21d12c5db (diff)
downloadscummvm-rg350-f3d340fb0ce72b9db59b8c701153bc82b595f75e.tar.gz
scummvm-rg350-f3d340fb0ce72b9db59b8c701153bc82b595f75e.tar.bz2
scummvm-rg350-f3d340fb0ce72b9db59b8c701153bc82b595f75e.zip
WIP for SAGA engine.
o text formatting is not consistent with rules, just indent utility is too dumb for that o it does not use OSystem, i.e. it runs on direct SDL calls o it may not even compile on your box o if you enable it, expect zillions of warnings o no sound Now it runs ITE intro as reinherit did svn-id: r13564
Diffstat (limited to 'saga/script.cpp')
-rw-r--r--saga/script.cpp684
1 files changed, 684 insertions, 0 deletions
diff --git a/saga/script.cpp b/saga/script.cpp
new file mode 100644
index 0000000000..4c84a9e069
--- /dev/null
+++ b/saga/script.cpp
@@ -0,0 +1,684 @@
+/* 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$
+ *
+ */
+/*
+
+ Description:
+
+ Scripting module: Script resource handling functions
+
+ Notes:
+*/
+
+#include "reinherit.h"
+
+#include "yslib.h"
+
+/*
+ * Uses the following modules:
+\*--------------------------------------------------------------------------*/
+#include "rscfile_mod.h"
+#include "game_mod.h"
+#include "text_mod.h"
+#include "console_mod.h"
+#include "cvar_mod.h"
+
+/*
+ * Begin module component
+\*--------------------------------------------------------------------------*/
+#include "script_mod.h"
+#include "script.h"
+#include "sstack.h"
+#include "sthread.h"
+
+namespace Saga {
+
+R_SCRIPT_MODULE ScriptModule;
+
+int SCRIPT_Register(void)
+{
+
+ CVAR_RegisterFunc(CF_script_info,
+ "script_info", NULL, R_CVAR_NONE, 0, 0);
+
+ CVAR_RegisterFunc(CF_script_exec,
+ "script_exec", "<Script number>", R_CVAR_NONE, 1, 1);
+
+ CVAR_RegisterFunc(CF_script_togglestep,
+ "script_togglestep", NULL, R_CVAR_NONE, 0, 0);
+
+ return R_SUCCESS;
+}
+
+int SCRIPT_Init(void)
+/*--------------------------------------------------------------------------*\
+ * Initializes the scripting module.
+ * Loads script resource look-up table, initializes script data system
+\*--------------------------------------------------------------------------*/
+{
+ R_RSCFILE_CONTEXT *s_lut_ctxt;
+
+ uchar *rsc_ptr;
+ size_t rsc_len;
+
+ const uchar *read_ptr;
+ const uchar *read_ptr2;
+
+ int result;
+ int i;
+
+ R_printf(R_STDOUT, "Initializing scripting subsystem.\n");
+
+ /* Load script resource file context
+ * \*---------------------------------------------------------------------- */
+ result = GAME_GetFileContext(&ScriptModule.script_ctxt,
+ R_GAME_SCRIPTFILE, 0);
+ if (result != R_SUCCESS) {
+
+ R_printf(R_STDERR, "Couldn't get script file context.\n");
+
+ return R_FAILURE;
+ }
+
+ /* Load script LUT resource
+ * \*---------------------------------------------------------------------- */
+ result = GAME_GetFileContext(&s_lut_ctxt, R_GAME_RESOURCEFILE, 0);
+ if (result != R_SUCCESS) {
+
+ R_printf(R_STDERR, "Couldn't get resource file context.\n");
+
+ return R_FAILURE;
+ }
+
+ result = RSC_LoadResource(s_lut_ctxt,
+ ITE_SCRIPT_LUT, &rsc_ptr, &rsc_len);
+ if (result != R_SUCCESS) {
+
+ R_printf(R_STDERR,
+ "Error: Couldn't load script resource look-up table.\n");
+
+ return R_FAILURE;
+ }
+
+ /* Create logical script LUT from resource
+ * \*---------------------------------------------------------------------- */
+ if (rsc_len % R_S_LUT_ENTRYLEN_ITECD == 0) {
+
+ ScriptModule.script_lut_entrylen = R_S_LUT_ENTRYLEN_ITECD;
+ } else if (rsc_len % R_S_LUT_ENTRYLEN_ITEDISK == 0) {
+
+ ScriptModule.script_lut_entrylen = R_S_LUT_ENTRYLEN_ITEDISK;
+ } else {
+ R_printf(R_STDERR,
+ "Error: Invalid script lookup table length.\n");
+ return R_FAILURE;
+ }
+
+ /* Calculate number of entries */
+ ScriptModule.script_lut_max =
+ rsc_len / ScriptModule.script_lut_entrylen;
+
+ /* Allocate space for logical LUT */
+ ScriptModule.script_lut = (R_SCRIPT_LUT_ENTRY *)malloc(ScriptModule.script_lut_max *
+ sizeof(R_SCRIPT_LUT_ENTRY));
+ if (ScriptModule.script_lut == NULL) {
+ R_printf(R_STDERR,
+ "Error: Couldn't allocate memory for script resource "
+ "look-up table.\n");
+ return R_MEM;
+ }
+
+ /* Convert LUT resource to logical LUT */
+ read_ptr = rsc_ptr;
+ for (i = 0; i < ScriptModule.script_lut_max; i++) {
+
+ read_ptr2 = read_ptr;
+
+ ScriptModule.script_lut[i].script_rn =
+ ys_read_u16_le(read_ptr2, &read_ptr2);
+
+ ScriptModule.script_lut[i].diag_list_rn =
+ ys_read_u16_le(read_ptr2, &read_ptr2);
+
+ ScriptModule.script_lut[i].voice_lut_rn =
+ ys_read_u16_le(read_ptr2, &read_ptr2);
+
+ /* Skip the unused portion of the structure */
+ read_ptr += ScriptModule.script_lut_entrylen;
+ }
+
+ RSC_FreeResource(rsc_ptr);
+
+ /* Any voice lookup table resources present? */
+ for (i = 0; i < ScriptModule.script_lut_max; i++) {
+
+ if (ScriptModule.script_lut[i].voice_lut_rn) {
+
+ ScriptModule.voice_lut_present = 1;
+ break;
+ }
+ }
+
+ /* Initialize script submodules
+ * \*---------------------------------------------------------------------- */
+ ScriptModule.thread_list = ys_dll_create();
+
+ if (SDATA_Init() != R_SUCCESS) {
+ free(ScriptModule.script_lut);
+ return R_FAILURE;
+ }
+
+ ScriptModule.initialized = 1;
+ return R_SUCCESS;
+}
+
+int SCRIPT_Shutdown(void)
+/*--------------------------------------------------------------------------*\
+ * Shut down script module gracefully; free all allocated module resources
+\*--------------------------------------------------------------------------*/
+{
+ YS_DL_NODE *thread_node;
+ R_SCRIPT_THREAD *thread;
+
+ if (!ScriptModule.initialized) {
+
+ return R_FAILURE;
+ }
+
+ R_printf(R_STDOUT, "Shutting down scripting subsystem.\n");
+
+ /* Free script lookup table */
+ free(ScriptModule.script_lut);
+
+ /* Stop all threads and destroy them */
+
+ for (thread_node = ys_dll_head(ScriptModule.thread_list);
+ thread_node != NULL; thread_node = ys_dll_next(thread_node)) {
+
+ thread = (R_SCRIPT_THREAD *)ys_dll_get_data(thread_node);
+
+ STHREAD_Destroy(thread);
+ }
+
+ ScriptModule.initialized = 0;
+
+ return R_SUCCESS;
+}
+
+int SCRIPT_Load(int script_num)
+/*--------------------------------------------------------------------------*\
+ * Loads a script; including script bytecode and dialogue list
+\*--------------------------------------------------------------------------*/
+{
+
+ R_SCRIPTDATA *script_data;
+
+ uchar *bytecode_p;
+ size_t bytecode_len;
+ ulong scriptl_rn;
+
+ uchar *diagl_p;
+ size_t diagl_len;
+ ulong diagl_rn;
+
+ uchar *voicelut_p;
+ size_t voicelut_len;
+ ulong voicelut_rn;
+
+ int result;
+
+ if (GAME_GetGameType() == R_GAMETYPE_IHNM) {
+ return R_SUCCESS;
+ }
+
+ /* Validate script number */
+ if ((script_num < 0) || (script_num > ScriptModule.script_lut_max)) {
+ R_printf(R_STDERR, "SCRIPT_Load(): Invalid script number!\n");
+ return R_FAILURE;
+ }
+
+ /* Release old script data if present */
+ SCRIPT_Free();
+
+ /* Initialize script data structure
+ * \*---------------------------------------------------------------------- */
+ R_printf(R_STDOUT, "Loading script data for script #%d.\n",
+ script_num);
+
+ script_data = (R_SCRIPTDATA *)malloc(sizeof *script_data);
+ if (script_data == NULL) {
+ R_printf(R_STDERR, "Memory allocation failed.\n");
+ return R_MEM;
+ }
+
+ script_data->loaded = 0;
+
+ /* Initialize script pointers */
+ script_data->diag = NULL;
+ script_data->bytecode = NULL;
+ script_data->voice = NULL;
+
+ /* Load script bytecode
+ * \*---------------------------------------------------------------------- */
+ scriptl_rn = ScriptModule.script_lut[script_num].script_rn;
+
+ result = RSC_LoadResource(ScriptModule.script_ctxt,
+ scriptl_rn, &bytecode_p, &bytecode_len);
+ if (result != R_SUCCESS) {
+ R_printf(R_STDERR,
+ "Error loading script bytecode resource.\n");
+ free(script_data);
+ return R_FAILURE;
+ }
+
+ script_data->bytecode = SCRIPT_LoadBytecode(bytecode_p, bytecode_len);
+
+ if (script_data->bytecode == NULL) {
+ R_printf(R_STDERR,
+ "Error interpreting script bytecode resource.\n");
+ free(script_data);
+ RSC_FreeResource(bytecode_p);
+ return R_FAILURE;
+ }
+
+ /* Load script dialogue list
+ * \*---------------------------------------------------------------------- */
+ diagl_rn = ScriptModule.script_lut[script_num].diag_list_rn;
+
+ /* Load dialogue list resource */
+ result = RSC_LoadResource(ScriptModule.script_ctxt,
+ diagl_rn, &diagl_p, &diagl_len);
+ if (result != R_SUCCESS) {
+ R_printf(R_STDERR, "Error loading dialogue list resource.\n");
+ free(script_data);
+ RSC_FreeResource(bytecode_p);
+ return R_FAILURE;
+ }
+
+ /* Convert dialogue list resource to logical dialogue list */
+ script_data->diag = SCRIPT_LoadDialogue(diagl_p, diagl_len);
+ if (script_data->diag == NULL) {
+ R_printf(R_STDERR,
+ "Error interpreting dialogue list resource.\n");
+ free(script_data);
+ RSC_FreeResource(bytecode_p);
+ RSC_FreeResource(diagl_p);
+ return R_FAILURE;
+ }
+
+ /* Load voice resource lookup table
+ * \*---------------------------------------------------------------------- */
+ if (ScriptModule.voice_lut_present) {
+
+ voicelut_rn = ScriptModule.script_lut[script_num].voice_lut_rn;
+
+ /* Load voice LUT resource */
+ result = RSC_LoadResource(ScriptModule.script_ctxt,
+ voicelut_rn, &voicelut_p, &voicelut_len);
+ if (result != R_SUCCESS) {
+
+ R_printf(R_STDERR,
+ "Error loading voice LUT resource.\n");
+
+ free(script_data);
+ RSC_FreeResource(bytecode_p);
+ RSC_FreeResource(diagl_p);
+
+ return R_FAILURE;
+ }
+
+ /* Convert voice LUT resource to logical voice LUT */
+ script_data->voice = SCRIPT_LoadVoiceLUT(voicelut_p,
+ voicelut_len, script_data);
+ if (script_data->voice == NULL) {
+ R_printf(R_STDERR,
+ "Error interpreting voice LUT resource.\n");
+
+ free(script_data);
+ RSC_FreeResource(bytecode_p);
+ RSC_FreeResource(diagl_p);
+ RSC_FreeResource(voicelut_p);
+
+ return R_FAILURE;
+ }
+
+ }
+
+ /* Finish initialization
+ * \*---------------------------------------------------------------------- */
+ script_data->loaded = 1;
+ ScriptModule.current_script = script_data;
+
+ return R_SUCCESS;
+}
+
+int SCRIPT_Free(void)
+/*--------------------------------------------------------------------------*\
+ * Frees all resources associated with current script.
+\*--------------------------------------------------------------------------*/
+{
+
+ if (ScriptModule.current_script == NULL) {
+ return R_FAILURE;
+ }
+
+ if (!ScriptModule.current_script->loaded) {
+ return R_FAILURE;
+ }
+
+ R_printf(R_STDOUT, "Releasing script data.\n");
+
+ /* Finish initialization
+ * \*---------------------------------------------------------------------- */
+ if (ScriptModule.current_script->diag != NULL) {
+ free(ScriptModule.current_script->diag->str);
+ free(ScriptModule.current_script->diag->str_off);
+ }
+ free(ScriptModule.current_script->diag);
+
+ if (ScriptModule.current_script->bytecode != NULL) {
+ free(ScriptModule.current_script->bytecode->entrypoints);
+ RSC_FreeResource(ScriptModule.current_script->bytecode->
+ bytecode_p);
+ }
+
+ free(ScriptModule.current_script->bytecode);
+
+ if (ScriptModule.voice_lut_present) {
+ free(ScriptModule.current_script->voice->voices);
+ free(ScriptModule.current_script->voice);
+ }
+
+ free(ScriptModule.current_script);
+
+ ScriptModule.current_script = NULL;
+
+ return R_SUCCESS;
+}
+
+R_SCRIPT_BYTECODE *SCRIPT_LoadBytecode(const uchar * bytecode_p,
+ size_t bytecode_len)
+/*--------------------------------------------------------------------------*\
+ * Reads the entrypoint table from a script bytecode resource in memory.
+ * Returns NULL on failure.
+\*--------------------------------------------------------------------------*/
+{
+
+ const uchar *read_p = bytecode_p;
+ R_PROC_TBLENTRY *bc_ep_tbl = NULL;
+ R_SCRIPT_BYTECODE *bc_new_data = NULL;
+
+ unsigned long n_entrypoints; /* Number of entrypoints */
+ size_t ep_tbl_offset; /* Offset of bytecode entrypoint table */
+
+ unsigned long i;
+
+ R_printf(R_STDOUT, "Loading script bytecode...\n");
+
+ /* The first two uint32 values are the number of entrypoints, and the
+ * offset to the entrypoint table, respectively. */
+
+ n_entrypoints = ys_read_u32_le(read_p, &read_p);
+ ep_tbl_offset = ys_read_u32_le(read_p, &read_p);
+
+ /* Check that the entrypoint table offset is valid. */
+ if ((bytecode_len - ep_tbl_offset) <
+ (n_entrypoints * R_SCRIPT_TBLENTRY_LEN)) {
+
+ R_printf(R_STDERR, "Invalid table offset.\n");
+ return NULL;
+ }
+
+ if (n_entrypoints > R_SCRIPT_MAX) {
+ R_printf(R_STDERR, "Script limit exceeded.\n");
+ return NULL;
+ }
+
+ /* Allocate a new bytecode resource information structure and table of
+ * entrypoints */
+
+ bc_new_data = (R_SCRIPT_BYTECODE *)malloc(sizeof *bc_new_data);
+ if (bc_new_data == NULL) {
+ R_printf(R_STDERR,
+ "Memory allocation failure loading script bytecode.\n");
+ return NULL;
+ }
+
+ bc_ep_tbl = (R_PROC_TBLENTRY *)malloc(n_entrypoints * sizeof *bc_ep_tbl);
+ if (bc_ep_tbl == NULL) {
+ R_printf(R_STDERR,
+ "Memory allocation failure creating script entrypoint table.\n");
+ free(bc_new_data);
+ return NULL;
+ }
+
+ /* Read in the entrypoint table */
+
+ read_p = bytecode_p + ep_tbl_offset;
+
+ for (i = 0; i < n_entrypoints; i++) {
+ /* 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 */
+ bc_ep_tbl[i].name_offset = ys_read_u16_le(read_p, &read_p);
+ bc_ep_tbl[i].offset = ys_read_u16_le(read_p, &read_p);
+
+ /* Perform a simple range check on offset values */
+ if ((bc_ep_tbl[i].name_offset > bytecode_len) ||
+ (bc_ep_tbl[i].offset > bytecode_len)) {
+
+ R_printf(R_STDERR,
+ "Invalid offset encountered in script entrypoint table.\n");
+ free(bc_new_data);
+ free(bc_ep_tbl);
+ return NULL;
+ }
+ }
+
+ bc_new_data->bytecode_p = (uchar *) bytecode_p;
+ 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;
+}
+
+R_DIALOGUE_LIST *SCRIPT_LoadDialogue(const uchar * dialogue_p,
+ size_t dialogue_len)
+/*--------------------------------------------------------------------------*\
+ * Reads a logical dialogue list from a dialogue list resource in memory.
+ * Returns NULL on failure.
+\*--------------------------------------------------------------------------*/
+{
+ const uchar *read_p = dialogue_p;
+
+ R_DIALOGUE_LIST *dialogue_list;
+ uint n_dialogue;
+
+ uint i;
+ size_t offset;
+
+ R_printf(R_STDOUT, "Loading dialogue list...\n");
+
+ /* Allocate dialogue list structure */
+ dialogue_list = (R_DIALOGUE_LIST *)malloc(sizeof *dialogue_list);
+ if (dialogue_list == NULL) {
+ return NULL;
+ }
+
+ /* First uint16 is the offset of the first string */
+ offset = ys_read_u16_le(read_p, &read_p);
+ if (offset > dialogue_len) {
+ R_printf(R_STDERR, "Error, invalid string offset.\n");
+ return NULL;
+ }
+
+ /* Calculate table length */
+ n_dialogue = offset / 2;
+ dialogue_list->n_dialogue = n_dialogue;
+
+ /* Allocate table of string pointers */
+ dialogue_list->str = (char **)malloc(n_dialogue * sizeof(char *));
+ if (dialogue_list->str == NULL) {
+ free(dialogue_list);
+ return NULL;
+ }
+
+ /* Allocate table of string offsets */
+ 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;
+ }
+
+ /* Read in tables from dialogue list resource */
+ read_p = dialogue_p;
+ for (i = 0; i < n_dialogue; i++) {
+ offset = ys_read_u16_le(read_p, &read_p);
+
+ if (offset > dialogue_len) {
+ R_printf(R_STDERR, "Error, invalid string offset.\n");
+ free(dialogue_list->str);
+ free(dialogue_list->str_off);
+ free(dialogue_list);
+ return NULL;
+ }
+ dialogue_list->str[i] = (char *)dialogue_p + offset;
+ dialogue_list->str_off[i] = offset;
+ }
+
+ return dialogue_list;
+}
+
+R_VOICE_LUT *SCRIPT_LoadVoiceLUT(const uchar * voicelut_p,
+ size_t voicelut_len, R_SCRIPTDATA * script)
+/*--------------------------------------------------------------------------*\
+ * Reads a logical voice LUT from a voice LUT resource in memory.
+ * Returns NULL on failure.
+\*--------------------------------------------------------------------------*/
+{
+ const uchar *read_p = voicelut_p;
+
+ R_VOICE_LUT *voice_lut;
+
+ uint n_voices;
+ uint i;
+
+ 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) {
+ R_printf(R_STDERR, "Error: Voice LUT entries do not match "
+ "dialogue entries.\n");
+ return NULL;
+ }
+
+ voice_lut->voices = (int *)malloc(n_voices * sizeof *voice_lut->voices);
+ if (voice_lut->voices == NULL) {
+
+ return NULL;
+ }
+
+ for (i = 0; i < n_voices; i++) {
+
+ voice_lut->voices[i] = ys_read_u16_le(read_p, &read_p);
+ }
+
+ return voice_lut;
+}
+
+void CF_script_info(int argc, char *argv[])
+{
+
+ ulong n_entrypoints;
+ ulong i;
+ char *name_ptr;
+
+ if (ScriptModule.current_script == NULL) {
+ return;
+ }
+
+ if (!ScriptModule.current_script->loaded) {
+ return;
+ }
+
+ n_entrypoints = ScriptModule.current_script->bytecode->n_entrypoints;
+
+ CON_Print("Current script contains %d entrypoints:", n_entrypoints);
+
+ for (i = 0; i < n_entrypoints; i++) {
+ name_ptr = (char *)
+ ScriptModule.current_script->bytecode->bytecode_p +
+ ScriptModule.current_script->bytecode->entrypoints[i].
+ name_offset;
+
+ CON_Print("%lu: %s", i, name_ptr);
+ }
+
+ return;
+}
+
+void CF_script_exec(int argc, char *argv[])
+{
+ uint ep_num;
+
+ if (argc < 1) {
+ return;
+ }
+
+ ep_num = atoi(argv[0]);
+
+ if (ScriptModule.dbg_thread == NULL) {
+
+ CON_Print("Creating debug thread...");
+ ScriptModule.dbg_thread = STHREAD_Create();
+
+ if (ScriptModule.dbg_thread == NULL) {
+ CON_Print("Thread creation failed.");
+ return;
+ }
+ }
+
+ if (ep_num >= ScriptModule.current_script->bytecode->n_entrypoints) {
+ CON_Print("Invalid entrypoint.");
+ return;
+ }
+
+ STHREAD_Execute(ScriptModule.dbg_thread, ep_num);
+
+ return;
+}
+
+void CF_script_togglestep(int argc, char *argv[])
+{
+ ScriptModule.dbg_singlestep = !ScriptModule.dbg_singlestep;
+
+ return;
+}
+
+} // End of namespace Saga