forked from UnderminersTeam/Underanalyzer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVMData.cs
809 lines (695 loc) · 26.9 KB
/
VMData.cs
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
using System;
namespace Underanalyzer;
/// <summary>
/// Represents a single code entry, as seen in a game's data file.
/// </summary>
public interface IGMCode
{
/// <summary>
/// Name of the code entry.
/// </summary>
public IGMString Name { get; }
/// <summary>
/// Length of the code entry's VM instructions, in bytes.
/// </summary>
public int Length { get; }
/// <summary>
/// Gets an instruction at the specified index, in this code entry.
/// </summary>
public IGMInstruction GetInstruction(int index);
/// <summary>
/// Returns the number of instructions in this code entry.
/// </summary>
public int InstructionCount { get; }
/// <summary>
/// Returns the offset within the instructions (in bytes) from which this code entry begins.
/// </summary>
public int StartOffset { get; }
/// <summary>
/// Parent code entry, if this is a sub-function entry. Otherwise, if a root code entry, this is <see langword="null"/>.
/// </summary>
public IGMCode? Parent { get; }
/// <summary>
/// Gets a child code entry at the specified index.
/// </summary>
public IGMCode GetChild(int index);
/// <summary>
/// Returns the number of children of this code entry. If this is a sub-function entry, this is <c>0</c>.
/// </summary>
public int ChildCount { get; }
/// <summary>
/// The number of arguments this code entry takes in. Expected to be <c>0</c> for root entries.
/// </summary>
public int ArgumentCount { get; }
/// <summary>
/// The number of local variables this code entry uses. Root entries tend to include an additional 1 for <c>arguments</c>.
/// </summary>
public int LocalCount { get; }
}
/// <summary>
/// Represents a single instruction within a code entry.
/// </summary>
public interface IGMInstruction
{
/// <summary>
/// Mnemonic attribute used for opcodes.
/// </summary>
/// <param name="mnemonic">A unique shorthand identifier.</param>
[AttributeUsage(AttributeTargets.Field)]
public class OpcodeInfo(string mnemonic) : Attribute
{
/// <summary>
/// Unique shorthand identifier used for this opcode.
/// </summary>
public string Mnemonic { get; } = mnemonic;
}
/// <summary>
/// Mnemonic attribute used for VM data types.
/// </summary>
/// <param name="mnemonic">A unique character to represent this type.</param>
/// <param name="size">How many bytes the type takes on the VM stack.</param>
[AttributeUsage(AttributeTargets.Field)]
public class DataTypeInfo(char mnemonic, int size) : Attribute
{
/// <summary>
/// Unique character used to represent this data type.
/// </summary>
public char Mnemonic { get; } = mnemonic;
/// <summary>
/// Size in bytes taken on the VM stack.
/// </summary>
public int Size { get; } = size;
}
/// <summary>
/// The opcode types used by an instruction.
/// </summary>
public enum Opcode : byte
{
/// <summary>
/// Converts the top of the stack from one type to another.
/// Mnemonic: "conv"
/// </summary>
[OpcodeInfo("conv")]
Convert = 0x07,
/// <summary>
/// Pops two values from the stack, multiplies them, and pushes the result.
/// Mnemonic: "mul"
/// </summary>
[OpcodeInfo("mul")]
Multiply = 0x08,
/// <summary>
/// Pops two values from the stack, divides them, and pushes the result.
/// Mnemonic: "div"
/// </summary>
/// <remarks>The second popped value is divided by the first popped value.</remarks>
[OpcodeInfo("div")]
Divide = 0x09,
/// <summary>
/// Pops two values from the stack, performs a GML "div" operation (division with remainder), and pushes the result.
/// Mnemonic: "rem"
/// </summary>
/// <remarks>The second popped value is divided (with remainder) by the first popped value.</remarks>
[OpcodeInfo("rem")]
GMLDivRemainder = 0x0A,
/// <summary>
/// Pops two values from the stack, performs a GML "mod"/% operation, and pushes the result.
/// Mnemonic: "mod"
/// </summary>
/// <remarks>The second popped value is modulo'd against the first popped value.</remarks>
[OpcodeInfo("mod")]
GMLModulo = 0x0B,
/// <summary>
/// Pops two values from the stack, adds them, and pushes the result.
/// Mnemonic: "add"
/// </summary>
[OpcodeInfo("add")]
Add = 0x0C,
/// <summary>
/// Pops two values from the stack, subtracts them, and pushes the result.
/// Mnemonic: "sub"
/// </summary>
/// <remarks>The second popped value is subtracted by the first popped value.</remarks>
[OpcodeInfo("sub")]
Subtract = 0x0D,
/// <summary>
/// Pops two values from the stack, performs an AND operation, and pushes the result.
/// This can be done bitwise or logically.
/// Mnemonic: "and"
/// </summary>
[OpcodeInfo("and")]
And = 0x0E,
/// <summary>
/// Pops two values from the stack, performs an OR operation, and pushes the result.
/// This can be done bitwise or logically.
/// Mnemonic: "or"
/// </summary>
[OpcodeInfo("or")]
Or = 0x0F,
/// <summary>
/// Pops two values from the stack, performs an XOR operation, and pushes the result.
/// This can be done bitwise or logically.
/// Mnemonic: "xor"
/// </summary>
[OpcodeInfo("xor")]
Xor = 0x10,
/// <summary>
/// Negates the top value of the stack.
/// Mnemonic: "neg"
/// </summary>
[OpcodeInfo("neg")]
Negate = 0x11,
/// <summary>
/// Performs a boolean NOT operation on the top value of the stack (modifying it).
/// Mnemonic: "not"
/// </summary>
[OpcodeInfo("not")]
Not = 0x12,
/// <summary>
/// Pops two values from the stack, performs a bitwise left shift operation ( <c><<</c> ), and pushes the result.
/// Mnemonic: "shl"
/// </summary>
/// <remarks>The second popped value is shifted left by the first popped value.</remarks>
[OpcodeInfo("shl")]
ShiftLeft = 0x13,
/// <summary>
/// Pops two values from the stack, performs a bitwise right shift operation ( <c>>></c> ), and pushes the result.
/// Mnemonic: "shr"
/// </summary>
/// <remarks>The second popped value is shifted right by the first popped value.</remarks>
[OpcodeInfo("shr")]
ShiftRight = 0x14,
/// <summary>
/// Pops two values from the stack, compares them using a <see cref="ComparisonType"/>, and pushes a boolean result.
/// Mnemonic: "set", and unofficially "cmp"
/// </summary>
[OpcodeInfo("cmp")]
Compare = 0x15,
/// <summary>
/// Pops a value from the stack, and generally stores it in a variable, array, or otherwise.
/// Has an alternate mode that can swap values around on the stack.
/// Mnemonic: "pop"
/// </summary>
[OpcodeInfo("pop")]
Pop = 0x45,
/// <summary>
/// Duplicates values on the stack, or swaps them around ("dup swap" mode).
/// Behavior depends on instruction parameters, both in data sizes and mode.
/// Mnemonic: "dup"
/// </summary>
[OpcodeInfo("dup")]
Duplicate = 0x86,
/// <summary>
/// Pops a value from the stack, and returns from the current function/script with that value as the return value.
/// Mnemonic: "ret"
/// </summary>
[OpcodeInfo("ret")]
Return = 0x9C,
/// <summary>
/// Returns from the current function/script/event with no return value.
/// Mnemonic: "exit"
/// </summary>
[OpcodeInfo("exit")]
Exit = 0x9D,
/// <summary>
/// Pops a value from the stack, and discards it.
/// Mnemonic: "popz"
/// </summary>
[OpcodeInfo("popz")]
PopDelete = 0x9E,
/// <summary>
/// Branches to another instruction in the code entry.
/// Mnemonic: "b"
/// </summary>
[OpcodeInfo("b")]
Branch = 0xB6,
/// <summary>
/// Pops a boolean/int32 value from the stack. If <see langword="true"/>/nonzero, branches to another instruction in the code entry.
/// Mnemonic: "bt"
/// </summary>
[OpcodeInfo("bt")]
BranchTrue = 0xB7,
/// <summary>
/// Pops a boolean/int32 value from the stack. If <see langword="false"/>/zero, branches to another instruction in the code entry.
/// Mnemonic: "bf"
/// </summary>
[OpcodeInfo("bf")]
BranchFalse = 0xB8,
/// <summary>
/// Pushes a "with" context, used for GML "with" statements, to the VM environment/self instance stack.
/// Mnemonic: "pushenv"
/// </summary>
[OpcodeInfo("pushenv")]
PushWithContext = 0xBA,
/// <summary>
/// Pops/ends a "with" context, used for GML "with" statements, from the VM environment/self instance stack.
/// This instruction will branch to its encoded address until no longer iterating instances, where the context will finally be gone for good.
/// If a flag is encoded in this instruction, then this will always terminate the loop, and branch to the encoded address.
/// Mnemonic: "popenv"
/// </summary>
[OpcodeInfo("popenv")]
PopWithContext = 0xBB,
/// <summary>
/// Pushes a constant value onto the stack. Can vary in size depending on value type.
/// Mnemonic: "push"
/// </summary>
[OpcodeInfo("push")]
Push = 0xC0,
/// <summary>
/// Pushes a value stored in a local variable onto the stack.
/// Mnemonic: "pushl", or unofficially "pushloc"
/// </summary>
[OpcodeInfo("pushloc")]
PushLocal = 0xC1,
/// <summary>
/// Pushes a value stored in a global variable onto the stack.
/// Mnemonic: "pushg", or unofficially "pushglb"
/// </summary>
[OpcodeInfo("pushglb")]
PushGlobal = 0xC2,
/// <summary>
/// Pushes a value stored in a GameMaker builtin variable onto the stack.
/// Mnemonic: "pushb", or unofficially "pushbltn"
/// </summary>
[OpcodeInfo("pushbltn")]
PushBuiltin = 0xC3,
/// <summary>
/// Pushes an immediate signed 32-bit integer value onto the stack, encoded as a signed 16-bit integer.
/// Mnemonic: "pushi"
/// </summary>
[OpcodeInfo("pushi")]
PushImmediate = 0x84,
/// <summary>
/// Calls a GML script/function, using its ID. Arguments are prepared prior to this instruction, in reverse order.
/// Argument count is encoded in this instruction. Arguments are popped off of the stack.
/// Mnemonic: "call"
/// </summary>
[OpcodeInfo("call")]
Call = 0xD9,
/// <summary>
/// Pops two values off of the stack, and then calls a GML script/function using those values, representing
/// the "self" instance to be used when calling, as well as the reference to the function being called.
/// Arguments are dealt with identically to "call".
/// Mnemonic: "call.v" (an exception to normal rules!), or unofficially "callv"
/// </summary>
[OpcodeInfo("callv")]
CallVariable = 0x99,
/// <summary>
/// Performs extended operations that are detailed in the <see cref="ExtendedOpcode"/> enum.
/// Often referred to as "break", but there are multiple mnemonics for this opcode.
/// </summary>
[OpcodeInfo("break")]
Extended = 0xFF
}
/// <summary>
/// Represents multiple extended opcodes used by instructions with the Extended opcode.
/// </summary>
public enum ExtendedOpcode : short
{
/// <summary>
/// Verifies an array index is within proper bounds, typically for multi-dimensional arrays.
/// Mnemonic: "chkindex"
/// </summary>
[OpcodeInfo("chkindex")]
CheckArrayIndex = -1,
/// <summary>
/// Pops two values from the stack, those being an index and an array reference.
/// Then, pushes the value stored at the passed-in array at the desired index.
/// That is, this is used only with multi-dimensional arrays, for the final/last index operation.
/// Mnemonic: "pushaf"
/// </summary>
[OpcodeInfo("pushaf")]
PushArrayFinal = -2,
/// <summary>
/// Pops three values from the stack, those being an index, an array reference, and a value.
/// Then, assigns the value to the array at the specified index.
/// Mnemonic: "popaf"
/// </summary>
[OpcodeInfo("popaf")]
PopArrayFinal = -3,
/// <summary>
/// Pops two values from the stack, those being an array reference and an index.
/// Then, pushes a new array reference from the passed-in array at the desired index, with the expectation that it will be further indexed into.
/// That is, this is used only with multi-dimensional arrays, for all index operations from the second through the second to last.
/// Mnemonic: "pushac"
/// </summary>
[OpcodeInfo("pushac")]
PushArrayContainer = -4,
/// <summary>
/// Sets a global variable in the VM (popped from stack), designated for tracking the now-deprecated array copy-on-write functionality in GML.
/// The value used is specific to certain locations in scripts. When array copy-on-write functionality is disabled, this
/// extended opcode is not used.
/// Mnemonic: "setowner"
/// </summary>
[OpcodeInfo("setowner")]
SetArrayOwner = -5,
/// <summary>
/// Pushes a boolean value to the stack, indicating whether static initialization has already occurred for this function (true), or otherwise false.
/// Enters a static variable initialization state.
/// Mnemonic: "isstaticok"
/// </summary>
[OpcodeInfo("isstaticok")]
HasStaticInitialized = -6,
/// <summary>
/// Exits a static variable initialization state.
/// Mnemonic: "setstatic"
/// </summary>
[OpcodeInfo("setstatic")]
ResetStatic = -7,
/// <summary>
/// Keeps track of an array reference temporarily. Used in multi-dimensional array compound assignment statements.
/// Presumed to be used for garbage collection purposes.
/// Mnemonic: "savearef"
/// </summary>
[OpcodeInfo("savearef")]
SaveArrayReference = -8,
/// <summary>
/// Restores a previously-tracked array reference. Used in multi-dimensional array compound assignment statements.
/// Presumed to be used for garbage collection purposes.
/// Mnemonic: "restorearef"
/// </summary>
[OpcodeInfo("restorearef")]
RestoreArrayReference = -9,
/// <summary>
/// Pops a value from the stack, and pushes a boolean result. The result is true if a "nullish" value, such as undefined or GML's pointer_null.
/// Mnemonic: "isnullish"
/// </summary>
[OpcodeInfo("isnullish")]
IsNullishValue = -10,
/// <summary>
/// Pushes an asset reference to the stack, encoded in an integer. Includes asset type and index.
/// Mnemonic: "pushref"
/// </summary>
[OpcodeInfo("pushref")]
PushReference = -11
}
/// <summary>
/// Basic logical comparison types used in the VM.
/// </summary>
public enum ComparisonType : byte
{
LesserThan = 1,
LesserEqualThan = 2,
EqualTo = 3,
NotEqualTo = 4,
GreaterEqualThan = 5,
GreaterThan = 6
}
/// <summary>
/// Different value data types used in the VM. Multiple are unused in VM bytecode, and are omitted here.
/// </summary>
public enum DataType : byte
{
/// <summary>
/// 64-bit floating point number.
/// </summary>
[DataTypeInfo('d', 8)]
Double = 0,
/// <summary>
/// 32-bit signed integer.
/// </summary>
[DataTypeInfo('i', 4)]
Int32 = 2,
/// <summary>
/// 64-bit signed integer.
/// </summary>
[DataTypeInfo('l', 8)]
Int64 = 3,
/// <summary>
/// Boolean, represented as 1 or 0, with a 32-bit integer.
/// </summary>
[DataTypeInfo('b', 4)]
Boolean = 4,
/// <summary>
/// Dynamic type representing any GML value. Externally known as a structure called <c>RValue</c>.
/// 128 bits in size, or 16 bytes.
/// </summary>
[DataTypeInfo('v', 16)]
Variable = 5,
/// <summary>
/// String, represented as a 32-bit ID.
/// </summary>
[DataTypeInfo('s', 4)]
String = 6,
/// <summary>
/// Represents a 16-bit integer.
/// </summary>
[DataTypeInfo('e', 4)]
Int16 = 15
}
/// <summary>
/// Represents the special types of instance IDs used in VM bytecode, as well as in GML overall.
/// Values greater than or equal to 0 can also be object asset IDs, depending on the version.
/// </summary>
public enum InstanceType : short
{
/// <summary>
/// Represents the current <c>self</c> instance.
/// </summary>
Self = -1,
/// <summary>
/// Represents the <c>other</c> context, which has multiple definitions based on the location used.
/// </summary>
Other = -2,
/// <summary>
/// Represents all active object instances. Assignment operations can perform a loop.
/// </summary>
All = -3,
/// <summary>
/// Represents no object/instance.
/// </summary>
Noone = -4,
/// <summary>
/// Used for global variables.
/// </summary>
Global = -5,
/// <summary>
/// Used for GML built-in variables.
/// </summary>
Builtin = -6,
/// <summary>
/// Used for local variables.
/// </summary>
Local = -7,
/// <summary>
/// Instance is stored in a Variable data type on the top of the stack.
/// </summary>
StackTop = -9,
/// <summary>
/// Used for function argument variables in GMLv2 (GMS 2.3).
/// </summary>
Argument = -15,
/// <summary>
/// Used for static variables.
/// </summary>
Static = -16
}
/// <summary>
/// Encoded variable type, used when referencing variables and functions in an instruction.
/// </summary>
public enum VariableType : byte
{
/// <summary>
/// Used for normal single-dimension array variables.
/// </summary>
Array = 0,
/// <summary>
/// Used when referencing a variable on another variable, e.g. a chain reference.
/// </summary>
StackTop = 0x80,
/// <summary>
/// Used for normal variables, without any arrays or chain references.
/// </summary>
Normal = 0xA0,
/// <summary>
/// Used when referencing variables on room instance IDs, e.g. something like "inst_01ABCDEF.x" in GML.
/// </summary>
Instance = 0xE0,
/// <summary>
/// Used in tandem with multi-dimensional array push operations (<see cref="ExtendedOpcode.PushArrayFinal"/> extended opcode).
/// </summary>
MultiPush = 0x10,
/// <summary>
/// Used in tandem with multi-dimensional array push and pop operations (<see cref="ExtendedOpcode.PushArrayFinal"/>/<see cref="ExtendedOpcode.PopArrayFinal"/> extended opcodes).
/// </summary>
MultiPushPop = 0x90
}
/// <summary>
/// The address of this instruction, in bytes, from the start of the containing code entry.
/// </summary>
public int Address { get; }
/// <summary>
/// The opcode of this instruction. Generally indicates what operation the instruction will perform.
/// </summary>
public Opcode Kind { get; }
/// <summary>
/// The extended opcode of this instruction, if <see cref="Kind"/> is <see cref="Opcode.Extended"/>.
/// </summary>
public ExtendedOpcode ExtKind { get; }
/// <summary>
/// For comparison instructions, represents the comparison kind.
/// </summary>
public ComparisonType ComparisonKind { get; }
/// <summary>
/// Represents the first data type argument of the instruction. This is encoded for every instruction.
/// </summary>
public DataType Type1 { get; }
/// <summary>
/// Represents the second data type argument of the instruction. This is encoded for every instruction.
/// </summary>
public DataType Type2 { get; }
/// <summary>
/// For instructions that have an instance type, represents the kind of instance or object ID.
/// </summary>
public InstanceType InstType { get; }
/// <summary>
/// For instructions that reference a variable, represents the variable being referenced.
/// </summary>
public IGMVariable? Variable { get; }
/// <summary>
/// For instructions that reference a function, represents the function being referenced.
/// </summary>
public IGMFunction? Function { get; }
/// <summary>
/// For instructions that reference a variable or function, this represents the variable type.
/// </summary>
public VariableType ReferenceVarType { get; }
/// <summary>
/// Represents a 64-bit floating point value for instructions that use it.
/// </summary>
public double ValueDouble { get; }
/// <summary>
/// Represents a 16-bit integer value for instructions that use it.
/// </summary>
public short ValueShort { get; }
/// <summary>
/// Represents a 32-bit integer value for instructions that use it.
/// </summary>
public int ValueInt { get; }
/// <summary>
/// Represents a 64-bit integer value for instructions that use it.
/// </summary>
public long ValueLong { get; }
/// <summary>
/// Represents a boolean value for instructions that use it.
/// </summary>
public bool ValueBool { get; }
/// <summary>
/// Represents a string value for instructions that push strings.
/// </summary>
public IGMString? ValueString { get; }
/// <summary>
/// Represents a branch offset for branch instructions, in bytes.
/// </summary>
public int BranchOffset { get; }
/// <summary>
/// For <see cref="Opcode.PopWithContext"/> instructions, represents whether the flag is set to exit the <c>with</c> loop early.
/// </summary>
public bool PopWithContextExit { get; }
/// <summary>
/// For duplication instructions, represents the size of the data to duplicate by (before considering data type).
/// </summary>
public byte DuplicationSize { get; }
/// <summary>
/// For duplication instructions, this is nonzero only when in "dup swap" mode.
/// When nonzero, the value is in the same location as the comparison type, for basic single-type instructions.
/// This value should be that byte, but bitwise AND'd against 0x7F, and then shifted to the right 3 bits.
/// </summary>
public byte DuplicationSize2 { get; }
/// <summary>
/// Returns the number of arguments encoded in this instruction, for <see cref="Opcode.Call"/> and <see cref="Opcode.CallVariable"/> instructions.
/// </summary>
public int ArgumentCount { get; }
/// <summary>
/// For <c>pop.e.v</c> instructions, this should return either 5 or 6, depending on the "pop swap" size.
/// </summary>
public int PopSwapSize { get; }
/// <summary>
/// For <see cref="Opcode.Extended"/> instructions with <see cref="ExtendedOpcode.PushReference"/> opcode,
/// this is the ID of the asset supplied with the instruction, if <see cref="Function"/> is <see langword="null"/>.
/// </summary>
public int AssetReferenceId { get; }
/// <summary>
/// For <see cref="Opcode.Extended"/> instructions with <see cref="ExtendedOpcode.PushReference"/> opcode,
/// this returns the type of the asset supplied with the instruction, if <see cref="Function"/> is <see langword="null"/>.
/// </summary>
public AssetType GetAssetReferenceType(IGameContext context);
/// <summary>
/// Returns size of an instruction, in bytes.
/// </summary>
internal static int GetSize(IGMInstruction instr)
{
if (instr.Variable is not null || instr.Function is not null)
{
return 8;
}
switch (instr.Kind)
{
case Opcode.Push or
Opcode.PushLocal or
Opcode.PushGlobal or
Opcode.PushBuiltin or
Opcode.PushImmediate:
if (instr.Type1 is (DataType.Double or DataType.Int64))
{
return 12;
}
if (instr.Type1 != DataType.Int16)
{
return 8;
}
break;
case Opcode.Extended:
if (instr.Type1 == DataType.Int32)
{
return 8;
}
break;
}
return 4;
}
}
/// <summary>
/// Represents a GameMaker variable entry.
/// </summary>
public interface IGMVariable
{
/// <summary>
/// The name of the variable.
/// </summary>
public IGMString Name { get; }
/// <summary>
/// Represents the type of instance used for the variable.
/// </summary>
public IGMInstruction.InstanceType InstanceType { get; }
/// <summary>
/// The ID of the variable in the game's data file.
/// This can sometimes be -6, representing the instance type of a built-in variable.
/// </summary>
public int VariableID { get; }
}
/// <summary>
/// Represents a GameMaker function entry.
/// </summary>
public interface IGMFunction
{
/// <summary>
/// The name of the function.
/// </summary>
public IGMString Name { get; }
}
/// <summary>
/// Represents a string reference from a game's data file.
/// Used to disambiguate identical string contents.
/// </summary>
public interface IGMString
{
/// <summary>
/// The actual content of the string.
/// </summary>
public string Content { get; }
}