Initial commit
This commit is contained in:
2042
engines/ags/engine/script/cc_instance.cpp
Normal file
2042
engines/ags/engine/script/cc_instance.cpp
Normal file
File diff suppressed because it is too large
Load Diff
243
engines/ags/engine/script/cc_instance.h
Normal file
243
engines/ags/engine/script/cc_instance.h
Normal file
@@ -0,0 +1,243 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_CC_INSTANCE_H
|
||||
#define AGS_ENGINE_SCRIPT_CC_INSTANCE_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/map.h"
|
||||
#include "ags/engine/ac/timer.h"
|
||||
#include "ags/shared/script/cc_internal.h"
|
||||
#include "ags/shared/script/cc_script.h" // ccScript
|
||||
#include "ags/engine/script/non_blocking_script_function.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS;
|
||||
|
||||
#define INSTF_SHAREDATA 1
|
||||
#define INSTF_ABORTED 2
|
||||
#define INSTF_FREE 4
|
||||
#define INSTF_RUNNING 8 // set by main code to confirm script isn't stuck
|
||||
|
||||
// Size of stack in RuntimeScriptValues (aka distinct variables)
|
||||
#define CC_STACK_SIZE 256
|
||||
// Size of stack in bytes (raw data storage)
|
||||
#define CC_STACK_DATA_SIZE (1024 * sizeof(int32_t))
|
||||
#define MAX_CALL_STACK 128
|
||||
#define MAX_FUNCTION_PARAMS 20
|
||||
|
||||
// 256 because we use 8 bits to hold instance number
|
||||
#define MAX_LOADED_INSTANCES 256
|
||||
|
||||
#define INSTANCE_ID_SHIFT 24LL
|
||||
#define INSTANCE_ID_MASK 0x00000000000000ffLL
|
||||
#define INSTANCE_ID_REMOVEMASK 0x0000000000ffffffLL
|
||||
|
||||
// Script executor debugging flag:
|
||||
// enables mistake checks, but slows things down!
|
||||
#ifndef DEBUG_CC_EXEC
|
||||
#define DEBUG_CC_EXEC (0)
|
||||
#endif
|
||||
|
||||
struct ScriptInstruction {
|
||||
ScriptInstruction() = default;
|
||||
ScriptInstruction(int code, int instid) : Code(code), InstanceId(instid) {}
|
||||
|
||||
int32_t Code = 0;
|
||||
int32_t InstanceId = 0;
|
||||
};
|
||||
|
||||
struct ScriptOperation {
|
||||
ScriptInstruction Instruction;
|
||||
RuntimeScriptValue Args[MAX_SCMD_ARGS];
|
||||
int ArgCount = 0;
|
||||
|
||||
// Helper functions for clarity of intent:
|
||||
// returns argN, 1-based
|
||||
inline const RuntimeScriptValue &Arg1() const { return Args[0]; }
|
||||
inline const RuntimeScriptValue &Arg2() const { return Args[1]; }
|
||||
inline const RuntimeScriptValue &Arg3() const { return Args[2]; }
|
||||
// returns argN as a integer literal
|
||||
inline int Arg1i() const { return Args[0].IValue; }
|
||||
inline int Arg2i() const { return Args[1].IValue; }
|
||||
inline int Arg3i() const { return Args[2].IValue; }
|
||||
};
|
||||
|
||||
struct ScriptVariable {
|
||||
ScriptVariable() {
|
||||
ScAddress = -1; // address = 0 is valid one, -1 means undefined
|
||||
}
|
||||
|
||||
int32_t ScAddress; // original 32-bit relative data address, written in compiled script;
|
||||
// if we are to use Map or HashMap, this could be used as Key
|
||||
RuntimeScriptValue RValue;
|
||||
};
|
||||
|
||||
struct FunctionCallStack;
|
||||
|
||||
struct ScriptPosition {
|
||||
ScriptPosition()
|
||||
: Line(0) {
|
||||
}
|
||||
|
||||
ScriptPosition(const Shared::String §ion, int32_t line)
|
||||
: Section(section)
|
||||
, Line(line) {
|
||||
}
|
||||
|
||||
Shared::String Section;
|
||||
int32_t Line;
|
||||
};
|
||||
|
||||
|
||||
// Running instance of the script
|
||||
struct ccInstance {
|
||||
public:
|
||||
typedef std::unordered_map<int32_t, ScriptVariable> ScVarMap;
|
||||
typedef std::shared_ptr<ScVarMap> PScVarMap;
|
||||
public:
|
||||
int32_t flags;
|
||||
PScVarMap globalvars;
|
||||
char *globaldata;
|
||||
int32_t globaldatasize;
|
||||
// Executed byte-code. Unlike ccScript's code array which is int32_t, the one
|
||||
// in ccInstance must be intptr_t to accommodate real pointers placed after
|
||||
// performing fixups.
|
||||
intptr_t *code;
|
||||
ccInstance *runningInst; // might point to another instance if in far call
|
||||
int32_t codesize;
|
||||
char *strings;
|
||||
int32_t stringssize;
|
||||
RuntimeScriptValue *exports;
|
||||
RuntimeScriptValue *stack;
|
||||
int num_stackentries;
|
||||
// An array for keeping stack data; stack entries reference unknown data from here
|
||||
// TODO: probably change to dynamic array later
|
||||
char *stackdata; // for storing stack data of unknown type
|
||||
char *stackdata_ptr;// works similar to original stack pointer, points to the next unused byte in stack data array
|
||||
int32_t stackdatasize; // conventional size of stack data in bytes
|
||||
//
|
||||
RuntimeScriptValue registers[CC_NUM_REGISTERS];
|
||||
int32_t pc; // program counter
|
||||
int32_t line_number; // source code line number
|
||||
PScript instanceof;
|
||||
int loadedInstanceId;
|
||||
int returnValue;
|
||||
|
||||
int callStackSize;
|
||||
int32_t callStackLineNumber[MAX_CALL_STACK];
|
||||
int32_t callStackAddr[MAX_CALL_STACK];
|
||||
ccInstance *callStackCodeInst[MAX_CALL_STACK];
|
||||
|
||||
// array of real import indexes used in script
|
||||
uint32_t *resolved_imports;
|
||||
int numimports;
|
||||
|
||||
char *code_fixups;
|
||||
|
||||
// returns the currently executing instance, or NULL if none
|
||||
static ccInstance *GetCurrentInstance(void);
|
||||
// clears recorded stack of current instances
|
||||
// FIXME: reimplement this in a safer way, this must be done automatically
|
||||
// when destroying all script instances, e.g. on game quit.
|
||||
static void FreeInstanceStack();
|
||||
// create a runnable instance of the supplied script
|
||||
static std::unique_ptr<ccInstance> CreateFromScript(PScript script);
|
||||
static std::unique_ptr<ccInstance> CreateEx(PScript scri, const ccInstance *joined);
|
||||
static void SetExecTimeout(unsigned sys_poll_ms, unsigned abort_ms, unsigned abort_loops);
|
||||
|
||||
ccInstance();
|
||||
~ccInstance();
|
||||
// Create a runnable instance of the same script, sharing global memory
|
||||
std::unique_ptr<ccInstance> Fork();
|
||||
// Specifies that when the current function returns to the script, it
|
||||
// will stop and return from CallInstance
|
||||
void Abort();
|
||||
// Aborts instance, then frees the memory later when it is done with
|
||||
void AbortAndDestroy();
|
||||
|
||||
// Call an exported function in the script
|
||||
int CallScriptFunction(const char *funcname, int32_t num_params, const RuntimeScriptValue *params);
|
||||
|
||||
// Get the script's execution position and callstack as human-readable text
|
||||
Shared::String GetCallStack(int max_lines = INT_MAX) const;
|
||||
// Get the script's execution position
|
||||
void GetScriptPosition(ScriptPosition &script_pos) const;
|
||||
// Get the address of an exported symbol (function or variable) in the script
|
||||
RuntimeScriptValue GetSymbolAddress(const char *symname) const;
|
||||
void DumpInstruction(const ScriptOperation &op) const;
|
||||
// Tells whether this instance is in the process of executing the byte-code
|
||||
bool IsBeingRun() const;
|
||||
// Notifies that the game was being updated (script not hanging)
|
||||
void NotifyAlive();
|
||||
|
||||
// For each import, find the instance that corresponds to it and save it
|
||||
// in resolved_imports[]. Return whether the function is successful
|
||||
bool ResolveScriptImports(const ccScript *scri);
|
||||
|
||||
// Using resolved_imports[], resolve the IMPORT fixups
|
||||
// Also change CALLEXT op-codes to CALLAS when they pertain to a script instance
|
||||
bool ResolveImportFixups(const ccScript *scri);
|
||||
|
||||
private:
|
||||
bool _Create(PScript scri, const ccInstance *joined);
|
||||
// free the memory associated with the instance
|
||||
void Free();
|
||||
|
||||
bool CreateGlobalVars(const ccScript *scri);
|
||||
bool AddGlobalVar(const ScriptVariable &glvar);
|
||||
ScriptVariable *FindGlobalVar(int32_t var_addr);
|
||||
bool CreateRuntimeCodeFixups(const ccScript *scri);
|
||||
|
||||
// Begin executing script starting from the given bytecode index
|
||||
int Run(int32_t curpc);
|
||||
|
||||
// Stack processing
|
||||
// Push writes new value and increments stack ptr;
|
||||
// stack ptr now points to the __next empty__ entry
|
||||
void PushValueToStack(const RuntimeScriptValue &rval);
|
||||
void PushDataToStack(int32_t num_bytes);
|
||||
// Pop decrements stack ptr, returns last stored value and invalidates! stack tail;
|
||||
// stack ptr now points to the __next empty__ entry
|
||||
RuntimeScriptValue PopValueFromStack();
|
||||
// helper function to pop & dump several values
|
||||
void PopValuesFromStack(int32_t num_entries);
|
||||
void PopDataFromStack(int32_t num_bytes);
|
||||
// Return stack ptr at given offset from stack tail;
|
||||
// Offset is in data bytes; program stack ptr is __not__ changed
|
||||
RuntimeScriptValue GetStackPtrOffsetRw(int32_t rw_offset);
|
||||
|
||||
// Function call stack processing
|
||||
void PushToFuncCallStack(FunctionCallStack &func_callstack, const RuntimeScriptValue &rval);
|
||||
void PopFromFuncCallStack(FunctionCallStack &func_callstack, int32_t num_entries);
|
||||
|
||||
// Last time the script was noted of being "alive"
|
||||
AGS_Clock::time_point _lastAliveTs;
|
||||
};
|
||||
|
||||
extern void script_commands_init();
|
||||
extern void script_commands_free();
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
102
engines/ags/engine/script/executing_script.cpp
Normal file
102
engines/ags/engine/script/executing_script.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/* 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 "ags/globals.h"
|
||||
#include "ags/engine/script/executing_script.h"
|
||||
#include "ags/engine/debugging/debug_log.h"
|
||||
#include "ags/engine/debugging/debugger.h"
|
||||
#include "ags/engine/script/script.h"
|
||||
#include "ags/shared/ac/game_version.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
QueuedScript::QueuedScript()
|
||||
: Instance(kScInstGame)
|
||||
, ParamCount(0) {
|
||||
}
|
||||
|
||||
int ExecutingScript::queue_action(PostScriptAction act, int data, const char *aname) {
|
||||
if (numPostScriptActions >= MAX_QUEUED_ACTIONS)
|
||||
quitprintf("!%s: Cannot queue action, post-script queue full", aname);
|
||||
|
||||
// A strange behavior in pre-2.7.0 games allowed to call NewRoom right after
|
||||
// RestartGame, cancelling RestartGame. Probably an unintended effect.
|
||||
// We try to emulate this here, by simply removing all ePSARestartGame.
|
||||
if ((_G(loaded_game_file_version) < kGameVersion_270) && (act == ePSANewRoom)) {
|
||||
for (int i = 0; i < numPostScriptActions; i++) {
|
||||
if (postScriptActions[i] == ePSARestartGame) {
|
||||
debug("Removing spurious RestartGame event! index = %d numPostScriptActions = %d", i, numPostScriptActions);
|
||||
for (int j = i; j < numPostScriptActions; j++) {
|
||||
postScriptActions[j] = postScriptActions[j + 1];
|
||||
postScriptActionData[j] = postScriptActionData[j + 1];
|
||||
postScriptActionNames[j] = postScriptActionNames[j + 1];
|
||||
postScriptActionPositions[j] = postScriptActionPositions[j + 1];
|
||||
}
|
||||
i--; // make sure to remove multiple ePSARestartGame
|
||||
numPostScriptActions--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numPostScriptActions > 0) {
|
||||
// if something that will terminate the room has already
|
||||
// been queued, don't allow a second thing to be queued
|
||||
switch (postScriptActions[numPostScriptActions - 1]) {
|
||||
case ePSANewRoom:
|
||||
case ePSARestoreGame:
|
||||
case ePSARestoreGameDialog:
|
||||
case ePSARunAGSGame:
|
||||
case ePSARestartGame:
|
||||
quitprintf("!%s: Cannot run this command, since there was a %s command already queued to run in \"%s\", line %d",
|
||||
aname, postScriptActionNames[numPostScriptActions - 1],
|
||||
postScriptActionPositions[numPostScriptActions - 1].Section.GetCStr(), postScriptActionPositions[numPostScriptActions - 1].Line);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
postScriptActions[numPostScriptActions] = act;
|
||||
postScriptActionData[numPostScriptActions] = data;
|
||||
postScriptActionNames[numPostScriptActions] = aname;
|
||||
get_script_position(postScriptActionPositions[numPostScriptActions]);
|
||||
numPostScriptActions++;
|
||||
return numPostScriptActions - 1;
|
||||
}
|
||||
|
||||
void ExecutingScript::run_another(const char *namm, ScriptInstType scinst, size_t param_count, const RuntimeScriptValue *params) {
|
||||
if (numanother < MAX_QUEUED_SCRIPTS)
|
||||
numanother++;
|
||||
else {
|
||||
/*debug_script_warn("Warning: too many scripts to run, ignored %s(%d,%d)",
|
||||
script_run_another[numanother - 1], run_another_p1[numanother - 1],
|
||||
run_another_p2[numanother - 1]);*/
|
||||
}
|
||||
int thisslot = numanother - 1;
|
||||
QueuedScript &script = ScFnQueue[thisslot];
|
||||
script.FnName.SetString(namm, MAX_FUNCTION_NAME_LEN);
|
||||
script.Instance = scinst;
|
||||
script.ParamCount = param_count;
|
||||
for (size_t p = 0; p < MAX_QUEUED_PARAMS && p < param_count; ++p)
|
||||
script.Params[p] = params[p];
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
81
engines/ags/engine/script/executing_script.h
Normal file
81
engines/ags/engine/script/executing_script.h
Normal file
@@ -0,0 +1,81 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_EXECUTING_SCRIPT_H
|
||||
#define AGS_ENGINE_SCRIPT_EXECUTING_SCRIPT_H
|
||||
|
||||
#include "ags/engine/script/cc_instance.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
enum PostScriptAction {
|
||||
ePSANewRoom,
|
||||
ePSAInvScreen,
|
||||
ePSARestoreGame,
|
||||
ePSARestoreGameDialog,
|
||||
ePSARunAGSGame,
|
||||
ePSARunDialog,
|
||||
ePSARestartGame,
|
||||
ePSASaveGame,
|
||||
ePSASaveGameDialog
|
||||
};
|
||||
|
||||
#define MAX_QUEUED_SCRIPTS 4
|
||||
#define MAX_QUEUED_ACTIONS 5
|
||||
#define MAX_QUEUED_ACTION_DESC 100
|
||||
#define MAX_FUNCTION_NAME_LEN 60
|
||||
#define MAX_QUEUED_PARAMS 4
|
||||
|
||||
enum ScriptInstType {
|
||||
kScInstGame,
|
||||
kScInstRoom
|
||||
};
|
||||
|
||||
struct QueuedScript {
|
||||
Shared::String FnName;
|
||||
ScriptInstType Instance;
|
||||
size_t ParamCount;
|
||||
RuntimeScriptValue Params[MAX_QUEUED_PARAMS];
|
||||
|
||||
QueuedScript();
|
||||
};
|
||||
|
||||
struct ExecutingScript {
|
||||
ccInstance *inst = nullptr;
|
||||
// owned fork; CHECKME: this seem unused in the current engine
|
||||
std::unique_ptr<ccInstance> forkedInst{};
|
||||
PostScriptAction postScriptActions[MAX_QUEUED_ACTIONS]{};
|
||||
const char *postScriptActionNames[MAX_QUEUED_ACTIONS]{};
|
||||
ScriptPosition postScriptActionPositions[MAX_QUEUED_ACTIONS]{};
|
||||
char postScriptSaveSlotDescription[MAX_QUEUED_ACTIONS][MAX_QUEUED_ACTION_DESC]{};
|
||||
int postScriptActionData[MAX_QUEUED_ACTIONS]{};
|
||||
int numPostScriptActions = 0;
|
||||
QueuedScript ScFnQueue[MAX_QUEUED_SCRIPTS]{};
|
||||
int numanother = 0;
|
||||
|
||||
ExecutingScript() = default;
|
||||
int queue_action(PostScriptAction act, int data, const char *aname);
|
||||
void run_another(const char *namm, ScriptInstType scinst, size_t param_count, const RuntimeScriptValue *params);
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
110
engines/ags/engine/script/exports.cpp
Normal file
110
engines/ags/engine/script/exports.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Registering symbols for the script system
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#include "ags/shared/ac/game_struct_defines.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
extern void RegisterAudioChannelAPI();
|
||||
extern void RegisterAudioClipAPI();
|
||||
extern void RegisterButtonAPI();
|
||||
extern void RegisterCharacterAPI(ScriptAPIVersion base_api, ScriptAPIVersion compat_api);
|
||||
extern void RegisterContainerAPI();
|
||||
extern void RegisterDateTimeAPI();
|
||||
extern void RegisterDialogAPI();
|
||||
extern void RegisterDialogOptionsRenderingAPI();
|
||||
extern void RegisterDrawingSurfaceAPI(ScriptAPIVersion base_api, ScriptAPIVersion compat_api);
|
||||
extern void RegisterDynamicSpriteAPI();
|
||||
extern void RegisterFileAPI();
|
||||
extern void RegisterGameAPI();
|
||||
extern void RegisterGlobalAPI();
|
||||
extern void RegisterGUIAPI();
|
||||
extern void RegisterGUIControlAPI();
|
||||
extern void RegisterHotspotAPI();
|
||||
extern void RegisterInventoryItemAPI();
|
||||
extern void RegisterInventoryWindowAPI();
|
||||
extern void RegisterLabelAPI();
|
||||
extern void RegisterListBoxAPI();
|
||||
extern void RegisterMathAPI();
|
||||
extern void RegisterMouseAPI();
|
||||
extern void RegisterObjectAPI();
|
||||
extern void RegisterOverlayAPI();
|
||||
extern void RegisterParserAPI();
|
||||
extern void RegisterRegionAPI();
|
||||
extern void RegisterRoomAPI();
|
||||
extern void RegisterScreenAPI();
|
||||
extern void RegisterSliderAPI();
|
||||
extern void RegisterSpeechAPI(ScriptAPIVersion base_api, ScriptAPIVersion compat_api);
|
||||
extern void RegisterStringAPI();
|
||||
extern void RegisterSystemAPI();
|
||||
extern void RegisterTextBoxAPI();
|
||||
extern void RegisterViewFrameAPI();
|
||||
extern void RegisterViewportAPI();
|
||||
|
||||
extern void RegisterStaticObjects();
|
||||
|
||||
void setup_script_exports(ScriptAPIVersion base_api, ScriptAPIVersion compat_api) {
|
||||
RegisterAudioChannelAPI();
|
||||
RegisterAudioClipAPI();
|
||||
RegisterButtonAPI();
|
||||
RegisterCharacterAPI(base_api, compat_api);
|
||||
RegisterContainerAPI();
|
||||
RegisterDateTimeAPI();
|
||||
RegisterDialogAPI();
|
||||
RegisterDialogOptionsRenderingAPI();
|
||||
RegisterDrawingSurfaceAPI(base_api, compat_api);
|
||||
RegisterDynamicSpriteAPI();
|
||||
RegisterFileAPI();
|
||||
RegisterGameAPI();
|
||||
RegisterGlobalAPI();
|
||||
RegisterGUIAPI();
|
||||
RegisterGUIControlAPI();
|
||||
RegisterHotspotAPI();
|
||||
RegisterInventoryItemAPI();
|
||||
RegisterInventoryWindowAPI();
|
||||
RegisterLabelAPI();
|
||||
RegisterListBoxAPI();
|
||||
RegisterMathAPI();
|
||||
RegisterMouseAPI();
|
||||
RegisterObjectAPI();
|
||||
RegisterOverlayAPI();
|
||||
RegisterParserAPI();
|
||||
RegisterRegionAPI();
|
||||
RegisterRoomAPI();
|
||||
RegisterScreenAPI();
|
||||
RegisterSliderAPI();
|
||||
RegisterSpeechAPI(base_api, compat_api);
|
||||
RegisterStringAPI();
|
||||
RegisterSystemAPI();
|
||||
RegisterTextBoxAPI();
|
||||
RegisterViewFrameAPI();
|
||||
RegisterViewportAPI();
|
||||
|
||||
RegisterStaticObjects();
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
37
engines/ags/engine/script/exports.h
Normal file
37
engines/ags/engine/script/exports.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Registering symbols for the script system
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_EXPORTS_H
|
||||
#define AGS_ENGINE_SCRIPT_EXPORTS_H
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
void setup_script_exports(ScriptAPIVersion base_api, ScriptAPIVersion compat_api);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
51
engines/ags/engine/script/non_blocking_script_function.h
Normal file
51
engines/ags/engine/script/non_blocking_script_function.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_NON_BLOCKING_SCRIPT_FUNCTION_H
|
||||
#define AGS_ENGINE_SCRIPT_NON_BLOCKING_SCRIPT_FUNCTION_H
|
||||
|
||||
#include "common/std/vector.h"
|
||||
#include "ags/engine/ac/runtime_defines.h"
|
||||
#include "ags/engine/script/runtime_script_value.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct NonBlockingScriptFunction {
|
||||
const char *functionName;
|
||||
int numParameters;
|
||||
RuntimeScriptValue params[3];
|
||||
bool roomHasFunction;
|
||||
bool globalScriptHasFunction;
|
||||
std::vector<bool> moduleHasFunction;
|
||||
bool atLeastOneImplementationExists;
|
||||
|
||||
NonBlockingScriptFunction(const char *funcName, int numParams) {
|
||||
this->functionName = funcName;
|
||||
this->numParameters = numParams;
|
||||
atLeastOneImplementationExists = false;
|
||||
roomHasFunction = true;
|
||||
globalScriptHasFunction = true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
206
engines/ags/engine/script/runtime_script_value.cpp
Normal file
206
engines/ags/engine/script/runtime_script_value.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
/* 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 "ags/shared/script/cc_common.h"
|
||||
#include "ags/engine/script/runtime_script_value.h"
|
||||
#include "ags/engine/ac/dynobj/cc_script_object.h"
|
||||
#include "ags/shared/util/memory.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
//
|
||||
// NOTE to future optimizers: I am using 'this' ptr here to better
|
||||
// distinguish Runtime Values.
|
||||
//
|
||||
|
||||
uint8_t RuntimeScriptValue::ReadByte() const {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr:
|
||||
case kScValGlobalVar:
|
||||
if (RValue->Type == kScValData) {
|
||||
return *(uint8_t *)(GetRValuePtrWithOffset());
|
||||
} else {
|
||||
return static_cast<uint8_t>(RValue->IValue);
|
||||
}
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
return this->ObjMgr->ReadInt8(this->Ptr, this->IValue);
|
||||
default:
|
||||
return *((uint8_t *)this->GetPtrWithOffset());
|
||||
}
|
||||
}
|
||||
|
||||
int16_t RuntimeScriptValue::ReadInt16() const {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr:
|
||||
if (RValue->Type == kScValData) {
|
||||
return *(int16_t *)(GetRValuePtrWithOffset());
|
||||
} else {
|
||||
return static_cast<int16_t>(RValue->IValue);
|
||||
}
|
||||
case kScValGlobalVar:
|
||||
if (RValue->Type == kScValData) {
|
||||
return Memory::ReadInt16LE(GetRValuePtrWithOffset());
|
||||
} else {
|
||||
return static_cast<int16_t>(RValue->IValue);
|
||||
}
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
return this->ObjMgr->ReadInt16(this->Ptr, this->IValue);
|
||||
|
||||
default:
|
||||
return *((int16_t *)this->GetPtrWithOffset());
|
||||
}
|
||||
}
|
||||
|
||||
int32_t RuntimeScriptValue::ReadInt32() const {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr:
|
||||
if (RValue->Type == kScValData) {
|
||||
return *(int32_t *)(GetRValuePtrWithOffset());
|
||||
} else {
|
||||
return static_cast<int32_t>(RValue->IValue);
|
||||
}
|
||||
case kScValGlobalVar:
|
||||
if (RValue->Type == kScValData) {
|
||||
return Memory::ReadInt32LE(GetRValuePtrWithOffset());
|
||||
} else {
|
||||
return static_cast<uint32_t>(RValue->IValue);
|
||||
}
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
return this->ObjMgr->ReadInt32(this->Ptr, this->IValue);
|
||||
default:
|
||||
return *((int32_t *)this->GetPtrWithOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeScriptValue::WriteByte(uint8_t val) {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr:
|
||||
case kScValGlobalVar:
|
||||
if (RValue->Type == kScValData) {
|
||||
*(uint8_t *)(GetRValuePtrWithOffset()) = val;
|
||||
} else {
|
||||
RValue->SetUInt8(val); // set RValue as int
|
||||
}
|
||||
break;
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
this->ObjMgr->WriteInt8(this->Ptr, this->IValue, val);
|
||||
break;
|
||||
default:
|
||||
*((uint8_t *)this->GetPtrWithOffset()) = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeScriptValue::WriteInt16(int16_t val) {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr:
|
||||
if (RValue->Type == kScValData) {
|
||||
*(int16_t *)(GetRValuePtrWithOffset()) = val;
|
||||
} else {
|
||||
RValue->SetInt16(val); // set RValue as int
|
||||
}
|
||||
break;
|
||||
case kScValGlobalVar:
|
||||
if (RValue->Type == kScValData) {
|
||||
Memory::WriteInt16LE(GetRValuePtrWithOffset(), val);
|
||||
} else {
|
||||
RValue->SetInt16(val); // set RValue as int
|
||||
}
|
||||
break;
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
this->ObjMgr->WriteInt16(this->Ptr, this->IValue, val);
|
||||
break;
|
||||
default:
|
||||
*((int16_t *)this->GetPtrWithOffset()) = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeScriptValue::WriteInt32(int32_t val) {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr:
|
||||
if (RValue->Type == kScValData) {
|
||||
*(int32_t *)(GetRValuePtrWithOffset()) = val;
|
||||
} else {
|
||||
RValue->SetInt32(val); // set RValue as int
|
||||
}
|
||||
break;
|
||||
case kScValGlobalVar:
|
||||
if (RValue->Type == kScValData) {
|
||||
Memory::WriteInt32LE(GetRValuePtrWithOffset(), val);
|
||||
} else {
|
||||
RValue->SetInt32(val); // set RValue as int
|
||||
}
|
||||
break;
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
this->ObjMgr->WriteInt32(this->Ptr, this->IValue, val);
|
||||
break;
|
||||
default:
|
||||
*((int32_t *)this->GetPtrWithOffset()) = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RuntimeScriptValue &RuntimeScriptValue::DirectPtr() {
|
||||
if (Type == kScValGlobalVar || Type == kScValStackPtr) {
|
||||
int ival = IValue;
|
||||
*this = *RValue;
|
||||
IValue += ival;
|
||||
}
|
||||
|
||||
if (Ptr) {
|
||||
if (Type == kScValScriptObject)
|
||||
Ptr = ObjMgr->GetFieldPtr(Ptr, IValue);
|
||||
else
|
||||
Ptr = PtrU8 + IValue;
|
||||
IValue = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
RuntimeScriptValue &RuntimeScriptValue::DirectPtrObj() {
|
||||
if (Type == kScValGlobalVar || Type == kScValStackPtr)
|
||||
*this = *RValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void *RuntimeScriptValue::GetDirectPtr() const {
|
||||
const RuntimeScriptValue *temp_val = this;
|
||||
int ival = temp_val->IValue;
|
||||
if (temp_val->Type == kScValGlobalVar || temp_val->Type == kScValStackPtr) {
|
||||
temp_val = temp_val->RValue;
|
||||
ival += temp_val->IValue;
|
||||
}
|
||||
if (temp_val->Type == kScValScriptObject)
|
||||
return temp_val->ObjMgr->GetFieldPtr(temp_val->Ptr, ival);
|
||||
else
|
||||
return temp_val->PtrU8 + ival;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
445
engines/ags/engine/script/runtime_script_value.h
Normal file
445
engines/ags/engine/script/runtime_script_value.h
Normal file
@@ -0,0 +1,445 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Runtime script value struct
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_RUNTIME_SCRIPT_VALUE_H
|
||||
#define AGS_ENGINE_SCRIPT_RUNTIME_SCRIPT_VALUE_H
|
||||
|
||||
#include "ags/engine/ac/dynobj/cc_script_object.h"
|
||||
#include "ags/engine/ac/dynobj/cc_static_array.h"
|
||||
#include "ags/engine/script/script_api.h"
|
||||
#include "ags/shared/util/memory.h"
|
||||
|
||||
#include "ags/plugins/plugin_base.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
enum ScriptValueType {
|
||||
kScValUndefined, // to detect errors
|
||||
kScValInteger, // as strictly 32-bit integer (for integer math)
|
||||
kScValFloat, // as float (for floating point math), 32-bit
|
||||
kScValPluginArg, // an 32-bit value, passed to a script function when called
|
||||
// directly by plugin; is allowed to represent object pointer
|
||||
kScValStackPtr, // as a pointer to stack entry
|
||||
kScValData, // as a container for randomly sized data (usually array)
|
||||
kScValGlobalVar, // as a pointer to script variable; used only for global vars,
|
||||
// as pointer to local vars must have StackPtr type so that the
|
||||
// stack allocation could work
|
||||
kScValStringLiteral,// as a pointer to literal string (array of chars)
|
||||
kScValStaticArray, // as a pointer to static global array (of static or dynamic objects)
|
||||
kScValScriptObject, // as a pointer to managed script object
|
||||
kScValPluginObject, // as a pointer to object managed by plugin (similar to
|
||||
// kScValScriptObject, but has backward-compatible limitations)
|
||||
kScValStaticFunction,// as a pointer to static function
|
||||
kScValPluginFunction,// temporary workaround for plugins (unsafe function ptr)
|
||||
kScValObjectFunction,// as a pointer to object member function, gets object pointer as
|
||||
// first parameter
|
||||
kScValCodePtr // as a pointer to element in byte-code array
|
||||
};
|
||||
|
||||
struct RuntimeScriptValue {
|
||||
public:
|
||||
RuntimeScriptValue() {
|
||||
Type = kScValUndefined;
|
||||
IValue = 0;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 0;
|
||||
}
|
||||
|
||||
RuntimeScriptValue(int32_t val) {
|
||||
Type = kScValInteger;
|
||||
IValue = val;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
}
|
||||
|
||||
ScriptValueType Type;
|
||||
Common::String methodName;
|
||||
// The 32-bit value used for integer/float math and for storing
|
||||
// variable/element offset relative to object (and array) address
|
||||
union {
|
||||
int32_t IValue; // access Value as int32 type
|
||||
float FValue; // access Value as float type
|
||||
};
|
||||
// Pointer is used for storing... pointers - to objects, arrays,
|
||||
// functions and stack entries (other RSV)
|
||||
union {
|
||||
void *Ptr; // generic data pointer
|
||||
uint8_t *PtrU8; // byte buffer pointer
|
||||
char *CStr; // char buffer pointer
|
||||
RuntimeScriptValue *RValue;// access ptr as a pointer to Runtime Value
|
||||
ScriptAPIFunction *SPfn; // access ptr as a pointer to Script API Static Function
|
||||
ScriptAPIObjectFunction *ObjPfn; // access ptr as a pointer to Script API Object Function
|
||||
};
|
||||
// TODO: separation to Ptr and MgrPtr is only needed so far as there's
|
||||
// a separation between Script*, Dynamic* and game entity classes.
|
||||
// Once those classes are merged, it will no longer be needed.
|
||||
union {
|
||||
void *MgrPtr; // generic object manager pointer
|
||||
IScriptObject *ObjMgr; // script object manager
|
||||
CCStaticArray *ArrMgr; // static array manager
|
||||
};
|
||||
// The "real" size of data, either one stored in I/FValue,
|
||||
// or the one referenced by Ptr. Used for calculating stack
|
||||
// offsets.
|
||||
// Original AGS scripts always assumed pointer is 32-bit.
|
||||
// Therefore for stored pointers Size is always 4 both for x32
|
||||
// and x64 builds, so that the script is interpreted correctly.
|
||||
int Size;
|
||||
|
||||
inline bool IsValid() const {
|
||||
return Type != kScValUndefined;
|
||||
}
|
||||
|
||||
inline bool IsNull() const {
|
||||
return Ptr == nullptr && IValue == 0;
|
||||
}
|
||||
|
||||
inline bool GetAsBool() const {
|
||||
return !IsNull();
|
||||
}
|
||||
|
||||
inline void *GetPtrWithOffset() const {
|
||||
return PtrU8 + IValue;
|
||||
}
|
||||
|
||||
inline void *GetRValuePtrWithOffset() const {
|
||||
return static_cast<uint8_t *>(RValue->GetPtrWithOffset()) + this->IValue;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &Invalidate() {
|
||||
*this = RuntimeScriptValue();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetUInt8(uint8_t val) {
|
||||
Type = kScValInteger;
|
||||
methodName.clear();
|
||||
IValue = val;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetInt16(int16_t val) {
|
||||
Type = kScValInteger;
|
||||
methodName.clear();
|
||||
IValue = val;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 2;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetInt32(int32_t val) {
|
||||
Type = kScValInteger;
|
||||
methodName.clear();
|
||||
IValue = val;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetFloat(float val) {
|
||||
Type = kScValFloat;
|
||||
methodName.clear();
|
||||
FValue = val;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetInt32AsBool(bool val) {
|
||||
return SetInt32(val ? 1 : 0);
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetFloatAsBool(bool val) {
|
||||
return SetFloat(val ? 1.0F : 0.0F);
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetPluginArgument(int32_t val) {
|
||||
Type = kScValPluginArg;
|
||||
methodName.clear();
|
||||
IValue = val;
|
||||
Ptr = nullptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetStackPtr(RuntimeScriptValue *stack_entry) {
|
||||
Type = kScValStackPtr;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
RValue = stack_entry;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetData(void *data, int size) {
|
||||
Type = kScValData;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
Ptr = data;
|
||||
MgrPtr = nullptr;
|
||||
Size = size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetGlobalVar(RuntimeScriptValue *glvar_value) {
|
||||
Type = kScValGlobalVar;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
RValue = glvar_value;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO: size?
|
||||
inline RuntimeScriptValue &SetStringLiteral(const char *str) {
|
||||
Type = kScValStringLiteral;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
Ptr = const_cast<char *>(str);
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetStaticArray(void *object, CCStaticArray *manager) {
|
||||
Type = kScValStaticArray;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
Ptr = object;
|
||||
ArrMgr = manager;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetScriptObject(void *object, IScriptObject *manager) {
|
||||
Type = kScValScriptObject;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
Ptr = object;
|
||||
ObjMgr = manager;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetPluginObject(void *object, IScriptObject *manager) {
|
||||
Type = kScValPluginObject;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
Ptr = object;
|
||||
ObjMgr = manager;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetScriptObject(ScriptValueType type, void *object, IScriptObject *manager) {
|
||||
Type = type;
|
||||
IValue = 0;
|
||||
Ptr = object;
|
||||
ObjMgr = manager;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetStaticFunction(ScriptAPIFunction *pfn) {
|
||||
Type = kScValStaticFunction;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
SPfn = pfn;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetPluginMethod(Plugins::ScriptContainer *sc, const Common::String &method) {
|
||||
Type = kScValPluginFunction;
|
||||
methodName = method;
|
||||
Ptr = sc;
|
||||
MgrPtr = nullptr;
|
||||
IValue = 0;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetObjectFunction(ScriptAPIObjectFunction *pfn) {
|
||||
Type = kScValObjectFunction;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
ObjPfn = pfn;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue &SetCodePtr(void *ptr) {
|
||||
Type = kScValCodePtr;
|
||||
methodName.clear();
|
||||
IValue = 0;
|
||||
Ptr = ptr;
|
||||
MgrPtr = nullptr;
|
||||
Size = 4;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline RuntimeScriptValue operator !() const {
|
||||
return RuntimeScriptValue().SetInt32AsBool(!GetAsBool());
|
||||
}
|
||||
|
||||
inline bool operator ==(const RuntimeScriptValue &rval) const {
|
||||
if (rval.Type == kScValPluginFunction) {
|
||||
assert(!rval.methodName.empty());
|
||||
return (Type == kScValPluginFunction) && (rval.methodName == methodName);
|
||||
}
|
||||
|
||||
return ((intptr_t)Ptr + (intptr_t)IValue) == ((intptr_t)rval.Ptr + (intptr_t)rval.IValue);
|
||||
}
|
||||
inline bool operator !=(const RuntimeScriptValue &rval) const {
|
||||
return !(*this == rval);
|
||||
}
|
||||
|
||||
// FIXME: find out all certain cases when we are reading a pointer and store it
|
||||
// as 32-bit value here. There should be a solution to distinct these cases and
|
||||
// store value differently, otherwise it won't work for 64-bit build.
|
||||
inline RuntimeScriptValue ReadValue() const {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr: {
|
||||
// FIXME: join the kScValStackPtr with kScValData using some flag?
|
||||
switch (RValue->Type) {
|
||||
case kScValData:
|
||||
// read from the stack memory buffer
|
||||
return RuntimeScriptValue().SetInt32(*(int32_t *)(GetRValuePtrWithOffset()));
|
||||
default:
|
||||
// return the stack entry itself
|
||||
return *RValue;
|
||||
}
|
||||
}
|
||||
case kScValGlobalVar: {
|
||||
// FIXME: join the kScValGlobalVar with kScValData using some flag?
|
||||
switch (RValue->Type) {
|
||||
case kScValData:
|
||||
// read from the global memory buffer
|
||||
return RuntimeScriptValue().SetInt32(AGS::Shared::Memory::ReadInt32LE(GetRValuePtrWithOffset()));
|
||||
default:
|
||||
// return the gvar entry itself
|
||||
return *RValue;
|
||||
}
|
||||
}
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject:
|
||||
return RuntimeScriptValue().SetInt32(this->ObjMgr->ReadInt32(this->Ptr, this->IValue));
|
||||
default:
|
||||
return RuntimeScriptValue().SetInt32(*(int32_t *)this->GetPtrWithOffset());
|
||||
}
|
||||
}
|
||||
|
||||
// Notice, that there are only two valid cases when a pointer may be written:
|
||||
// when the destination is a stack entry or global variable of free type
|
||||
// (not kScValData type).
|
||||
// In any other case, only the numeric value (integer/float) will be written.
|
||||
inline void WriteValue(const RuntimeScriptValue &rval) {
|
||||
switch (this->Type) {
|
||||
case kScValStackPtr: {
|
||||
// FIXME: join the kScValStackPtr with kScValData using some flag?
|
||||
switch (RValue->Type) {
|
||||
case kScValData:
|
||||
// write into the stack memory buffer
|
||||
*(int32_t *)(GetRValuePtrWithOffset()) = rval.IValue;
|
||||
break;
|
||||
default:
|
||||
// write into the stack entry
|
||||
*RValue = rval;
|
||||
// On stack we assume each item has at least 4 bytes (with exception
|
||||
// of arrays - kScValData). This is why we fixup the size in case
|
||||
// the assigned value is less (char, int16).
|
||||
RValue->Size = 4;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kScValGlobalVar: {
|
||||
// FIXME: join the kScValGlobalVar with kScValData using some flag?
|
||||
switch (RValue->Type) {
|
||||
case kScValData:
|
||||
// write into the global memory buffer
|
||||
AGS::Shared::Memory::WriteInt32LE(GetRValuePtrWithOffset(), rval.IValue);
|
||||
break;
|
||||
default:
|
||||
// write into the gvar entry
|
||||
*RValue = rval;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kScValStaticArray:
|
||||
case kScValScriptObject: {
|
||||
this->ObjMgr->WriteInt32(this->Ptr, this->IValue, rval.IValue);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
*((int32_t *)this->GetPtrWithOffset()) = rval.IValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Plugins::PluginMethod pluginMethod() const {
|
||||
return Plugins::PluginMethod((Plugins::PluginBase *)Ptr, methodName);
|
||||
}
|
||||
|
||||
// Helper functions for reading or writing values from/to
|
||||
// object, referenced by this Runtime Value.
|
||||
// Copy implementation depends on value type.
|
||||
uint8_t ReadByte() const;
|
||||
int16_t ReadInt16() const;
|
||||
int32_t ReadInt32() const;
|
||||
void WriteByte(uint8_t val);
|
||||
void WriteInt16(int16_t val);
|
||||
void WriteInt32(int32_t val);
|
||||
|
||||
// Convert to most simple pointer type by resolving RValue ptrs and applying offsets;
|
||||
// non pointer types are left unmodified
|
||||
RuntimeScriptValue &DirectPtr();
|
||||
// Similar to above, a slightly speed-optimised version for situations when we can
|
||||
// tell for certain that we are expecting a pointer to the object and not its (first) field.
|
||||
RuntimeScriptValue &DirectPtrObj();
|
||||
// Resolve and return direct pointer to the referenced data; non pointer types return IValue
|
||||
void *GetDirectPtr() const;
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
970
engines/ags/engine/script/script.cpp
Normal file
970
engines/ags/engine/script/script.cpp
Normal file
@@ -0,0 +1,970 @@
|
||||
/* 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 "ags/engine/script/script.h"
|
||||
#include "ags/shared/ac/common.h"
|
||||
#include "ags/engine/ac/character.h"
|
||||
#include "ags/engine/ac/dialog.h"
|
||||
#include "ags/engine/ac/event.h"
|
||||
#include "ags/engine/ac/game.h"
|
||||
#include "ags/shared/ac/game_setup_struct.h"
|
||||
#include "ags/engine/ac/game_state.h"
|
||||
#include "ags/engine/ac/global_audio.h"
|
||||
#include "ags/engine/ac/global_character.h"
|
||||
#include "ags/engine/ac/global_dialog.h"
|
||||
#include "ags/engine/ac/global_display.h"
|
||||
#include "ags/engine/ac/global_game.h"
|
||||
#include "ags/engine/ac/global_gui.h"
|
||||
#include "ags/engine/ac/global_hotspot.h"
|
||||
#include "ags/engine/ac/global_object.h"
|
||||
#include "ags/engine/ac/global_room.h"
|
||||
#include "ags/engine/ac/global_video.h"
|
||||
#include "ags/engine/ac/inv_window.h"
|
||||
#include "ags/engine/ac/mouse.h"
|
||||
#include "ags/engine/ac/room.h"
|
||||
#include "ags/engine/ac/room_object.h"
|
||||
#include "ags/shared/script/cc_common.h"
|
||||
#include "ags/engine/debugging/debugger.h"
|
||||
#include "ags/engine/debugging/debug_log.h"
|
||||
#include "ags/engine/main/game_run.h"
|
||||
#include "ags/engine/media/video/video.h"
|
||||
#include "ags/engine/script/script_runtime.h"
|
||||
#include "ags/shared/util/string_compat.h"
|
||||
#include "ags/engine/media/audio/audio_system.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
static bool DoRunScriptFuncCantBlock(ccInstance *sci, NonBlockingScriptFunction *funcToRun, bool hasTheFunc);
|
||||
static char scfunctionname[MAX_FUNCTION_NAME_LEN + 1];
|
||||
|
||||
int run_dialog_request(int parmtr) {
|
||||
_GP(play).stop_dialog_at_end = DIALOG_RUNNING;
|
||||
RuntimeScriptValue params[]{ parmtr };
|
||||
RunScriptFunction(_G(gameinst).get(), "dialog_request", 1, params);
|
||||
|
||||
if (_GP(play).stop_dialog_at_end == DIALOG_STOP) {
|
||||
_GP(play).stop_dialog_at_end = DIALOG_NONE;
|
||||
return -2;
|
||||
}
|
||||
if (_GP(play).stop_dialog_at_end >= DIALOG_NEWTOPIC) {
|
||||
int tval = _GP(play).stop_dialog_at_end - DIALOG_NEWTOPIC;
|
||||
_GP(play).stop_dialog_at_end = DIALOG_NONE;
|
||||
return tval;
|
||||
}
|
||||
if (_GP(play).stop_dialog_at_end >= DIALOG_NEWROOM) {
|
||||
int roomnum = _GP(play).stop_dialog_at_end - DIALOG_NEWROOM;
|
||||
_GP(play).stop_dialog_at_end = DIALOG_NONE;
|
||||
NewRoom(roomnum);
|
||||
return -2;
|
||||
}
|
||||
_GP(play).stop_dialog_at_end = DIALOG_NONE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void run_function_on_non_blocking_thread(NonBlockingScriptFunction *funcToRun) {
|
||||
|
||||
update_script_mouse_coords();
|
||||
|
||||
int room_changes_was = _GP(play).room_changes;
|
||||
funcToRun->atLeastOneImplementationExists = false;
|
||||
|
||||
// run modules
|
||||
// modules need a forkedinst for this to work
|
||||
for (size_t i = 0; i < _G(numScriptModules); ++i) {
|
||||
funcToRun->moduleHasFunction[i] = DoRunScriptFuncCantBlock(_GP(moduleInstFork)[i].get(), funcToRun, funcToRun->moduleHasFunction[i]);
|
||||
|
||||
if (room_changes_was != _GP(play).room_changes)
|
||||
return;
|
||||
}
|
||||
|
||||
funcToRun->globalScriptHasFunction = DoRunScriptFuncCantBlock(_G(gameinstFork).get(), funcToRun, funcToRun->globalScriptHasFunction);
|
||||
|
||||
if (room_changes_was != _GP(play).room_changes || _G(abort_engine))
|
||||
return;
|
||||
|
||||
funcToRun->roomHasFunction = DoRunScriptFuncCantBlock(_G(roominstFork).get(), funcToRun, funcToRun->roomHasFunction);
|
||||
}
|
||||
|
||||
int run_interaction_event(const ObjectEvent &obj_evt, Interaction *nint, int evnt, int chkAny, bool isInv) {
|
||||
assert(nint);
|
||||
if (!nint)
|
||||
return 0;
|
||||
|
||||
if (evnt < 0 || (size_t)evnt >= nint->Events.size() ||
|
||||
(nint->Events[evnt].Response.get() == nullptr) || (nint->Events[evnt].Response->Cmds.size() == 0)) {
|
||||
// no response defined for this event
|
||||
// If there is a response for "Any Click", then abort now so as to
|
||||
// run that instead
|
||||
if (chkAny < 0);
|
||||
else if ((size_t)chkAny < nint->Events.size() &&
|
||||
(nint->Events[chkAny].Response.get() != nullptr) && (nint->Events[chkAny].Response->Cmds.size() > 0))
|
||||
return 0;
|
||||
|
||||
// Otherwise, run unhandled_event
|
||||
run_unhandled_event(obj_evt, evnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_GP(play).check_interaction_only) {
|
||||
_GP(play).check_interaction_only = 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cmdsrun = 0, retval = 0;
|
||||
// Right, so there were some commands defined in response to the event.
|
||||
retval = run_interaction_commandlist(obj_evt, nint->Events[evnt].Response.get(), &nint->Events[evnt].TimesRun, &cmdsrun);
|
||||
|
||||
if (_G(abort_engine))
|
||||
return -1;
|
||||
|
||||
// An inventory interaction, but the wrong item was used
|
||||
if ((isInv) && (cmdsrun == 0))
|
||||
run_unhandled_event(obj_evt, evnt);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Returns 0 normally, or -1 to indicate that the NewInteraction has
|
||||
// become invalid and don't run another interaction on it
|
||||
// (eg. a room change occurred)
|
||||
int run_interaction_script(const ObjectEvent &obj_evt, InteractionScripts *nint, int evnt, int chkAny) {
|
||||
assert(nint);
|
||||
if (!nint)
|
||||
return 0;
|
||||
|
||||
if (evnt < 0 || static_cast<size_t>(evnt) >= nint->ScriptFuncNames.size() || nint->ScriptFuncNames[evnt].IsEmpty()) {
|
||||
// no response defined for this event
|
||||
// If there is a response for "Any Click", then abort now so as to
|
||||
// run that instead
|
||||
if (chkAny < 0);
|
||||
else if (!nint->ScriptFuncNames[chkAny].IsEmpty())
|
||||
return 0;
|
||||
|
||||
// Otherwise, run unhandled_event
|
||||
run_unhandled_event(obj_evt, evnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (_GP(play).check_interaction_only) {
|
||||
_GP(play).check_interaction_only = 2; // CHECKME: wth is "2"?
|
||||
return -1;
|
||||
}
|
||||
|
||||
const int room_was = _GP(play).room_changes;
|
||||
|
||||
// TODO: find a way to generalize all the following hard-coded behavior
|
||||
|
||||
// Character or Inventory require a global script call
|
||||
const ScriptInstType inst_type = (strstr(obj_evt.BlockName.GetCStr(), "character") != nullptr) || (strstr(obj_evt.BlockName.GetCStr(), "inventory") != nullptr) ?
|
||||
kScInstGame : kScInstRoom;
|
||||
|
||||
// Room events do not require additional params
|
||||
if ((strstr(obj_evt.BlockName.GetCStr(), "room") != nullptr)) {
|
||||
QueueScriptFunction(inst_type, nint->ScriptFuncNames[evnt].GetCStr());
|
||||
}
|
||||
// Regions only require 1 param - dynobj ref
|
||||
else if ((strstr(obj_evt.BlockName.GetCStr(), "region") != nullptr)) {
|
||||
QueueScriptFunction(inst_type, nint->ScriptFuncNames[evnt].GetCStr(), 1, &obj_evt.DynObj);
|
||||
}
|
||||
// Other types (characters, objects, invitems, hotspots) require
|
||||
// 2 params - dynobj ref and the interaction mode (aka verb)
|
||||
else {
|
||||
RuntimeScriptValue params[]{obj_evt.DynObj, RuntimeScriptValue().SetInt32(obj_evt.Mode)};
|
||||
QueueScriptFunction(inst_type, nint->ScriptFuncNames[evnt].GetCStr(), 2, params);
|
||||
}
|
||||
|
||||
// if the room changed within the action
|
||||
if (room_was != _GP(play).room_changes)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_global_script() {
|
||||
constexpr int kscript_create_error = -3;
|
||||
|
||||
ccSetOption(SCOPT_AUTOIMPORT, 1);
|
||||
|
||||
// NOTE: this function assumes that the module lists have their elements preallocated!
|
||||
|
||||
std::vector<ccInstance *> all_insts; // gather all to resolve exports below
|
||||
for (size_t i = 0; i < _G(numScriptModules); ++i) {
|
||||
_GP(moduleInst)[i] = ccInstance::CreateFromScript(_GP(scriptModules)[i]);
|
||||
if (!_GP(moduleInst)[i])
|
||||
return kscript_create_error;
|
||||
all_insts.push_back(_GP(moduleInst)[i].get()); // this is only for temp reference
|
||||
}
|
||||
|
||||
_G(gameinst) = ccInstance::CreateFromScript(_GP(gamescript));
|
||||
if (!_G(gameinst))
|
||||
return kscript_create_error;
|
||||
all_insts.push_back(_G(gameinst).get()); // this is only for temp reference
|
||||
|
||||
if (_GP(dialogScriptsScript)) {
|
||||
_G(dialogScriptsInst) = ccInstance::CreateFromScript(_GP(dialogScriptsScript));
|
||||
if (!_G(dialogScriptsInst))
|
||||
return kscript_create_error;
|
||||
all_insts.push_back(_G(dialogScriptsInst).get()); // this is only for temp reference
|
||||
}
|
||||
|
||||
// Resolve the script imports after all the scripts have been loaded
|
||||
for (auto &inst : all_insts) {
|
||||
if (!inst->ResolveScriptImports(inst->instanceof.get()))
|
||||
return kscript_create_error;
|
||||
if (!inst->ResolveImportFixups(inst->instanceof.get()))
|
||||
return kscript_create_error;
|
||||
}
|
||||
|
||||
// Create the forks for 'repeatedly_execute_always' after resolving
|
||||
// because they copy their respective originals including the resolve information
|
||||
for (size_t module_idx = 0; module_idx < _G(numScriptModules); module_idx++) {
|
||||
auto fork = _GP(moduleInst)[module_idx]->Fork();
|
||||
if (!fork)
|
||||
return kscript_create_error;
|
||||
|
||||
_GP(moduleInstFork)[module_idx] = std::move(fork);
|
||||
_GP(moduleRepExecAddr)[module_idx] = _GP(moduleInst)[module_idx]->GetSymbolAddress(REP_EXEC_NAME);
|
||||
}
|
||||
|
||||
_G(gameinstFork) = _G(gameinst)->Fork();
|
||||
if (_G(gameinstFork) == nullptr)
|
||||
return kscript_create_error;
|
||||
|
||||
ccSetOption(SCOPT_AUTOIMPORT, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cancel_all_scripts() {
|
||||
for (int i = 0; i < _G(num_scripts); ++i) {
|
||||
auto &sc = _G(scripts)[i];
|
||||
if (sc.inst) {
|
||||
(sc.forkedInst) ? sc.inst->AbortAndDestroy() : sc.inst->Abort();
|
||||
}
|
||||
sc.numanother = 0;
|
||||
}
|
||||
_G(num_scripts) = 0;
|
||||
// in case the script is running on non-blocking thread (rep-exec-always etc)
|
||||
auto inst = ccInstance::GetCurrentInstance();
|
||||
if (inst)
|
||||
inst->Abort();
|
||||
}
|
||||
|
||||
ccInstance *GetScriptInstanceByType(ScriptInstType sc_inst) {
|
||||
if (sc_inst == kScInstGame)
|
||||
return _G(gameinst).get();
|
||||
else if (sc_inst == kScInstRoom)
|
||||
return _G(roominst).get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QueueScriptFunction(ScriptInstType sc_inst, const char *fn_name, size_t param_count, const RuntimeScriptValue *params) {
|
||||
if (_G(inside_script))
|
||||
// queue the script for the run after current script is finished
|
||||
_G(curscript)->run_another(fn_name, sc_inst, param_count, params);
|
||||
else
|
||||
// if no script is currently running, run the requested script right away
|
||||
RunScriptFunctionAuto(sc_inst, fn_name, param_count, params);
|
||||
}
|
||||
|
||||
static bool DoRunScriptFuncCantBlock(ccInstance *sci, NonBlockingScriptFunction *funcToRun, bool hasTheFunc) {
|
||||
if (!hasTheFunc)
|
||||
return (false);
|
||||
|
||||
_G(no_blocking_functions)++;
|
||||
int result = sci->CallScriptFunction(funcToRun->functionName, funcToRun->numParameters, funcToRun->params);
|
||||
|
||||
if (_G(abort_engine))
|
||||
return false;
|
||||
|
||||
if (result == -2) {
|
||||
// the function doesn't exist, so don't try and run it again
|
||||
hasTheFunc = false;
|
||||
} else if ((result != 0) && (result != 100)) {
|
||||
quit_with_script_error(funcToRun->functionName);
|
||||
} else {
|
||||
funcToRun->atLeastOneImplementationExists = true;
|
||||
}
|
||||
|
||||
// this might be nested, so don't disrupt blocked scripts
|
||||
cc_clear_error();
|
||||
_G(no_blocking_functions)--;
|
||||
return (hasTheFunc);
|
||||
}
|
||||
|
||||
static int PrepareTextScript(ccInstance *sci, const char **tsname) {
|
||||
cc_clear_error();
|
||||
// FIXME: try to make it so this function is not called with NULL sci
|
||||
if (sci == nullptr) return -1;
|
||||
if (sci->GetSymbolAddress(tsname[0]).IsNull()) {
|
||||
cc_error("no such function in script");
|
||||
return -2;
|
||||
}
|
||||
if (sci->IsBeingRun()) {
|
||||
cc_error("script is already in execution");
|
||||
return -3;
|
||||
}
|
||||
ExecutingScript exscript;
|
||||
exscript.inst = sci;
|
||||
_G(scripts)[_G(num_scripts)] = std::move(exscript);
|
||||
_G(curscript) = &_G(scripts)[_G(num_scripts)];
|
||||
_G(num_scripts)++;
|
||||
if (_G(num_scripts) >= MAX_SCRIPT_AT_ONCE)
|
||||
quit("too many nested text script instances created");
|
||||
// in case script_run_another is the function name, take a backup
|
||||
snprintf(scfunctionname, sizeof(scfunctionname), "%s", tsname[0]);
|
||||
tsname[0] = &scfunctionname[0];
|
||||
update_script_mouse_coords();
|
||||
_G(inside_script)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RunScriptFunction(ccInstance *sci, const char *tsname, size_t numParam, const RuntimeScriptValue *params) {
|
||||
int oldRestoreCount = _G(gameHasBeenRestored);
|
||||
// TODO: research why this is really necessary, and refactor to avoid such hacks!
|
||||
// First, save the current ccError state
|
||||
// This is necessary because we might be attempting
|
||||
// to run Script B, while Script A is still running in the
|
||||
// background.
|
||||
// If CallInstance here has an error, it would otherwise
|
||||
// also abort Script A because ccError is a global variable.
|
||||
ScriptError cachedCcError = cc_get_error();
|
||||
|
||||
cc_clear_error();
|
||||
int toret = PrepareTextScript(sci, &tsname);
|
||||
if (toret) {
|
||||
cc_error(cachedCcError);
|
||||
return -18;
|
||||
}
|
||||
cc_clear_error();
|
||||
toret = _G(curscript)->inst->CallScriptFunction(tsname, numParam, params);
|
||||
|
||||
// 100 is if Aborted (eg. because we are LoadAGSGame'ing)
|
||||
if (!_G(abort_engine) && (toret != 0) && (toret != -2) && (toret != 100)) {
|
||||
quit_with_script_error(tsname);
|
||||
}
|
||||
|
||||
_G(post_script_cleanup_stack)++;
|
||||
|
||||
if (_G(post_script_cleanup_stack) > 50)
|
||||
quitprintf("!post_script_cleanup call stack exceeded: possible recursive function call? running %s", tsname);
|
||||
|
||||
post_script_cleanup();
|
||||
|
||||
_G(post_script_cleanup_stack)--;
|
||||
|
||||
// restore cached error state
|
||||
cc_error(cachedCcError);
|
||||
|
||||
// if the game has been restored, ensure that any further scripts are not run
|
||||
if ((oldRestoreCount != _G(gameHasBeenRestored)) && (_G(eventClaimed) == EVENT_INPROGRESS))
|
||||
_G(eventClaimed) = EVENT_CLAIMED;
|
||||
|
||||
return toret;
|
||||
}
|
||||
|
||||
void RunScriptFunctionInModules(const char *tsname, size_t param_count, const RuntimeScriptValue *params) {
|
||||
for (size_t i = 0; i < _G(numScriptModules); ++i)
|
||||
RunScriptFunction(_GP(moduleInst)[i].get(), tsname, param_count, params);
|
||||
RunScriptFunction(_G(gameinst).get(), tsname, param_count, params);
|
||||
}
|
||||
|
||||
int RunScriptFunctionInRoom(const char *tsname, size_t param_count, const RuntimeScriptValue *params) {
|
||||
// Some room callbacks are considered to be obligatory; for historical reasons these are
|
||||
// identified by having no parameters;
|
||||
// TODO: this is a hack, this should be defined either by function type, or as an arg
|
||||
const bool strict_room_event = (param_count == 0);
|
||||
int toret = RunScriptFunction(_G(roominst).get(), tsname, param_count, params);
|
||||
// If it's a obligatory room event, and return code means missing function - error
|
||||
if (strict_room_event && (toret == -18))
|
||||
quitprintf("RunScriptFunction: error %d (%s) trying to run '%s' (Room %d)",
|
||||
toret, cc_get_error().ErrorString.GetCStr(), tsname, _G(displayed_room));
|
||||
return toret;
|
||||
}
|
||||
|
||||
// Run non-claimable event in all script modules, except room, break if certain events occurred
|
||||
static int RunUnclaimableEvent(const char *tsname) {
|
||||
const int room_changes_was = _GP(play).room_changes;
|
||||
const int restore_game_count_was = _G(gameHasBeenRestored);
|
||||
for (size_t i = 0; i < _G(numScriptModules); ++i) {
|
||||
if (!_GP(moduleRepExecAddr)[i].IsNull())
|
||||
RunScriptFunction(_GP(moduleInst)[i].get(), tsname);
|
||||
// Break on room change or save restoration
|
||||
if ((room_changes_was != _GP(play).room_changes) ||
|
||||
(restore_game_count_was != _G(gameHasBeenRestored)))
|
||||
return 0;
|
||||
}
|
||||
return RunScriptFunction(_G(gameinst).get(), tsname);
|
||||
}
|
||||
|
||||
static int RunClaimableEvent(const char *tsname, size_t param_count, const RuntimeScriptValue *params) {
|
||||
// Run claimable event chain in script modules and room script
|
||||
bool eventWasClaimed;
|
||||
int toret = run_claimable_event(tsname, true, param_count, params, &eventWasClaimed);
|
||||
// Break on event claim
|
||||
if (eventWasClaimed)
|
||||
return toret;
|
||||
return RunScriptFunction(_G(gameinst).get(), tsname, param_count, params);
|
||||
}
|
||||
|
||||
int RunScriptFunctionAuto(ScriptInstType sc_inst, const char *tsname, size_t param_count, const RuntimeScriptValue *params) {
|
||||
// If told to use a room instance, then run only there
|
||||
if (sc_inst == kScInstRoom)
|
||||
return RunScriptFunctionInRoom(tsname, param_count, params);
|
||||
// Rep-exec is only run in script modules, but not room script
|
||||
// (because room script has its own callback, attached to event slot)
|
||||
if (strcmp(tsname, REP_EXEC_NAME) == 0) {
|
||||
return RunUnclaimableEvent(REP_EXEC_NAME);
|
||||
}
|
||||
// Claimable event is run in all the script modules and room script,
|
||||
// before running in the globalscript instance
|
||||
if ((strcmp(tsname, _G(tsnames)[kTS_KeyPress]) == 0) || (strcmp(tsname, _G(tsnames)[kTS_MouseClick]) == 0) ||
|
||||
(strcmp(tsname, _G(tsnames)[kTS_TextInput]) == 0) || (strcmp(tsname, "on_event") == 0)) {
|
||||
return RunClaimableEvent(tsname, param_count, params);
|
||||
}
|
||||
// Else run on the single chosen script instance
|
||||
ccInstance *sci = GetScriptInstanceByType(sc_inst);
|
||||
if (!sci)
|
||||
return 0;
|
||||
return RunScriptFunction(sci, tsname, param_count, params);
|
||||
}
|
||||
|
||||
void AllocScriptModules() {
|
||||
// NOTE: this preallocation possibly required to safeguard some algorithms
|
||||
_GP(moduleInst).resize(_G(numScriptModules));
|
||||
_GP(moduleInstFork).resize(_G(numScriptModules));
|
||||
_GP(moduleRepExecAddr).resize(_G(numScriptModules));
|
||||
_GP(repExecAlways).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(lateRepExecAlways).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(getDialogOptionsDimensionsFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(renderDialogOptionsFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(getDialogOptionUnderCursorFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(runDialogOptionMouseClickHandlerFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(runDialogOptionKeyPressHandlerFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(runDialogOptionTextInputHandlerFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(runDialogOptionRepExecFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
_GP(runDialogOptionCloseFunc).moduleHasFunction.resize(_G(numScriptModules), true);
|
||||
for (auto &val : _GP(moduleRepExecAddr)) {
|
||||
val.Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAllScriptInstances() {
|
||||
ccInstance::FreeInstanceStack();
|
||||
FreeRoomScriptInstance();
|
||||
|
||||
// NOTE: don't know why, but Forks must be deleted prior to primary inst,
|
||||
// or bad things will happen; TODO: investigate and make this less fragile
|
||||
_G(gameinstFork).reset();
|
||||
_G(gameinst).reset();
|
||||
_G(dialogScriptsInst).reset();
|
||||
_GP(moduleInstFork).clear();
|
||||
_GP(moduleInst).clear();
|
||||
}
|
||||
|
||||
void FreeRoomScriptInstance() {
|
||||
// NOTE: don't know why, but Forks must be deleted prior to primary inst,
|
||||
// or bad things will happen; TODO: investigate and make this less fragile
|
||||
_G(roominstFork).reset();
|
||||
_G(roominst).reset();
|
||||
}
|
||||
|
||||
void FreeGlobalScripts() {
|
||||
_G(numScriptModules) = 0;
|
||||
|
||||
_GP(gamescript).reset();
|
||||
_GP(scriptModules).clear();
|
||||
_GP(dialogScriptsScript).reset();
|
||||
|
||||
_GP(repExecAlways).moduleHasFunction.clear();
|
||||
_GP(lateRepExecAlways).moduleHasFunction.clear();
|
||||
_GP(getDialogOptionsDimensionsFunc).moduleHasFunction.clear();
|
||||
_GP(renderDialogOptionsFunc).moduleHasFunction.clear();
|
||||
_GP(getDialogOptionUnderCursorFunc).moduleHasFunction.clear();
|
||||
_GP(runDialogOptionMouseClickHandlerFunc).moduleHasFunction.clear();
|
||||
_GP(runDialogOptionKeyPressHandlerFunc).moduleHasFunction.clear();
|
||||
_GP(runDialogOptionTextInputHandlerFunc).moduleHasFunction.clear();
|
||||
_GP(runDialogOptionRepExecFunc).moduleHasFunction.clear();
|
||||
_GP(runDialogOptionCloseFunc).moduleHasFunction.clear();
|
||||
}
|
||||
|
||||
String GetScriptName(ccInstance *sci) {
|
||||
// TODO: have script name a ccScript's member?
|
||||
// TODO: check script modules too?
|
||||
if (!sci)
|
||||
return "Not in a script";
|
||||
else if (sci->instanceof == _GP(gamescript))
|
||||
return "Global script";
|
||||
else if (sci->instanceof == _GP(thisroom).CompiledScript)
|
||||
return String::FromFormat("Room %d script", _G(displayed_room));
|
||||
return "Unknown script";
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
|
||||
char bname[MAX_FUNCTION_NAME_LEN + 1], bne[MAX_FUNCTION_NAME_LEN + 1];
|
||||
char *make_ts_func_name(const char *base, int iii, int subd) {
|
||||
int err = snprintf(bname, MAX_FUNCTION_NAME_LEN, base, iii);
|
||||
if (err >= (int)sizeof(bname))
|
||||
debug_script_warn("Function name length limit exceeded: %s (%d)", base, iii);
|
||||
err = snprintf(bne, MAX_FUNCTION_NAME_LEN, "%s_%c", bname, subd + 'a');
|
||||
if (err >= (int)sizeof(bne))
|
||||
debug_script_warn("Function name length limit exceeded: %s", bname);
|
||||
return &bne[0];
|
||||
}
|
||||
|
||||
void post_script_cleanup() {
|
||||
// should do any post-script stuff here, like go to new room
|
||||
if (cc_has_error())
|
||||
quit(cc_get_error().ErrorString);
|
||||
|
||||
ExecutingScript copyof;
|
||||
if (_G(num_scripts) > 0) { // save until the end of function
|
||||
copyof = std::move(_G(scripts)[_G(num_scripts) - 1]);
|
||||
copyof.forkedInst.reset(); // don't need it further
|
||||
_G(num_scripts)--;
|
||||
}
|
||||
_G(inside_script)--;
|
||||
|
||||
if (_G(num_scripts) > 0)
|
||||
_G(curscript) = &_G(scripts)[_G(num_scripts) - 1];
|
||||
else {
|
||||
_G(curscript) = nullptr;
|
||||
}
|
||||
// if (abort_executor) user_disabled_data2=aborted_ip;
|
||||
|
||||
int old_room_number = _G(displayed_room);
|
||||
|
||||
// FIXME: sync audio in case any screen changing or time-consuming post-script actions were scheduled
|
||||
if (copyof.numPostScriptActions > 0) {
|
||||
sync_audio_playback();
|
||||
}
|
||||
|
||||
// run the queued post-script actions
|
||||
for (int ii = 0; ii < copyof.numPostScriptActions; ii++) {
|
||||
int thisData = copyof.postScriptActionData[ii];
|
||||
|
||||
switch (copyof.postScriptActions[ii]) {
|
||||
case ePSANewRoom:
|
||||
// only change rooms when all scripts are done
|
||||
if (_G(num_scripts) == 0) {
|
||||
new_room(thisData, _G(playerchar));
|
||||
// don't allow any pending room scripts from the old room
|
||||
// in run_another to be executed
|
||||
return;
|
||||
} else
|
||||
_G(curscript)->queue_action(ePSANewRoom, thisData, "NewRoom");
|
||||
break;
|
||||
case ePSAInvScreen:
|
||||
invscreen();
|
||||
break;
|
||||
case ePSARestoreGame:
|
||||
cancel_all_scripts();
|
||||
try_restore_save(thisData);
|
||||
return;
|
||||
case ePSARestoreGameDialog:
|
||||
restore_game_dialog();
|
||||
return;
|
||||
case ePSARunAGSGame:
|
||||
cancel_all_scripts();
|
||||
_G(load_new_game) = thisData;
|
||||
return;
|
||||
case ePSARunDialog:
|
||||
do_conversation(thisData);
|
||||
break;
|
||||
case ePSARestartGame:
|
||||
cancel_all_scripts();
|
||||
restart_game();
|
||||
return;
|
||||
case ePSASaveGame:
|
||||
save_game(thisData, copyof.postScriptSaveSlotDescription[ii]);
|
||||
break;
|
||||
case ePSASaveGameDialog:
|
||||
save_game_dialog();
|
||||
break;
|
||||
default:
|
||||
quitprintf("undefined post script action found: %d", copyof.postScriptActions[ii]);
|
||||
}
|
||||
// if the room changed in a conversation, for example, abort
|
||||
if (old_room_number != _G(displayed_room) || _G(abort_engine)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (copyof.numPostScriptActions > 0) {
|
||||
sync_audio_playback();
|
||||
}
|
||||
|
||||
for (int jj = 0; jj < copyof.numanother; jj++) {
|
||||
old_room_number = _G(displayed_room);
|
||||
QueuedScript &script = copyof.ScFnQueue[jj];
|
||||
RunScriptFunctionAuto(script.Instance, script.FnName.GetCStr(), script.ParamCount, script.Params);
|
||||
if (script.Instance == kScInstRoom && script.ParamCount == 1) {
|
||||
// some bogus hack for "on_call" event handler
|
||||
_GP(play).roomscript_finished = 1;
|
||||
}
|
||||
|
||||
// if they've changed rooms, cancel any further pending scripts
|
||||
if ((_G(displayed_room) != old_room_number) || (_G(load_new_game)))
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void quit_with_script_error(const char *functionName) {
|
||||
// TODO: clean up the error reporting logic. Now engine will append call
|
||||
// stack info in quit_check_for_error_state() but only in case of explicit
|
||||
// script error ("!" type), and not in other case.
|
||||
const auto &error = cc_get_error();
|
||||
if (error.IsUserError)
|
||||
quitprintf("!Error running function '%s':\n%s", functionName, error.ErrorString.GetCStr());
|
||||
else
|
||||
quitprintf("Error running function '%s':\n%s\n\n%s", functionName,
|
||||
error.ErrorString.GetCStr(), error.CallStack.GetCStr());
|
||||
}
|
||||
|
||||
int get_nivalue(InteractionCommandList *nic, int idx, int parm) {
|
||||
if (nic->Cmds[idx].Data[parm].Type == AGS::Shared::kInterValVariable) {
|
||||
// return the value of the variable
|
||||
return get_interaction_variable(nic->Cmds[idx].Data[parm].Value)->Value;
|
||||
}
|
||||
return nic->Cmds[idx].Data[parm].Value;
|
||||
}
|
||||
|
||||
InteractionVariable *get_interaction_variable(int varindx) {
|
||||
|
||||
if ((varindx >= LOCAL_VARIABLE_OFFSET) && ((size_t)varindx < LOCAL_VARIABLE_OFFSET + _GP(thisroom).LocalVariables.size()))
|
||||
return &_GP(thisroom).LocalVariables[varindx - LOCAL_VARIABLE_OFFSET];
|
||||
|
||||
if ((varindx < 0) || (varindx >= _G(numGlobalVars)))
|
||||
quit("!invalid interaction variable specified");
|
||||
|
||||
return &_G(globalvars)[varindx];
|
||||
}
|
||||
|
||||
InteractionVariable *FindGraphicalVariable(const char *varName) {
|
||||
int ii;
|
||||
for (ii = 0; ii < _G(numGlobalVars); ii++) {
|
||||
if (_G(globalvars)[ii].Name.CompareNoCase(varName) == 0)
|
||||
return &_G(globalvars)[ii];
|
||||
}
|
||||
for (size_t i = 0; i < _GP(thisroom).LocalVariables.size(); ++i) {
|
||||
if (_GP(thisroom).LocalVariables[i].Name.CompareNoCase(varName) == 0)
|
||||
return &_GP(thisroom).LocalVariables[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define IPARAM1 get_nivalue(nicl, i, 0)
|
||||
#define IPARAM2 get_nivalue(nicl, i, 1)
|
||||
#define IPARAM3 get_nivalue(nicl, i, 2)
|
||||
#define IPARAM4 get_nivalue(nicl, i, 3)
|
||||
#define IPARAM5 get_nivalue(nicl, i, 4)
|
||||
|
||||
struct TempEip {
|
||||
int oldval;
|
||||
TempEip(int newval) {
|
||||
oldval = get_our_eip();
|
||||
set_our_eip(newval);
|
||||
}
|
||||
~TempEip() {
|
||||
set_our_eip(oldval);
|
||||
}
|
||||
};
|
||||
|
||||
// the 'cmdsrun' parameter counts how many commands are run.
|
||||
// if a 'Inv Item Was Used' check does not pass, it doesn't count
|
||||
// so cmdsrun remains 0 if no inventory items matched
|
||||
int run_interaction_commandlist(const ObjectEvent &obj_evt, InteractionCommandList *nicl, int *timesrun, int *cmdsrun) {
|
||||
if (nicl == nullptr)
|
||||
return -1;
|
||||
|
||||
const char *evblockbasename = obj_evt.BlockName.GetCStr();
|
||||
const int evblocknum = obj_evt.BlockID;
|
||||
for (size_t i = 0; i < nicl->Cmds.size(); i++) {
|
||||
cmdsrun[0] ++;
|
||||
const int room_was = _GP(play).room_changes;
|
||||
|
||||
switch (nicl->Cmds[i].Type) {
|
||||
case 0: // Do nothing
|
||||
break;
|
||||
case 1: { // Run script
|
||||
TempEip tempip(4001);
|
||||
RuntimeScriptValue rval_null;
|
||||
if ((strstr(evblockbasename, "character") != nullptr) || (strstr(evblockbasename, "inventory") != nullptr)) {
|
||||
// Character or Inventory (global script)
|
||||
const char *torun = make_ts_func_name(evblockbasename, evblocknum, nicl->Cmds[i].Data[0].Value);
|
||||
// we are already inside the mouseclick event of the script, can't nest calls
|
||||
QueueScriptFunction(kScInstGame, torun);
|
||||
} else {
|
||||
// Other (room script)
|
||||
const char *torun = make_ts_func_name(evblockbasename, evblocknum, nicl->Cmds[i].Data[0].Value);
|
||||
QueueScriptFunction(kScInstRoom, torun);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: // Add score (first time)
|
||||
if (timesrun[0] > 0)
|
||||
break;
|
||||
timesrun[0] ++;
|
||||
// fall through
|
||||
case 3: // Add score
|
||||
GiveScore(IPARAM1);
|
||||
break;
|
||||
case 4: // Display Message
|
||||
/* if (comprdata<0)
|
||||
_G(display_message_aschar)=evb->data[ss];*/
|
||||
DisplayMessage(IPARAM1);
|
||||
break;
|
||||
case 5: // Play Music
|
||||
PlayMusicResetQueue(IPARAM1);
|
||||
break;
|
||||
case 6: // Stop Music
|
||||
stopmusic();
|
||||
break;
|
||||
case 7: // Play Sound
|
||||
play_sound(IPARAM1);
|
||||
break;
|
||||
case 8: // Play Flic
|
||||
PlayFlic(IPARAM1, IPARAM2);
|
||||
break;
|
||||
case 9: { // Run Dialog
|
||||
RunDialog(IPARAM1);
|
||||
// if they changed room within the dialog script,
|
||||
// the interaction command list is no longer valid
|
||||
if (room_was != _GP(play).room_changes)
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 10: // Enable Dialog Option
|
||||
SetDialogOption(IPARAM1, IPARAM2, 1);
|
||||
break;
|
||||
case 11: // Disable Dialog Option
|
||||
SetDialogOption(IPARAM1, IPARAM2, 0);
|
||||
break;
|
||||
case 12: // Go To Screen
|
||||
Character_ChangeRoomAutoPosition(_G(playerchar), IPARAM1, IPARAM2);
|
||||
return -1;
|
||||
case 13: // Add Inventory
|
||||
add_inventory(IPARAM1);
|
||||
break;
|
||||
case 14: // Move Object
|
||||
MoveObject(IPARAM1, IPARAM2, IPARAM3, IPARAM4);
|
||||
// if they want to wait until finished, do so
|
||||
if (IPARAM5)
|
||||
GameLoopUntilNotMoving(&_G(objs)[IPARAM1].moving);
|
||||
break;
|
||||
case 15: // Object Off
|
||||
ObjectOff(IPARAM1);
|
||||
break;
|
||||
case 16: // Object On
|
||||
ObjectOn(IPARAM1);
|
||||
break;
|
||||
case 17: // Set Object View
|
||||
SetObjectView(IPARAM1, IPARAM2);
|
||||
break;
|
||||
case 18: // Animate Object
|
||||
AnimateObject4(IPARAM1, IPARAM2, IPARAM3, IPARAM4);
|
||||
break;
|
||||
case 19: // Move Character
|
||||
if (IPARAM4)
|
||||
MoveCharacterBlocking(IPARAM1, IPARAM2, IPARAM3, 0);
|
||||
else
|
||||
MoveCharacter(IPARAM1, IPARAM2, IPARAM3);
|
||||
break;
|
||||
case 20: // If Inventory Item was used
|
||||
if (_GP(play).usedinv == IPARAM1) {
|
||||
if (_GP(game).options[OPT_NOLOSEINV] == 0)
|
||||
lose_inventory(_GP(play).usedinv);
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
} else
|
||||
cmdsrun[0] --;
|
||||
break;
|
||||
case 21: // if player has inventory item
|
||||
if (_G(playerchar)->inv[IPARAM1] > 0)
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
break;
|
||||
case 22: // if a character is moving
|
||||
if (_GP(game).chars[IPARAM1].walking)
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
break;
|
||||
case 23: // if two variables are equal
|
||||
if (IPARAM1 == IPARAM2)
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
break;
|
||||
case 24: // Stop character walking
|
||||
StopMoving(IPARAM1);
|
||||
break;
|
||||
case 25: // Go to screen at specific co-ordinates
|
||||
NewRoomEx(IPARAM1, IPARAM2, IPARAM3);
|
||||
return -1;
|
||||
case 26: // Move NPC to different room
|
||||
if (!is_valid_character(IPARAM1))
|
||||
quit("!Move NPC to different room: invalid character specified");
|
||||
_GP(game).chars[IPARAM1].room = IPARAM2;
|
||||
break;
|
||||
case 27: // Set character view
|
||||
SetCharacterView(IPARAM1, IPARAM2);
|
||||
break;
|
||||
case 28: // Release character view
|
||||
ReleaseCharacterView(IPARAM1);
|
||||
break;
|
||||
case 29: // Follow character
|
||||
FollowCharacter(IPARAM1, IPARAM2);
|
||||
break;
|
||||
case 30: // Stop following
|
||||
FollowCharacter(IPARAM1, -1);
|
||||
break;
|
||||
case 31: // Disable hotspot
|
||||
DisableHotspot(IPARAM1);
|
||||
break;
|
||||
case 32: // Enable hotspot
|
||||
EnableHotspot(IPARAM1);
|
||||
break;
|
||||
case 33: // Set variable value
|
||||
get_interaction_variable(nicl->Cmds[i].Data[0].Value)->Value = IPARAM2;
|
||||
break;
|
||||
case 34: // Run animation
|
||||
AnimateCharacter4(IPARAM1, IPARAM2, IPARAM3, 0);
|
||||
GameLoopUntilValueIsZero(&_GP(game).chars[IPARAM1].animating);
|
||||
break;
|
||||
case 35: // Quick animation
|
||||
SetCharacterView(IPARAM1, IPARAM2);
|
||||
AnimateCharacter4(IPARAM1, IPARAM3, IPARAM4, 0);
|
||||
GameLoopUntilValueIsZero(&_GP(game).chars[IPARAM1].animating);
|
||||
ReleaseCharacterView(IPARAM1);
|
||||
break;
|
||||
case 36: // Set idle animation
|
||||
SetCharacterIdle(IPARAM1, IPARAM2, IPARAM3);
|
||||
break;
|
||||
case 37: // Disable idle animation
|
||||
SetCharacterIdle(IPARAM1, -1, -1);
|
||||
break;
|
||||
case 38: // Lose inventory item
|
||||
lose_inventory(IPARAM1);
|
||||
break;
|
||||
case 39: // Show GUI
|
||||
InterfaceOn(IPARAM1);
|
||||
break;
|
||||
case 40: // Hide GUI
|
||||
InterfaceOff(IPARAM1);
|
||||
break;
|
||||
case 41: // Stop running more commands
|
||||
return -1;
|
||||
case 42: // Face location
|
||||
FaceLocation(IPARAM1, IPARAM2, IPARAM3);
|
||||
break;
|
||||
case 43: // Pause command processor
|
||||
scrWait(IPARAM1);
|
||||
break;
|
||||
case 44: // Change character view
|
||||
ChangeCharacterView(IPARAM1, IPARAM2);
|
||||
break;
|
||||
case 45: // If player character is
|
||||
if (GetPlayerCharacter() == IPARAM1)
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
break;
|
||||
case 46: // if cursor mode is
|
||||
if (GetCursorMode() == IPARAM1)
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
break;
|
||||
case 47: // if player has been to room
|
||||
if (HasBeenToRoom(IPARAM1))
|
||||
if (run_interaction_commandlist(obj_evt, nicl->Cmds[i].Children.get(), timesrun, cmdsrun))
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
quit("unknown new interaction command");
|
||||
break;
|
||||
}
|
||||
|
||||
if (_G(abort_engine))
|
||||
return -1;
|
||||
|
||||
// if the room changed within the action, nicl is no longer valid
|
||||
if (room_was != _GP(play).room_changes)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// check and abort game if the script is currently
|
||||
// inside the rep_exec_always function
|
||||
void can_run_delayed_command() {
|
||||
if (_G(no_blocking_functions))
|
||||
quit("!This command cannot be used within non-blocking events such as " REP_EXEC_ALWAYS_NAME);
|
||||
}
|
||||
|
||||
void run_unhandled_event(const ObjectEvent &obj_evt, int evnt) {
|
||||
|
||||
if (_GP(play).check_interaction_only)
|
||||
return;
|
||||
|
||||
const char *evblockbasename = obj_evt.BlockName.GetCStr();
|
||||
const int evblocknum = obj_evt.BlockID;
|
||||
int evtype = 0;
|
||||
|
||||
if (ags_strnicmp(evblockbasename, "hotspot", 7) == 0) evtype = 1;
|
||||
else if (ags_strnicmp(evblockbasename, "object", 6) == 0) evtype = 2;
|
||||
else if (ags_strnicmp(evblockbasename, "character", 9) == 0) evtype = 3;
|
||||
else if (ags_strnicmp(evblockbasename, "inventory", 9) == 0) evtype = 5;
|
||||
else if (ags_strnicmp(evblockbasename, "region", 6) == 0)
|
||||
return; // no unhandled_events for regions
|
||||
|
||||
// clicked Hotspot 0, so change the type code
|
||||
if ((evtype == 1) && (evblocknum == 0) && (evnt != 0) && (evnt != 5) && (evnt != 6))
|
||||
evtype = 4;
|
||||
if ((evtype == 1) && ((evnt == 0) || (evnt == 5) || (evnt == 6)))
|
||||
; // character stands on hotspot, mouse moves over hotspot, any click
|
||||
else if ((evtype == 2) && (evnt == 4)); // any click on object
|
||||
else if ((evtype == 3) && (evnt == 4)); // any click on character
|
||||
else if (evtype > 0) {
|
||||
can_run_delayed_command();
|
||||
RuntimeScriptValue params[] = { evtype, evnt };
|
||||
QueueScriptFunction(kScInstGame, "unhandled_event", 2, params);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_script_position(ScriptPosition &script_pos) {
|
||||
ccInstance *cur_instance = ccInstance::GetCurrentInstance();
|
||||
if (cur_instance) {
|
||||
cur_instance->GetScriptPosition(script_pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String cc_format_error(const String &message) {
|
||||
if (_G(currentline) > 0)
|
||||
return String::FromFormat("Error (line %d): %s", _G(currentline), message.GetCStr());
|
||||
else
|
||||
return String::FromFormat("Error (line unknown): %s", message.GetCStr());
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
153
engines/ags/engine/script/script.h
Normal file
153
engines/ags/engine/script/script.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_SCRIPT_H
|
||||
#define AGS_ENGINE_SCRIPT_SCRIPT_H
|
||||
|
||||
#include "common/std/memory.h"
|
||||
#include "common/std/vector.h"
|
||||
|
||||
#include "ags/engine/script/cc_instance.h"
|
||||
#include "ags/engine/script/executing_script.h"
|
||||
#include "ags/engine/script/non_blocking_script_function.h"
|
||||
#include "ags/engine/ac/dynobj/script_system.h"
|
||||
#include "ags/shared/game/interactions.h"
|
||||
#include "ags/shared/util/string.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using AGS::Shared::String;
|
||||
using AGS::Shared::Interaction;
|
||||
using AGS::Shared::InteractionCommandList;
|
||||
using AGS::Shared::InteractionScripts;
|
||||
using AGS::Shared::InteractionVariable;
|
||||
|
||||
#define LATE_REP_EXEC_ALWAYS_NAME "late_repeatedly_execute_always"
|
||||
#define REP_EXEC_ALWAYS_NAME "repeatedly_execute_always"
|
||||
#define REP_EXEC_NAME "repeatedly_execute"
|
||||
|
||||
// ObjectEvent - a struct holds data of the object's interaction event,
|
||||
// such as object's reference and accompanying parameters
|
||||
struct ObjectEvent {
|
||||
// Name of the script block to run, may be used as a formatting string;
|
||||
// has a form of "objecttype%d"
|
||||
String BlockName;
|
||||
// Script block's ID, commonly corresponds to the object's ID
|
||||
int BlockID = 0;
|
||||
// Dynamic object this event was called for (if applicable)
|
||||
RuntimeScriptValue DynObj;
|
||||
// Interaction mode that triggered this event (if applicable)
|
||||
int Mode = MODE_NONE;
|
||||
|
||||
ObjectEvent() = default;
|
||||
ObjectEvent(const String &block_name, int block_id = 0)
|
||||
: BlockName(block_name), BlockID(block_id) {}
|
||||
ObjectEvent(const String &block_name, int block_id,
|
||||
const RuntimeScriptValue &dyn_obj, int mode = MODE_NONE)
|
||||
: BlockName(block_name), BlockID(block_id), DynObj(dyn_obj), Mode(mode) {}
|
||||
};
|
||||
|
||||
int run_dialog_request(int parmtr);
|
||||
void run_function_on_non_blocking_thread(NonBlockingScriptFunction *funcToRun);
|
||||
|
||||
// TODO: run_interaction_event() and run_interaction_script()
|
||||
// are in most part duplicating each other, except for the script callback run method.
|
||||
// May these types be made children of the same base, or stored in a group struct?
|
||||
// This would also let simplify the calling code in RunObjectInteraction, etc.
|
||||
//
|
||||
// Runs the ObjectEvent using an old interaction callback type of 'evnt' index,
|
||||
// or alternatively of 'chkAny' index, if previous does not exist;
|
||||
// 'isInv' tells if this is a inventory event (it has a slightly different handling for that)
|
||||
// Returns 0 normally, or -1 telling of a game state change (eg. a room change occurred).
|
||||
int run_interaction_event(const ObjectEvent &obj_evt, Interaction *nint, int evnt, int chkAny = -1, bool isInv = false);
|
||||
// Runs the ObjectEvent using a script callback of 'evnt' index,
|
||||
// or alternatively of 'chkAny' index, if previous does not exist
|
||||
// Returns 0 normally, or -1 telling of a game state change (eg. a room change occurred).
|
||||
int run_interaction_script(const ObjectEvent &obj_evt, InteractionScripts *nint, int evnt, int chkAny = -1);
|
||||
int run_interaction_commandlist(const ObjectEvent &obj_evt, InteractionCommandList *nicl, int *timesrun, int *cmdsrun);
|
||||
void run_unhandled_event(const ObjectEvent &obj_evt, int evnt);
|
||||
int create_global_script();
|
||||
void cancel_all_scripts();
|
||||
|
||||
ccInstance *GetScriptInstanceByType(ScriptInstType sc_inst);
|
||||
// Queues a script function to be run either called by the engine or from another script
|
||||
void QueueScriptFunction(ScriptInstType sc_inst, const char *fn_name, size_t param_count = 0,
|
||||
const RuntimeScriptValue *params = nullptr);
|
||||
// Try to run a script function on a given script instance
|
||||
int RunScriptFunction(ccInstance *sci, const char *tsname, size_t param_count = 0,
|
||||
const RuntimeScriptValue *params = nullptr);
|
||||
// Run a script function in all the regular script modules, in order, where available
|
||||
// includes globalscript, but not the current room script.
|
||||
void RunScriptFunctionInModules(const char *tsname, size_t param_count = 0,
|
||||
const RuntimeScriptValue *params = nullptr);
|
||||
// Run an obligatory script function in the current room script
|
||||
int RunScriptFunctionInRoom(const char *tsname, size_t param_count = 0,
|
||||
const RuntimeScriptValue *params = nullptr);
|
||||
// Try to run a script function, guessing the behavior by its name and script instance type;
|
||||
// depending on the type may run a claimable callback chain
|
||||
int RunScriptFunctionAuto(ScriptInstType sc_inst, const char *fn_name, size_t param_count = 0,
|
||||
const RuntimeScriptValue *params = nullptr);
|
||||
|
||||
// Preallocates script module instances
|
||||
void AllocScriptModules();
|
||||
// Delete all the script instance objects
|
||||
void FreeAllScriptInstances();
|
||||
// Delete only the current room script instance
|
||||
void FreeRoomScriptInstance();
|
||||
// Deletes all the global scripts and modules;
|
||||
// this frees all of the bytecode and runtime script memory.
|
||||
void FreeGlobalScripts();
|
||||
|
||||
String GetScriptName(ccInstance *sci);
|
||||
|
||||
//=============================================================================
|
||||
|
||||
char *make_ts_func_name(const char *base, int iii, int subd);
|
||||
// Performs various updates to the game after script interpreter returns control to the engine.
|
||||
// Executes actions and does changes that are not executed immediately at script command, for
|
||||
// optimisation and other reasons.
|
||||
void post_script_cleanup();
|
||||
void quit_with_script_error(const char *functionName);
|
||||
int get_nivalue(InteractionCommandList *nic, int idx, int parm);
|
||||
InteractionVariable *get_interaction_variable(int varindx);
|
||||
InteractionVariable *FindGraphicalVariable(const char *varName);
|
||||
void can_run_delayed_command();
|
||||
|
||||
// Gets current running script position
|
||||
bool get_script_position(ScriptPosition &script_pos);
|
||||
String cc_get_callstack(int max_lines = INT_MAX);
|
||||
|
||||
// [ikm] we keep ccInstances saved in unique_ptrs globally for now
|
||||
// (e.g. as opposed to shared_ptrs), because the script handling part of the
|
||||
// engine is quite fragile and prone to errors whenever the instance is not
|
||||
// **deleted** in precise time. This is related to:
|
||||
// - ccScript's "instances" counting, which affects script exports reg/unreg;
|
||||
// - loadedInstances array.
|
||||
// One of the examples is the save restoration, that may occur in the midst
|
||||
// of a post-script cleanup process, whilst the engine's stack still has
|
||||
// references to the ccInstances that are going to be deleted on cleanup.
|
||||
// Ideally, this part of the engine should be refactored awhole with a goal
|
||||
// to make it safe and consistent.
|
||||
typedef std::unique_ptr<ccInstance> UInstance;
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
246
engines/ags/engine/script/script_api.cpp
Normal file
246
engines/ags/engine/script/script_api.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/* 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 "ags/shared/ac/game_version.h"
|
||||
#include "ags/shared/script/cc_common.h"
|
||||
#include "ags/engine/script/runtime_script_value.h"
|
||||
#include "ags/engine/script/script_api.h"
|
||||
#include "ags/shared/util/math.h"
|
||||
#include "ags/shared/util/utf8.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
using namespace AGS::Shared;
|
||||
|
||||
enum FormatParseResult {
|
||||
kFormatParseNone,
|
||||
kFormatParseInvalid,
|
||||
kFormatParseLiteralPercent,
|
||||
kFormatParseArgInteger,
|
||||
kFormatParseArgFloat,
|
||||
kFormatParseArgCharacter,
|
||||
kFormatParseArgString,
|
||||
kFormatParseArgPointer,
|
||||
|
||||
kFormatParseArgFirst = kFormatParseArgInteger,
|
||||
kFormatParseArgLast = kFormatParseArgPointer
|
||||
};
|
||||
|
||||
// Helper functions for getting parameter value either from script val array or va_list
|
||||
inline int GetArgInt(const RuntimeScriptValue *sc_args, va_list *varg_ptr, int arg_idx) {
|
||||
if (varg_ptr)
|
||||
return va_arg(*varg_ptr, int);
|
||||
else
|
||||
return sc_args[arg_idx].IValue;
|
||||
}
|
||||
|
||||
inline float GetArgFloat(const RuntimeScriptValue *sc_args, va_list *varg_ptr, int arg_idx) {
|
||||
// note that script variables store only floats, but va_list has floats promoted to double
|
||||
if (varg_ptr)
|
||||
return (float)va_arg(*varg_ptr, double);
|
||||
else
|
||||
return sc_args[arg_idx].FValue;
|
||||
}
|
||||
|
||||
inline const char *GetArgPtr(const RuntimeScriptValue *sc_args, va_list *varg_ptr, int arg_idx) {
|
||||
if (varg_ptr)
|
||||
return va_arg(*varg_ptr, const char *);
|
||||
else
|
||||
return reinterpret_cast<const char *>(sc_args[arg_idx].Ptr);
|
||||
}
|
||||
|
||||
|
||||
// TODO: this implementation can be further optimised by either not calling
|
||||
// snprintf but formatting values ourselves, or by using some library method
|
||||
// that supports customizing, such as getting arguments in a custom way.
|
||||
const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
|
||||
const RuntimeScriptValue *sc_args, int32_t sc_argc, va_list *varg_ptr) {
|
||||
if (!buffer || buf_length == 0) {
|
||||
cc_error("Internal error in ScriptSprintf: buffer is null");
|
||||
return "";
|
||||
}
|
||||
if (!format) {// NOTE: interpreter (usually) catches null-pointer sent as format at some stage earlier
|
||||
cc_error("Internal error in ScriptSprintf: format string is null");
|
||||
return "";
|
||||
}
|
||||
if (!varg_ptr && sc_argc > 0 && !sc_args) {
|
||||
cc_error("Internal error in ScriptSprintf: args pointer is null");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Expected format character count:
|
||||
// percent sign: 1
|
||||
// flag: 1
|
||||
// field width 10 (an uint32 number)
|
||||
// precision sign 1
|
||||
// precision 10 (an uint32 number)
|
||||
// length modifier 2
|
||||
// type 1
|
||||
// NOTE: although width and precision will
|
||||
// not likely be defined by a 10-digit
|
||||
// number, such case is theoretically valid.
|
||||
const size_t fmtbuf_size = 27;
|
||||
char fmtbuf[fmtbuf_size];
|
||||
char *fmt_bufptr;
|
||||
char *fmt_bufendptr = &fmtbuf[fmtbuf_size - 1];
|
||||
|
||||
char *out_ptr = buffer;
|
||||
// save 1 character for null terminator
|
||||
const char *out_endptr = buffer + buf_length - 1;
|
||||
const char *fmt_ptr = format;
|
||||
int32_t arg_idx = 0;
|
||||
|
||||
ptrdiff_t avail_outbuf;
|
||||
int snprintf_res;
|
||||
FormatParseResult fmt_done;
|
||||
|
||||
// Parse the format string, looking for argument placeholders
|
||||
while (*fmt_ptr && out_ptr != out_endptr) {
|
||||
// Try to put argument into placeholder
|
||||
if (*fmt_ptr == '%') {
|
||||
avail_outbuf = out_endptr - out_ptr;
|
||||
fmt_bufptr = fmtbuf;
|
||||
*(fmt_bufptr++) = '%';
|
||||
snprintf_res = 0;
|
||||
fmt_done = kFormatParseNone;
|
||||
|
||||
// Parse placeholder
|
||||
while (*(++fmt_ptr) && fmt_done == kFormatParseNone && fmt_bufptr != fmt_bufendptr) {
|
||||
*(fmt_bufptr++) = *fmt_ptr;
|
||||
switch (*fmt_ptr) {
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'o':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
fmt_done = kFormatParseArgInteger;
|
||||
break;
|
||||
case 'c':
|
||||
fmt_done = kFormatParseArgCharacter;
|
||||
break;
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G':
|
||||
case 'a':
|
||||
case 'A':
|
||||
fmt_done = kFormatParseArgFloat;
|
||||
break;
|
||||
case 'p':
|
||||
fmt_done = kFormatParseArgPointer;
|
||||
break;
|
||||
case 's':
|
||||
fmt_done = kFormatParseArgString;
|
||||
break;
|
||||
case '%':
|
||||
// This may be a literal percent sign ('%%')
|
||||
if (fmt_bufptr - fmtbuf == 2) {
|
||||
fmt_done = kFormatParseLiteralPercent;
|
||||
}
|
||||
// ...Otherwise we reached the next placeholder
|
||||
else {
|
||||
fmt_ptr--;
|
||||
fmt_bufptr--;
|
||||
fmt_done = kFormatParseInvalid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Deal with the placeholder parsing results
|
||||
if (fmt_done == kFormatParseLiteralPercent) {
|
||||
// literal percent sign
|
||||
*(out_ptr++) = '%';
|
||||
continue;
|
||||
} else if (fmt_done >= kFormatParseArgFirst && fmt_done <= kFormatParseArgLast &&
|
||||
(varg_ptr || arg_idx < sc_argc)) {
|
||||
// Print the actual value
|
||||
// NOTE: snprintf is called with avail_outbuf + 1 here, because we let it use our reserved
|
||||
// character for null-terminator, in case we are at the end of the buffer
|
||||
*fmt_bufptr = 0; // terminate the format buffer, we are going to use it
|
||||
switch (fmt_done) {
|
||||
case kFormatParseArgInteger:
|
||||
snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgInt(sc_args, varg_ptr, arg_idx)); break;
|
||||
case kFormatParseArgFloat:
|
||||
snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgFloat(sc_args, varg_ptr, arg_idx)); break;
|
||||
case kFormatParseArgCharacter:
|
||||
{
|
||||
int chr = GetArgInt(sc_args, varg_ptr, arg_idx);
|
||||
char cbuf[5]{};
|
||||
usetc(cbuf, chr);
|
||||
snprintf_res = snprintf(out_ptr, avail_outbuf + 1, "%s", cbuf);
|
||||
break;
|
||||
}
|
||||
case kFormatParseArgString:
|
||||
{
|
||||
const char *p = GetArgPtr(sc_args, varg_ptr, arg_idx);
|
||||
// Do extra checks for %s placeholder
|
||||
if (fmt_done == kFormatParseArgString && !p) {
|
||||
if (_G(loaded_game_file_version) < kGameVersion_320) {
|
||||
// explicitly put "(null)" into the placeholder
|
||||
p = "(null)";
|
||||
} else {
|
||||
cc_error("!ScriptSprintf: formatting argument %d is expected to be a string, but it is a null pointer", arg_idx + 1);
|
||||
return "";
|
||||
}
|
||||
} else if (fmt_done == kFormatParseArgString && p == buffer) {
|
||||
cc_error("!ScriptSprintf: formatting argument %d is a pointer to output buffer", arg_idx + 1);
|
||||
return "";
|
||||
}
|
||||
snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, p);
|
||||
break;
|
||||
}
|
||||
case kFormatParseArgPointer:
|
||||
snprintf_res = snprintf(out_ptr, avail_outbuf + 1, fmtbuf, GetArgPtr(sc_args, varg_ptr, arg_idx)); break;
|
||||
default: /* should not happen */ break;
|
||||
}
|
||||
|
||||
arg_idx++;
|
||||
if (snprintf_res >= 0) {
|
||||
// snprintf returns maximal number of characters, so limit it with buffer size
|
||||
out_ptr += MIN<ptrdiff_t>(snprintf_res, avail_outbuf);
|
||||
continue;
|
||||
}
|
||||
// -- pass further to invalid format case
|
||||
}
|
||||
|
||||
// If format was not valid, or there are no available
|
||||
// parameters, just copy stored format buffer as it is
|
||||
size_t copy_len = MIN(MIN<ptrdiff_t>(fmt_bufptr - fmtbuf, fmtbuf_size - 1), avail_outbuf);
|
||||
memcpy(out_ptr, fmtbuf, copy_len);
|
||||
out_ptr += copy_len;
|
||||
}
|
||||
// If there's no placeholder, simply copy the character to output buffer
|
||||
else {
|
||||
*(out_ptr++) = *(fmt_ptr++);
|
||||
}
|
||||
}
|
||||
|
||||
// Terminate the string
|
||||
*out_ptr = 0;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
645
engines/ags/engine/script/script_api.h
Normal file
645
engines/ags/engine/script/script_api.h
Normal file
@@ -0,0 +1,645 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
//
|
||||
// Script API function type and helper macros for forwarding runtime script
|
||||
// values to real engine functions.
|
||||
//
|
||||
//=============================================================================
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_SCRIPT_API_H
|
||||
#define AGS_ENGINE_SCRIPT_SCRIPT_API_H
|
||||
|
||||
//include <stdarg.h>
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/engine/ac/runtime_defines.h"
|
||||
#include "ags/shared/debugging/out.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct RuntimeScriptValue;
|
||||
|
||||
// TODO: replace void* with base object class when possible; also put array class for parameters
|
||||
typedef RuntimeScriptValue ScriptAPIFunction(const RuntimeScriptValue *params, int32_t param_count);
|
||||
typedef RuntimeScriptValue ScriptAPIObjectFunction(void *self, const RuntimeScriptValue *params, int32_t param_count);
|
||||
|
||||
// Sprintf that takes either script values or common argument list from plugin.
|
||||
// Uses EITHER sc_args/sc_argc or varg_ptr as parameter list, whichever is not
|
||||
// NULL, with varg_ptr having HIGHER priority.
|
||||
const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format,
|
||||
const RuntimeScriptValue *sc_args, int32_t sc_argc, va_list *varg_ptr);
|
||||
// Sprintf that takes script values as arguments
|
||||
inline const char *ScriptSprintf(char *buffer, size_t buf_length, const char *format, const RuntimeScriptValue *args, int32_t argc) {
|
||||
return ScriptSprintf(buffer, buf_length, format, args, argc, nullptr);
|
||||
}
|
||||
// Variadic sprintf (needed, because all arguments are pushed as pointer-sized values). Currently used only when plugin calls
|
||||
// exported engine function. Should be removed when this plugin issue is resolved.
|
||||
inline const char *ScriptVSprintf(char *buffer, size_t buf_length, const char *format, va_list &arg_ptr) {
|
||||
return ScriptSprintf(buffer, buf_length, format, nullptr, 0, &arg_ptr);
|
||||
}
|
||||
|
||||
// Helper macro for registering an API function for both script and plugin,
|
||||
// for the common case where they have similar names: the script's "translator"
|
||||
// function's name is derived from the real one by adding a "Sc_" prefix.
|
||||
#define API_FN_PAIR(FN_NAME) Sc_##FN_NAME, (void *)FN_NAME
|
||||
|
||||
// Helper macros for script functions;
|
||||
// asserting for internal mistakes; suppressing "unused param" warnings
|
||||
#define ASSERT_SELF(METHOD) \
|
||||
(void)params; (void)param_count; \
|
||||
assert((self != NULL) && "Object pointer is null in call to API function")
|
||||
#define ASSERT_PARAM_COUNT(FUNCTION, X) \
|
||||
(void)params; (void)param_count; \
|
||||
assert((params != NULL && param_count >= X) && "Not enough parameters in call to API function")
|
||||
#define ASSERT_VARIABLE_VALUE(VARIABLE) \
|
||||
(void)params; (void)param_count; \
|
||||
assert((params != NULL && param_count >= 1) && "Not enough parameters to set API property")
|
||||
#define ASSERT_OBJ_PARAM_COUNT(METHOD, X) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
ASSERT_PARAM_COUNT(METHOD, X)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls to ScriptSprintf with automatic translation
|
||||
|
||||
#define API_SCALL_SCRIPT_SPRINTF(FUNCTION, PARAM_COUNT) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, PARAM_COUNT); \
|
||||
char ScSfBuffer[STD_BUFFER_SIZE]; \
|
||||
const char *scsf_buffer = ScriptSprintf(ScSfBuffer, STD_BUFFER_SIZE, get_translation(params[PARAM_COUNT - 1].CStr), params + PARAM_COUNT, param_count - PARAM_COUNT)
|
||||
|
||||
#define API_OBJCALL_SCRIPT_SPRINTF(METHOD, PARAM_COUNT) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, PARAM_COUNT); \
|
||||
char ScSfBuffer[STD_BUFFER_SIZE]; \
|
||||
const char *scsf_buffer = ScriptSprintf(ScSfBuffer, STD_BUFFER_SIZE, get_translation(params[PARAM_COUNT - 1].CStr), params + PARAM_COUNT, param_count - PARAM_COUNT)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls to ScriptSprintf without translation
|
||||
|
||||
#define API_SCALL_SCRIPT_SPRINTF_PURE(FUNCTION, PARAM_COUNT) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, PARAM_COUNT); \
|
||||
char ScSfBuffer[STD_BUFFER_SIZE]; \
|
||||
const char *scsf_buffer = ScriptSprintf(ScSfBuffer, STD_BUFFER_SIZE, params[PARAM_COUNT - 1].CStr, params + PARAM_COUNT, param_count - PARAM_COUNT)
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls to ScriptSprintfV (unsafe plugin variant)
|
||||
|
||||
#define API_PLUGIN_SCRIPT_SPRINTF(FORMAT_STR) \
|
||||
va_list args; \
|
||||
va_start(args, FORMAT_STR); \
|
||||
char ScSfBuffer[STD_BUFFER_SIZE]; \
|
||||
const char *scsf_buffer = ScriptVSprintf(ScSfBuffer, STD_BUFFER_SIZE, get_translation(FORMAT_STR), args); \
|
||||
va_end(args)
|
||||
|
||||
#define API_PLUGIN_SCRIPT_SPRINTF_PURE(FORMAT_STR) \
|
||||
va_list args; \
|
||||
va_start(args, FORMAT_STR); \
|
||||
char ScSfBuffer[STD_BUFFER_SIZE]; \
|
||||
const char *scsf_buffer = ScriptVSprintf(ScSfBuffer, STD_BUFFER_SIZE, FORMAT_STR, args); \
|
||||
va_end(args)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls to static functions
|
||||
//
|
||||
// IMPORTANT: please note following: historically AGS compiler did not have
|
||||
// proper "void" type and allowed to store the return value of "void" API
|
||||
// functions as an integer (although that value did not have any practical
|
||||
// meaning). For backwards compatibility we actually return integer value
|
||||
// of '0' in all the VOID script API functions!
|
||||
//
|
||||
|
||||
#define API_SCALL_VOID(FUNCTION) \
|
||||
(void)params; (void)param_count; \
|
||||
FUNCTION(); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PBOOL(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
FUNCTION(params[0].GetAsBool()); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
FUNCTION(params[0].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT2(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT3(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT4(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 4); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT5(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT6(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 6); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
FUNCTION(params[0].IValue, (P1CLASS*)params[1].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT_POBJ2(FUNCTION, P1CLASS, P2CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
FUNCTION(params[0].IValue, (P1CLASS*)params[1].Ptr, (P2CLASS*)params[2].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT2_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, (P1CLASS*)params[2].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT3_POBJ_PINT(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, (P1CLASS*)params[3].Ptr, params[4].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PINT4_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, (P1CLASS*)params[4].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_PFLOAT2(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
FUNCTION(params[0].FValue, params[1].FValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
FUNCTION((P1CLASS*)params[0].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_POBJ_PINT(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_POBJ_PINT2(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue, params[2].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_VOID_POBJ2(FUNCTION, P1CLASS, P2CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
FUNCTION((P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_SCALL_INT(FUNCTION) \
|
||||
(void)params; (void)param_count; \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION())
|
||||
|
||||
#define API_SCALL_INT_PINT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue))
|
||||
|
||||
#define API_SCALL_INT_PINT2(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue, params[1].IValue))
|
||||
|
||||
#define API_SCALL_INT_PINT3(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue))
|
||||
|
||||
#define API_SCALL_INT_PINT4(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 4); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue))
|
||||
|
||||
#define API_SCALL_INT_PINT4_PFLOAT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5) \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[3].FValue))
|
||||
|
||||
#define API_SCALL_INT_PINT5(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue))
|
||||
|
||||
#define API_SCALL_INT_PFLOAT_PINT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].FValue, params[1].IValue))
|
||||
|
||||
#define API_SCALL_INT_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION((P1CLASS*)params[0].Ptr))
|
||||
|
||||
#define API_SCALL_INT_POBJ_PINT(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue))
|
||||
|
||||
#define API_SCALL_INT_POBJ_PINT2(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue, params[2].IValue))
|
||||
|
||||
#define API_SCALL_INT_POBJ2(FUNCTION, P1CLASS, P2CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION((P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr))
|
||||
|
||||
#define API_SCALL_INT_PINT_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32(FUNCTION(params[0].IValue, (P1CLASS*)params[1].Ptr))
|
||||
|
||||
#define API_SCALL_FLOAT(FUNCTION) \
|
||||
(void)params; (void)param_count; \
|
||||
return RuntimeScriptValue().SetFloat(FUNCTION())
|
||||
|
||||
#define API_SCALL_FLOAT_PINT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetFloat(FUNCTION(params[0].IValue))
|
||||
|
||||
#define API_SCALL_FLOAT_PFLOAT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetFloat(FUNCTION(params[0].FValue))
|
||||
|
||||
#define API_SCALL_FLOAT_PFLOAT2(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetFloat(FUNCTION(params[0].FValue, params[1].FValue))
|
||||
|
||||
#define API_SCALL_BOOL(FUNCTION) \
|
||||
(void)params; (void)param_count; \
|
||||
return RuntimeScriptValue().SetInt32AsBool(FUNCTION())
|
||||
|
||||
#define API_SCALL_BOOL_POBJ(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(FUNCTION((P1CLASS*)params[0].Ptr))
|
||||
|
||||
#define API_SCALL_BOOL_PINT(FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(FUNCTION(params[0].IValue))
|
||||
|
||||
#define API_SCALL_BOOL_POBJ_PINT(FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue))
|
||||
|
||||
#define API_SCALL_BOOL_POBJ2(FUNCTION, P1CLASS, P2CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(FUNCTION((P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr))
|
||||
|
||||
#define API_SCALL_OBJ(RET_CLASS, RET_MGR, FUNCTION) \
|
||||
(void)params; (void)param_count; \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION()), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_SCALL_OBJ(RET_CLASS, RET_MGR, FUNCTION) \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION()), &RET_MGR)
|
||||
*/
|
||||
#define API_SCALL_OBJ_PINT(RET_CLASS, RET_MGR, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION(params[0].IValue)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_SCALL_OBJ_PINT(RET_CLASS, RET_MGR, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION(params[0].IValue)), &RET_MGR)
|
||||
*/
|
||||
#define API_SCALL_OBJ_POBJ_PINT_PBOOL(RET_CLASS, RET_MGR, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
return RuntimeScriptValue().SetScriptObject((void*)(RET_CLASS*)FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue, params[2].GetAsBool()), &RET_MGR)
|
||||
|
||||
#define API_SCALL_OBJ_PINT2(RET_CLASS, RET_MGR, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION(params[0].IValue, params[1].IValue)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_SCALL_OBJ_PINT2(RET_CLASS, RET_MGR, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION(params[0].IValue, params[1].IValue)), &RET_MGR)
|
||||
*/
|
||||
#define API_SCALL_OBJ_PINT3_POBJ(RET_CLASS, RET_MGR, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 4); \
|
||||
return RuntimeScriptValue().SetScriptObject((void*)(RET_CLASS*)FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, (P1CLASS*)params[3].Ptr), &RET_MGR)
|
||||
|
||||
#define API_SCALL_OBJ_POBJ(RET_CLASS, RET_MGR, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION((P1CLASS*)params[0].Ptr)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_SCALL_OBJ_POBJ(RET_CLASS, RET_MGR, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)FUNCTION((P1CLASS*)params[0].Ptr)), &RET_MGR)
|
||||
*/
|
||||
#define API_SCALL_OBJAUTO(RET_CLASS, FUNCTION) \
|
||||
(void)params; (void)param_count; \
|
||||
RET_CLASS* ret_obj = FUNCTION(); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
RET_CLASS* ret_obj = FUNCTION(params[0].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT2(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT3(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT4(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 4); \
|
||||
RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT5(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT2_PBOOL(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 3); \
|
||||
RET_CLASS *ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].GetAsBool()); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT3_PBOOL(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 4); \
|
||||
RET_CLASS *ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].GetAsBool()); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PINT3_PBOOL2(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
RET_CLASS *ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].GetAsBool(), params[4].GetAsBool()); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_PBOOL2(RET_CLASS, FUNCTION) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
RET_CLASS* ret_obj = FUNCTION(params[0].GetAsBool(), params[1].GetAsBool()); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_POBJ(RET_CLASS, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 1); \
|
||||
RET_CLASS* ret_obj = FUNCTION((P1CLASS*)params[0].Ptr); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_POBJ_PINT(RET_CLASS, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
RET_CLASS* ret_obj = (RET_CLASS*)FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_SCALL_OBJAUTO_POBJ_PINT4(RET_CLASS, FUNCTION, P1CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 5); \
|
||||
RET_CLASS* ret_obj = FUNCTION((P1CLASS*)params[0].Ptr, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
|
||||
#define API_SCALL_STOBJ_POBJ2(RET_CLASS, FUNCTION, P1CLASS, P2CLASS) \
|
||||
ASSERT_PARAM_COUNT(FUNCTION, 2); \
|
||||
RET_CLASS* ret_obj = FUNCTION((P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr); \
|
||||
return RuntimeScriptValue().SetStaticObject(ret_obj, &_GP(GlobalStaticManager))
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Calls to object functions
|
||||
|
||||
#define API_OBJCALL_VOID(CLASS, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
METHOD((CLASS*)self); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
METHOD((CLASS*)self, params[0].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT2(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT3(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT4(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 4); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT5(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 5); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT6(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 6); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT7(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 7); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue, params[6].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT8(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 8); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue, params[6].IValue, params[7].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PFLOAT(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
METHOD((CLASS*)self, params[0].FValue); \
|
||||
return RuntimeScriptValue()
|
||||
|
||||
#define API_OBJCALL_VOID_PFLOAT2(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
METHOD((CLASS*)self, params[0].FValue, params[1].FValue); \
|
||||
return RuntimeScriptValue()
|
||||
|
||||
#define API_OBJCALL_VOID_PBOOL(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
METHOD((CLASS*)self, params[0].GetAsBool()); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT_PBOOL(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].GetAsBool()); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
METHOD((CLASS*)self, params[0].IValue, (P1CLASS*)params[1].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT3_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 4); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, (P1CLASS*)params[3].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_PINT5_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 6); \
|
||||
METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, (P1CLASS*)params[5].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_POBJ_PINT(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, params[1].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_POBJ_PINT2(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, params[1].IValue, params[2].IValue); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_VOID_POBJ2(CLASS, METHOD, P1CLASS, P2CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr); \
|
||||
return RuntimeScriptValue((int32_t)0)
|
||||
|
||||
#define API_OBJCALL_INT(CLASS, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self))
|
||||
|
||||
#define API_OBJCALL_INT_PINT(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self, params[0].IValue))
|
||||
|
||||
#define API_OBJCALL_INT_PINT_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self, params[0].IValue, params[1].CStr))
|
||||
|
||||
#define API_OBJCALL_INT_PINT2(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self, params[0].IValue, params[1].IValue))
|
||||
|
||||
#define API_OBJCALL_INT_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr))
|
||||
|
||||
#define API_OBJCALL_INT_POBJ_PINT(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, params[1].IValue))
|
||||
|
||||
#define API_OBJCALL_INT_POBJ_PBOOL(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetInt32(METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, params[1].GetAsBool()))
|
||||
|
||||
#define API_OBJCALL_FLOAT(CLASS, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
return RuntimeScriptValue().SetFloat(METHOD((CLASS*)self))
|
||||
|
||||
#define API_OBJCALL_BOOL(CLASS, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(METHOD((CLASS*)self))
|
||||
|
||||
#define API_OBJCALL_BOOL_PINT(CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(METHOD((CLASS*)self, params[0].IValue))
|
||||
|
||||
#define API_OBJCALL_BOOL_POBJ(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr))
|
||||
|
||||
#define API_OBJCALL_BOOL_POBJ_PINT(CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, params[1].IValue))
|
||||
|
||||
#define API_OBJCALL_BOOL_POBJ2(CLASS, METHOD, P1CLASS, P2CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr))
|
||||
|
||||
#define API_OBJCALL_BOOL(CLASS, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
return RuntimeScriptValue().SetInt32AsBool(METHOD((CLASS*)self))
|
||||
|
||||
#define API_OBJCALL_OBJ_PINT_POBJ(CLASS, RET_CLASS, RET_MGR, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetScriptObject((void*)METHOD((CLASS*)self, params[0].IValue, (P1CLASS*)params[1].Ptr), &RET_MGR)
|
||||
|
||||
#define API_OBJCALL_OBJ_POBJ2_PINT(CLASS, RET_CLASS, RET_MGR, METHOD, P1CLASS, P2CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
return RuntimeScriptValue().SetScriptObject((void*)METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr, params[2].IValue), &RET_MGR)
|
||||
|
||||
#define API_OBJCALL_OBJ_POBJ2_PBOOL(CLASS, RET_CLASS, RET_MGR, METHOD, P1CLASS, P2CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr, params[2].GetAsBool())), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_OBJCALL_OBJ_POBJ2_PBOOL(CLASS, RET_CLASS, RET_MGR, METHOD, P1CLASS, P2CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr, (P2CLASS*)params[1].Ptr, params[2].GetAsBool())), &RET_MGR)
|
||||
*/
|
||||
#define API_OBJCALL_OBJ(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_OBJCALL_OBJ(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self)), &RET_MGR)
|
||||
*/
|
||||
#define API_OBJCALL_OBJ_PINT(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self, params[0].IValue)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_OBJCALL_OBJ_PINT(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self, params[0].IValue)), &RET_MGR)
|
||||
*/
|
||||
#define API_OBJCALL_OBJ_PINT2(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self, params[0].IValue, params[1].IValue)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_OBJCALL_OBJ_PINT2(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 2); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self, params[0].IValue, params[1].IValue)), &RET_MGR)
|
||||
*/
|
||||
#define API_OBJCALL_OBJ_PINT3(CLASS, RET_CLASS, RET_MGR, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
return RuntimeScriptValue().SetScriptObject((void*)(RET_CLASS*)METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].IValue), &RET_MGR)
|
||||
|
||||
#define API_OBJCALL_OBJ_POBJ(CLASS, RET_CLASS, RET_MGR, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr)), &RET_MGR)
|
||||
/*
|
||||
#define API_CONST_OBJCALL_OBJ_POBJ(CLASS, RET_CLASS, RET_MGR, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
return RuntimeScriptValue().SetScriptObject(const_cast<void *>((const void *)(RET_CLASS*)METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr)), &RET_MGR)
|
||||
*/
|
||||
#define API_OBJCALL_OBJAUTO(CLASS, RET_CLASS, METHOD) \
|
||||
ASSERT_SELF(METHOD); \
|
||||
RET_CLASS* ret_obj = METHOD((CLASS*)self); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_OBJCALL_OBJAUTO_PINT2_PBOOL(CLASS, RET_CLASS, METHOD) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 3); \
|
||||
RET_CLASS* ret_obj = METHOD((CLASS*)self, params[0].IValue, params[1].IValue, params[2].GetAsBool()); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
#define API_OBJCALL_OBJAUTO_POBJ(CLASS, RET_CLASS, METHOD, P1CLASS) \
|
||||
ASSERT_OBJ_PARAM_COUNT(METHOD, 1); \
|
||||
RET_CLASS* ret_obj = METHOD((CLASS*)self, (P1CLASS*)params[0].Ptr); \
|
||||
return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj)
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
183
engines/ags/engine/script/script_runtime.cpp
Normal file
183
engines/ags/engine/script/script_runtime.cpp
Normal file
@@ -0,0 +1,183 @@
|
||||
/* 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 "ags/engine/script/script_runtime.h"
|
||||
#include "ags/engine/ac/dynobj/cc_dynamic_array.h"
|
||||
#include "ags/shared/script/cc_common.h"
|
||||
#include "ags/engine/script/system_imports.h"
|
||||
#include "ags/globals.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
bool ccAddExternalStaticFunction(const String &name, ScriptAPIFunction *pfn) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetStaticFunction(pfn), nullptr) != UINT32_MAX;
|
||||
}
|
||||
|
||||
bool ccAddExternalObjectFunction(const String &name, ScriptAPIObjectFunction *pfn) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetObjectFunction(pfn), nullptr) != UINT32_MAX;
|
||||
}
|
||||
|
||||
bool ccAddExternalFunctionForPlugin(const String &name, Plugins::ScriptContainer *instance) {
|
||||
return _GP(simp_for_plugin).add(name, RuntimeScriptValue().SetPluginMethod(instance, name), nullptr) != UINT32_MAX;
|
||||
}
|
||||
|
||||
bool ccAddExternalStaticFunction361(const String &name, ScriptAPIFunction *scfn, void *dirfn) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetStaticFunction(scfn), nullptr) != UINT32_MAX &&
|
||||
(!dirfn ||
|
||||
_GP(simp_for_plugin).add(name, RuntimeScriptValue().SetPluginMethod((Plugins::ScriptContainer *)dirfn, name), nullptr) != UINT32_MAX);
|
||||
}
|
||||
|
||||
bool ccAddExternalObjectFunction361(const String &name, ScriptAPIObjectFunction *scfn, void *dirfn) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetObjectFunction(scfn), nullptr) != UINT32_MAX &&
|
||||
(!dirfn ||
|
||||
_GP(simp_for_plugin).add(name, RuntimeScriptValue().SetPluginMethod((Plugins::ScriptContainer *)dirfn, name), nullptr) != UINT32_MAX);
|
||||
}
|
||||
|
||||
bool ccAddExternalFunction361(const ScFnRegister &scfnreg) {
|
||||
String name = String::Wrapper(scfnreg.Name);
|
||||
return _GP(simp).add(name, scfnreg.Fn, nullptr) != UINT32_MAX &&
|
||||
(scfnreg.PlFn.IsNull() ||
|
||||
_GP(simp_for_plugin).add(name, scfnreg.PlFn, nullptr) != UINT32_MAX);
|
||||
}
|
||||
|
||||
bool ccAddExternalPluginFunction(const String &name, Plugins::ScriptContainer *instance) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetPluginMethod(instance, name), nullptr) != UINT32_MAX;
|
||||
}
|
||||
|
||||
bool ccAddExternalStaticArray(const String &name, void *ptr, CCStaticArray *array_mgr) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetStaticArray(ptr, array_mgr), nullptr) != UINT32_MAX;
|
||||
}
|
||||
|
||||
bool ccAddExternalScriptObject(const String &name, void *ptr, IScriptObject *manager) {
|
||||
return _GP(simp).add(name, RuntimeScriptValue().SetScriptObject(ptr, manager), nullptr) != UINT32_MAX;
|
||||
}
|
||||
|
||||
bool ccAddExternalScriptSymbol(const String &name, const RuntimeScriptValue &prval, ccInstance *inst) {
|
||||
return _GP(simp).add(name, prval, inst) != UINT32_MAX;
|
||||
}
|
||||
|
||||
void ccRemoveExternalSymbol(const String &name) {
|
||||
_GP(simp).remove(name);
|
||||
}
|
||||
|
||||
void ccRemoveAllSymbols() {
|
||||
_GP(simp).clear();
|
||||
}
|
||||
|
||||
void *ccGetSymbolAddress(const String &name) {
|
||||
const ScriptImport *import = _GP(simp).getByName(name);
|
||||
if (import) {
|
||||
return import->Value.Ptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Plugins::PluginMethod ccGetSymbolAddressForPlugin(const String &name) {
|
||||
const ScriptImport *import = _GP(simp_for_plugin).getByName(name);
|
||||
if (import) {
|
||||
return Plugins::PluginMethod((Plugins::ScriptContainer *)import->Value.Ptr, name);
|
||||
} else {
|
||||
// Also search the internal symbol table for non-function symbols
|
||||
import = _GP(simp).getByName(name);
|
||||
if (import) {
|
||||
return Plugins::PluginMethod((Plugins::ScriptContainer *)import->Value.Ptr, name);
|
||||
}
|
||||
}
|
||||
|
||||
return Plugins::PluginMethod();
|
||||
}
|
||||
|
||||
void *ccGetScriptObjectAddress(const String &name, const String &type) {
|
||||
const auto *imp = _GP(simp).getByName(name);
|
||||
if (!imp)
|
||||
return nullptr;
|
||||
if (imp->Value.Type != kScValScriptObject && imp->Value.Type != kScValPluginObject)
|
||||
return nullptr;
|
||||
if (type != imp->Value.ObjMgr->GetType())
|
||||
return nullptr;
|
||||
return imp->Value.Ptr;
|
||||
}
|
||||
|
||||
void ccSetScriptAliveTimer(unsigned sys_poll_timeout, unsigned abort_timeout, unsigned abort_loops) {
|
||||
ccInstance::SetExecTimeout(sys_poll_timeout, abort_timeout, abort_loops);
|
||||
}
|
||||
|
||||
void ccNotifyScriptStillAlive() {
|
||||
ccInstance *cur_inst = ccInstance::GetCurrentInstance();
|
||||
if (cur_inst)
|
||||
cur_inst->NotifyAlive();
|
||||
}
|
||||
|
||||
void ccSetDebugHook(new_line_hook_type jibble) {
|
||||
_G(new_line_hook) = jibble;
|
||||
}
|
||||
|
||||
NumberPtr call_function(const Plugins::PluginMethod &method, const RuntimeScriptValue *object, int numparm, const RuntimeScriptValue *parms) {
|
||||
if (!method) {
|
||||
cc_error("invalid method in call_function");
|
||||
return -1;
|
||||
}
|
||||
if (numparm > 0 && !parms) {
|
||||
cc_error("invalid parameters array in call_function");
|
||||
return -1;
|
||||
}
|
||||
|
||||
intptr_t parm_value[9];
|
||||
if (object) {
|
||||
parm_value[0] = (intptr_t)object->GetPtrWithOffset();
|
||||
numparm++;
|
||||
}
|
||||
|
||||
for (int ival = object ? 1 : 0, iparm = 0; ival < numparm; ++ival, ++iparm) {
|
||||
switch (parms[iparm].Type) {
|
||||
case kScValInteger:
|
||||
case kScValFloat: // AGS passes floats, copying their values into long variable
|
||||
case kScValPluginArg:
|
||||
parm_value[ival] = (intptr_t)parms[iparm].IValue;
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
parm_value[ival] = (intptr_t)parms[iparm].GetPtrWithOffset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// AN IMPORTANT NOTE ON PARAMS
|
||||
// The original AGS interpreter did a bunch of dodgy function pointers with
|
||||
// varying numbers of parameters, which were all intptr_t. To simply matters
|
||||
// now that we only supported plugins implemented in code, and not DLLs,
|
||||
// we use a simplified Common::Array containing the parameters and result
|
||||
|
||||
if (numparm > 9) {
|
||||
cc_error("too many arguments in call to function");
|
||||
return -1;
|
||||
} else {
|
||||
// Build the parameters
|
||||
Plugins::ScriptMethodParams params;
|
||||
for (int i = 0; i < numparm; ++i)
|
||||
params.push_back(parm_value[i]);
|
||||
|
||||
// Call the method
|
||||
return method(params);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
109
engines/ags/engine/script/script_runtime.h
Normal file
109
engines/ags/engine/script/script_runtime.h
Normal file
@@ -0,0 +1,109 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_SCRIPT_RUNTIME_H
|
||||
#define AGS_ENGINE_SCRIPT_SCRIPT_RUNTIME_H
|
||||
|
||||
#include "ags/shared/core/types.h"
|
||||
#include "ags/shared/script/cc_script.h" // ccScript
|
||||
#include "ags/engine/script/cc_instance.h" // ccInstance
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct IScriptObject;
|
||||
|
||||
using AGS::Shared::String;
|
||||
|
||||
// Helper struct for declaring a script API function registration
|
||||
struct ScFnRegister {
|
||||
const char *Name = nullptr;
|
||||
RuntimeScriptValue Fn; // for script VM calls
|
||||
RuntimeScriptValue PlFn; // for plugins, direct unsafe call style
|
||||
|
||||
ScFnRegister() = default;
|
||||
ScFnRegister(const char *name, ScriptAPIFunction *fn, void *plfn = nullptr)
|
||||
: Name(name), Fn(RuntimeScriptValue().SetStaticFunction(fn)), PlFn(RuntimeScriptValue().SetPluginMethod((Plugins::ScriptContainer *)plfn, nullptr)) {}
|
||||
ScFnRegister(const char *name, ScriptAPIObjectFunction *fn, void *plfn = nullptr)
|
||||
: Name(name), Fn(RuntimeScriptValue().SetObjectFunction(fn)), PlFn(RuntimeScriptValue().SetPluginMethod((Plugins::ScriptContainer *)plfn, nullptr)) {}
|
||||
template<typename TPlFn>
|
||||
ScFnRegister(const char *name, ScriptAPIFunction *fn, TPlFn plfn)
|
||||
: Name(name), Fn(RuntimeScriptValue().SetStaticFunction(fn)), PlFn(RuntimeScriptValue().SetPluginMethod((Plugins::ScriptContainer *)plfn, nullptr)) {}
|
||||
template<typename TPlFn>
|
||||
ScFnRegister(const char *name, ScriptAPIObjectFunction *fn, TPlFn plfn)
|
||||
: Name(name), Fn(RuntimeScriptValue().SetObjectFunction(fn)), PlFn(RuntimeScriptValue().SetPluginMethod((Plugins::ScriptContainer *)plfn, nullptr)) {}
|
||||
};
|
||||
|
||||
// Following functions register engine API symbols for script and plugins.
|
||||
// Calls from script is handled by specific "translator" functions, which
|
||||
// unpack script interpreter's values into the real arguments and call the
|
||||
// actual engine's function. For plugins we have to provide actual engine
|
||||
// function directly.
|
||||
bool ccAddExternalStaticFunction(const String &name, ScriptAPIFunction *pfn);
|
||||
bool ccAddExternalObjectFunction(const String &name, ScriptAPIObjectFunction *pfn);
|
||||
bool ccAddExternalFunctionForPlugin(const String &name, Plugins::ScriptContainer *sc);
|
||||
// Register a function, exported from a plugin. Requires direct function pointer only.
|
||||
bool ccAddExternalPluginFunction(const String &name, Plugins::ScriptContainer *sc);
|
||||
// Register engine objects for script's access.
|
||||
bool ccAddExternalStaticArray(const String &name, void *ptr, CCStaticArray *array_mgr);
|
||||
bool ccAddExternalScriptObject(const String &name, void *ptr, IScriptObject *manager);
|
||||
// Register script own functions (defined in the linked scripts)
|
||||
bool ccAddExternalScriptSymbol(const String &name, const RuntimeScriptValue &prval, ccInstance *inst);
|
||||
// Remove the script access to a variable or function in your program
|
||||
void ccRemoveExternalSymbol(const String &name);
|
||||
// Remove all external symbols, allowing you to start from scratch
|
||||
void ccRemoveAllSymbols();
|
||||
|
||||
// FIXME: These functions should replace the older ones; for now they are duplicated to ease
|
||||
// the transition
|
||||
bool ccAddExternalStaticFunction361(const String &name, ScriptAPIFunction *scfn, void *dirfn = nullptr);
|
||||
bool ccAddExternalObjectFunction361(const String &name, ScriptAPIObjectFunction *scfn, void *dirfn = nullptr);
|
||||
bool ccAddExternalFunction361(const ScFnRegister &scfnreg);
|
||||
// Registers an array of static functions
|
||||
template<size_t N>
|
||||
inline void ccAddExternalFunctions361(const ScFnRegister (&arr)[N]) {
|
||||
for (const ScFnRegister *it = arr; it != (arr + N); ++it)
|
||||
ccAddExternalFunction361(*it);
|
||||
}
|
||||
|
||||
// Get the address of an exported variable in the script
|
||||
void *ccGetSymbolAddress(const String &name);
|
||||
// Get a registered symbol's direct pointer; this is used solely for plugins
|
||||
Plugins::PluginMethod ccGetSymbolAddressForPlugin(const String &name);
|
||||
// Get a registered Script Object, optionally restricting to the given type name
|
||||
void *ccGetScriptObjectAddress(const String &name, const String &type);
|
||||
|
||||
// DEBUG HOOK
|
||||
typedef void (*new_line_hook_type)(ccInstance *, int);
|
||||
void ccSetDebugHook(new_line_hook_type jibble);
|
||||
|
||||
// Set the script interpreter timeout values:
|
||||
// * sys_poll_timeout - defines the timeout (ms) at which the interpreter will run system events poll;
|
||||
// * abort_timeout - [temp disabled] defines the timeout (ms) at which the interpreter will cancel with error.
|
||||
// * abort_loops - max script loops without an engine update after which the interpreter will error;
|
||||
void ccSetScriptAliveTimer(unsigned sys_poll_timeout, unsigned abort_timeout, unsigned abort_loops);
|
||||
// reset the current while loop counter
|
||||
void ccNotifyScriptStillAlive();
|
||||
// for calling exported plugin functions old-style
|
||||
NumberPtr call_function(const Plugins::PluginMethod &method, const RuntimeScriptValue *object, int numparm, const RuntimeScriptValue *parms);
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
135
engines/ags/engine/script/system_imports.cpp
Normal file
135
engines/ags/engine/script/system_imports.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/* 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 "ags/engine/script/system_imports.h"
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
uint32_t SystemImports::add(const String &name, const RuntimeScriptValue &value, ccInstance *anotherscr) {
|
||||
uint32_t ixof = get_index_of(name);
|
||||
// Check if symbol already exists
|
||||
if (ixof != UINT32_MAX) {
|
||||
// Only allow override if not a script-exported function
|
||||
if (anotherscr == nullptr) {
|
||||
imports[ixof].Value = value;
|
||||
imports[ixof].InstancePtr = anotherscr;
|
||||
}
|
||||
return ixof;
|
||||
}
|
||||
|
||||
ixof = imports.size();
|
||||
for (size_t i = 0; i < imports.size(); ++i) {
|
||||
if (imports[i].Name == nullptr) {
|
||||
ixof = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
btree[name] = ixof;
|
||||
if (ixof == imports.size())
|
||||
imports.push_back(ScriptImport());
|
||||
imports[ixof].Name = name;
|
||||
imports[ixof].Value = value;
|
||||
imports[ixof].InstancePtr = anotherscr;
|
||||
return ixof;
|
||||
}
|
||||
|
||||
void SystemImports::remove(const String &name) {
|
||||
uint32_t idx = get_index_of(name);
|
||||
if (idx == UINT32_MAX)
|
||||
return;
|
||||
btree.erase(imports[idx].Name);
|
||||
imports[idx].Name = nullptr;
|
||||
imports[idx].Value.Invalidate();
|
||||
imports[idx].InstancePtr = nullptr;
|
||||
}
|
||||
|
||||
const ScriptImport *SystemImports::getByName(const String &name) {
|
||||
uint32_t o = get_index_of(name);
|
||||
if (o == UINT32_MAX)
|
||||
return nullptr;
|
||||
|
||||
return &imports[o];
|
||||
}
|
||||
|
||||
const ScriptImport *SystemImports::getByIndex(uint32_t index) {
|
||||
if (index >= imports.size())
|
||||
return nullptr;
|
||||
|
||||
return &imports[index];
|
||||
}
|
||||
|
||||
uint32_t SystemImports::get_index_of(const String &name) {
|
||||
IndexMap::const_iterator it = btree.find(name);
|
||||
if (it != btree.end())
|
||||
return it->_value;
|
||||
|
||||
// CHECKME: what are "mangled names" and where do they come from?
|
||||
String mangled_name = String::FromFormat("%s$", name.GetCStr());
|
||||
// if it's a function with a mangled name, allow it
|
||||
it = btree.lower_bound(mangled_name);
|
||||
if (it != btree.end() && it->_key.CompareLeft(mangled_name) == 0)
|
||||
return it->_value;
|
||||
|
||||
if (name.GetLength() > 3) {
|
||||
size_t c = name.FindCharReverse('^');
|
||||
if (c != String::NoIndex && (c == name.GetLength() - 2 || c == name.GetLength() - 3)) {
|
||||
// Function with number of prametrs on the end
|
||||
// attempt to find it without the param count
|
||||
return get_index_of(name.Left(c));
|
||||
}
|
||||
}
|
||||
return UINT32_MAX;
|
||||
}
|
||||
|
||||
String SystemImports::findName(const RuntimeScriptValue &value) {
|
||||
for (const auto &import : imports) {
|
||||
if (import.Value == value) {
|
||||
return import.Name;
|
||||
}
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
void SystemImports::RemoveScriptExports(ccInstance *inst) {
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &import : imports) {
|
||||
if (import.Name == nullptr)
|
||||
continue;
|
||||
|
||||
if (import.InstancePtr == inst) {
|
||||
btree.erase(import.Name);
|
||||
import.Name = nullptr;
|
||||
import.Value.Invalidate();
|
||||
import.InstancePtr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SystemImports::clear() {
|
||||
btree.clear();
|
||||
imports.clear();
|
||||
}
|
||||
|
||||
} // namespace AGS3
|
||||
66
engines/ags/engine/script/system_imports.h
Normal file
66
engines/ags/engine/script/system_imports.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AGS_ENGINE_SCRIPT_CC_SYSTEM_IMPORTS_H
|
||||
#define AGS_ENGINE_SCRIPT_CC_SYSTEM_IMPORTS_H
|
||||
|
||||
#include "common/std/map.h"
|
||||
#include "ags/engine/script/cc_instance.h" // ccInstance
|
||||
|
||||
namespace AGS3 {
|
||||
|
||||
struct IScriptObject;
|
||||
|
||||
using AGS::Shared::String;
|
||||
|
||||
struct ScriptImport {
|
||||
ScriptImport() {
|
||||
InstancePtr = nullptr;
|
||||
}
|
||||
|
||||
String Name; // import's uid
|
||||
RuntimeScriptValue Value;
|
||||
ccInstance *InstancePtr; // script instance
|
||||
};
|
||||
|
||||
struct SystemImports {
|
||||
private:
|
||||
// Note we can't use a hash-map here, because we sometimes need to search
|
||||
// by partial keys.
|
||||
typedef std::map<String, uint32_t> IndexMap;
|
||||
|
||||
std::vector<ScriptImport> imports;
|
||||
IndexMap btree;
|
||||
|
||||
public:
|
||||
uint32_t add(const String &name, const RuntimeScriptValue &value, ccInstance *inst);
|
||||
void remove(const String &name);
|
||||
const ScriptImport *getByName(const String &name);
|
||||
uint32_t get_index_of(const String &name);
|
||||
const ScriptImport *getByIndex(uint32_t index);
|
||||
String findName(const RuntimeScriptValue &value);
|
||||
void RemoveScriptExports(ccInstance *inst);
|
||||
void clear();
|
||||
};
|
||||
|
||||
} // namespace AGS3
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user