Skip to content

Lua Scripting

REghZy edited this page Nov 12, 2025 · 36 revisions

Accessible in the tools menu (Tools > Scripting), this is an alternative to the Task Sequencer for those who prefer coding rather than visual programming.

Example

See Examples for some examples.

See Engine Functions for info about all of the engine functions

See JRPC for JRPC functions

See 3d Math for matrix4x4 and vector math

Note

Make sure to use local global variables. There seems to be an issue with either the lua compiler or runtime where writing to a global within closure functions can cause the script throw an error due to missing UpValues.

If you don't want to do this, then instead of doing s_MyGlobal = variable, do _G["s_MyGlobal"] = variable

Connection

Scripts will use the engine's connection by default. However, like task sequences, each script can have its own dedicated connection. Click the 3rd button on the bottom-left of the screen to open the connection dialog, or right-click the tab and click Connect to Console.

Engine Functions

The engine table contains the functions for network operations. The list of available methods:

readnumber(address : string|number, type : string) -> number
writenumber(address : string|number, type : string, value : number)

-- Reads an ASCII string with the given number of chars
readstring(address : string|number, length : number) -> string

-- Read a null-terminated string from the console
readcstr(address : string|number) -> string

writestring(address : string|number, value : string)
readbytes(address : string|number, count : number) -> table

-- The 'bytes' argument can be either an array table of numbers or a comma-separated string of numbers: `{0, 1, 2, 255}` or `"0,1, 2,255"`
writebytes(address : string|number, bytes : table|string)

-- Reads a row-major 4x4 float matrix (64 bytes) as a table (containing 16 elements). Row major as in the first 4 elements are on the first row.
readmat4x4(address : string|number) -> table

-- Writes a row-major 4x4 float matrix, which is 64 bytes, to the console at the given address.
writemat4x4(address : string|number, matrix : table)

readvec3(address : string|number) -> table
writevec3(address : string|number, vector : table)
readvec4(address : string|number) -> table
writevec4(address : string|number, vector : table)

-- The return value is whether the frozen state was changed. E.g. if freezing when already frozen, this returns false.
setfrozen(freeze : boolean) -> boolean

isfrozen() -> boolean
  • sendnotification(icon : string, message : string?)

    The XNotifyLogo Types section contains a list of available logo types. The message is optional, and some icons do not use the message.

    engine.sendnotification("GAMERTAG_SENT_YOU_A_MESSAGE", "CooLGamer")
  • drivelist() -> array[name] (as in, returns an "array" table containing each drive name)

    local things = engine.drivelist()
    for i, drive in pairs(things) do
        print(drive)
    end
-- Returns an "array" table of files and folders, where each value in the table is the **name** (relative to the given path). 
-- Directories end with the path separator char (which is `\` on xbox). You can use `engine.pathseparator()` to find out what it is. 
-- The parameters `includeFiles` and `includeDirs` specify whether to include files and directories in the results. Setting both to false will yield no results. 
getfiles(path : string, includeFiles : boolean, includeDirs : boolean) -> array[name]

deleterecursive(path : string)
launchfile(path : string)
movefile(oldPath : string, newPath : string)
mkdir(dirPath : string)
pathseparator() -> string

Functions that take an address must be either a number or a string. For strings, prefix with "0x" to parse as hexadecimal.

Functions that take a number-based data type must be one of byte, short, int, long, float or double.

All of these functions will produce an error if not connected to a console.

Coroutines

These are not currently enabled, because LuaCSharp (the library used for lua scripting) does not appear to use native threads for each coroutine, but instead switches between them. And having tested them, there seems to be a bug with them never stopping, so they are disabled for now.

And also, any network operation (i.e. reading or writing values, reading from file system, etc.) requires obtaining a lock (BusyLock token) on the connection so that no other thread may access it during that operation. This makes coroutines practically useless for scripts trying to do things with the console.

JRPC

When JRPC2 is installed on your console, these functions allow you to make remote calls. Be really careful with what you do, of course. This is still a WIP, so testing and bug reports would be appreciated.

The functions are accessible via the jrpc global table.

Function Arguments

These functions accept arguments in the forms of a "tuple" table, which is an array of two elements:

  • Type (string). One of: byte, int, ulong, float, byte[], int[], ulong[], float[] and string
  • Value (any, based on the type)

Array values are strings with comma-separated values, e.g. an int array argument could be {"int[]", "0,1,32,0x40"}.

Return value "rcptype"

This is used for function calls with return values. Supported values:

  • byte
  • int
  • ulong
  • float
  • byte[length] e.g. byte[4]
  • int[length] e.g. int[4]
  • ulong[length] e.g. ulong[4]
  • float[length] e.g. float[4]
  • string

Note

Integer values represented by strings (including in arrays, since they must be strings), are parsed as decimal by default. Prefix with "0x" to parse as hexadecimal.

Example arguments:

callvoidat("0x12345678", {"byte", "0"}, {"byte", "0x40"})
callvoidat("0x12345678", {"string", "hello!"}, {"int", 24}, {"int[]", "0x89502234,0x2455003D"})
callvoidat("0x12345678", {"string", ""}, {"int", 24}, {"byte[]", "0xFF,255"})
-- UNTESTED!
local text = callin("string", "MyCoolModule", "0x20", {"int", 24}, {"byte[]", "0xFF,255,127,0x7F"})
local myArrayTableWithLen32 = callat("byte[32]", "0x80004000", {"int[]", "24,0x7fffffff"})

Example Usages

MW3 (TU23) void SV_SetConfigString(int index, string value)

jrpc.callvoidat("0x822CB3E8", {"int", "0x3FA"}, {"string", "rank_prestige10"})

Available Functions

engine.getprocaddress(module : string, ordinal : number|string)

jrpc.callvoidat(address : number|string, args...)
jrpc.callvoidat_vm(address : number|string, args...)
jrpc.callvoidat_sys(address : number|string, args...)
jrpc.callvoidat_vm_sys(address : number|string, args...)

jrpc.callat(returnType : rcptype, address : number|string, args...) -> value
jrpc.callat_vm(returnType : rcptype, address : number|string, args...) -> value
jrpc.callat_sys(returnType : rcptype, address : number|string, args...) -> value
jrpc.callat_vm_sys(returnType : rcptype, address : number|string, args...) -> value

jrpc.callvoidin(module : string, ordinal : number|string, args...)
jrpc.callvoidin_vm(module : string, ordinal : number|string, args...)
jrpc.callvoidin_sys(module : string, ordinal : number|string, args...)
jrpc.callvoidin_vm_sys(module : string, ordinal : number|string, args...)

jrpc.callin(returnType : rcptype, module : string, ordinal : number|string, args...) -> value
jrpc.callin_vm(returnType : rcptype, module : string, ordinal : number|string, args...) -> value
jrpc.callin_sys(returnType : rcptype, module : string, ordinal : number|string, args...) -> value
jrpc.callin_vm_sys(returnType : rcptype, module : string, ordinal : number|string, args...) -> value

For more info on rpctype, see RPCType

Debug functions

Note

These are currently only available in the debug build of MemoryEngine360.

Important

Breakpoints do not work on my Falcon, so perhaps they only work on actual devkit consoles or require additional software.

The debugger table contains engine debugging functions. This is different from the debug table, which is for debugging the lua environment, and is limited to when running Memory Engine with an IDE debugger attached and built in debug mode.

debugger.add_breakpoint(address : number|string)
debugger.remove_breakpoint(address : number|string)
-- Sets a hardware breakpoint that suspends the title execution when the memory section is referenced
debugger.set_data_breakpoint(address : number|string, type: string, size : number|string)
  • type must be one of:
    • "w": Break if address is written
    • "rw": Break if address is read or written
    • "exec": Break if the address is executed
    • "none": Remove data breakpoint
  • size must be 1, 2 or 4
-- Increment thread suspend counter. Non-zero means thread is suspended
debugger.incr_suspend(threadId : number|string)

-- Decrease thread suspend counter. Zero means thread can run
debugger.decr_suspend(threadId : number|string)

debugger.find_functions(addresses... : number|string) -> table
    Returns a table of tables, where each sub tables contains the keys:
      - "module" : string
      - "address" : number
      - "size" : number
      - "unwind_info" : number (but this may be changed in the future)

XNotifyLogo Types

For notifications, this is a list of supported icon types (XNotifyLogo):

  • XBOX_LOGO
  • NEW_MESSAGE_LOGO
  • FRIEND_REQUEST_LOGO
  • NEW_MESSAGE
  • FLASHING_XBOX_LOGO
  • GAMERTAG_SENT_YOU_A_MESSAGE
  • GAMERTAG_SINGED_OUT
  • GAMERTAG_SIGNEDIN
  • GAMERTAG_SIGNED_INTO_XBOX_LIVE
  • GAMERTAG_SIGNED_IN_OFFLINE
  • GAMERTAG_WANTS_TO_CHAT
  • DISCONNECTED_FROM_XBOX_LIVE
  • DOWNLOAD
  • FLASHING_MUSIC_SYMBOL
  • FLASHING_HAPPY_FACE
  • FLASHING_FROWNING_FACE
  • FLASHING_DOUBLE_SIDED_HAMMER
  • GAMERTAG_WANTS_TO_CHAT_2
  • PLEASE_REINSERT_MEMORY_UNIT
  • PLEASE_RECONNECT_CONTROLLERM
  • GAMERTAG_HAS_JOINED_CHAT
  • GAMERTAG_HAS_LEFT_CHAT
  • GAME_INVITE_SENT
  • FLASH_LOGO
  • PAGE_SENT_TO
  • FOUR_2
  • FOUR_3
  • ACHIEVEMENT_UNLOCKED
  • FOUR_9
  • GAMERTAG_WANTS_TO_TALK_IN_VIDEO_KINECT
  • VIDEO_CHAT_INVITE_SENT
  • READY_TO_PLAY
  • CANT_DOWNLOAD_X
  • DOWNLOAD_STOPPED_FOR_X
  • FLASHING_XBOX_CONSOLE
  • X_SENT_YOU_A_GAME_MESSAGE
  • DEVICE_FULL
  • FOUR_7
  • FLASHING_CHAT_ICON
  • ACHIEVEMENTS_UNLOCKED
  • X_HAS_SENT_YOU_A_NUDGE
  • MESSENGER_DISCONNECTED
  • BLANK
  • CANT_SIGN_IN_MESSENGER
  • MISSED_MESSENGER_CONVERSATION
  • FAMILY_TIMER_X_TIME_REMAINING
  • DISCONNECTED_XBOX_LIVE_11_MINUTES_REMAINING
  • KINECT_HEALTH_EFFECTS
  • FOUR_5
  • GAMERTAG_WANTS_YOU_TO_JOIN_AN_XBOX_LIVE_PARTY
  • PARTY_INVITE_SENT
  • GAME_INVITE_SENT_TO_XBOX_LIVE_PARTY
  • KICKED_FROM_XBOX_LIVE_PARTY
  • NULLED
  • DISCONNECTED_XBOX_LIVE_PARTY
  • DOWNLOADED
  • CANT_CONNECT_XBL_PARTY
  • GAMERTAG_HAS_JOINED_XBL_PARTY
  • GAMERTAG_HAS_LEFT_XBL_PARTY
  • GAMER_PICTURE_UNLOCKED
  • AVATAR_AWARD_UNLOCKED
  • JOINED_XBL_PARTY
  • PLEASE_REINSERT_USB_STORAGE_DEVICE
  • PLAYER_MUTED
  • PLAYER_UNMUTED
  • FLASHING_CHAT_SYMBOL
  • UPDATING

Standard Lua Tables and Functions

The standard string, table and math functions and constants are available. Most OS functions are removed, except for "clock", "date" and "time".

Most global functions remain, with an additional being "sleep" which sleeps for X number of seconds.

Table bit32

If you wish to use bitwise operations:

  • arshift(a, b) -> return b < 0 ? (a << -b) : (a >> b)
  • band(...) -> return n1 & n2 & nX...
  • bnot(x) -> return ~x
  • bor(...) -> return n1 | n2 | n...
  • btest(...) -> return band(...) != 0
  • bxor(...) -> return n1 ^ n2 ^ n...
  • extract(v, field, width = 1) -> return (v >> field) & ((1 << width) - 1)
  • lrotate(a, b)
  • lshift(a, b)
  • replace(a, b, c, d = 1)
  • rrotate(a, b)
  • rshift(a, b)

If you want to format a number as a hex string: print(string.format("%x", bit32.bor(0x1000, 0x1))) will print "1001"

Examples

Here are some usage examples.

Freezing console

local didFreeze = engine.setfrozen(true)
local didUnFreeze = engine.setfrozen(false)

Tree

function isdirectory(path)
    return path:sub(-1) == "\\"
end

function printchildren(filePath, prefix)
    local tab = engine.getfiles(filePath, true, true)
    for i, val in pairs(tab) do
        print(prefix .. val)
        if (isdirectory(val)) then
            printchildren(filePath .. val, prefix .. "  ")
        end
    end    
end

local myDrives = engine.drivelist()
for i, drive in pairs(myDrives) do
    print(drive)
    printchildren(drive, "  ")
end

3d Math

A vec3 value is a table consisting of 3 elements: X, Y and Z. A vec4 is the same but contains W as the 4th element.

vec3.add(a : table, b : table) -> table
vec3.sub(a : table, b : table) -> table
vec3.dot(a : table, b : table) -> number
vec3.cross(a : table, b : table) -> table
vec3.mag(v : table) -> number
vec3.mag2(v : table) -> number
vec3.normalize(v : table) -> table
vec3.scale(v : table, s : number) -> table
vec3.lerp(a : table, b : table, t : number) -> table
vec3.distance(a : table, b : table) -> number

vec4.add(a : table, b : table) -> table
vec4.sub(a : table, b : table) -> table
vec4.dot(a : table, b : table) -> number
vec4.mag(v : table) -> number
vec4.mag2(v : table) -> number
vec4.normalize(v : table) -> table
vec4.scale(v : table, s : number) -> table
vec4.lerp(a : table, b : table, t : number) -> table

The mat4x4 table contains matrix4x4 functions. These are all static and operate using tables which should contain 16 numeric elements.

Matrices are read and written as row major, meaning they're stored in memory as such:

   M11 M12 M13 M14 ->
-> M21 M22 M23 M24 ->
-> M31 M32 M33 M34 ->
-> M41 M42 M43 M44

Where M11 is the first element, M14 is the 4th element, and M43 is second to last element.

A transformation matrix is stored as such:

SX 0  0  0
0  SY 0  0
0  0  SY 0
PX PY PZ 1

Where SX, SY, SZ is the scale, and PX, PY, and PZ is the translation.

-- Creates an identity matrix
mat4x4.identity() -> table
mat4x4.add(a : table, b : table) -> table
mat4x4.sub(a : table, b : table) -> table

-- Multiply a matrix by another matrix or a scalar
mat4x4.mul(a : table, b : table|number) -> table

-- Flips the matrix diagonally. Useful if a game stores matrices in memory as column-major, since reading and writing matrices is treated as row-major.
mat4x4.transpose(matrix : table) -> table

-- Tries to invert a matrix, or returns nil
mat4x4.invert(matrix : table) -> table?

mat4x4.make_orthographic(width : number, height : number, near : number, far : number) -> table
mat4x4.make_perspective(width : number, height : number, near : number, far : number) -> table
mat4x4.make_ortho_off_center(left : number, right : number, bottom : number, top : number, near : number, far : number) -> table
mat4x4.make_perspective_off_center(left : number, right : number, bottom : number, top : number, near : number, far : number) -> table
mat4x4.make_perspective_fov(fov : number, aspectRatio : number, near : number, far : number) -> table
mat4x4.make_reflection(plane : vec4) -> table
mat4x4.make_shadow(lightDirection : vec3, plane : vec4) -> table
mat4x4.make_world_matrix(position : vec3, forward : vec3, up : vec3) -> table
mat4x4.make_billboard(objPos : vec3, camPos : vec3, rotateAxis : vec3, camForward : vec3, objForward : vec3) -> table
mat4x4.make_look_at(eye : vec3, target : vec3, up : vec3) -> table
mat4x4.make_translation(x : number, y : number, z : number) -> table
mat4x4.make_scale(x : number, y : number, z : number) -> table
mat4x4.make_rot_x(radians : number) -> table
mat4x4.make_rot_y(radians : number) -> table
mat4x4.make_rot_z(radians : number) -> table
mat4x4.from_axis_angle(axis : vec3, radians : number) -> table
mat4x4.from_yaw_pitch_roll(yaw : number, pitch : number, roll : number) -> table

Clone this wiki locally