-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathConsoleCommands.d
186 lines (159 loc) · 5.73 KB
/
ConsoleCommands.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/***********************************\
CONSOLECOMMANDS
\***********************************/
//========================================
// [intern] Class / Variables
//========================================
class CCItem {
var int fncID;
var string cmd;
};
instance CCItem@(CCItem);
const int _CC_List = 0; // Non-persistent record of all CCs
//========================================
// Check if CC is registered
//========================================
func int CC_Active(var func function) {
if (!_CC_List) {
return FALSE;
};
var int symID; symID = MEM_GetFuncID(function);
// Iterate over all registered CCs
var zCArray a; a = _^(_CC_List);
repeat(i, a.numInArray); var int i;
var CCItem cc; cc = _^(MEM_ReadIntArray(a.array, i));
if (cc.fncID == symID) {
return TRUE;
};
end;
return FALSE;
};
//========================================
// Remove CC
//========================================
func void CC_Remove(var func function) {
if (!_CC_List) {
return;
};
var int symID; symID = MEM_GetFuncID(function);
// Iterate over all registered CCs
var zCArray a; a = _^(_CC_List);
repeat(i, a.numInArray); var int i;
var int ccPtr; ccPtr = MEM_ReadIntArray(a.array, i);
var CCItem cc; cc = _^(ccPtr);
if (cc.fncID == symID) {
MEM_ArrayRemoveIndex(_CC_List, ccPtr);
MEM_Free(ccPtr);
};
end;
};
//========================================
// [intern] Register auto-completion
//========================================
func void CC_AutoComplete(var string commandPrefix, var string description) {
var int descPtr; descPtr = _@s(description);
var int comPtr; comPtr = _@s(commandPrefix);
const int call = 0;
if (CALL_Begin(call)) {
CALL_PtrParam(_@(descPtr));
CALL_PtrParam(_@(comPtr));
CALL__thiscall(_@(zcon_address_lego), zCConsole__Register);
call = CALL_End();
};
};
//========================================
// Register new CC
//========================================
func void CC_Register(var func function, var string commandPrefix, var string description) {
// Remove any left over handles (from LeGo 2.4.0) if they are unarchived from old game saves
if (hasHndl(CCItem@)) {
foreachHndl(CCItem@, _CCItem_deleteHandles);
};
// Only add if not already present
if (CC_Active(function)) {
return;
};
// Check validity of function signature
var int symID; symID = MEM_GetFuncID(function);
var zCPar_Symbol symb; symb = _^(MEM_GetSymbolByIndex(symID));
if ((symb.bitfield & zCPar_Symbol_bitfield_ele) != 1) || (symb.offset != (zPAR_TYPE_STRING >> 12)) {
MEM_Error(ConcatStrings("CONSOLECOMMANDS: Function has to have one parameter and needs to return a string: ",
symb.name));
return;
};
symb = _^(MEM_GetSymbolByIndex(symID+1));
if ((symb.bitfield & zCPar_Symbol_bitfield_type) != zPAR_TYPE_STRING) {
MEM_Error(ConcatStrings("CONSOLECOMMANDS: Function parameter needs to be a string: ", symb.name));
return;
};
// Register auto-completion
commandPrefix = STR_Upper(commandPrefix);
CC_AutoComplete(commandPrefix, description);
// Create CC object
var int ccPtr; ccPtr = create(CCItem@);
var CCItem cc; cc = _^(ccPtr);
cc.fncID = symID;
cc.cmd = commandPrefix;
// Initialize once
if (!_CC_List) {
_CC_List = MEM_ArrayCreate();
};
// Add CC to 'list'
MEM_ArrayInsert(_CC_List, ccPtr);
};
//========================================
// [intern] Engine hook
//========================================
func void _CC_Hook() {
if (!_CC_List) {
return;
};
// Get query entered into console
var int stackOffset; stackOffset = MEMINT_SwitchG1G2(/*2ach*/ 684, /*424h*/ 1060);
var string query; query = MEM_ReadString(MEM_ReadInt(ESP+stackOffset+4));
// Iterate over all registered CCs
var zCArray a; a = _^(_CC_List);
repeat(i, a.numInArray); var int i;
var CCItem cc; cc = _^(MEM_ReadIntArray(a.array, i));
// Check if entered query starts with defined command
if (STR_StartsWith(query, cc.cmd)) {
// Cut off everything after the command
var int cmdLen; cmdLen = STR_Len(cc.cmd);
var int qryLen; qryLen = STR_Len(query);
STR_SubStr(query, cmdLen, qryLen-cmdLen); // Leave on data stack
// Call the CC function (argument already on data stack)
MEM_CallByID(cc.fncID);
var string ret; ret = MEM_PopStringResult();
// If the CC function returns a valid string, stop the loop
// This additional check allows multiple CCs with the same command
if (!Hlp_StrCmp(ret, "")) {
MEM_WriteString(EAX, ret);
break;
};
};
end;
};
//=======================================
// Simple LeGo console command
//=======================================
func string CC_LeGo(var string _) {
var int s; s = SB_New();
SB(LeGO_Version);
SBc(10); SBc(13);
_LeGo_Flags;
MEM_Call(LeGo_FlagsHR);
SB(MEM_PopStringResult());
var string ret; ret = SB_ToString();
SB_Destroy();
return ret;
};
//========================================
// [intern] Old game save compatibility
//========================================
// ConsoleCommands have been rewritten to not be stored to game saves. To ensure compatibility with old game saves from
// LeGo 2.4.0, this function is necessary to prevent errors on unarchiving, because 'fncID' was stored as string.
func void CCItem_Unarchiver(var CCItem this) {};
func int _CCItem_deleteHandles(var int hndl) {
delete(hndl);
return rContinue;
};