This protocol was designed to be simple to understand and implement.
However, nxdk-rdt only provides helper functions which are enough to inject your own code into an Xbox. nxdk-rdt itself does not provide a high-level set of functions to do anything useful on their own. To find out how to create these high-level functions using nxdk-rdt, see the examples section.
Reasons for this design are:
-
Compatibility and abstraction: Most nxdk-rdt functions have equivalents in gdbstub, kd and xbdm. We keep nxdk-rdt simple, and will implement high-level functionality for all backends elsewhere.
-
Powerful: Consider a case where a flag is written to MMIO registers and the code has to wait for a flag to change. Querying the state over the network is slow, and designing our own script system to run on the Xbox also isn't ideal. By injecting natively run x86 code into the Xbox, we can solve every sitution without restrictions.
-
Simple: Writing a new client for nxdk-rdt only takes a couple of minutes. Other than sockets, there are also no dependencies. This gives users the freedom to use their favorite tools.
- U8 = Unsigned 8 bit value (byte)
- U16 = Unsigned 16 bit value, little-endian
- U32 = Unsigned 32 bit value, little-endian
A request frame groups multiple requests which will be executed in sequence.
The request frame starts with a U8 which stores how many requests are in this frame. The requests follow in order of planned execution, with each request consisting of a U8 command index, followed by it's parameters.
The available requests are limited in functionality, but provide enough functionality to implement custom commands using the Call
request.
Sets the compression for all requests on this connection, starting at the next request in this frame. Calling this function will always end the current compression stream and start a new one.
Supported algorithms:
- 0: None
- 1: LZ4 frame
Sets the compression for all responses on this connection, starting at the response for the next request.
See SetRequestCompression
for a list of supported algorithms.
Allocates size
bytes of data and returns the virtual_address
.
Free
will be called automatically for each allocation when the connection is closed.
Frees the memory allocation at address
which was created using Allocate
Reads size
bytes from virtual memory at virtual_address
and returns it.
There are no guarantees made how memory is accessed.
This makes the function unsuitable for MMIO.
Writes size
bytes from data
to the memory at virtual_address
.
FIXME: Could also implement this using something like SetExecutable(U32 virtual_address, size?)
(not guaranteeing executable from Allocate
, and checking in Call
) to avoid people from forgetting this.
Forces the instruction cache to be flushed.
This should be done before using Call
when the code has been updated.
Calls a function at virtual_address
.
It is guaranteed that the function will always be called from the same thread.
The function is called using the x86 call
instruction and must return.
If you need to send or receive data, you can use Allocate
to allocate space and use Read
and Write
to communicate.
The response frame starts with a U8 which stores how many requests were executed successfully. In case of errors, this value might be lower than the number of requested commands. The response for each successfully completed request in the request frame follows in the order of the requests.
I'd like something which allows to load commands into the guest, much like an extension loader. That way we could provide the same binary file for each programming language which would register extensions. Howver, it should be less invasive and work using the existing command set and protocol.
For examples, see XboxDev/python-scripts