Skip to content

Commit

Permalink
Greatly improve docs and prepare for public release
Browse files Browse the repository at this point in the history
  • Loading branch information
N3rdL0rd committed Dec 19, 2024
1 parent 49e32c6 commit a141bfd
Show file tree
Hide file tree
Showing 12 changed files with 2,955 additions and 1,015 deletions.
2 changes: 1 addition & 1 deletion crashlink/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ def save_and_exit() -> None:
os.unlink(file)
return
try:
with open(file, "r", encoding="utf-8") as f2: # whyyyy mypy, whyyyy???
with open(file, "r", encoding="utf-8") as f2: # whyyyy mypy, whyyyy???
modified = f2.read()

lines = modified.split("\n")
Expand Down
2 changes: 1 addition & 1 deletion crashlink/decomp.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ def __init__(self, code: Bytecode, filename: gIndex, line: int, class_name: gInd
self.method_name = method_name

def __repr__(self) -> str:
return f"<IRTrace>" # TODO: resolve globals
return f"<IRTrace>" # TODO: resolve globals


class IRFunction:
Expand Down
6 changes: 3 additions & 3 deletions crashlink/globals.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
from io import BytesIO
from typing import Any, BinaryIO

VERSION: str = "pre-alpha"
VERSION: str = "v0.0.1a"
"""
The version of crashlink.
"""

LONG_VERSION: str = "crashlink - Pure Python HashLink bytecode parser/disassembler/decompiler/modding tool - " + VERSION
LONG_VERSION: str = "crashlink - Pure Python HashLink bytecode multitool - " + VERSION
"""
String displayed in the help message for the CLI.
"""

DEBUG: bool = True
DEBUG: bool = False
"""
Whether to enable certain features meant only for development or debugging of crashlink.
"""
Expand Down
199 changes: 122 additions & 77 deletions crashlink/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,132 +127,177 @@
Definitions of all 98 supported opcodes in the HashLink VM.
Basic Operations:
- Mov: Copy value from src into dst register (dst = src)
- Int: Load i32 constant from pool into dst (dst = @ptr)
- Float: Load f64 constant from pool into dst (dst = @ptr)
- Bool: Set boolean value in dst (dst = true/false)
- Bytes: Load byte array from constant pool into dst (dst = @ptr)
- String: Load string from constant pool into dst (dst = @ptr)
- Null: Set dst register to null (dst = null)
- Mov: Copy value from src into dst register (`dst = src`)
- Int: Load i32 constant from pool into dst (`dst = @ptr`)
- Float: Load f64 constant from pool into dst (`dst = @ptr`)
- Bool: Set boolean value in dst (`dst = true/false`)
- Bytes: Load byte array from constant pool into dst (`dst = @ptr`)
- String: Load string from constant pool into dst (`dst = @ptr`)
- Null: Set dst register to null (`dst = null`)
Arithmetic:
- Add: Add two numbers (dst = a + b)
- Sub: Subtract two numbers (dst = a - b)
- Mul: Multiply two numbers (dst = a * b)
- SDiv: Signed division (dst = a / b)
- UDiv: Unsigned division (dst = a / b)
- SMod: Signed modulo (dst = a % b)
- UMod: Unsigned modulo (dst = a % b)
- Add: Add two numbers (`dst = a + b`)
- Sub: Subtract two numbers (`dst = a - b`)
- Mul: Multiply two numbers (`dst = a * b`)
- SDiv: Signed division (`dst = a / b`)
- UDiv: Unsigned division (`dst = a / b`)
- SMod: Signed modulo (`dst = a % b`)
- UMod: Unsigned modulo (`dst = a % b`)
Bitwise:
- Shl: Left shift (dst = a << b)
- SShr: Signed right shift (dst = a >> b)
- UShr: Unsigned right shift (dst = a >>> b)
- And: Bitwise AND (dst = a & b)
- Or: Bitwise OR (dst = a | b)
- Xor: Bitwise XOR (dst = a ^ b)
- Neg: Negate value (dst = -src)
- Not: Boolean NOT (dst = !src)
- Shl: Left shift (`dst = a << b`)
- SShr: Signed right shift (`dst = a >> b`)
- UShr: Unsigned right shift (`dst = a >>> b`)
- And: Bitwise AND (`dst = a & b`)
- Or: Bitwise OR (`dst = a | b`)
- Xor: Bitwise XOR (`dst = a ^ b`)
- Neg: Negate value (`dst = -src`)
- Not: Boolean NOT (`dst = !src`)
Increment/Decrement:
- Incr: Increment value (dst++)
- Decr: Decrement value (dst--)
- Incr: Increment value (`dst++`)
- Decr: Decrement value (`dst--`)
Function Calls:
- Call0: Call function with no args (dst = fun())
- Call1: Call function with 1 arg (dst = fun(arg0))
- Call2: Call function with 2 args (dst = fun(arg0, arg1))
- Call3: Call function with 3 args (dst = fun(arg0, arg1, arg2))
- Call4: Call function with 4 args (dst = fun(arg0, arg1, arg2, arg3))
- CallN: Call function with N args (dst = fun(args...))
- CallMethod: Call method with N args (dst = obj.field(args...))
- CallThis: Call this method with N args (dst = this.field(args...))
- CallClosure: Call closure with N args (dst = fun(args...))
- Call0: Call function with no args (`dst = fun()`)
- Call1: Call function with 1 arg (`dst = fun(arg0)`)
- Call2: Call function with 2 args (`dst = fun(arg0, arg1)`)
- Call3: Call function with 3 args (`dst = fun(arg0, arg1, arg2)`)
- Call4: Call function with 4 args (`dst = fun(arg0, arg1, arg2, arg3)`)
- CallN: Call function with N args (`dst = fun(args...)`)
- CallMethod: Call method with N args (`dst = obj.field(args...)`)
- CallThis: Call this method with N args (`dst = this.field(args...)`)
- CallClosure: Call closure with N args (`dst = fun(args...)`)
Closures:
- StaticClosure: Create closure from function (dst = fun)
- InstanceClosure: Create closure from object method (dst = obj.fun)
- VirtualClosure: Create closure from object field (dst = obj.field)
- StaticClosure: Create closure from function (`dst = fun`)
- InstanceClosure: Create closure from object method (`dst = obj.fun`)
- VirtualClosure: Create closure from object field (`dst = obj.field`)
Global Variables:
- GetGlobal: Get global value (dst = @global)
- SetGlobal: Set global value (@global = src)
- GetGlobal: Get global value (`dst = @global`)
- SetGlobal: Set global value (`@global = src`)
Fields:
- Field: Get object field (dst = obj.field)
- SetField: Set object field (obj.field = src)
- GetThis: Get this field (dst = this.field)
- SetThis: Set this field (this.field = src)
- DynGet: Get dynamic field (dst = obj[field])
- DynSet: Set dynamic field (obj[field] = src)
- Field: Get object field (`dst = obj.field`)
- SetField: Set object field (`obj.field = src`)
- GetThis: Get this field (`dst = this.field`)
- SetThis: Set this field (`this.field = src`)
- DynGet: Get dynamic field (`dst = obj[field]`)
- DynSet: Set dynamic field (`obj[field] = src`)
Control Flow:
- JTrue: Jump if true (if cond jump by offset)
- JFalse: Jump if false (if !cond jump by offset)
- JNull: Jump if null (if reg == null jump by offset)
- JNotNull: Jump if not null (if reg != null jump by offset)
- JTrue: Jump if true (`if cond jump by offset`)
- JFalse: Jump if false (`if !cond jump by offset`)
- JNull: Jump if null (`if reg == null jump by offset`)
- JNotNull: Jump if not null (`if reg != null jump by offset`)
- JSLt/JSGte/JSGt/JSLte: Signed comparison jumps
- JULt/JUGte: Unsigned comparison jumps
- JNotLt/JNotGte: Negated comparison jumps
- JEq: Jump if equal (if a == b jump by offset)
- JNotEq: Jump if not equal (if a != b jump by offset)
- JEq: Jump if equal (`if a == b jump by offset`)
- JNotEq: Jump if not equal (`if a != b jump by offset`)
- JAlways: Unconditional jump
- Label: Target for backward jumps (loops)
- Switch: Multi-way branch based on integer value
Type Conversions:
- ToDyn: Convert to dynamic type (dst = (dyn)src)
- ToSFloat: Convert to signed float (dst = (float)src)
- ToUFloat: Convert to unsigned float (dst = (float)src)
- ToInt: Convert to int (dst = (int)src)
- ToDyn: Convert to dynamic type (`dst = (dyn)src`)
- ToSFloat: Convert to signed float (`dst = (float)src`)
- ToUFloat: Convert to unsigned float (`dst = (float)src`)
- ToInt: Convert to int (`dst = (int)src`)
- SafeCast: Safe type cast with runtime check
- UnsafeCast: Unchecked type cast
- ToVirtual: Convert to virtual type
Exception Handling:
- Ret: Return from function (return ret)
- Ret: Return from function (`return ret`)
- Throw: Throw exception
- Rethrow: Rethrow exception
- Trap: Setup try-catch block
- EndTrap: End try-catch block
- NullCheck: Throw if null (if reg == null throw)
- NullCheck: Throw if null (`if reg == null throw`)
Memory Operations:
- GetI8: Read i8 from bytes (dst = bytes[index])
- GetI16: Read i16 from bytes (dst = bytes[index])
- GetMem: Read from memory (dst = bytes[index])
- GetArray: Get array element (dst = array[index])
- SetI8: Write i8 to bytes (bytes[index] = src)
- SetI16: Write i16 to bytes (bytes[index] = src)
- SetMem: Write to memory (bytes[index] = src)
- SetArray: Set array element (array[index] = src)
- GetI8: Read i8 from bytes (`dst = bytes[index]`)
- GetI16: Read i16 from bytes (`dst = bytes[index]`)
- GetMem: Read from memory (`dst = bytes[index]`)
- GetArray: Get array element (`dst = array[index]`)
- SetI8: Write i8 to bytes (`bytes[index] = src`)
- SetI16: Write i16 to bytes (`bytes[index] = src`)
- SetMem: Write to memory (`bytes[index] = src`)
- SetArray: Set array element (`array[index] = src`)
Objects:
- New: Allocate new object (dst = new typeof(dst))
- ArraySize: Get array length (dst = len(array))
- Type: Get type object (dst = type ty)
- GetType: Get value's type (dst = typeof src)
- GetTID: Get type ID (dst = typeof src)
- New: Allocate new object (`dst = new typeof(dst)`)
- ArraySize: Get array length (`dst = len(array)`)
- Type: Get type object (`dst = type ty`)
- GetType: Get value's type (`dst = typeof src`)
- GetTID: Get type ID (`dst = typeof src`)
References:
- Ref: Create reference (dst = &src)
- Unref: Read reference (dst = *src)
- Setref: Write reference (*dst = src)
- Ref: Create reference (`dst = &src`)
- Unref: Read reference (`dst = *src`)
- Setref: Write reference (`*dst = src`)
- RefData: Get reference data
- RefOffset: Get reference with offset
Enums:
- MakeEnum: Create enum variant (dst = construct(args...))
- EnumAlloc: Create enum with defaults (dst = construct())
- EnumIndex: Get enum tag (dst = variant of value)
- EnumField: Get enum field (dst = (value as construct).field)
- SetEnumField: Set enum field (value.field = src)
- MakeEnum: Create enum variant (`dst = construct(args...)`)
- EnumAlloc: Create enum with defaults (`dst = construct()`)
- EnumIndex: Get enum tag (`dst = variant of value`)
- EnumField: Get enum field (`dst = (value as construct).field`)
- SetEnumField: Set enum field (`value.field = src`)
Other:
- Assert: Debug break
- Nop: No operation
- Prefetch: CPU memory prefetch hint
- Asm: Inline x86 assembly
If you want to see a more detailed pseudocode for any given instance of `Opcode`, you can use `crashlink.disasm.pseudo_from_op()` to get a human-readable representation of the operation.
If you're using the CLI in patch mode, you'll see opcodes in a format like:
```hlasm
Int. 0. 0
Int. 2. 1
GetGlobal. 3. 3
Add. 4. 0. 2
Sub. 5. 0. 2
Mul. 6. 0. 2
ToSFloat. 8. 0
ToSFloat. 9. 2
SDiv. 8. 8. 9
SMod. 7. 0. 2
Shl. 10. 0. 2
JSLt. 0. 2. 2
Bool. 11. False
JAlways. 1
Bool. 11. True
JSLt. 0. 2. 2
Bool. 12. False
JAlways. 1
Bool. 12. True
Nop.
CallN. [1, 2, 3]
Ret. 1
```
When writing opcodes in this format, seperate each argument to the opcode with a period. For opcodes that require lists, pass them in JSON format, e.g. `CallN. [reg, reg, reg, reg]`.
"""
Loading

0 comments on commit a141bfd

Please sign in to comment.