| 1 | /** |
| 2 | * @fileoverview Script Compiler / Decompiler / Runner |
| 3 | * @author Ylian Saint-Hilaire |
| 4 | * @copyright Intel Corporation 2018-2022 |
| 5 | * @license Apache-2.0 |
| 6 | * @version v0.1.0e |
| 7 | */ |
| 8 | |
| 9 | /*jslint node: true */ |
| 10 | /*jshint node: true */ |
| 11 | /*jshint strict:false */ |
| 12 | /*jshint -W097 */ |
| 13 | /*jshint esversion: 6 */ |
| 14 | "use strict"; |
| 15 | |
| 16 | module.exports.CreateAmtScriptEngine = function () { |
| 17 | var o = {}; |
| 18 | |
| 19 | // Core functions |
| 20 | const script_functionTable1 = ['nop', 'jump', 'set', 'print', 'dialog', 'getitem', 'substr', 'indexof', 'split', 'join', 'length', 'jsonparse', 'jsonstr', 'add', 'substract', 'parseint', 'wsbatchenum', 'wsput', 'wscreate', 'wsdelete', 'wsexec', 'scriptspeed', 'wssubscribe', 'wsunsubscribe', 'readchar', 'signwithdummyca']; |
| 21 | |
| 22 | // functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6) |
| 23 | const script_functionTable2 = ['encodeuri', 'decodeuri', 'passwordcheck', 'atob', 'btoa', 'hex2str', 'str2hex', 'random', 'md5', 'maketoarray', 'readshort', 'readshortx', 'readint', 'readsint', 'readintx', 'shorttostr', 'shorttostrx', 'inttostr', 'inttostrx']; |
| 24 | |
| 25 | // functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6) |
| 26 | //script_functionTableX2 = [encodeURI, decodeURI, passwordcheck, window.atob.bind(window), window.btoa.bind(window), hex2rstr, rstr2hex, random, rstr_md5, MakeToArray, ReadShort, ReadShortX, ReadInt, ReadSInt, ReadIntX, ShortToStr, ShortToStrX, IntToStr, IntToStrX]; |
| 27 | |
| 28 | // Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6) |
| 29 | const script_functionTable3 = ['pullsystemstatus', 'pulleventlog', 'pullauditlog', 'pullcertificates', 'pullwatchdog', 'pullsystemdefense', 'pullhardware', 'pulluserinfo', 'pullremoteaccess', 'highlightblock', 'disconnect', 'getsidstring', 'getsidbytearray']; |
| 30 | |
| 31 | /* |
| 32 | // Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6) |
| 33 | script_functionTableX3 = [ |
| 34 | PullSystemStatus |
| 35 | , |
| 36 | // ###BEGIN###{EventLog} |
| 37 | PullEventLog |
| 38 | // ###END###{EventLog} |
| 39 | , |
| 40 | // ###BEGIN###{AuditLog} |
| 41 | PullAuditLog |
| 42 | // ###END###{AuditLog} |
| 43 | , |
| 44 | // ###BEGIN###{Certificates} |
| 45 | PullCertificates |
| 46 | // ###END###{Certificates} |
| 47 | , |
| 48 | // ###BEGIN###{AgentPresence} |
| 49 | PullWatchdog |
| 50 | // ###END###{AgentPresence} |
| 51 | , |
| 52 | // ###BEGIN###{SystemDefense} |
| 53 | PullSystemDefense |
| 54 | // ###END###{SystemDefense} |
| 55 | , |
| 56 | // ###BEGIN###{HardwareInfo} |
| 57 | PullHardware |
| 58 | // ###END###{HardwareInfo} |
| 59 | , |
| 60 | PullUserInfo |
| 61 | , |
| 62 | // ###BEGIN###{RemoteAccess} |
| 63 | PullRemoteAccess |
| 64 | // ###END###{RemoteAccess} |
| 65 | , |
| 66 | // ###BEGIN###{Scripting-Editor} |
| 67 | script_HighlightBlock |
| 68 | // ###END###{Scripting-Editor} |
| 69 | , |
| 70 | // ###BEGIN###{ComputerSelector} |
| 71 | disconnect |
| 72 | // ###END###{ComputerSelector} |
| 73 | , |
| 74 | function (runner, x) { return GetSidString(x); } |
| 75 | , |
| 76 | function (runner, x) { return GetSidByteArray(x); } |
| 77 | ]; |
| 78 | |
| 79 | // Setup the script state |
| 80 | o.script_setup = function(binary, startvars) { |
| 81 | var obj = { startvars: startvars }; |
| 82 | if (binary.length < 6) { console.error('Invalid script length'); return null; } // Script must have at least 6 byte header |
| 83 | if (ReadInt(binary, 0) != 0x247D2945) { console.error('Invalid binary script'); return null; } // Check the script magic header |
| 84 | if (ReadShort(binary, 4) > 1) { console.error('Unsupported script version'); return null; } // Check the script version |
| 85 | obj.script = binary.substring(6); |
| 86 | // obj.onStep; |
| 87 | // obj.onConsole; |
| 88 | |
| 89 | // Reset the script to the start |
| 90 | obj.reset = function (stepspeed) { |
| 91 | obj.stop(); |
| 92 | obj.ip = 0; |
| 93 | obj.variables = startvars; |
| 94 | obj.state = 1; |
| 95 | } |
| 96 | |
| 97 | // Start the script |
| 98 | obj.start = function (stepspeed) { |
| 99 | obj.stop(); |
| 100 | obj.stepspeed = stepspeed; |
| 101 | if (stepspeed > 0) { obj.timer = setInterval(function () { obj.step() }, stepspeed); } |
| 102 | } |
| 103 | |
| 104 | // Stop the script |
| 105 | obj.stop = function () { |
| 106 | if (obj.timer != null) { clearInterval(obj.timer); } |
| 107 | obj.timer = null; |
| 108 | obj.stepspeed = 0; |
| 109 | } |
| 110 | |
| 111 | // function used to load and store variable values |
| 112 | obj.getVar = function (name) { if (name == undefined) return undefined; return obj.getVarEx(name.split('.'), obj.variables); } |
| 113 | obj.getVarEx = function (name, val) { try { if (name == undefined) return undefined; if (name.length == 0) return val; return obj.getVarEx(name.slice(1), val[name[0]]); } catch (e) { return null; } } |
| 114 | obj.setVar = function (name, val) { obj.setVarEx(name.split('.'), obj.variables, val); } |
| 115 | obj.setVarEx = function (name, vars, val) { if (name.length == 1) { vars[name[0]] = val; } else { obj.setVarEx(name.slice(1), vars[name[0]], val); } } |
| 116 | |
| 117 | // Run the script one step forward |
| 118 | obj.step = function () { |
| 119 | if (obj.state != 1) return; |
| 120 | if (obj.ip < obj.script.length) { |
| 121 | var cmdid = ReadShort(obj.script, obj.ip); |
| 122 | var cmdlen = ReadShort(obj.script, obj.ip + 2); |
| 123 | var argcount = ReadShort(obj.script, obj.ip + 4); |
| 124 | var argptr = obj.ip + 6; |
| 125 | var args = []; |
| 126 | |
| 127 | // Clear all temp variables (This is optional) |
| 128 | for (var i in obj.variables) { if (i.startsWith('__')) { delete obj.variables[i]; } } |
| 129 | |
| 130 | // Loop on each argument, moving forward by the argument length each time |
| 131 | for (var i = 0; i < argcount; i++) { |
| 132 | var arglen = ReadShort(obj.script, argptr); |
| 133 | var argval = obj.script.substring(argptr + 2, argptr + 2 + arglen); |
| 134 | var argtyp = argval.charCodeAt(0); |
| 135 | argval = argval.substring(1); |
| 136 | if (argtyp < 2) { |
| 137 | // Get the value and replace all {var} with variable values |
| 138 | while (argval.split("{").length > 1) { var t = argval.split("{").pop().split("}").shift(); argval = argval.replace('{' + t + '}', obj.getVar(t)); } |
| 139 | if (argtyp == 1) { obj.variables['__' + i] = decodeURI(argval); argval = '__' + i; } // If argtyp is 1, this is a literal. Store in temp variable. |
| 140 | args.push(argval); |
| 141 | } |
| 142 | if (argtyp == 2 || argtyp == 3) { |
| 143 | obj.variables['__' + i] = ReadSInt(argval, 0); |
| 144 | args.push('__' + i); |
| 145 | } |
| 146 | argptr += (2 + arglen); |
| 147 | } |
| 148 | |
| 149 | // Move instruction pointer forward by command size |
| 150 | obj.ip += cmdlen; |
| 151 | |
| 152 | // Get all variable values |
| 153 | var argsval = []; |
| 154 | for (var i = 0; i < 10; i++) { argsval.push(obj.getVar(args[i])); } |
| 155 | var storeInArg0; |
| 156 | |
| 157 | try { |
| 158 | if (cmdid < 10000) { |
| 159 | // Lets run the actual command |
| 160 | switch (cmdid) { |
| 161 | case 0: // nop |
| 162 | break; |
| 163 | case 1: // jump(label) or jump(label, a, compare, b) |
| 164 | if (argsval[2]) { |
| 165 | if ( |
| 166 | (argsval[2] == '<' && argsval[1] < argsval[3]) || |
| 167 | (argsval[2] == '<=' && argsval[1] <= argsval[3]) || |
| 168 | (argsval[2] == '!=' && argsval[1] != argsval[3]) || |
| 169 | (argsval[2] == '=' && argsval[1] == argsval[3]) || |
| 170 | (argsval[2] == '>=' && argsval[1] >= argsval[3]) || |
| 171 | (argsval[2] == '>' && argsval[1] > argsval[3]) |
| 172 | ) { obj.ip = argsval[0]; } |
| 173 | } else { |
| 174 | obj.ip = argsval[0]; // Set the instruction pointer to the new location in the script |
| 175 | } |
| 176 | break; |
| 177 | case 2: // set(variable, value) |
| 178 | if (args[1] == undefined) delete obj.variables[args[0]]; else obj.setVar(args[0], argsval[1]); |
| 179 | break; |
| 180 | case 3: // print(message) |
| 181 | if (obj.onConsole) { obj.onConsole(obj.toString(argsval[0]), obj); } else { console.log(obj.toString(argsval[0])); } |
| 182 | // Q(obj.consoleid).value += () + '\n'); Q(obj.console).scrollTop = Q(obj.console).scrollHeight; |
| 183 | break; |
| 184 | case 4: // dialog(title, content, buttons) |
| 185 | obj.state = 2; |
| 186 | obj.dialog = true; |
| 187 | setDialogMode(11, argsval[0], argsval[2], obj.xxStepDialogOk, argsval[1], obj); |
| 188 | break; |
| 189 | case 5: // getitem(a, b, c) |
| 190 | for (var i in argsval[1]) { if (argsval[1][i][argsval[2]] == argsval[3]) { storeInArg0 = i; } }; |
| 191 | break; |
| 192 | case 6: // substr(variable_dest, variable_src, index, len) |
| 193 | storeInArg0 = argsval[1].substr(argsval[2], argsval[3]); |
| 194 | break; |
| 195 | case 7: // indexOf(variable_dest, variable_src, index, len) |
| 196 | storeInArg0 = argsval[1].indexOf(argsval[2]); |
| 197 | break; |
| 198 | case 8: // split(variable_dest, variable_src, separator) |
| 199 | storeInArg0 = argsval[1].split(argsval[2]); |
| 200 | break; |
| 201 | case 9: // join(variable_dest, variable_src, separator) |
| 202 | storeInArg0 = argsval[1].join(argsval[2]); |
| 203 | break; |
| 204 | case 10: // length(variable_dest, variable_src) |
| 205 | storeInArg0 = argsval[1].length; |
| 206 | break; |
| 207 | case 11: // jsonparse(variable_dest, json) |
| 208 | storeInArg0 = JSON.parse(argsval[1]); |
| 209 | break; |
| 210 | case 12: // jsonstr(variable_dest, variable_src) |
| 211 | storeInArg0 = JSON.stringify(argsval[1]); |
| 212 | break; |
| 213 | case 13: // add(variable_dest, variable_src, value) |
| 214 | storeInArg0 = (argsval[1] + argsval[2]); |
| 215 | break; |
| 216 | case 14: // substract(variable_dest, variable_src, value) |
| 217 | storeInArg0 = (argsval[1] - argsval[2]); |
| 218 | break; |
| 219 | case 15: // parseInt(variable_dest, variable_src) |
| 220 | storeInArg0 = parseInt(argsval[1]); |
| 221 | break; |
| 222 | case 16: // wsbatchenum(name, objectList) |
| 223 | obj.state = 2; |
| 224 | obj.amtstack.BatchEnum(argsval[0], argsval[1], obj.xxWsmanReturn, obj); |
| 225 | break; |
| 226 | case 17: // wsput(name, args) |
| 227 | obj.state = 2; |
| 228 | obj.amtstack.Put(argsval[0], argsval[1], obj.xxWsmanReturn, obj); |
| 229 | break; |
| 230 | case 18: // wscreate(name, args) |
| 231 | obj.state = 2; |
| 232 | obj.amtstack.Create(argsval[0], argsval[1], obj.xxWsmanReturn, obj); |
| 233 | break; |
| 234 | case 19: // wsdelete(name, args) |
| 235 | obj.state = 2; |
| 236 | obj.amtstack.Delete(argsval[0], argsval[1], obj.xxWsmanReturn, obj); |
| 237 | break; |
| 238 | case 20: // wsexec(name, method, args, selectors) |
| 239 | obj.state = 2; |
| 240 | obj.amtstack.Exec(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3]); |
| 241 | break; |
| 242 | case 21: // Script Speed |
| 243 | obj.stepspeed = argsval[0]; |
| 244 | if (obj.timer != null) { clearInterval(obj.timer); obj.timer = setInterval(function () { obj.step() }, obj.stepspeed); } |
| 245 | break; |
| 246 | case 22: // wssubscribe(name, delivery, url, selectors, opaque, user, pass) |
| 247 | obj.state = 2; |
| 248 | obj.amtstack.Subscribe(argsval[0], argsval[1], argsval[2], obj.xxWsmanReturn, obj, 0, argsval[3], argsval[4], argsval[5], argsval[6]); |
| 249 | break; |
| 250 | case 23: // wsunsubscribe(name, selectors) |
| 251 | obj.state = 2; |
| 252 | obj.amtstack.UnSubscribe(argsval[0], obj.xxWsmanReturn, obj, 0, argsval[1]); |
| 253 | break; |
| 254 | case 24: // readchar(str, pos) |
| 255 | console.log(argsval[1], argsval[2], argsval[1].charCodeAt(argsval[2])); |
| 256 | storeInArg0 = argsval[1].charCodeAt(argsval[2]); |
| 257 | break; |
| 258 | case 25: // signWithDummyCa |
| 259 | // ###BEGIN###{Certificates} |
| 260 | obj.state = 2; |
| 261 | // DERKey, xxCaPrivateKey, certattributes, issuerattributes |
| 262 | amtcert_signWithCaKey(argsval[0], null, argsval[1], { 'CN': 'Untrusted Root Certificate' }, obj.xxSignWithDummyCaReturn); |
| 263 | // ###END###{Certificates} |
| 264 | break; |
| 265 | default: { |
| 266 | obj.state = 9; |
| 267 | console.error("Script Error, unknown command: " + cmdid); |
| 268 | } |
| 269 | } |
| 270 | } else { |
| 271 | if (cmdid < 20000) { |
| 272 | // functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6) |
| 273 | storeInArg0 = script_functionTableX2[cmdid - 10000](argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]); |
| 274 | } else { |
| 275 | // Optional functions of type ARG1 = func(ARG2, ARG3, ARG4, ARG5, ARG6) |
| 276 | if (script_functionTableX3 && script_functionTableX3[cmdid - 20000]) { |
| 277 | storeInArg0 = script_functionTableX3[cmdid - 20000](obj, argsval[1], argsval[2], argsval[3], argsval[4], argsval[5], argsval[6]); // Note that optional calls start with "obj" as first argument. |
| 278 | } |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | if (storeInArg0 != undefined) obj.setVar(args[0], storeInArg0); |
| 283 | } catch (e) { |
| 284 | if (typeof e == 'object') { e = e.message; } |
| 285 | obj.setVar('_exception', e); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | if (obj.state == 1 && obj.ip >= obj.script.length) { obj.state = 0; obj.stop(); } |
| 290 | if (obj.onStep) obj.onStep(obj); |
| 291 | return obj; |
| 292 | }; |
| 293 | |
| 294 | obj.xxStepDialogOk = function (button) { |
| 295 | obj.variables['DialogSelect'] = button; |
| 296 | obj.state = 1; |
| 297 | obj.dialog = false; |
| 298 | if (obj.onStep) obj.onStep(obj); |
| 299 | }; |
| 300 | |
| 301 | // ###BEGIN###{**ClosureAdvancedMode} |
| 302 | obj.xxWsmanReturnFix = function (x) { |
| 303 | if (!x || x == null) return; |
| 304 | if (x.Header) { x['Header'] = x.Header; delete x.Header; } |
| 305 | if (x.Body) { x['Body'] = x.Body; delete x.Body; } |
| 306 | if (x.Responses) { x['Responses'] = x.Responses; delete x.Responses; } |
| 307 | if (x.Response) { x['Response'] = x.Response; delete x.Response; } |
| 308 | if (x.ReturnValueStr) { x['ReturnValueStr'] = x.ReturnValueStr; delete x.ReturnValueStr; } |
| 309 | }; |
| 310 | // ###END###{**ClosureAdvancedMode} |
| 311 | |
| 312 | obj.xxWsmanReturn = function (stack, name, responses, status) { |
| 313 | // ###BEGIN###{**ClosureAdvancedMode} |
| 314 | // This is required when Google Closure is used |
| 315 | if (responses) { |
| 316 | obj.xxWsmanReturnFix(responses); |
| 317 | for (var i in responses) { |
| 318 | obj.xxWsmanReturnFix(responses[i]); |
| 319 | for (var j in responses[i]) { obj.xxWsmanReturnFix(responses[i][j]); } |
| 320 | } |
| 321 | } |
| 322 | // ###END###{**ClosureAdvancedMode} |
| 323 | obj.setVar(name, responses); |
| 324 | obj.setVar('wsman_result', status); |
| 325 | obj.setVar('wsman_result_str', ((httpErrorTable[status]) ? (httpErrorTable[status]) : ('Error #' + status))); |
| 326 | obj.state = 1; |
| 327 | if (obj.onStep) obj.onStep(obj); |
| 328 | }; |
| 329 | |
| 330 | // ###BEGIN###{Certificates} |
| 331 | obj.xxSignWithDummyCaReturn = function (cert) { |
| 332 | obj.setVar('signed_cert', btoa(_arrayBufferToString(cert))); |
| 333 | obj.state = 1; |
| 334 | if (obj.onStep) obj.onStep(obj); |
| 335 | }; |
| 336 | // ###END###{Certificates} |
| 337 | |
| 338 | obj.toString = function (x) { if (typeof x == 'object') return JSON.stringify(x); return x; }; |
| 339 | |
| 340 | obj.reset(); |
| 341 | return obj; |
| 342 | } |
| 343 | */ |
| 344 | |
| 345 | var ReadShort = function (v, p) { return (v.charCodeAt(p) << 8) + v.charCodeAt(p + 1); }; |
| 346 | var ReadShortX = function (v, p) { return (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }; |
| 347 | var ReadInt = function (v, p) { return (v.charCodeAt(p) * 0x1000000) + (v.charCodeAt(p + 1) << 16) + (v.charCodeAt(p + 2) << 8) + v.charCodeAt(p + 3); }; // We use "*0x1000000" instead of "<<24" because the shift converts the number to signed int32. |
| 348 | var ReadIntX = function (v, p) { return (v.charCodeAt(p + 3) * 0x1000000) + (v.charCodeAt(p + 2) << 16) + (v.charCodeAt(p + 1) << 8) + v.charCodeAt(p); }; |
| 349 | var ShortToStr = function (v) { return String.fromCharCode((v >> 8) & 0xFF, v & 0xFF); }; |
| 350 | var ShortToStrX = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF); }; |
| 351 | var IntToStr = function (v) { return String.fromCharCode((v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); }; |
| 352 | var IntToStrX = function (v) { return String.fromCharCode(v & 0xFF, (v >> 8) & 0xFF, (v >> 16) & 0xFF, (v >> 24) & 0xFF); }; |
| 353 | |
| 354 | // Argument types: 0 = Variable, 1 = String, 2 = Integer, 3 = Label |
| 355 | o.script_compile = function (script, onmsg) { |
| 356 | var r = '', scriptlines = script.split('\n'), labels = {}, labelswap = [], swaps = []; |
| 357 | // Go thru each script line and encode it |
| 358 | for (var i in scriptlines) { |
| 359 | var scriptline = scriptlines[i]; |
| 360 | if (scriptline.startsWith('##SWAP ')) { var x = scriptline.split(' '); if (x.length == 3) { swaps[x[1]] = x[2]; } } // Add a swap instance |
| 361 | if (scriptline[0] == '#' || scriptline.length == 0) continue; // Skip comments & blank lines |
| 362 | for (var x in swaps) { scriptline = scriptline.split(x).join(swaps[x]); } // Apply all swaps |
| 363 | var keywords = scriptline.match(/"[^"]*"|[^\s"]+/g); |
| 364 | if ((keywords == null) || (keywords.length == 0)) continue; // Skip blank lines |
| 365 | if (scriptline[0] == ':') { labels[keywords[0].toUpperCase()] = r.length; continue; } // Mark a label position |
| 366 | var funcIndex = script_functionTable1.indexOf(keywords[0].toLowerCase()); |
| 367 | if (funcIndex == -1) { funcIndex = script_functionTable2.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 10000; } |
| 368 | if (funcIndex == -1) { funcIndex = script_functionTable3.indexOf(keywords[0].toLowerCase()); if (funcIndex >= 0) funcIndex += 20000; } // Optional methods |
| 369 | if (funcIndex == -1) { if (onmsg) { onmsg("Unabled to compile, unknown command: " + keywords[0]); } return ''; } |
| 370 | // Encode CommandId, CmdSize, ArgCount, Arg1Len, Arg1, Arg2Len, Arg2... |
| 371 | var cmd = ShortToStr(keywords.length - 1); |
| 372 | for (var j in keywords) { |
| 373 | if (j == 0) continue; |
| 374 | if (keywords[j][0] == ':') { |
| 375 | labelswap.push([keywords[j], r.length + cmd.length + 7]); // Add a label swap |
| 376 | cmd += ShortToStr(5) + String.fromCharCode(3) + IntToStr(0xFFFFFFFF); // Put an empty label |
| 377 | } else { |
| 378 | var argint = parseInt(keywords[j]); |
| 379 | if (argint == keywords[j]) { |
| 380 | cmd += ShortToStr(5) + String.fromCharCode(2) + IntToStr(argint); |
| 381 | } else { |
| 382 | if (keywords[j][0] == '"' && keywords[j][keywords[j].length - 1] == '"') { |
| 383 | cmd += ShortToStr(keywords[j].length - 1) + String.fromCharCode(1) + keywords[j].substring(1, keywords[j].length - 1); |
| 384 | } else { |
| 385 | cmd += ShortToStr(keywords[j].length + 1) + String.fromCharCode(0) + keywords[j]; |
| 386 | } |
| 387 | } |
| 388 | } |
| 389 | } |
| 390 | cmd = ShortToStr(funcIndex) + ShortToStr(cmd.length + 4) + cmd; |
| 391 | r += cmd; |
| 392 | } |
| 393 | // Perform all the needed label swaps |
| 394 | for (i in labelswap) { |
| 395 | var label = labelswap[i][0].toUpperCase(), position = labelswap[i][1], target = labels[label]; |
| 396 | if (target == undefined) { if (onmsg) { onmsg("Unabled to compile, unknown label: " + label); } return ''; } |
| 397 | r = r.substr(0, position) + IntToStr(target) + r.substr(position + 4); |
| 398 | } |
| 399 | return IntToStr(0x247D2945) + ShortToStr(1) + r; |
| 400 | }; |
| 401 | |
| 402 | // Decompile the script, intended for debugging only |
| 403 | o.script_decompile = function (binary, onecmd) { |
| 404 | var r = '', ptr = 6, labels = {}; |
| 405 | if (onecmd >= 0) { |
| 406 | ptr = onecmd; // If we are decompiling just one command, set the ptr to that command. |
| 407 | } else { |
| 408 | if (binary.length < 6) { return '# Invalid script length'; } |
| 409 | var magic = ReadInt(binary, 0); |
| 410 | var version = ReadShort(binary, 4); |
| 411 | if (magic != 0x247D2945) { return '# Invalid binary script: ' + magic; } |
| 412 | if (version != 1) { return '# Invalid script version'; } |
| 413 | } |
| 414 | // Loop on each command, moving forward by the command length each time. |
| 415 | while (ptr < binary.length) { |
| 416 | var cmdid = ReadShort(binary, ptr); |
| 417 | var cmdlen = ReadShort(binary, ptr + 2); |
| 418 | var argcount = ReadShort(binary, ptr + 4); |
| 419 | var argptr = ptr + 6; |
| 420 | var argstr = ''; |
| 421 | if (!(onecmd >= 0)) { r += ":label" + (ptr - 6) + "\n"; } |
| 422 | // Loop on each argument, moving forward by the argument length each time |
| 423 | for (var i = 0; i < argcount; i++) { |
| 424 | var arglen = ReadShort(binary, argptr); |
| 425 | var argval = binary.substring(argptr + 2, argptr + 2 + arglen); |
| 426 | var argtyp = argval.charCodeAt(0); |
| 427 | if (argtyp == 0) { argstr += ' ' + argval.substring(1); } // Variable |
| 428 | else if (argtyp == 1) { argstr += ' \"' + argval.substring(1) + '\"'; } // String |
| 429 | else if (argtyp == 2) { argstr += ' ' + ReadInt(argval, 1); } // Integer |
| 430 | else if (argtyp == 3) { // Label |
| 431 | var target = ReadInt(argval, 1); |
| 432 | var label = labels[target]; |
| 433 | if (!label) { label = ":label" + target; labels[label] = target; } |
| 434 | argstr += ' ' + label; |
| 435 | } |
| 436 | argptr += (2 + arglen); |
| 437 | } |
| 438 | // Go in the script function table to decode the function |
| 439 | if (cmdid < 10000) { |
| 440 | r += script_functionTable1[cmdid] + argstr + "\n"; |
| 441 | } else { |
| 442 | if (cmdid >= 20000) { |
| 443 | r += script_functionTable3[cmdid - 20000] + argstr + "\n"; // Optional methods |
| 444 | } else { |
| 445 | r += script_functionTable2[cmdid - 10000] + argstr + "\n"; |
| 446 | } |
| 447 | } |
| 448 | ptr += cmdlen; |
| 449 | if (onecmd >= 0) return r; // If we are decompiling just one command, exit now |
| 450 | } |
| 451 | // Remove all unused labels |
| 452 | var scriptlines = r.split('\n'); |
| 453 | r = ''; |
| 454 | for (var i in scriptlines) { |
| 455 | var line = scriptlines[i]; |
| 456 | if (line[0] != ':') { r += line + '\n'; } else { if (labels[line]) { r += line + '\n'; } } |
| 457 | } |
| 458 | return r; |
| 459 | }; |
| 460 | |
| 461 | // Convert the list of blocks into a script that can be compiled |
| 462 | o.script_blocksToScript = function (script_BuildingBlocks, script_BlockScript) { |
| 463 | var script = ''; |
| 464 | if (script_BuildingBlocks) { |
| 465 | if (script_BuildingBlocks['_start']) { script += '##### Starting Block #####\r\n' + script_BuildingBlocks['_start']['code'] + '\r\n\r\n'; } |
| 466 | for (var i in script_BlockScript) { |
| 467 | var code = script_BlockScript[i]['code']; |
| 468 | code = code.split("%%%~%%%").join(i); |
| 469 | for (var j in script_BlockScript[i]['vars']) { code = code.split("%%%" + j + "%%%").join(script_BlockScript[i]['vars'][j]['value']); } |
| 470 | script += '##### Block: ' + script_BlockScript[i]['name'] + ' #####\r\nHighlightBlock __t ' + i + '\r\n' + code + '\r\n\r\n'; |
| 471 | } |
| 472 | if (script_BuildingBlocks['_end']) { script += '##### Ending Block #####\r\n' + script_BuildingBlocks['_end']['code'] + '\r\nHighlightBlock\r\n'; } |
| 473 | } |
| 474 | return script; |
| 475 | }; |
| 476 | |
| 477 | return o; |
| 478 | }; |