Redux basic API
Settings
All of the settings are exposed to Lua via the PCSX.settings
table. It contains pseudo-tables that are reflections of the internal objects, and can be used to read and write the settings. The exact list of settings can vary quickly over time, so making a full list here would be fruitless. It is possible however to traverse the settings using pprint
for example. The semantic of the settings is the same as from within the GUI, with the same caveats. For example, disabling the dynamic recompiler requires a reboot of the emulator.
ImGui interaction
PCSX-Redux will periodically try to call the Lua function DrawImguiFrame
to allow the Lua code to draw some widgets on screen. The function will be called exactly once per actual UI frame draw, which, when the emulator is running, will correspond to the emulated GPU's vsync. If the function throws an exception however, it will be disabled until recompiled with new code.
Events Engine interaction & Execution Contexts
LuaJIT C callbacks aren't called from a safe execution context that can allow for coroutine resuming, and luv's execution context doesn't have any error handling.
It is possible to defer executing code to the main loop of PCSX-Redux, which can (a) resume coroutines and (b) execute code in a safe context. The function PCSX.nextTick(func)
will execute the given function in the next main loop iteration. Here's some examples of how to use it:
1 2 3 4 5 6 7 8 9 10 |
|
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 |
|
Of course, this can also delay processing significantly, as the main loop is usually bound to the speed of the UI, which can mean up to 20ms of delay.
Constants
The table PCSX.CONSTS
contains numerical constants used throughout the rest of the API. Keeping an up to date list here is too exhausting, and it's simpler to print them using pprint(PCSX.CONSTS)
.
Pads
You can access the pads API through PCSX.SIO0.slots[s].pads[p]
where s
is the slot number and p
is the pad number, both indexed from 1, Lua-style. So PCSX.SIO0.slots[1].pads[1]
accesses the first pad, and PCSX.SIO0.slots[2].pads[1]
accesses the second pad.
Each Pad table has the following functions:
1 2 3 4 5 |
|
The button constants can be found in PCSX.CONSTS.PAD.BUTTON
.
You can for instance press the button Down on the first pad using the following code:
1 |
|
Execution flow
The Lua code has the following API functions available to it in order to control the execution flow of the emulator:
PCSX.pauseEmulator()
PCSX.resumeEmulator()
PCSX.softResetEmulator()
PCSX.hardResetEmulator()
It's also possible to manipulate savestates using the following functions:
PCSX.createSaveState() -- returns a slice representing the savestate
PCSX.loadSaveState(slice)
PCSX.loadSaveState(file)
Additionally, the following function returns a string containing the .proto file used to serialize the savestate:
PCSX.getSaveStateProtoSchema()
Note that the actual savestates made from the UI are gzip-compressed, but the functions above don't compress or decompress the data, so if trying to reload a savestate made from the UI, it'll need to be decompressed first, possibly through the zReader File object.
Overall, this means the following is possible:
1 2 3 4 5 6 7 8 |
|
Messages
The globals print
and printError
are available, and will display logs in the Lua Console. You can also use PCSX.log
to display a line in the general Log window. All three functions should behave the way you'd expect from the normal print
function in mainstream Lua.
GUI
You can move the cursor within the assembly window and the first memory view using the following functions:
PCSX.GUI.jumpToPC(pc)
PCSX.GUI.jumpToMemory(address[, width])
GPU
You can take a screenshot of the current view of the emulated display using the following:
PCSX.GPU.takeScreenShot()
This will return a struct that has the following fields:
1 2 3 4 5 |
|
The Slice
will contain the raw bytes of the screenshot data. It's meant to be written out using the :writeMoveSlice()
method on a File
object. The width
and height
will be the width and height of the screenshot, in pixels. The bpp
will be either BPP_16
or BPP_24
, depending on the color depth of the screenshot. The size of the data
Slice will be height * width
multiplied by the number of bytes per pixel, depending on the bpp
.
Loading and executing code
While the basic Lua functions dofile
and loadfile
exist, some alternative functions are available to load and execute code in a more flexible way.
Support.extra.addArchive(filename)
will load the given zip file, and will make it available to theSupport.extra.dofile
function. It is equivalent to the-archive
command line flag. Note that if a file namedautoexec.lua
is found in the zip file, it will be executed automatically.Support.extra.dofile(filename)
will load the given file, and execute it. It is equivalent todofile
, but will also search for the file next to the currently loaded Lua file which is calling this function, and will also search for the file in all of the loaded zip files, either through the command line, or through theSupport.extra.addArchive
function.Support.extra.loadfile(filename)
will load the given file, and return a function that can be called to execute the file. It is equivalent toloadfile
, but has the same file search algorithm asSupport.extra.dofile
.Support.extra.open(filename)
will open the given file as read only, and return aFile
object. It is roughly equivalent toSupport.File.open
, but has the same file search algorithm asSupport.extra.dofile
.
If given the following directory structure:
1 2 3 4 |
|
If test/baz.lua
contains the following code:
1 |
|
1 2 |
|
test/baz.lua
from the zip file bar.zip
, run it, which will in turn load test2/qux.lua
from the zip file bar.zip
again, and execute it.
This allows distributing complex "mods" as zip files, which can be loaded and executed from the command line or the console.
Miscellaneous
-
PCSX.quit([code])
schedules the emulator to quit. It's not instantaneous, and will only quit after the current block of Lua code has finished executing, which will be before the next main loop iteration. Thecode
parameter is optional, and will be the exit code of the emulator. If not specified, it'll default to 0. -
PCSX.getCPUCycles()
returns an unsigned 64-bit number indicating how many CPU cycles have elapsed. This can be paired with thePCSX.CONSTS.CPU.CLOCKSPEED
constant to determine how much emulated time has passed. -
PCSX.Adpcm.NewEncoder
will return an Adpcm encoder object. The object has the following methods: :reset([mode])
will reset the encoder, and set the mode to the given mode. The mode can be'Normal'
,'XA'
,'High'
,'Low'
,'FourBits'
. The default mode is'Normal'
, which enables all the filters available in the SPU. The'XA'
mode limits the encoder to the filters available in the XA ADPCM format. The'High'
mode uses the high-pass filter, and the'Low'
mode uses the low-pass filter. The'FourBits'
mode forces plain 4-bit Adpcm encoding.:processBlock(inData, [outData], [channels])
will encode the given ffi input buffer, and write the result to the given ffi output buffer. The input buffer should be a buffer of 16-bit signed integers, and the output buffer should be a buffer of 16-bit signed integers. The channels parameter is optional, and will default to 2. The input buffer should contain exactly 28 samples, and so does the output buffer. If the output buffer is not given, the function will return a new buffer with the result. LuaBuffers are also accepted as input and output buffers. The function will return three values: the output buffer, the filter index used, and the shifting used. The function is intended to be used as an intermediate computation step, and the output still needs to be processed into 4 bits or 8 bits samples.:processSPUBlock(inData, [outData], [blockAttribute])
will encode the given ffi input buffer, and write the result to the given ffi output buffer. The input buffer should be a buffer of 16-bit signed integers, and the output buffer should be a buffer which is at least 16 bytes large. The blockAttribute parameter is optional, and will default to'OneShot'
. The input buffer should contain exactly 28 samples. If the output buffer is not given, the function will return a new buffer with the result. LuaBuffers are also accepted as input and output buffers. The function will return the encoded block, suitable for SPU usage. TheblockAttribute
parameter can be one of the following strings:'OneShot'
,'OneShotEnd'
,'LoopStart'
,'LoopBody'
,'LoopEnd'
.:finishSPU([outData])
will write the opinionated end of sample looping block, as prescribed by the original Sony API. The output buffer should be a buffer which is at least 16 bytes large. If the output buffer is not given, the function will return a new buffer with the result. LuaBuffers are also accepted as output buffers. The function will return the encoded block, suitable for SPU usage.:processXABlock(inData, [outData], [xaMode], [channels])
will encode the given ffi input buffer, and write the result to the given ffi output buffer. The input buffer should be a buffer of 16-bit signed integers, and the output buffer should be a buffer which is at least 128 bytes large. Note that a MODE2 FORM2 XA sector requires subheaders and 18 of these blocks. The xaMode parameter is optional, and will default to'XAFourBits'
. The other valid value is'XAEightBits'
. It will defines the encoding output between either 4-bit and 8-bit. The channels parameter is optional, and will default to 1. If the output buffer is not given, the function will return a new buffer with the result. LuaBuffers are also accepted as input and output buffers. The function will return the encoded block, suitable for XA usage. The amount of required input samples varies depending of the number of channels and the encoding mode:- 4-bit mono: 224 samples aka 448 bytes
- 4-bit stereo: 112 samples aka 448 bytes
- 8-bit mono: 112 samples aka 224 bytes
- 8-bit stereo: 56 samples aka 224 bytes
Using the encoder to process an input audio file is as simple as:
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 |
|