mirror of
https://github.com/astromaddie/HaloCE-VR.git
synced 2026-10-19 10:00:13 +02:00
Recover transparent geometry submit (+1,285 meaningful code, zero exact losses); reconcile COMMON ownership and Q10 85+EXT1 credit, actions/main data, and separately disclosed denominator corrections. Preserve canonical-only repairs and all held research. C09 is ordinary disclosed cleanup, not a claim of proven scaffolding intent. Clean rebuild, 1,663 tests, parks, audits, provider links and independent EXT1 check pass. Local reconciliation only.
3164 lines
84 KiB
C
3164 lines
84 KiB
C
/*
|
|
HS_RUNTIME.C
|
|
|
|
symbols in this file:
|
|
000B9850 0030:
|
|
_hs_syntax_nth (0000)
|
|
000B9880 0050:
|
|
_hs_inspect_boolean (0000)
|
|
000B98D0 0050:
|
|
_hs_inspect_real (0000)
|
|
000B9920 0050:
|
|
_hs_inspect_short_integer (0000)
|
|
000B9970 0050:
|
|
_hs_inspect_long_integer (0000)
|
|
000B99C0 0050:
|
|
_hs_inspect_string (0000)
|
|
000B9A10 0090:
|
|
_hs_inspect_enum (0000)
|
|
000B9AA0 0100:
|
|
_hs_runtime_initialize (0000)
|
|
000B9BA0 0080:
|
|
_hs_runtime_dispose_from_old_map (0000)
|
|
000B9C20 0010:
|
|
_hs_runtime_dispose (0000)
|
|
000B9C30 00b0:
|
|
_expression_get_function_name (0000)
|
|
000B9CE0 00f0:
|
|
_hs_thread_new (0000)
|
|
000B9DD0 0050:
|
|
_hs_thread_delete (0000)
|
|
000B9E20 0080:
|
|
_hs_thread_format (0000)
|
|
000B9EA0 0080:
|
|
_hs_stack_push (0000)
|
|
000B9F20 0020:
|
|
_hs_stack_pop (0000)
|
|
000B9F40 0150:
|
|
_hs_stack_allocate (0000)
|
|
000BA090 00b0:
|
|
_hs_wake (0000)
|
|
000BA140 0060:
|
|
_hs_find_thread_by_script (0000)
|
|
000BA1A0 0080:
|
|
_hs_find_thread_by_name (0000)
|
|
000BA220 0020:
|
|
_hs_long_to_boolean (0000)
|
|
000BA240 0020:
|
|
_hs_short_to_boolean (0000)
|
|
000BA260 0020:
|
|
_hs_string_to_boolean (0000)
|
|
000BA280 0010:
|
|
_hs_data_to_void (0000)
|
|
000BA290 0020:
|
|
_hs_short_to_real (0000)
|
|
000BA2B0 0010:
|
|
_hs_long_to_real (0000)
|
|
000BA2C0 0020:
|
|
_hs_enum_to_real (0000)
|
|
000BA2E0 0020:
|
|
_hs_real_to_short (0000)
|
|
000BA300 0010:
|
|
_hs_real_to_long (0000)
|
|
000BA310 0010:
|
|
_hs_long_to_short (0000)
|
|
000BA320 0040:
|
|
_hs_object_name_to_object_list (0000)
|
|
000BA360 0030:
|
|
_hs_object_to_object_list (0000)
|
|
000BA390 0080:
|
|
_hs_object_type_can_cast (0000)
|
|
000BA410 0100:
|
|
_hs_can_cast (0000)
|
|
000BA510 00c0:
|
|
_hs_cast (0000)
|
|
000BA5D0 0580:
|
|
_hs_global_reconcile_read (0000)
|
|
000BAB50 0190:
|
|
_hs_global_reconcile_write (0000)
|
|
000BACE0 0040:
|
|
_script_error (0000)
|
|
000BAD20 0020:
|
|
_hs_runtime_get_executing_thread_name (0000)
|
|
000BAD40 0020:
|
|
_hs_wake_by_name (0000)
|
|
000BAD60 0180:
|
|
_render_debug_scripting (0000)
|
|
000BAEE0 0440:
|
|
_render_debug_trigger_volumes (0000)
|
|
000BB320 0120:
|
|
_hs_return (0000)
|
|
000BB440 0040:
|
|
_hs_global_evaluate (0000)
|
|
000BB480 00f0:
|
|
_hs_evaluate_wake (0000)
|
|
000BB570 0170:
|
|
_hs_evaluate (0000)
|
|
000BB6E0 0060:
|
|
_hs_script_evaluate (0000)
|
|
000BB740 01c0:
|
|
_hs_arguments_evaluate (0000)
|
|
000BB900 0030:
|
|
_hs_macro_function_evaluate (0000)
|
|
000BB930 00d0:
|
|
_hs_evaluate_begin (0000)
|
|
000BBA00 0210:
|
|
_hs_evaluate_begin_random (0000)
|
|
000BBC10 0190:
|
|
_hs_evaluate_if (0000)
|
|
000BBDA0 0140:
|
|
_hs_evaluate_set (0000)
|
|
000BBEE0 0130:
|
|
_hs_evaluate_logical (0000)
|
|
000BC010 0180:
|
|
_hs_evaluate_arithmetic (0000)
|
|
000BC190 00e0:
|
|
_hs_evaluate_equality (0000)
|
|
000BC270 0210:
|
|
_hs_evaluate_inequality (0000)
|
|
000BC480 01c0:
|
|
_hs_evaluate_sleep (0000)
|
|
000BC640 0200:
|
|
_hs_evaluate_sleep_until (0000)
|
|
000BC840 0100:
|
|
_hs_evaluate_inspect (0000)
|
|
000BC940 0120:
|
|
_hs_evaluate_object_cast_up (0000)
|
|
000BCA60 0180:
|
|
_hs_evaluate_debug_string (0000)
|
|
000BCBE0 02f0:
|
|
_hs_thread_main (0000)
|
|
000BCED0 02d0:
|
|
_hs_runtime_initialize_for_new_map (0000)
|
|
000BD1A0 00b0:
|
|
_hs_runtime_update (0000)
|
|
000BD250 00c0:
|
|
_hs_runtime_evaluate (0000)
|
|
0026E880 0017:
|
|
??_C@_0BH@JDOAAMFH@type?$DN?$DN_hs_type_boolean?$AA@ (0000)
|
|
0026E898 0030:
|
|
??_C@_0DA@OFBKMPEO@c?3?2halo?2source?2hs?2hs_library_int@ (0000)
|
|
0026E8C8 0003:
|
|
??_C@_02NJPGOMH@?$CFf?$AA@ (0000)
|
|
0026E8CC 0014:
|
|
??_C@_0BE@OILODGFF@type?$DN?$DN_hs_type_real?$AA@ (0000)
|
|
0026E8E0 001d:
|
|
??_C@_0BN@EMEJADII@type?$DN?$DN_hs_type_short_integer?$AA@ (0000)
|
|
0026E900 0004:
|
|
??_C@_03JALODAI@?$CFld?$AA@ (0000)
|
|
0026E904 001c:
|
|
??_C@_0BM@KMJMLGIL@type?$DN?$DN_hs_type_long_integer?$AA@ (0000)
|
|
0026E920 0016:
|
|
??_C@_0BG@BOOAAIJG@type?$DN?$DN_hs_type_string?$AA@ (0000)
|
|
0026E938 0033:
|
|
??_C@_0DD@LFJLKEGO@enum_value?$DO?$DN0?5?$CG?$CG?5enum_value?$DMenum@ (0000)
|
|
0026E96C 0016:
|
|
??_C@_0BG@BHHILIEN@HS_TYPE_IS_ENUM?$CItype?$CJ?$AA@ (0000)
|
|
0026E984 0025:
|
|
??_C@_0CF@LGMLOJMN@couldn?8t?5allocate?5scripting?5glob@ (0000)
|
|
0026E9AC 000c:
|
|
??_C@_0M@IAKOKLGB@index?$CB?$DNNONE?$AA@ (0000)
|
|
0026E9B8 0024:
|
|
??_C@_0CE@JAGJENNE@raise?5MAXIMUM_NUMBER_OF_HS_GLOBA@ (0000)
|
|
0026E9DC 001f:
|
|
??_C@_0BP@HNAHKNME@c?3?2halo?2SOURCE?2hs?2hs_runtime?4c?$AA@ (0000)
|
|
0026E9FC 000b:
|
|
??_C@_0L@KLMNBLNA@hs?5globals?$AA@ (0000)
|
|
0026EA08 000a:
|
|
??_C@_09NFPDIMGO@hs?5thread?$AA@ (0000)
|
|
0026EA14 0010:
|
|
??_C@_0BA@DMAAAIAF@?$CIend?5of?5script?$CJ?$AA@ (0000)
|
|
0026EA24 0033:
|
|
??_C@_0DD@EECLOJLE@type?$CB?$DN_hs_thread_type_script?5?$HM?$HM?5@ (0000)
|
|
0026EA58 002a:
|
|
??_C@_0CK@PILPJPBD@type?$DO?$DN0?5?$CG?$CG?5type?$DMNUMBER_OF_HS_THR@ (0000)
|
|
0026EA84 003a:
|
|
??_C@_0DK@IFFNCFDC@hs_thread_get?$CIthread_index?$CJ?9?$DOtyp@ (0000)
|
|
0026EAC0 0014:
|
|
??_C@_0BE@FDONEJOD@?$FLglobal?5initialize?$FN?$AA@ (0000)
|
|
0026EAD4 0012:
|
|
??_C@_0BC@DIMBPFCM@?$FLconsole?5command?$FN?$AA@ (0000)
|
|
0026EAE8 003a:
|
|
??_C@_0DK@FJONKAAK@a?5problem?5occurred?5while?5executi@ (0000)
|
|
0026EB24 0010:
|
|
??_C@_0BA@OPEHPPEI@stack?5overflow?4?$AA@ (0000)
|
|
0026EB34 003f:
|
|
??_C@_0DP@HONBOJOM@?$CIbyte?5?$CK?$CJ?5?$CInew_frame?$CL1?$CJ?$DMthread?9?$DOs@ (0000)
|
|
0026EB78 0046:
|
|
??_C@_0EG@GLGLNCNE@frame?9?$DOdata?$CLframe?9?$DOsize?$CLsize?$DM?$DNth@ (0000)
|
|
0026EBC0 002f:
|
|
??_C@_0CP@LGELPKKM@attempt?5to?5allocate?5zero?5space?5f@ (0000)
|
|
0026EBF0 0011:
|
|
??_C@_0BB@IJDFEIOE@corrupted?5stack?4?$AA@ (0000)
|
|
0026EC04 0015:
|
|
??_C@_0BF@NOEAJPHF@valid_thread?$CIthread?$CJ?$AA@ (0000)
|
|
0026EC1C 003a:
|
|
??_C@_0DK@CIPMAIGO@desired_type?$DO?$DN0?5?$CG?$CG?5desired_type?$DM@ (0000)
|
|
0026EC58 0038:
|
|
??_C@_0DI@FBLLEJHE@actual_type?$DO?$DN0?5?$CG?$CG?5actual_type?$DMNU@ (0000)
|
|
0026EC90 001c:
|
|
??_C@_0BM@LDIPEIGF@hs_type_valid?$CIdesired_type?$CJ?$AA@ (0000)
|
|
0026ECAC 003b:
|
|
??_C@_0DL@KFHDEBEB@actual_type?$DN?$DN_hs_passthrough?5?$HM?$HM?5@ (0000)
|
|
0026ECE8 000e:
|
|
??_C@_0O@OAGBFBKC@bad?5typecast?4?$AA@ (0000)
|
|
0026ECF8 0027:
|
|
??_C@_0CH@CMGFPKPO@hs_can_cast?$CIactual_type?0?5desired@ (0000)
|
|
0026ED20 002b:
|
|
??_C@_0CL@BFAIEDCL@script?5?$CFs?5needs?5to?5be?5recompiled@ (0000)
|
|
0026ED4C 0011:
|
|
??_C@_0BB@OIKAMJPF@no?5reason?5given?4?$AA@ (0000)
|
|
0026ED60 000a:
|
|
??_C@_09DJENBFJK@?$FLunknown?$FN?$AA@ (0000)
|
|
0026ED6C 0003:
|
|
??_C@_02BGAGALCL@?$HMt?$AA@ (0000)
|
|
0026ED70 0028:
|
|
??_C@_0CI@KMEEEBHP@?$HMn?$HMn?$HMnscript?5name?$HMtsleep?5time?$HMtf@ (0000)
|
|
0026ED98 0028:
|
|
??_C@_0CI@PIIEBFIK@script_name_node?9?$DOtype?$DN?$DN_hs_type@ (0000)
|
|
0026EDC0 0042:
|
|
??_C@_0EC@EIHIFDEJ@TEST_FLAG?$CIscript_name_node?9?$DOflag@ (0000)
|
|
0026EE04 0018:
|
|
??_C@_0BI@DEHGHNBF@?$CKexpression_index?$DN?$DNNONE?$AA@ (0000)
|
|
0026EE1C 001e:
|
|
??_C@_0BO@IEBGLNDF@unexpected?5actual?5parameters?4?$AA@ (0000)
|
|
0026EE40 004b:
|
|
??_C@_0EL@EPENHJGN@hs_syntax_get?$CI?$CKexpression_index?$CJ@ (0000)
|
|
0026EE8C 0017:
|
|
??_C@_0BH@JHPKLPNE@corrupted?5syntax?5tree?4?$AA@ (0000)
|
|
0026EEA4 0018:
|
|
??_C@_0BI@JEAIILKP@?$CKexpression_index?$CB?$DNNONE?$AA@ (0000)
|
|
0026EEBC 0023:
|
|
??_C@_0CD@LMIAMJPE@function_index?$DN?$DN_hs_function_beg@ (0000)
|
|
0026EEE0 001a:
|
|
??_C@_0BK@ICIEGLDA@?$CKargument_count?$DMLONG_BITS?$AA@ (0000)
|
|
0026EEFC 002a:
|
|
??_C@_0CK@HFFDNMBO@function_index?$DN?$DN_hs_function_beg@ (0000)
|
|
0026EF28 0052:
|
|
??_C@_0FC@PECKPIAM@parameter_types?$FL0?$FN?$DN?$DN_hs_type_sho@ (0000)
|
|
0026EF7C 0028:
|
|
??_C@_0CI@EGAEHKHM@attempt?5to?5convert?5object?5?$CFs?5to?5@ (0000)
|
|
0026EFA4 0013:
|
|
??_C@_0BD@HGOCLPEG@function?9?$DOevaluate?$AA@ (0000)
|
|
0026EFB8 0023:
|
|
??_C@_0CD@JFOOFKBP@found?5a?5static?5script?5at?5topleve@ (0000)
|
|
0026EFE0 004f:
|
|
??_C@_0EP@ECAJPAK@script?9?$DOscript_type?$CB?$DN_hs_script_@ (0000)
|
|
0026F030 001b:
|
|
??_C@_0BL@LKLEHFKI@ran?5out?5of?5script?5threads?4?$AA@ (0000)
|
|
0026F04C 002c:
|
|
??_C@_0CM@EIHAGODH@a?5global?5initialization?5attempte@ (0000)
|
|
0026F078 0020:
|
|
??_C@_0CA@COEBOOJE@internal_thread?9?$DOsleep_until?$DN?$DN0?$AA@ (0000)
|
|
0026F098 0036:
|
|
??_C@_0DG@NJPPFAFA@there?5are?5not?5enough?5threads?5to?5@ (0000)
|
|
002E2370 264c:
|
|
_hs_type_inspectors (0000)
|
|
_typecasting_procedures (00c8)
|
|
004535AC 0008:
|
|
_hs_runtime_globals (0000)
|
|
*/
|
|
|
|
/* ---------- headers */
|
|
|
|
#include "cseries/cseries.h"
|
|
#include "ai/ai_debug_scripting.h"
|
|
#include "ai/ai_script.h"
|
|
#include "hs/hs.h"
|
|
#include "hs/hs_library_internal.h"
|
|
#include "hs/hs_library_internal_runtime.h"
|
|
#include "hs/object_lists.h"
|
|
#include "hs/hs_scenario_definitions.h"
|
|
#include "math/real_math.h"
|
|
#include "memory/data.h"
|
|
#include "objects/objects.h"
|
|
#include "objects/object_types.h"
|
|
#include "physics/collisions.h"
|
|
#include "render/render.h"
|
|
#include "render/render_debug.h"
|
|
#include "scenario/scenario.h"
|
|
#include "scenario/scenario_definitions.h"
|
|
#include "saved games/game_state.h"
|
|
#include "text/draw_string.h"
|
|
#include "cseries/errors.h"
|
|
#include "main/console.h"
|
|
#include "game/game.h"
|
|
|
|
/* ---------- constants */
|
|
|
|
enum
|
|
{
|
|
_hs_thread_type_script = 0,
|
|
_hs_thread_type_global_initialize,
|
|
_hs_thread_type_console_command,
|
|
NUMBER_OF_HS_THREAD_TYPES,
|
|
};
|
|
|
|
enum
|
|
{
|
|
_hs_syntax_node_primitive_bit = 0,
|
|
_hs_syntax_node_script_bit,
|
|
_hs_syntax_node_global_bit,
|
|
};
|
|
|
|
enum
|
|
{
|
|
MAXIMUM_HS_DEBUG_STRING_ARGUMENTS = 32
|
|
};
|
|
|
|
enum
|
|
{
|
|
HS_THREAD_STACK_SIZE = 0x200
|
|
};
|
|
|
|
enum
|
|
{
|
|
_hs_thread_in_function_call_bit = 0,
|
|
_hs_thread_sleeping_bit,
|
|
_hs_global_external_bit = 15,
|
|
};
|
|
|
|
/* ---------- macros */
|
|
|
|
#define hs_thread_get(thread_index) \
|
|
((struct hs_thread_datum *)datum_get(hs_thread_data, (thread_index)))
|
|
|
|
#define hs_syntax_get(expression_index) \
|
|
((struct hs_syntax_node *)datum_get(hs_syntax_data, (expression_index)))
|
|
|
|
/* a thread is valid when it lies inside the thread data array and its stack pointer and
|
|
the fill mark of its topmost frame lie inside its own inline stack buffer */
|
|
#define valid_thread(thread) \
|
|
((byte *)(thread)>=(byte *)hs_thread_data->data && \
|
|
(byte *)(thread)<(byte *)hs_thread_data->data+hs_thread_data->count*hs_thread_data->size && \
|
|
(byte *)(thread)->stack>=(thread)->stack_data && \
|
|
(byte *)(thread)->stack<(thread)->stack_data+HS_THREAD_STACK_SIZE && \
|
|
(thread)->stack->data+(thread)->stack->size<=(thread)->stack_data+HS_THREAD_STACK_SIZE)
|
|
|
|
#define HS_GLOBAL_DESIGNATOR_IS_EXTERNAL(designator) \
|
|
TEST_FLAG((designator), _hs_global_external_bit)
|
|
|
|
#define HS_GLOBAL_DESIGNATOR_TO_INDEX(designator) \
|
|
((designator)&0x7fff)
|
|
|
|
/* every runtime failure is reported through the executing script's name */
|
|
#define match_hs_assert(file, line, thread_index, expr, reason) \
|
|
match_vassert(file, line, expr, csprintf(temporary, \
|
|
"a problem occurred while executing the script %s: %s (%s)", \
|
|
hs_thread_format(thread_index), reason, #expr))
|
|
|
|
/* the four ordered comparisons of hs_evaluate_inequality, expanded once per operand
|
|
representation; each expansion reports its own case's location */
|
|
#define HS_EVALUATE_INEQUALITY(first, second, line) \
|
|
value0 = (first); \
|
|
value1 = (second); \
|
|
switch (function_index) \
|
|
{ \
|
|
case _hs_function_gt: \
|
|
comparison = value0>value1; \
|
|
break; \
|
|
case _hs_function_lt: \
|
|
comparison = value0<value1; \
|
|
break; \
|
|
case _hs_function_gte: \
|
|
comparison = value0>=value1; \
|
|
break; \
|
|
case _hs_function_lte: \
|
|
comparison = value0<=value1; \
|
|
break; \
|
|
default: \
|
|
comparison = FALSE; \
|
|
match_vassert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", line, FALSE, NULL); \
|
|
break; \
|
|
}
|
|
|
|
typedef long (*hs_typecasting_procedure)(long value);
|
|
|
|
typedef void (*hs_debug_string_procedure)(
|
|
long name_count,
|
|
char const **names);
|
|
|
|
typedef void (*hs_inspection_procedure)(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
|
|
/* ---------- structures */
|
|
|
|
struct hs_runtime_globals
|
|
{
|
|
boolean initialized;
|
|
short executing_thread_index;
|
|
};
|
|
|
|
struct hs_stack_frame
|
|
{
|
|
struct hs_stack_frame *previous;
|
|
long expression_index;
|
|
void *result;
|
|
short size;
|
|
byte data[2];
|
|
};
|
|
|
|
struct hs_global_datum
|
|
{
|
|
short identifier;
|
|
short unused;
|
|
long value;
|
|
};
|
|
|
|
struct hs_thread_datum
|
|
{
|
|
short identifier;
|
|
byte type;
|
|
byte flags;
|
|
long script_index;
|
|
long sleep_until;
|
|
long previous_sleep_until;
|
|
struct hs_stack_frame *stack;
|
|
long result;
|
|
byte stack_data[0x200];
|
|
};
|
|
|
|
typedef char hs_thread_datum_size_assert[
|
|
sizeof(struct hs_thread_datum) == 0x218 ? 1 : -1];
|
|
|
|
/* ---------- prototypes */
|
|
|
|
static void hs_inspect_boolean(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
static void hs_inspect_real(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
static void hs_inspect_short_integer(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
static void hs_inspect_long_integer(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
static void hs_inspect_string(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
static void hs_inspect_enum(
|
|
short type,
|
|
long value,
|
|
char *buffer);
|
|
static long hs_long_to_boolean(
|
|
long n);
|
|
static long hs_short_to_boolean(
|
|
long s);
|
|
static long hs_string_to_boolean(
|
|
long n);
|
|
static long hs_data_to_void(
|
|
long n);
|
|
static long hs_short_to_real(
|
|
long s);
|
|
static long hs_long_to_real(
|
|
long l);
|
|
static long hs_enum_to_real(
|
|
long e);
|
|
static long hs_real_to_short(
|
|
long r);
|
|
static long hs_real_to_long(
|
|
long r);
|
|
static long hs_long_to_short(
|
|
long l);
|
|
static long hs_object_name_to_object_list(
|
|
long object_name_index);
|
|
static long hs_object_to_object_list(
|
|
long object_index);
|
|
static boolean hs_object_type_can_cast(
|
|
short actual_type,
|
|
short desired_type);
|
|
static char const *hs_thread_format(
|
|
long thread_index);
|
|
static void hs_stack_pop(
|
|
long thread_index);
|
|
static void hs_wake(
|
|
long thread_index);
|
|
static long hs_find_thread_by_script(
|
|
short script_index);
|
|
static long hs_find_thread_by_name(
|
|
char const *name);
|
|
static long hs_syntax_nth(
|
|
long expression_index,
|
|
short n);
|
|
static void hs_thread_delete(
|
|
long thread_index);
|
|
static long hs_thread_new(
|
|
short type,
|
|
long script_index);
|
|
static void hs_stack_push(
|
|
long thread_index);
|
|
static void *hs_stack_allocate(
|
|
long thread_index,
|
|
long size);
|
|
static void hs_evaluate(
|
|
long thread_index,
|
|
long expression_index,
|
|
long *destination);
|
|
static long hs_global_evaluate(
|
|
short global_designator);
|
|
static long *hs_arguments_evaluate(
|
|
long thread_index,
|
|
short formal_parameter_count,
|
|
short const *formal_parameters,
|
|
boolean initialize);
|
|
static boolean script_error(
|
|
long thread_index,
|
|
char const *reason,
|
|
char const *expression);
|
|
static void hs_script_evaluate(
|
|
short script_index,
|
|
long thread_index,
|
|
boolean initialize);
|
|
static void hs_thread_main(
|
|
long thread_index);
|
|
static void hs_global_reconcile_read(
|
|
word global_designator);
|
|
static void hs_global_reconcile_write(
|
|
word global_designator);
|
|
|
|
/* ---------- globals */
|
|
|
|
struct data_array *hs_global_data;
|
|
struct data_array *hs_thread_data;
|
|
extern struct data_array *hs_syntax_data;
|
|
extern short const hs_external_global_count;
|
|
extern short const hs_type_sizes[NUMBER_OF_HS_TYPES];
|
|
boolean debug_scripting;
|
|
unsigned long hs_debug_data[BIT_VECTOR_SIZE_IN_LONGS(MAXIMUM_TRIGGER_VOLUMES_PER_SCENARIO)];
|
|
typedef char verify_hs_debug_data_size[sizeof(hs_debug_data) == 0x20 ? 1 : -1];
|
|
static hs_inspection_procedure hs_type_inspectors[NUMBER_OF_HS_TYPES] =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
hs_inspect_boolean,
|
|
hs_inspect_real,
|
|
hs_inspect_short_integer,
|
|
hs_inspect_long_integer,
|
|
hs_inspect_string,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
hs_inspect_enum,
|
|
hs_inspect_enum,
|
|
hs_inspect_enum,
|
|
hs_inspect_enum,
|
|
hs_inspect_enum,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
};
|
|
hs_typecasting_procedure typecasting_procedures[NUMBER_OF_HS_TYPES][NUMBER_OF_HS_TYPES] =
|
|
{
|
|
{ NULL }, /* unparsed */
|
|
{ NULL }, /* special_form */
|
|
{ NULL }, /* function_name */
|
|
{ NULL }, /* passthrough */
|
|
{ /* void */
|
|
NULL, NULL, NULL, NULL,
|
|
NULL,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
hs_data_to_void, hs_data_to_void, hs_data_to_void, hs_data_to_void,
|
|
},
|
|
{ /* boolean */
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL,
|
|
hs_long_to_boolean,
|
|
hs_short_to_boolean,
|
|
hs_long_to_boolean,
|
|
hs_string_to_boolean,
|
|
},
|
|
{ /* real */
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL,
|
|
hs_short_to_real,
|
|
hs_long_to_real,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL,
|
|
hs_enum_to_real, hs_enum_to_real, hs_enum_to_real, hs_enum_to_real,
|
|
},
|
|
{ /* short_integer */
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL,
|
|
hs_real_to_short,
|
|
NULL,
|
|
hs_long_to_short,
|
|
},
|
|
{ /* long_integer */
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL,
|
|
hs_real_to_long,
|
|
NULL,
|
|
hs_long_to_short,
|
|
},
|
|
{ NULL }, /* string */
|
|
{ NULL }, /* script */
|
|
{ NULL }, /* trigger_volume */
|
|
{ NULL }, /* cutscene_flag */
|
|
{ NULL }, /* cutscene_camera_point */
|
|
{ NULL }, /* cutscene_title */
|
|
{ NULL }, /* cutscene_recording */
|
|
{ NULL }, /* device_group */
|
|
{ NULL }, /* ai */
|
|
{ NULL }, /* ai_command_list */
|
|
{ NULL }, /* starting_profile */
|
|
{ NULL }, /* conversation */
|
|
{ NULL }, /* navpoint */
|
|
{ NULL }, /* hud_message */
|
|
{ /* object_list */
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
object_list_from_ai_reference,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL,
|
|
hs_object_to_object_list, hs_object_to_object_list, hs_object_to_object_list, hs_object_to_object_list,
|
|
hs_object_to_object_list,
|
|
hs_object_name_to_object_list, hs_object_name_to_object_list, hs_object_name_to_object_list, hs_object_name_to_object_list,
|
|
hs_object_name_to_object_list, hs_object_name_to_object_list,
|
|
},
|
|
};
|
|
static struct hs_runtime_globals hs_runtime_globals;
|
|
|
|
/* per-type fallbacks used when an external global has no backing address */
|
|
extern boolean const _hs_type_boolean_default;
|
|
extern real const _hs_type_real_default;
|
|
extern short const _hs_type_short_integer_default;
|
|
extern long const _hs_type_long_integer_default;
|
|
extern char const *_hs_type_string_default;
|
|
extern short const _hs_type_script_default;
|
|
extern short const _hs_type_trigger_volume_default;
|
|
extern short const _hs_type_cutscene_flag_default;
|
|
extern short const _hs_type_cutscene_camera_point_default;
|
|
extern short const _hs_type_cutscene_title_default;
|
|
extern short const _hs_type_cutscene_recording_default;
|
|
extern short const _hs_type_device_group_default;
|
|
extern long const _hs_type_ai_default;
|
|
extern short const _hs_type_ai_command_list_default;
|
|
extern short const _hs_type_starting_profile_default;
|
|
extern short const _hs_type_conversation_default;
|
|
extern short const _hs_type_navpoint_default;
|
|
extern short const _hs_type_hud_message_default;
|
|
extern long const _hs_type_object_list_default;
|
|
extern long const _hs_type_sound_default;
|
|
extern long const _hs_type_effect_default;
|
|
extern long const _hs_type_damage_default;
|
|
extern long const _hs_type_looping_sound_default;
|
|
extern long const _hs_type_animation_graph_default;
|
|
extern long const _hs_type_actor_variant_default;
|
|
extern long const _hs_type_damage_effect_default;
|
|
extern long const _hs_type_object_definition_default;
|
|
extern short const _hs_type_enum_game_difficulty_default;
|
|
extern short const _hs_type_enum_team_default;
|
|
extern short const _hs_type_enum_ai_default_state_default;
|
|
extern short const _hs_type_enum_actor_type_default;
|
|
extern short const _hs_type_enum_hud_corner_default;
|
|
extern long const _hs_type_object_default;
|
|
extern long const _hs_type_unit_default;
|
|
extern long const _hs_type_vehicle_default;
|
|
extern long const _hs_type_weapon_default;
|
|
extern long const _hs_type_device_default;
|
|
extern long const _hs_type_scenery_default;
|
|
extern short const _hs_type_object_name_default;
|
|
boolean debug_trigger_volumes;
|
|
|
|
/* ---------- public code */
|
|
|
|
void hs_runtime_initialize(
|
|
void)
|
|
{
|
|
short global_index;
|
|
long index;
|
|
|
|
hs_thread_data = game_state_data_new("hs thread", 0x100, 0x218);
|
|
hs_global_data = game_state_data_new("hs globals", 0x400, 8);
|
|
if (hs_thread_data && hs_global_data)
|
|
{
|
|
match_vassert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0xa9,
|
|
hs_external_global_count*2<0x400,
|
|
"raise MAXIMUM_NUMBER_OF_HS_GLOBALS.");
|
|
data_make_valid(hs_global_data);
|
|
for (global_index = 0;
|
|
global_index<hs_external_global_count;
|
|
global_index++)
|
|
{
|
|
index = datum_new_at_index(hs_global_data,
|
|
DATUM_INDEX_NEW(global_index, 0xaced));
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0xb1, index!=NONE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
error(_error_immediate, "couldn't allocate scripting globals.");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_runtime_initialize_for_new_map(
|
|
void)
|
|
{
|
|
long internal_thread_index;
|
|
|
|
data_make_valid(hs_thread_data);
|
|
hs_runtime_globals.initialized = TRUE;
|
|
hs_runtime_globals.executing_thread_index = NONE;
|
|
internal_thread_index = hs_thread_new(_hs_thread_type_global_initialize, NONE);
|
|
|
|
if (global_scenario_index!=NONE)
|
|
{
|
|
struct scenario *scenario = global_scenario_get();
|
|
struct hs_thread_datum *internal_thread = hs_thread_get(internal_thread_index);
|
|
struct hs_global_datum *global_datum;
|
|
long global_datum_index;
|
|
short global_index;
|
|
short script_index;
|
|
|
|
for (global_index = 0;
|
|
global_index<scenario->hs_globals.count;
|
|
global_index++)
|
|
{
|
|
struct hs_global *global = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_globals,
|
|
global_index,
|
|
struct hs_global);
|
|
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL((word)global_index))
|
|
global_datum_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_index);
|
|
else
|
|
global_datum_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_index)+
|
|
hs_external_global_count;
|
|
datum_new_at_index(hs_global_data,
|
|
DATUM_INDEX_NEW(global_datum_index, 0xaced));
|
|
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL((word)global_index))
|
|
global_datum_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_index);
|
|
else
|
|
global_datum_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_index)+
|
|
hs_external_global_count;
|
|
global_datum = datum_get(hs_global_data, global_datum_index);
|
|
internal_thread->script_index = NONE;
|
|
internal_thread->stack->size = 0;
|
|
hs_evaluate(
|
|
internal_thread_index,
|
|
global->initialization_expression_index,
|
|
&global_datum->value);
|
|
|
|
if (TEST_FLAG(internal_thread->flags, _hs_thread_in_function_call_bit))
|
|
{
|
|
hs_thread_main(internal_thread_index);
|
|
if (global->type==_hs_type_object_list)
|
|
object_list_add_reference(hs_global_evaluate(global_index));
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0xe7, internal_thread_index,
|
|
internal_thread->sleep_until==0,
|
|
"a global initialization attempted to sleep.");
|
|
}
|
|
hs_global_reconcile_write(global_index);
|
|
}
|
|
|
|
hs_thread_delete(internal_thread_index);
|
|
|
|
for (script_index = 0;
|
|
script_index<scenario->hs_scripts.count;
|
|
script_index++)
|
|
{
|
|
struct hs_script *script = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->hs_scripts,
|
|
script_index,
|
|
struct hs_script);
|
|
|
|
if (script->script_type!=_hs_script_static &&
|
|
script->script_type!=_hs_script_stub)
|
|
{
|
|
if (hs_thread_new(_hs_thread_type_script, script_index)==NONE)
|
|
error(_error_immediate, "ran out of script threads.");
|
|
}
|
|
}
|
|
}
|
|
|
|
csmemset(hs_debug_data, 0,
|
|
BIT_VECTOR_SIZE_IN_BYTES(MAXIMUM_TRIGGER_VOLUMES_PER_SCENARIO));
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_runtime_dispose(
|
|
void)
|
|
{
|
|
data_make_invalid(hs_global_data);
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_runtime_dispose_from_old_map(
|
|
void)
|
|
{
|
|
short global_index;
|
|
|
|
data_make_invalid(hs_thread_data);
|
|
for (global_index = hs_external_global_count;
|
|
global_index<hs_global_data->count;
|
|
global_index++)
|
|
{
|
|
if (datum_try_and_get(hs_global_data, global_index))
|
|
datum_delete(hs_global_data, global_index);
|
|
}
|
|
hs_runtime_globals.initialized = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
static char const *expression_get_function_name(
|
|
long thread_index,
|
|
long expression_index)
|
|
{
|
|
long next_expression_index;
|
|
struct hs_syntax_node *syntax_node = hs_syntax_get(expression_index);
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
|
|
while (TRUE)
|
|
{
|
|
if (TEST_FLAG(syntax_node->flags, _hs_syntax_node_script_bit))
|
|
break;
|
|
|
|
if (syntax_node->index != 0 ||
|
|
expression_index != thread->stack->expression_index)
|
|
{
|
|
return hs_function_get((word)syntax_node->index)->name;
|
|
}
|
|
|
|
next_expression_index = *(long *)thread->stack->data;
|
|
if (next_expression_index == NONE)
|
|
return "(end of script)";
|
|
|
|
expression_index = next_expression_index;
|
|
syntax_node = hs_syntax_get(expression_index);
|
|
thread = hs_thread_get(thread_index);
|
|
}
|
|
|
|
return TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
syntax_node->index,
|
|
struct hs_script)->name;
|
|
}
|
|
|
|
char const *hs_runtime_get_executing_thread_name(
|
|
void)
|
|
{
|
|
char const *name;
|
|
|
|
name = NULL;
|
|
if (hs_runtime_globals.executing_thread_index != NONE)
|
|
name = hs_thread_format(hs_runtime_globals.executing_thread_index);
|
|
if (!name)
|
|
name = "[unknown]";
|
|
|
|
return name;
|
|
}
|
|
|
|
boolean hs_wake_by_name(
|
|
char const *name)
|
|
{
|
|
long thread_index;
|
|
boolean result;
|
|
|
|
thread_index = hs_find_thread_by_name(name);
|
|
result = FALSE;
|
|
if (thread_index != NONE)
|
|
{
|
|
hs_wake(thread_index);
|
|
result = TRUE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void render_debug_scripting(
|
|
void)
|
|
{
|
|
char string[0x2800];
|
|
short tab_stops[2];
|
|
|
|
if (debug_scripting)
|
|
{
|
|
long thread_index;
|
|
|
|
tab_stops[0] = 200;
|
|
tab_stops[1] = 300;
|
|
sprintf(string, "|n|n|nscript name|tsleep time|tfunction");
|
|
|
|
for (thread_index = data_next_index(hs_thread_data, NONE);
|
|
hs_runtime_globals.initialized && thread_index != NONE;
|
|
thread_index = data_next_index(hs_thread_data, thread_index))
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
|
|
if (thread->sleep_until >= 0)
|
|
{
|
|
sprintf(
|
|
string + csstrlen(string),
|
|
"|n%s|t",
|
|
hs_thread_format(thread_index));
|
|
sprintf(
|
|
string + csstrlen(string),
|
|
"%d",
|
|
thread->sleep_until
|
|
? thread->sleep_until - game_time_get()
|
|
: 0);
|
|
csstrcat(string, "|t");
|
|
|
|
if (thread->stack != (struct hs_stack_frame *)thread->stack_data &&
|
|
thread->sleep_until != -2)
|
|
{
|
|
csstrcat(
|
|
string,
|
|
expression_get_function_name(
|
|
thread_index,
|
|
thread->stack->expression_index));
|
|
}
|
|
}
|
|
}
|
|
|
|
string[0x400] = 0;
|
|
draw_string_set_tab_stops(tab_stops, NUMBEROF(tab_stops));
|
|
render_debug_string(TRUE, string);
|
|
draw_string_set_tab_stops(tab_stops, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void render_debug_trigger_volumes(
|
|
void)
|
|
{
|
|
if (debug_trigger_volumes)
|
|
{
|
|
real_matrix4x3 matrix;
|
|
short volume_index;
|
|
struct scenario *scenario = global_scenario_get();
|
|
|
|
for (volume_index = 0;
|
|
volume_index < scenario->trigger_volumes.count;
|
|
volume_index++)
|
|
{
|
|
struct scenario_trigger_volume *volume = TAG_BLOCK_GET_ELEMENT(
|
|
&scenario->trigger_volumes,
|
|
volume_index,
|
|
struct scenario_trigger_volume);
|
|
real_vector3d local_extent;
|
|
real_vector3d world_extent;
|
|
short edge_index;
|
|
real_point3d corner[4];
|
|
|
|
switch (volume->type)
|
|
{
|
|
case _scenario_trigger_volume_type_axis_aligned:
|
|
matrix = *global_identity4x3;
|
|
set_real_point3d(
|
|
&matrix.position,
|
|
volume->bounds.x0,
|
|
volume->bounds.y0,
|
|
volume->bounds.z0);
|
|
set_real_vector3d(
|
|
&local_extent,
|
|
volume->bounds.x1 - volume->bounds.x0,
|
|
volume->bounds.y1 - volume->bounds.y0,
|
|
volume->bounds.z1 - volume->bounds.z0);
|
|
world_extent = local_extent;
|
|
break;
|
|
|
|
case _scenario_trigger_volume_type_oriented:
|
|
local_extent = volume->extents;
|
|
world_extent = volume->extents;
|
|
matrix4x3_from_point_and_vectors(
|
|
&matrix,
|
|
&volume->position,
|
|
&volume->forward,
|
|
&volume->up);
|
|
matrix4x3_transform_vector(&matrix, &local_extent, &world_extent);
|
|
break;
|
|
|
|
/* matrix is left unassigned only by this default arm. Not reached unassigned: the
|
|
* arm's assertion failure calls system_exit, which does not return in January
|
|
* (0x47c960 jumps to halt_and_catch_fire 0x4f21c0, which loops or calls exit).
|
|
* Source-policy approval pending (2026-09-27 audit). */
|
|
default:
|
|
match_assert(
|
|
"c:\\halo\\SOURCE\\hs\\hs_runtime.c",
|
|
0x213,
|
|
!"unreachable");
|
|
break;
|
|
}
|
|
|
|
for (edge_index = 0; edge_index < 6; edge_index++)
|
|
{
|
|
real_vector3d sides[2] = { 0 };
|
|
short side = edge_index % 2;
|
|
short axis = edge_index / 2;
|
|
|
|
if (side)
|
|
{
|
|
point_from_line3d(&matrix.position, &world_extent, 1.0f, &corner[0]);
|
|
sides[0].n[(axis + 1) % 3] = -local_extent.n[(axis + 1) % 3];
|
|
sides[1].n[(axis + 2) % 3] = -local_extent.n[(axis + 2) % 3];
|
|
matrix4x3_transform_vector(&matrix, &sides[0], &sides[0]);
|
|
matrix4x3_transform_vector(&matrix, &sides[1], &sides[1]);
|
|
point_from_line3d(&corner[0], &sides[0], 1.0f, &corner[1]);
|
|
point_from_line3d(&corner[1], &sides[1], 1.0f, &corner[2]);
|
|
point_from_line3d(&corner[2], &sides[0], -1.0f, &corner[3]);
|
|
}
|
|
else
|
|
{
|
|
corner[0] = matrix.position;
|
|
sides[0].n[(axis + 1) % 3] = local_extent.n[(axis + 1) % 3];
|
|
sides[1].n[(axis + 2) % 3] = local_extent.n[(axis + 2) % 3];
|
|
matrix4x3_transform_vector(&matrix, &sides[0], &sides[0]);
|
|
matrix4x3_transform_vector(&matrix, &sides[1], &sides[1]);
|
|
point_from_line3d(&corner[0], &sides[0], 1.0f, &corner[1]);
|
|
point_from_line3d(&corner[1], &sides[1], 1.0f, &corner[2]);
|
|
point_from_line3d(&corner[2], &sides[0], -1.0f, &corner[3]);
|
|
}
|
|
|
|
if (BIT_VECTOR_TEST_FLAG(hs_debug_data, volume_index))
|
|
{
|
|
render_debug_polygon_edges(corner, 4, global_real_argb_blue);
|
|
}
|
|
else
|
|
{
|
|
real_argb_color color = *global_real_argb_blue;
|
|
|
|
color.alpha = 0.15f;
|
|
render_debug_polygon_edges(corner, 4, global_real_argb_red);
|
|
render_debug_polygon(corner, 4, &color);
|
|
}
|
|
}
|
|
|
|
{
|
|
real_point3d center;
|
|
real_vector3d ray;
|
|
struct collision_result result;
|
|
|
|
point_from_line3d(&matrix.position, &world_extent, 0.5f, ¢er);
|
|
vector_from_points3d(&render.camera.position, ¢er, &ray);
|
|
scale_vector3d(&ray, 0.95f, &ray);
|
|
if (!collision_test_vector(
|
|
_collision_test_for_line_of_sight_flags,
|
|
&render.camera.position,
|
|
&ray,
|
|
NONE,
|
|
&result))
|
|
{
|
|
if (BIT_VECTOR_TEST_FLAG(hs_debug_data, volume_index))
|
|
{
|
|
render_debug_string_at_point(TRUE, ¢er, volume->name, global_real_argb_yellow);
|
|
}
|
|
else
|
|
{
|
|
render_debug_string_at_point(TRUE, ¢er, volume->name, global_real_argb_white);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_runtime_update(
|
|
void)
|
|
{
|
|
if (hs_runtime_globals.initialized)
|
|
{
|
|
long time = game_time_get();
|
|
boolean console_command_running = FALSE;
|
|
long thread_index;
|
|
|
|
for (thread_index = data_next_index(hs_thread_data, NONE);
|
|
hs_runtime_globals.initialized && thread_index!=NONE;
|
|
thread_index = data_next_index(hs_thread_data, thread_index))
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
|
|
if (thread->type==_hs_thread_type_console_command)
|
|
console_command_running = TRUE;
|
|
if (thread->sleep_until>=0 && thread->sleep_until<=time)
|
|
hs_thread_main(thread_index);
|
|
}
|
|
|
|
object_list_gc();
|
|
if (!console_command_running && game_time_get()%16==0)
|
|
hs_node_gc();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
long hs_runtime_evaluate(
|
|
long expression_index)
|
|
{
|
|
long result = NONE;
|
|
|
|
if (hs_runtime_globals.initialized && expression_index!=NONE)
|
|
{
|
|
long thread_index = datum_new(hs_thread_data);
|
|
|
|
if (thread_index!=NONE)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
|
|
thread->stack = (struct hs_stack_frame *)thread->stack_data;
|
|
thread->stack->previous = NULL;
|
|
thread->stack->size = 0;
|
|
thread->stack->expression_index = NONE;
|
|
thread->type = _hs_thread_type_console_command;
|
|
thread->script_index = NONE;
|
|
thread->flags = 0;
|
|
thread->sleep_until = 0;
|
|
|
|
thread = hs_thread_get(thread_index);
|
|
hs_evaluate(thread_index, expression_index, &thread->result);
|
|
if (TEST_FLAG(thread->flags, _hs_thread_in_function_call_bit))
|
|
{
|
|
hs_thread_main(thread_index);
|
|
|
|
return NONE;
|
|
}
|
|
|
|
return thread->result;
|
|
}
|
|
|
|
error(_error_silent, "there are not enough threads to execute that command.");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void hs_evaluate_wake(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
long wake_thread_index;
|
|
struct hs_thread_datum *thread;
|
|
struct hs_syntax_node *script_name_node;
|
|
long script_name_node_index;
|
|
|
|
thread = datum_get(hs_thread_data, thread_index);
|
|
script_name_node_index = ((struct hs_syntax_node *)datum_get(
|
|
hs_syntax_data,
|
|
((struct hs_syntax_node *)datum_get(
|
|
hs_syntax_data,
|
|
thread->stack->expression_index))->data))->next_node_index;
|
|
script_name_node = datum_get(hs_syntax_data, script_name_node_index);
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x22c,
|
|
function_index==_hs_function_wake);
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x22d,
|
|
TEST_FLAG(script_name_node->flags, _hs_syntax_node_primitive_bit));
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x22e,
|
|
script_name_node->type==_hs_type_script);
|
|
|
|
wake_thread_index = hs_find_thread_by_script((short)script_name_node->data);
|
|
if (wake_thread_index != NONE)
|
|
hs_wake(wake_thread_index);
|
|
hs_return(thread_index, 0);
|
|
|
|
return;
|
|
}
|
|
|
|
/* ---------- private code */
|
|
|
|
static long hs_syntax_nth(
|
|
long expression_index,
|
|
short n)
|
|
{
|
|
short index;
|
|
|
|
for (index = 0; index<n; index++)
|
|
expression_index = hs_syntax_get(expression_index)->next_node_index;
|
|
|
|
return expression_index;
|
|
}
|
|
|
|
static long hs_thread_new(
|
|
short type,
|
|
long script_index)
|
|
{
|
|
long thread_index = datum_new(hs_thread_data);
|
|
struct hs_thread_datum *thread;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x26f,
|
|
type>=0 && type<NUMBER_OF_HS_THREAD_TYPES);
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x270,
|
|
type!=_hs_thread_type_script || script_index!=NONE);
|
|
|
|
if (thread_index!=NONE)
|
|
{
|
|
thread = hs_thread_get(thread_index);
|
|
thread->stack = (struct hs_stack_frame *)thread->stack_data;
|
|
thread->stack->previous = NULL;
|
|
thread->stack->size = 0;
|
|
thread->stack->expression_index = NONE;
|
|
thread->type = (byte)type;
|
|
thread->script_index = script_index;
|
|
thread->flags = 0;
|
|
if (script_index!=NONE &&
|
|
TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
script_index,
|
|
struct hs_script)->script_type==_hs_script_dormant)
|
|
{
|
|
thread->sleep_until = NONE-1;
|
|
}
|
|
else
|
|
{
|
|
thread->sleep_until = 0;
|
|
}
|
|
}
|
|
|
|
return thread_index;
|
|
}
|
|
|
|
static void hs_thread_delete(
|
|
long thread_index)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x290,
|
|
hs_thread_get(thread_index)->type!=_hs_thread_type_script);
|
|
|
|
datum_delete(hs_thread_data, thread_index);
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_stack_push(
|
|
long thread_index)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
struct hs_stack_frame *new_frame = (struct hs_stack_frame *)
|
|
((byte *)thread->stack+thread->stack->size+sizeof(struct hs_stack_frame));
|
|
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x35e, thread_index,
|
|
(byte *) (new_frame+1)<thread->stack_data+HS_THREAD_STACK_SIZE,
|
|
"stack overflow.");
|
|
|
|
new_frame->previous = thread->stack;
|
|
thread->stack = new_frame;
|
|
new_frame->size = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
static char const *hs_thread_format(
|
|
long thread_index)
|
|
{
|
|
struct hs_thread_datum *thread;
|
|
char const *name;
|
|
|
|
name = NULL;
|
|
thread = datum_get(hs_thread_data, thread_index);
|
|
switch (thread->type)
|
|
{
|
|
case _hs_thread_type_script:
|
|
name = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
((struct hs_thread_datum *)datum_get(
|
|
hs_thread_data,
|
|
thread_index))->script_index,
|
|
struct hs_script)->name;
|
|
break;
|
|
case _hs_thread_type_global_initialize:
|
|
name = "[global initialize]";
|
|
break;
|
|
case _hs_thread_type_console_command:
|
|
name = "[console command]";
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x2a9, TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
static void hs_stack_pop(
|
|
long thread_index)
|
|
{
|
|
struct hs_thread_datum *thread;
|
|
|
|
thread = datum_get(hs_thread_data, thread_index);
|
|
thread->stack = thread->stack->previous;
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_wake(
|
|
long thread_index)
|
|
{
|
|
struct hs_thread_datum *thread;
|
|
|
|
thread = datum_get(hs_thread_data, thread_index);
|
|
if (thread->sleep_until != NONE)
|
|
{
|
|
thread->sleep_until = 0;
|
|
if (TEST_FLAG(thread->flags, 1))
|
|
{
|
|
thread->sleep_until = thread->previous_sleep_until;
|
|
SET_FLAG(thread->flags, 1, FALSE);
|
|
}
|
|
else
|
|
{
|
|
if (thread->stack->expression_index != NONE)
|
|
{
|
|
if (((struct hs_syntax_node *)datum_get(hs_syntax_data, thread->stack->expression_index))->index == 0x14)
|
|
{
|
|
hs_stack_pop(thread_index);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (thread->stack->previous &&
|
|
thread->stack->previous->expression_index != NONE)
|
|
{
|
|
if (((struct hs_syntax_node *)datum_get(
|
|
hs_syntax_data,
|
|
thread->stack->previous->expression_index))->index == 0x14)
|
|
{
|
|
hs_stack_pop(thread_index);
|
|
hs_stack_pop(thread_index);
|
|
SET_FLAG(thread->flags, 0, FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static long hs_find_thread_by_name(
|
|
char const *name)
|
|
{
|
|
long thread_index;
|
|
|
|
thread_index = data_next_index(hs_thread_data, NONE);
|
|
while (thread_index != NONE)
|
|
{
|
|
struct hs_thread_datum *thread;
|
|
|
|
thread = datum_get(hs_thread_data, thread_index);
|
|
if (thread->script_index != NONE)
|
|
{
|
|
struct hs_script const *script;
|
|
|
|
script = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
thread->script_index,
|
|
struct hs_script);
|
|
if (_stricmp(script->name, name) == 0)
|
|
return thread_index;
|
|
}
|
|
thread_index = data_next_index(hs_thread_data, thread_index);
|
|
}
|
|
|
|
return NONE;
|
|
}
|
|
|
|
static long hs_find_thread_by_script(
|
|
short script_index)
|
|
{
|
|
long thread_index;
|
|
|
|
thread_index = data_next_index(hs_thread_data, NONE);
|
|
while (thread_index != NONE)
|
|
{
|
|
struct hs_thread_datum *thread;
|
|
|
|
thread = datum_get(hs_thread_data, thread_index);
|
|
if (thread->script_index == script_index)
|
|
return thread_index;
|
|
thread_index = data_next_index(hs_thread_data, thread_index);
|
|
}
|
|
|
|
return NONE;
|
|
}
|
|
|
|
static void hs_inspect_boolean(
|
|
short type,
|
|
long value,
|
|
char *buffer)
|
|
{
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x241,
|
|
type==_hs_type_boolean);
|
|
|
|
sprintf(buffer, "%s", (boolean)value ? "true" : "false");
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_inspect_real(
|
|
short type,
|
|
long value,
|
|
char *buffer)
|
|
{
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x24c,
|
|
type==_hs_type_real);
|
|
|
|
sprintf(buffer, "%f", *(real *)&value);
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_inspect_short_integer(
|
|
short type,
|
|
long value,
|
|
char *buffer)
|
|
{
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x257,
|
|
type==_hs_type_short_integer);
|
|
|
|
sprintf(buffer, "%d", (short)value);
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_inspect_long_integer(
|
|
short type,
|
|
long value,
|
|
char *buffer)
|
|
{
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x262,
|
|
type==_hs_type_long_integer);
|
|
|
|
sprintf(buffer, "%ld", value);
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_inspect_string(
|
|
short type,
|
|
long value,
|
|
char *buffer)
|
|
{
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x26d,
|
|
type==_hs_type_string);
|
|
|
|
sprintf(buffer, "%s", (char const *)value);
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_inspect_enum(
|
|
short type,
|
|
long value,
|
|
char *buffer)
|
|
{
|
|
struct hs_enum_definition const *enum_definition;
|
|
|
|
enum_definition = &hs_enum_table[type-_hs_type_enum_game_difficulty];
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x27b,
|
|
HS_TYPE_IS_ENUM(type));
|
|
match_vassert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x27c,
|
|
(short)value>=0 && (short)value<enum_definition->count,
|
|
"enum_value>=0 && enum_value<enum_definition->count");
|
|
|
|
sprintf(buffer, "%s", enum_definition->values[(short)value]);
|
|
|
|
return;
|
|
}
|
|
|
|
static long hs_long_to_boolean(
|
|
long n)
|
|
{
|
|
long result;
|
|
|
|
/* BUG: only the low byte of this uninitialised long is written, and the whole long is returned; its
|
|
upper three bytes are indeterminate. January homes result in the argument slot, so they are bits 8-31
|
|
of n; where hs_string_to_boolean inlines this function they come from its 'push ecx' slot, which holds
|
|
hs_cast's table index 254 (0x000000fe), so they are zero there. No January consumer reads a boolean
|
|
cell beyond its low byte, but hs_return and the pass-through forms copy the whole long into hs thread
|
|
stacks and hs globals, so these bytes reach the saved-game CRC and the save files
|
|
(game_state_write_to_persistent_storage, game_state_write_to_file, game_state_write_core). */
|
|
*(boolean *)&result = n==0;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_short_to_boolean(
|
|
long s)
|
|
{
|
|
long result;
|
|
|
|
/* BUG: only the low byte of this uninitialised long is written, and the whole long is returned; its
|
|
upper three bytes are indeterminate. January homes result in the argument slot, so they are bits 8-31
|
|
of s. No January consumer reads a boolean cell beyond its low byte, but hs_return and the pass-through
|
|
forms copy the whole long into hs thread stacks and hs globals, so these bytes reach the saved-game CRC
|
|
and the save files (game_state_write_to_persistent_storage, game_state_write_to_file,
|
|
game_state_write_core). */
|
|
*(boolean *)&result = (short)s==0;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_string_to_boolean(
|
|
long n)
|
|
{
|
|
return hs_long_to_boolean(csstrlen((char const *)n));
|
|
}
|
|
|
|
static long hs_data_to_void(
|
|
long n)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static long hs_short_to_real(
|
|
long s)
|
|
{
|
|
long result;
|
|
|
|
*(real *)&result = (real)(short)s;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_long_to_real(
|
|
long l)
|
|
{
|
|
long result;
|
|
|
|
*(real *)&result = (real)l;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_enum_to_real(
|
|
long e)
|
|
{
|
|
long result;
|
|
|
|
*(real *)&result = (real)((short)e+1);
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_real_to_short(
|
|
long r)
|
|
{
|
|
long result;
|
|
|
|
/* BUG: only the low word of this uninitialised long is written, and the whole long is returned; its
|
|
upper word is indeterminate. January homes result in the argument slot, so it is the upper word of r's
|
|
bit pattern. No January consumer reads a short cell beyond its low word, but hs_return and the
|
|
pass-through forms copy the whole long into hs thread stacks and hs globals, so these bytes reach the
|
|
saved-game CRC and the save files (game_state_write_to_persistent_storage, game_state_write_to_file,
|
|
game_state_write_core). */
|
|
*(short *)&result = (short)*(real *)&r;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_real_to_long(
|
|
long r)
|
|
{
|
|
return (long)*(real *)&r;
|
|
}
|
|
|
|
static long hs_long_to_short(
|
|
long l)
|
|
{
|
|
long result;
|
|
|
|
/* BUG: only the low word of this uninitialised long is written, and the whole long is returned; its
|
|
upper word is indeterminate. January homes result in the argument slot, so it is the upper word of l
|
|
(January returns l unchanged). No January consumer reads a short cell beyond its low word, but
|
|
hs_return and the pass-through forms copy the whole long into hs thread stacks and hs globals, so these
|
|
bytes reach the saved-game CRC and the save files (game_state_write_to_persistent_storage,
|
|
game_state_write_to_file, game_state_write_core). */
|
|
*(short *)&result = (short)l;
|
|
|
|
return result;
|
|
}
|
|
|
|
static long hs_object_name_to_object_list(
|
|
long object_name_index)
|
|
{
|
|
long object_index;
|
|
long object_list_index = NONE;
|
|
|
|
object_index = object_index_from_name_index((short)object_name_index);
|
|
if (object_index != NONE)
|
|
{
|
|
object_list_index = object_list_new();
|
|
object_list_add(object_list_index, object_index);
|
|
}
|
|
|
|
return object_list_index;
|
|
}
|
|
|
|
static long hs_object_to_object_list(
|
|
long object_index)
|
|
{
|
|
long object_list_index = NONE;
|
|
|
|
if (object_index != NONE)
|
|
{
|
|
object_list_index = object_list_new();
|
|
object_list_add(object_list_index, object_index);
|
|
}
|
|
|
|
return object_list_index;
|
|
}
|
|
|
|
static boolean hs_object_type_can_cast(
|
|
short actual_type,
|
|
short desired_type)
|
|
{
|
|
word actual_type_mask;
|
|
word desired_type_mask;
|
|
|
|
actual_type_mask = hs_object_type_masks[actual_type];
|
|
desired_type_mask = hs_object_type_masks[desired_type];
|
|
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x599,
|
|
actual_type>=0 && actual_type<NUMBER_OF_HS_OBJECT_TYPES);
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x59a,
|
|
desired_type>=0 && desired_type<NUMBER_OF_HS_OBJECT_TYPES);
|
|
|
|
return (actual_type_mask & desired_type_mask)==actual_type_mask;
|
|
}
|
|
|
|
boolean hs_can_cast(
|
|
short actual_type,
|
|
short desired_type)
|
|
{
|
|
short object_type;
|
|
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x5a4,
|
|
actual_type==_hs_passthrough || hs_type_valid(actual_type));
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x5a5,
|
|
hs_type_valid(desired_type));
|
|
|
|
if (actual_type==_hs_passthrough || actual_type==desired_type)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (HS_TYPE_IS_OBJECT(desired_type))
|
|
{
|
|
object_type = desired_type - _hs_type_object;
|
|
if (HS_TYPE_IS_OBJECT(actual_type))
|
|
{
|
|
return hs_object_type_can_cast(
|
|
actual_type-_hs_type_object,
|
|
object_type);
|
|
}
|
|
else if (!HS_TYPE_IS_OBJECT_NAME(actual_type))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
goto cast_object_type;
|
|
}
|
|
else if (HS_TYPE_IS_OBJECT_NAME(desired_type))
|
|
{
|
|
if (!HS_TYPE_IS_OBJECT_NAME(actual_type))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
object_type = desired_type - _hs_type_object_name;
|
|
|
|
cast_object_type:
|
|
return hs_object_type_can_cast(
|
|
actual_type-_hs_type_object_name,
|
|
object_type);
|
|
}
|
|
else
|
|
{
|
|
return typecasting_procedures[desired_type][actual_type] != NULL;
|
|
}
|
|
}
|
|
|
|
long hs_cast(
|
|
long thread_index,
|
|
short actual_type,
|
|
short desired_type,
|
|
long value)
|
|
{
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x5d8, thread_index,
|
|
hs_can_cast(actual_type, desired_type), "bad typecast.");
|
|
|
|
if (actual_type!=desired_type &&
|
|
actual_type!=_hs_passthrough &&
|
|
!HS_TYPE_IS_OBJECT_NAME(desired_type))
|
|
{
|
|
if (!HS_TYPE_IS_OBJECT(desired_type))
|
|
return typecasting_procedures[desired_type][actual_type](value);
|
|
else if (HS_TYPE_IS_OBJECT_NAME(actual_type))
|
|
return object_index_from_name_index((short)value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
void hs_return(
|
|
long thread_index,
|
|
long value)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
struct hs_syntax_node *expression = hs_syntax_get(thread->stack->expression_index);
|
|
short return_type;
|
|
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x325, thread_index,
|
|
valid_thread(thread), "corrupted stack.");
|
|
|
|
if (!TEST_FLAG(expression->flags, _hs_syntax_node_script_bit))
|
|
{
|
|
return_type = hs_function_get(expression->index)->return_type;
|
|
}
|
|
else
|
|
{
|
|
return_type = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
expression->index,
|
|
struct hs_script)->return_type;
|
|
}
|
|
|
|
*(long *)thread->stack->previous->result = hs_cast(
|
|
thread_index,
|
|
return_type,
|
|
expression->type,
|
|
value);
|
|
|
|
thread = hs_thread_get(thread_index);
|
|
thread->stack = thread->stack->previous;
|
|
|
|
return;
|
|
}
|
|
|
|
long *hs_macro_function_evaluate(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_function_definition *function = hs_function_get(function_index);
|
|
|
|
return hs_arguments_evaluate(
|
|
thread_index,
|
|
function->parameter_count,
|
|
function->parameter_types,
|
|
initialize);
|
|
}
|
|
|
|
void hs_evaluate_begin(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *expression_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *result = hs_stack_allocate(thread_index, sizeof(long));
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x15,
|
|
function_index==_hs_function_begin);
|
|
|
|
if (initialize)
|
|
{
|
|
*expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
*result = 0;
|
|
}
|
|
|
|
if (*expression_index!=NONE)
|
|
{
|
|
hs_evaluate(thread_index, *expression_index, result);
|
|
*expression_index = hs_syntax_get(*expression_index)->next_node_index;
|
|
}
|
|
else
|
|
{
|
|
hs_return(thread_index, *result);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void hs_evaluate_equality(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
short parameter_types[2];
|
|
long *arguments;
|
|
long result_long;
|
|
short type;
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x131,
|
|
function_index==_hs_function_equal || function_index==_hs_function_not_equal);
|
|
|
|
type = hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
hs_thread_get(thread_index)->stack->expression_index)->data)->next_node_index)->type;
|
|
parameter_types[0] = parameter_types[1] = type;
|
|
|
|
arguments = hs_arguments_evaluate(thread_index, 2, parameter_types, initialize);
|
|
if (arguments)
|
|
{
|
|
boolean equal = csmemcmp(arguments, arguments+1, hs_type_sizes[type])==0;
|
|
|
|
if (function_index==_hs_function_not_equal)
|
|
equal = !equal;
|
|
|
|
/* BUG: only the low byte of this uninitialised long is written, and the whole long is passed to
|
|
hs_return; its upper three bytes are indeterminate. January reuses the function_index argument
|
|
slot, which parameter_types has just filled with the argument type twice, so the long is
|
|
(type<<16)|equal. No January consumer reads a boolean cell beyond its low byte, but hs_return and
|
|
the pass-through forms copy the whole long into hs thread stacks and hs globals, so these bytes
|
|
reach the saved-game CRC and the save files (game_state_write_to_persistent_storage,
|
|
game_state_write_to_file, game_state_write_core). */
|
|
*(boolean *)&result_long = equal;
|
|
hs_return(thread_index, result_long);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_inequality(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
static short parameter_types[2];
|
|
long *arguments;
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x15d,
|
|
function_index>=_hs_function_gt && function_index<=_hs_function_lte);
|
|
|
|
parameter_types[0] = parameter_types[1] = hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
hs_thread_get(thread_index)->stack->expression_index)->data)->next_node_index)->type;
|
|
|
|
arguments = hs_arguments_evaluate(thread_index, 2, parameter_types, initialize);
|
|
if (arguments)
|
|
{
|
|
long result_long;
|
|
boolean comparison;
|
|
real value0;
|
|
real value1;
|
|
|
|
switch (parameter_types[0])
|
|
{
|
|
case _hs_type_real:
|
|
HS_EVALUATE_INEQUALITY(((real *)arguments)[0], ((real *)arguments)[1], 0x16b);
|
|
break;
|
|
case _hs_type_long_integer:
|
|
HS_EVALUATE_INEQUALITY((real)arguments[0], (real)arguments[1], 0x16e);
|
|
break;
|
|
default:
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x171,
|
|
parameter_types[0]==_hs_type_short_integer || HS_TYPE_IS_ENUM(parameter_types[0]));
|
|
|
|
HS_EVALUATE_INEQUALITY((real)(short)arguments[0], (real)(short)arguments[1], 0x172);
|
|
break;
|
|
}
|
|
|
|
/* BUG: only the low byte of this uninitialised long is written, and the whole long is passed to
|
|
hs_return; its upper three bytes are indeterminate. January reuses the function_index argument
|
|
slot, where it has just spilled value1, so they are bits 8-31 of value1's bit pattern. No January
|
|
consumer reads a boolean cell beyond its low byte, but hs_return and the pass-through forms copy
|
|
the whole long into hs thread stacks and hs globals, so these bytes reach the saved-game CRC and
|
|
the save files (game_state_write_to_persistent_storage, game_state_write_to_file,
|
|
game_state_write_core). */
|
|
*(boolean *)&result_long = comparison;
|
|
hs_return(thread_index, result_long);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_logical(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *expression_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
boolean *value = hs_stack_allocate(thread_index, sizeof(long));
|
|
boolean *result = hs_stack_allocate(thread_index, sizeof(boolean));
|
|
boolean and = function_index==_hs_function_and;
|
|
long result_long;
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0xcf,
|
|
function_index==_hs_function_and || function_index==_hs_function_or);
|
|
|
|
if (initialize)
|
|
{
|
|
*expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
*result = and;
|
|
}
|
|
else
|
|
{
|
|
boolean argument = *value;
|
|
|
|
if (and)
|
|
*result = *result && argument;
|
|
else
|
|
*result = *result || argument;
|
|
}
|
|
|
|
if (*expression_index!=NONE && *result==and)
|
|
{
|
|
hs_evaluate(thread_index, *expression_index, (long *)value);
|
|
*expression_index = hs_syntax_get(*expression_index)->next_node_index;
|
|
}
|
|
else
|
|
{
|
|
/* BUG: only the low byte of this uninitialised long is written, and the whole long is passed to
|
|
hs_return; its upper three bytes are indeterminate. January reuses the initialize argument slot,
|
|
so they are the upper bytes of the dword hs_thread_main pushed for initialize (stack residue from
|
|
hs_thread_main's frame). No January consumer reads a boolean cell beyond its low byte, but
|
|
hs_return and the pass-through forms copy the whole long into hs thread stacks and hs globals, so
|
|
these bytes reach the saved-game CRC and the save files (game_state_write_to_persistent_storage,
|
|
game_state_write_to_file, game_state_write_core). */
|
|
*(boolean *)&result_long = *result;
|
|
|
|
hs_return(thread_index, result_long);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_if(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *condition = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *expression_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *result = hs_stack_allocate(thread_index, sizeof(long));
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x77,
|
|
function_index==_hs_function_if);
|
|
|
|
if (initialize)
|
|
{
|
|
*condition = 0;
|
|
*expression_index = NONE;
|
|
hs_evaluate(thread_index, hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index,
|
|
condition);
|
|
}
|
|
else if (*expression_index==NONE)
|
|
{
|
|
if ((boolean)*condition)
|
|
{
|
|
*expression_index = hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index)->next_node_index;
|
|
}
|
|
else
|
|
{
|
|
*expression_index = hs_syntax_get(hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index)->
|
|
next_node_index)->next_node_index;
|
|
if (*expression_index==NONE)
|
|
{
|
|
hs_return(thread_index, 0);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
hs_evaluate(thread_index, *expression_index, result);
|
|
}
|
|
else
|
|
{
|
|
hs_return(thread_index, *result);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_set(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long variable_expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
struct hs_syntax_node *variable = hs_syntax_get(variable_expression_index);
|
|
short type;
|
|
long global_index;
|
|
|
|
hs_stack_allocate(thread_index, sizeof(long));
|
|
type = hs_global_get_type((short)variable->data);
|
|
|
|
if (initialize)
|
|
{
|
|
if (type==_hs_type_object_list)
|
|
object_list_remove_reference(hs_global_evaluate((short)variable->data));
|
|
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL(variable->data))
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX((short)variable->data);
|
|
else
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX((short)variable->data)+
|
|
hs_external_global_count;
|
|
|
|
hs_evaluate(thread_index,
|
|
hs_syntax_get(variable_expression_index)->next_node_index,
|
|
&((struct hs_global_datum *)datum_get(hs_global_data,
|
|
global_index))->value);
|
|
}
|
|
else
|
|
{
|
|
hs_global_reconcile_write((short)variable->data);
|
|
if (type==_hs_type_object_list)
|
|
object_list_add_reference(hs_global_evaluate((short)variable->data));
|
|
|
|
hs_return(thread_index, hs_global_evaluate((short)variable->data));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_inspect(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
char string[1024];
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *value = hs_stack_allocate(thread_index, sizeof(long));
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x2bc,
|
|
function_index==_hs_function_inspect);
|
|
|
|
if (initialize)
|
|
{
|
|
hs_evaluate(thread_index, hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index,
|
|
value);
|
|
}
|
|
else
|
|
{
|
|
struct hs_syntax_node *expression = hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index);
|
|
|
|
if (hs_type_inspectors[expression->type])
|
|
{
|
|
hs_type_inspectors[expression->type](expression->type, *value, string);
|
|
/* BUG (preserved for exact matching): the inspected value's text is passed as the
|
|
* format (January 0x4bc840 +0xdd..+0xe6), so inspecting a string that contains '%'
|
|
* reads arguments that were never passed. A corrected build should print it through
|
|
* "%s". Source-policy approval pending (2026-09-27 audit). */
|
|
console_printf(FALSE, string);
|
|
}
|
|
|
|
hs_return(thread_index, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_arithmetic(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
short *argument_index = hs_stack_allocate(thread_index, sizeof(short));
|
|
long *expression_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
real *value = hs_stack_allocate(thread_index, sizeof(real));
|
|
real *result = hs_stack_allocate(thread_index, sizeof(real));
|
|
long result_long;
|
|
|
|
if (initialize)
|
|
{
|
|
*argument_index = 0;
|
|
*expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
}
|
|
else
|
|
{
|
|
real argument = *value;
|
|
|
|
if (*argument_index==0)
|
|
{
|
|
*result = argument;
|
|
}
|
|
else
|
|
{
|
|
switch (function_index)
|
|
{
|
|
case _hs_function_plus:
|
|
*result = *result+argument;
|
|
break;
|
|
case _hs_function_minus:
|
|
*result = *result-argument;
|
|
break;
|
|
case _hs_function_times:
|
|
*result = *result*argument;
|
|
break;
|
|
case _hs_function_divide:
|
|
*result = *result/argument;
|
|
break;
|
|
case _hs_function_min:
|
|
*result = MIN(*result, argument);
|
|
break;
|
|
case _hs_function_max:
|
|
*result = MAX(*result, argument);
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x111, TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
*argument_index += 1;
|
|
}
|
|
|
|
if (*expression_index!=NONE)
|
|
{
|
|
hs_evaluate(thread_index, *expression_index, (long *)value);
|
|
*expression_index = hs_syntax_get(*expression_index)->next_node_index;
|
|
}
|
|
else
|
|
{
|
|
*(real *)&result_long = *result;
|
|
|
|
hs_return(thread_index, result_long);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_object_cast_up(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *object_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x2dc,
|
|
function_index>=_hs_function_object_to_unit &&
|
|
function_index<=_hs_function_object_to_unit);
|
|
|
|
if (initialize)
|
|
{
|
|
hs_evaluate(thread_index, hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index, object_index);
|
|
}
|
|
else if (*object_index!=NONE)
|
|
{
|
|
struct object_datum *object = object_get(*object_index);
|
|
short object_type = function_index-_hs_function_inspect;
|
|
|
|
if (TEST_FLAG((short)hs_object_type_masks[object_type], object->object.type))
|
|
{
|
|
hs_return(thread_index, *object_index);
|
|
}
|
|
else
|
|
{
|
|
error(_error_silent, "attempt to convert object %s to type %s",
|
|
tag_get_name(object->definition_index),
|
|
hs_type_names[object_type+_hs_type_object]);
|
|
hs_return(thread_index, NONE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hs_return(thread_index, NONE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_begin_random(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
short *argument_count = hs_stack_allocate(thread_index, sizeof(short));
|
|
long *evaluated = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *result = hs_stack_allocate(thread_index, sizeof(long));
|
|
short random;
|
|
short index;
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x45,
|
|
function_index==_hs_function_begin_random);
|
|
|
|
if (initialize)
|
|
{
|
|
long expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
|
|
*argument_count = 0;
|
|
while (expression_index!=NONE)
|
|
{
|
|
expression_index = hs_syntax_get(expression_index)->next_node_index;
|
|
*argument_count += 1;
|
|
}
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x50,
|
|
*argument_count<LONG_BITS);
|
|
|
|
csmemset(evaluated, 0, BIT_VECTOR_SIZE_IN_BYTES(*argument_count));
|
|
}
|
|
|
|
random = random_range(0, *argument_count);
|
|
for (index = 0; index<*argument_count; index++)
|
|
{
|
|
short choice = (short)((random+index)%*argument_count);
|
|
|
|
if (!BIT_VECTOR_TEST_FLAG(evaluated, choice))
|
|
{
|
|
hs_evaluate(thread_index, hs_syntax_nth(hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index, choice), result);
|
|
BIT_VECTOR_SET_FLAG(evaluated, choice, TRUE);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (index==*argument_count)
|
|
hs_return(thread_index, *result);
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_debug_string(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *expression_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *argument_count = hs_stack_allocate(thread_index, sizeof(long));
|
|
char const **arguments = hs_stack_allocate(thread_index,
|
|
MAXIMUM_HS_DEBUG_STRING_ARGUMENTS*sizeof(char const *));
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x304,
|
|
(function_index>=_hs_function_debug_string__first) &&
|
|
(function_index<=_hs_function_debug_string__last));
|
|
|
|
if (initialize)
|
|
{
|
|
*expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
*argument_count = 0;
|
|
csmemset((void *)arguments, 0,
|
|
MAXIMUM_HS_DEBUG_STRING_ARGUMENTS*sizeof(char const *));
|
|
}
|
|
|
|
if (*expression_index!=NONE && *argument_count<MAXIMUM_HS_DEBUG_STRING_ARGUMENTS)
|
|
{
|
|
long argument;
|
|
|
|
hs_evaluate(thread_index, *expression_index, &argument);
|
|
*expression_index = hs_syntax_get(*expression_index)->next_node_index;
|
|
arguments[*argument_count] = (char const *)argument;
|
|
*argument_count += 1;
|
|
}
|
|
else
|
|
{
|
|
hs_debug_string_procedure procedure;
|
|
|
|
switch (function_index)
|
|
{
|
|
case _hs_function_debug_string__ai_debug_communication_suppress:
|
|
procedure = ai_debug_communication_suppress;
|
|
break;
|
|
case _hs_function_debug_string__ai_debug_communication_ignore:
|
|
procedure = ai_debug_communication_ignore;
|
|
break;
|
|
case _hs_function_debug_string__ai_debug_communication_focus:
|
|
procedure = ai_debug_communication_focus;
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x330, TRUE);
|
|
system_exit(-1);
|
|
procedure = NULL;
|
|
break;
|
|
}
|
|
|
|
if (procedure)
|
|
procedure(*argument_count, arguments);
|
|
|
|
hs_return(thread_index, NONE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_sleep_until(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *condition = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *ticks = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *timeout = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *start_time = hs_stack_allocate(thread_index, sizeof(long));
|
|
short *argument_index = hs_stack_allocate(thread_index, sizeof(short));
|
|
long expression_index = hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index)->next_node_index;
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x1e5,
|
|
function_index==_hs_function_sleep_until);
|
|
|
|
if (initialize)
|
|
{
|
|
*(boolean *)condition = FALSE;
|
|
*start_time = game_time_get();
|
|
*argument_index = 0;
|
|
*(short *)ticks = 30;
|
|
*timeout = NONE;
|
|
|
|
if (expression_index!=NONE)
|
|
{
|
|
hs_evaluate(thread_index, expression_index, ticks);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (*argument_index==0)
|
|
{
|
|
*argument_index = 1;
|
|
if (expression_index!=NONE)
|
|
{
|
|
long timeout_expression_index =
|
|
hs_syntax_get(expression_index)->next_node_index;
|
|
|
|
if (timeout_expression_index!=NONE)
|
|
{
|
|
hs_evaluate(thread_index, timeout_expression_index, timeout);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (*argument_index==1)
|
|
{
|
|
if ((boolean)*condition ||
|
|
(*timeout!=NONE && game_time_get()>=*timeout+*start_time))
|
|
{
|
|
hs_return(thread_index, 0);
|
|
}
|
|
else
|
|
{
|
|
hs_evaluate(thread_index, hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index,
|
|
condition);
|
|
|
|
thread->sleep_until = game_time_get()+MAX(1, (short)*ticks);
|
|
if (*timeout!=NONE)
|
|
{
|
|
thread->sleep_until = MIN(*timeout+*start_time, thread->sleep_until);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void hs_evaluate_sleep(
|
|
short function_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *ticks = hs_stack_allocate(thread_index, sizeof(long));
|
|
long *script_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
short *argument_index = hs_stack_allocate(thread_index, sizeof(short));
|
|
long sleep_thread_index = thread_index;
|
|
|
|
match_assert("c:\\halo\\source\\hs\\hs_library_internal_runtime.h", 0x189,
|
|
function_index==_hs_function_sleep);
|
|
|
|
if (initialize)
|
|
{
|
|
hs_evaluate(thread_index, hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index, ticks);
|
|
*argument_index = 0;
|
|
}
|
|
else
|
|
{
|
|
if (*argument_index==0)
|
|
{
|
|
long expression_index = hs_syntax_get(hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index)->next_node_index;
|
|
|
|
*argument_index += 1;
|
|
if (expression_index!=NONE)
|
|
{
|
|
hs_evaluate(thread_index, expression_index, script_index);
|
|
|
|
return;
|
|
}
|
|
|
|
*script_index = NONE;
|
|
}
|
|
|
|
if (*argument_index!=0)
|
|
{
|
|
short sleep_ticks = (short)*ticks;
|
|
|
|
if (sleep_ticks!=0)
|
|
{
|
|
if ((short)*script_index!=NONE)
|
|
sleep_thread_index = hs_find_thread_by_script((short)*script_index);
|
|
|
|
if (sleep_thread_index!=NONE)
|
|
{
|
|
struct hs_thread_datum *sleep_thread = hs_thread_get(sleep_thread_index);
|
|
long sleep_until;
|
|
|
|
if (sleep_ticks<0)
|
|
sleep_until = NONE-1;
|
|
else
|
|
sleep_until = game_time_get()+sleep_ticks;
|
|
|
|
if (sleep_thread->sleep_until!=NONE)
|
|
{
|
|
if (sleep_thread_index!=thread_index &&
|
|
!TEST_FLAG(sleep_thread->flags, _hs_thread_sleeping_bit))
|
|
{
|
|
SET_FLAG(sleep_thread->flags, _hs_thread_sleeping_bit, TRUE);
|
|
sleep_thread->previous_sleep_until = sleep_thread->sleep_until;
|
|
}
|
|
|
|
hs_thread_get(sleep_thread_index)->sleep_until = sleep_until;
|
|
}
|
|
}
|
|
}
|
|
|
|
hs_return(thread_index, 0);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_thread_main(
|
|
long thread_index)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
struct hs_script *script = NULL;
|
|
|
|
hs_runtime_globals.executing_thread_index = (short)thread_index;
|
|
if (thread->type==_hs_thread_type_script)
|
|
{
|
|
script = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
thread->script_index,
|
|
struct hs_script);
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x2ba, thread_index,
|
|
script->script_type!=_hs_script_static &&
|
|
script->script_type!=_hs_script_stub,
|
|
"found a static script at toplevel.");
|
|
}
|
|
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x2bd, thread_index,
|
|
valid_thread(thread), "corrupted stack.");
|
|
|
|
thread->sleep_until = 0;
|
|
if (thread->stack==(struct hs_stack_frame *)thread->stack_data)
|
|
{
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x2c3, script);
|
|
|
|
thread->stack->size = 0;
|
|
hs_evaluate(thread_index, script->root_expression_index,
|
|
hs_stack_allocate(thread_index, sizeof(long)));
|
|
}
|
|
|
|
while (thread->stack!=(struct hs_stack_frame *)thread->stack_data &&
|
|
thread->sleep_until>=0 &&
|
|
(!game_in_progress() || thread->sleep_until<=game_time_get()) &&
|
|
hs_runtime_globals.initialized)
|
|
{
|
|
struct hs_syntax_node *expression = hs_syntax_get(thread->stack->expression_index);
|
|
boolean initialize = TEST_FLAG(thread->flags, _hs_thread_in_function_call_bit);
|
|
|
|
thread->stack->size = 0;
|
|
SET_FLAG(thread->flags, _hs_thread_in_function_call_bit, FALSE);
|
|
|
|
if (!TEST_FLAG(expression->flags, _hs_syntax_node_script_bit))
|
|
{
|
|
struct hs_function_definition *function = hs_function_get(expression->index);
|
|
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x2d8, function->evaluate);
|
|
|
|
function->evaluate(
|
|
expression->index,
|
|
thread_index,
|
|
initialize);
|
|
}
|
|
else
|
|
{
|
|
hs_script_evaluate(expression->index, thread_index, initialize);
|
|
}
|
|
}
|
|
|
|
if (thread->stack==(struct hs_stack_frame *)thread->stack_data)
|
|
{
|
|
if (thread->type==_hs_thread_type_script)
|
|
{
|
|
if (script->script_type==_hs_script_startup ||
|
|
script->script_type==_hs_script_dormant)
|
|
{
|
|
thread->sleep_until = NONE;
|
|
}
|
|
}
|
|
else if (thread->type==_hs_thread_type_console_command)
|
|
{
|
|
hs_thread_delete(thread_index);
|
|
}
|
|
}
|
|
|
|
hs_runtime_globals.executing_thread_index = NONE;
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_script_evaluate(
|
|
short script_index,
|
|
long thread_index,
|
|
boolean initialize)
|
|
{
|
|
struct hs_script *script = TAG_BLOCK_GET_ELEMENT(
|
|
&global_scenario_get()->hs_scripts,
|
|
script_index,
|
|
struct hs_script);
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *result = hs_stack_allocate(thread_index, sizeof(long));
|
|
|
|
if (initialize)
|
|
hs_evaluate(thread_index, script->root_expression_index, result);
|
|
else
|
|
hs_return(thread_index, *result);
|
|
|
|
return;
|
|
}
|
|
|
|
static void *hs_stack_allocate(
|
|
long thread_index,
|
|
long size)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
struct hs_stack_frame *frame = thread->stack;
|
|
void *result;
|
|
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x37d, thread_index,
|
|
valid_thread(thread), "corrupted stack.");
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x37e, thread_index,
|
|
size, "attempt to allocate zero space from the stack.");
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x37f, thread_index,
|
|
frame->data+frame->size+size<=thread->stack_data+HS_THREAD_STACK_SIZE,
|
|
"stack overflow.");
|
|
|
|
result = frame->data+frame->size;
|
|
frame->size += (short)size;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void hs_evaluate(
|
|
long thread_index,
|
|
long expression_index,
|
|
long *destination)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
struct hs_syntax_node *expression = hs_syntax_get(expression_index);
|
|
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x2ff, thread_index,
|
|
valid_thread(thread), "corrupted stack.");
|
|
match_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x300, destination);
|
|
|
|
if (TEST_FLAG(hs_syntax_get(expression_index)->flags, _hs_syntax_node_primitive_bit))
|
|
{
|
|
if (TEST_FLAG(expression->flags, _hs_syntax_node_global_bit))
|
|
{
|
|
*destination = hs_cast(
|
|
thread_index,
|
|
hs_global_get_type((short)expression->data),
|
|
expression->type,
|
|
hs_global_evaluate((short)expression->data));
|
|
}
|
|
else
|
|
{
|
|
*destination = hs_cast(
|
|
thread_index,
|
|
expression->index,
|
|
expression->type,
|
|
expression->data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
thread->stack->result = destination;
|
|
hs_stack_push(thread_index);
|
|
SET_FLAG(thread->flags, _hs_thread_in_function_call_bit, TRUE);
|
|
thread->stack->expression_index = expression_index;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_global_reconcile_read(
|
|
word global_designator)
|
|
{
|
|
struct hs_global_datum *global;
|
|
struct hs_external_global_definition *external;
|
|
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL(global_designator))
|
|
{
|
|
long global_index;
|
|
|
|
/* the general external/internal index test, repeated inside the external arm, is original (the debug build keeps it) */
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL(global_designator))
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator);
|
|
else
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator)+
|
|
hs_external_global_count;
|
|
global = datum_get(hs_global_data, global_index);
|
|
external = hs_global_external_get(
|
|
HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator));
|
|
|
|
switch (hs_global_get_type(global_designator))
|
|
{
|
|
case _hs_type_boolean:
|
|
*(boolean *)&global->value = external->address
|
|
? *(boolean *)external->address
|
|
: _hs_type_boolean_default;
|
|
break;
|
|
case _hs_type_real:
|
|
*(real *)&global->value = external->address
|
|
? *(real *)external->address
|
|
: _hs_type_real_default;
|
|
break;
|
|
case _hs_type_short_integer:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_short_integer_default;
|
|
break;
|
|
case _hs_type_long_integer:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_long_integer_default;
|
|
break;
|
|
case _hs_type_string:
|
|
*(char const **)&global->value = external->address
|
|
? *(char const * *)external->address
|
|
: _hs_type_string_default;
|
|
break;
|
|
case _hs_type_script:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_script_default;
|
|
break;
|
|
case _hs_type_trigger_volume:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_trigger_volume_default;
|
|
break;
|
|
case _hs_type_cutscene_flag:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_cutscene_flag_default;
|
|
break;
|
|
case _hs_type_cutscene_camera_point:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_cutscene_camera_point_default;
|
|
break;
|
|
case _hs_type_cutscene_title:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_cutscene_title_default;
|
|
break;
|
|
case _hs_type_cutscene_recording:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_cutscene_recording_default;
|
|
break;
|
|
case _hs_type_device_group:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_device_group_default;
|
|
break;
|
|
case _hs_type_ai:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_ai_default;
|
|
break;
|
|
case _hs_type_ai_command_list:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_ai_command_list_default;
|
|
break;
|
|
case _hs_type_starting_profile:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_starting_profile_default;
|
|
break;
|
|
case _hs_type_conversation:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_conversation_default;
|
|
break;
|
|
case _hs_type_navpoint:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_navpoint_default;
|
|
break;
|
|
case _hs_type_hud_message:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_hud_message_default;
|
|
break;
|
|
case _hs_type_object_list:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_object_list_default;
|
|
break;
|
|
case _hs_type_sound:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_sound_default;
|
|
break;
|
|
case _hs_type_effect:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_effect_default;
|
|
break;
|
|
case _hs_type_damage:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_damage_default;
|
|
break;
|
|
case _hs_type_looping_sound:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_looping_sound_default;
|
|
break;
|
|
case _hs_type_animation_graph:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_animation_graph_default;
|
|
break;
|
|
case _hs_type_actor_variant:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_actor_variant_default;
|
|
break;
|
|
case _hs_type_damage_effect:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_damage_effect_default;
|
|
break;
|
|
case _hs_type_object_definition:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_object_definition_default;
|
|
break;
|
|
case _hs_type_enum_game_difficulty:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_enum_game_difficulty_default;
|
|
break;
|
|
case _hs_type_enum_team:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_enum_team_default;
|
|
break;
|
|
case _hs_type_enum_ai_default_state:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_enum_ai_default_state_default;
|
|
break;
|
|
case _hs_type_enum_actor_type:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_enum_actor_type_default;
|
|
break;
|
|
case _hs_type_enum_hud_corner:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_enum_hud_corner_default;
|
|
break;
|
|
case _hs_type_object:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_object_default;
|
|
break;
|
|
case _hs_type_unit:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_unit_default;
|
|
break;
|
|
case _hs_type_vehicle:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_vehicle_default;
|
|
break;
|
|
case _hs_type_weapon:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_weapon_default;
|
|
break;
|
|
case _hs_type_device:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_device_default;
|
|
break;
|
|
case _hs_type_scenery:
|
|
global->value = external->address
|
|
? *(long *)external->address
|
|
: _hs_type_scenery_default;
|
|
break;
|
|
case _hs_type_object_name:
|
|
*(short *)&global->value = external->address
|
|
? *(short *)external->address
|
|
: _hs_type_object_name_default;
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x638, TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void hs_global_reconcile_write(
|
|
word global_designator)
|
|
{
|
|
struct hs_global_datum *global;
|
|
struct hs_external_global_definition *external;
|
|
real value;
|
|
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL(global_designator))
|
|
{
|
|
long global_index;
|
|
|
|
/* the general external/internal index test, repeated inside the external arm, is original (the debug build keeps it) */
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL(global_designator))
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator);
|
|
else
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator)+
|
|
hs_external_global_count;
|
|
global = datum_get(hs_global_data, global_index);
|
|
external = hs_global_external_get(
|
|
HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator));
|
|
|
|
switch (hs_global_get_type(global_designator))
|
|
{
|
|
case _hs_type_boolean:
|
|
if (external->address)
|
|
*(boolean *)external->address = (boolean)global->value;
|
|
break;
|
|
case _hs_type_real:
|
|
value = *(real *)&global->value;
|
|
if (external->address)
|
|
*(real *)external->address = value;
|
|
break;
|
|
case _hs_type_short_integer:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_long_integer:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_string:
|
|
if (external->address)
|
|
*(char const **)external->address = (char const *)global->value;
|
|
break;
|
|
case _hs_type_script:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_trigger_volume:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_cutscene_flag:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_cutscene_camera_point:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_cutscene_title:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_cutscene_recording:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_device_group:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_ai:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_ai_command_list:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_starting_profile:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_conversation:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_navpoint:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_hud_message:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_object_list:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_sound:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_effect:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_damage:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_looping_sound:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_animation_graph:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_actor_variant:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_damage_effect:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_object_definition:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_enum_game_difficulty:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_enum_team:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_enum_ai_default_state:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_enum_actor_type:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_enum_hud_corner:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
case _hs_type_object:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_unit:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_vehicle:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_weapon:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_device:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_scenery:
|
|
if (external->address)
|
|
*(long *)external->address = global->value;
|
|
break;
|
|
case _hs_type_object_name:
|
|
if (external->address)
|
|
*(short *)external->address = (short)global->value;
|
|
break;
|
|
default:
|
|
display_assert(NULL, "c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x671, TRUE);
|
|
system_exit(-1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static long hs_global_evaluate(
|
|
short global_designator)
|
|
{
|
|
long global_index;
|
|
|
|
hs_global_reconcile_read(global_designator);
|
|
if (HS_GLOBAL_DESIGNATOR_IS_EXTERNAL((word)global_designator))
|
|
{
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator);
|
|
}
|
|
else
|
|
{
|
|
global_index = HS_GLOBAL_DESIGNATOR_TO_INDEX(global_designator)+
|
|
hs_external_global_count;
|
|
}
|
|
|
|
return ((struct hs_global_datum *)datum_get(hs_global_data, global_index))->
|
|
value;
|
|
}
|
|
|
|
static long *hs_arguments_evaluate(
|
|
long thread_index,
|
|
short formal_parameter_count,
|
|
short const *formal_parameters,
|
|
boolean initialize)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
long *values = hs_stack_allocate(thread_index, formal_parameter_count*sizeof(long));
|
|
short *argument_index = hs_stack_allocate(thread_index, sizeof(short));
|
|
long *expression_index = hs_stack_allocate(thread_index, sizeof(long));
|
|
|
|
if (initialize)
|
|
{
|
|
*argument_index = 0;
|
|
*expression_index = hs_syntax_get(hs_syntax_get(
|
|
thread->stack->expression_index)->data)->next_node_index;
|
|
}
|
|
|
|
if (*argument_index<formal_parameter_count)
|
|
{
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x3c4, thread_index,
|
|
*expression_index!=NONE, "corrupted syntax tree.");
|
|
|
|
if (hs_syntax_get(*expression_index)->type!=formal_parameters[*argument_index])
|
|
{
|
|
script_error(thread_index, "unexpected actual parameters.",
|
|
"hs_syntax_get(*expression_index)->type==formal_parameters[*argument_index]");
|
|
|
|
return values;
|
|
}
|
|
|
|
hs_evaluate(thread_index, *expression_index, &values[*argument_index]);
|
|
*expression_index = hs_syntax_get(*expression_index)->next_node_index;
|
|
*argument_index += 1;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
match_hs_assert("c:\\halo\\SOURCE\\hs\\hs_runtime.c", 0x3d1, thread_index,
|
|
*expression_index==NONE, "corrupted syntax tree.");
|
|
|
|
return values;
|
|
}
|
|
|
|
static boolean script_error(
|
|
long thread_index,
|
|
char const *reason,
|
|
char const *expression)
|
|
{
|
|
struct hs_thread_datum *thread = hs_thread_get(thread_index);
|
|
|
|
error(_error_silent, "script %s needs to be recompiled. (%s: %s)",
|
|
hs_thread_format(thread_index),
|
|
reason ? reason : "no reason given.",
|
|
expression);
|
|
|
|
return FALSE;
|
|
}
|