Written 2026-10-06 from the third-party clients cloned in
references/of the UnnamedGpl3QtApp repository (read-only, git-ignored there). Nothing here has been checked against a console or an emulator yet: every fact is "what the references do", and the open questions at the end are to be settled on hardware.
XBDM is the Xbox 360 debug monitor: a line-based text protocol over TCP port 730,
with a few binary transfers, plus a small UDP name protocol on the same port. Every
console and emulator that offers it speaks the same protocol. This document is the
contract for UpdClient's XBDM protocol client (protocols/xbdm) and for the mock
server its tests run against. Two people should be able to build either side from
it alone. Where the references disagree, or a fact rests on one reference only, it
says so; where the client has to pick a behaviour anyway, the choice is marked
Contract:.
Citations are TAG:line (or TAG commit), relative to references/:
| Tag | File | Licence | What it is |
|---|---|---|---|
| XC | XBDM/src/Console.cpp |
MIT | C++ client by ClementDreptin, "for development versions of the Xbox 360" (XBDM/README.md:4) |
| XT | XBDM/test/TestServer.cpp |
MIT | That client's own mock server. Several known deviations from real consoles (section 7) |
| ONO | OpenNeighborhood-old/src/... |
Apache-2.0 | ImGui app built on XC |
| ON | OpenNeighborhood/src/lib/... (xbdm/xbdm.ts, xbdm/constants.ts, xbdm/utils.ts, consoles.ts, utils.ts) |
Apache-2.0 | Same author's later TypeScript client, fixed against a devkit (commit 7a33519) |
| NSc, NSd, NSx, NSg, NSu, NSt | NeighborSharp/NeighborSharp/XBDMConnection.cs, DataTypes.cs, Xbox360.cs, XboxOG.cs, IConsoleDiscovery.cs, NeighborTool/Program.cs |
LGPLv3 | C# client by InvoxiPlayGames |
| ED | EmDbg/EmDbg/XboxDebugger.cs |
LGPLv3 | Debugger on NeighborSharp; distinguishes devkit XBDM from the RGH "Natelx" XBDM |
| DT | DevTool/src/DevTool/... |
MIT | Devkit tool on Microsoft's XDevkit COM library (no raw protocol, but some facts) |
| MEc, MEf, MEr, MEp, MEe, MEj, MEi, MEt, MEd | MemoryEngine360/Xbox/MemEngine360.Xbox360XBDM/ Consoles/Xbdm/XbdmConsoleConnection.cs, Consoles/Xbdm/XbdmConsoleConnection.XbdmFeaturesImpl.cs, Consoles/Xbdm/XbdmResponseType.cs, ParamUtils.cs, XbdmEventUtils.cs, Consoles/Xbdm/Jrpc2FeaturesImpl.cs, Views/ConnectToXboxInfo.cs, Consoles/ConnectionTypeXbox360Xbdm.cs, Views/DiscoveredConsole.cs |
GPLv3 | Memory scanner/editor; the most complete raw client |
| ME | MemoryEngine360/MemEngine360/... |
GPLv3 | Its engine (scans, dumps, chunking) |
| XL, XLs, XLr | xefu-live/xefulive/xbdm.py, xefu-live/sdk/csharp/OgXbox/XbdmSocket.cs, xefu-live/README.md |
none stated | Live memory editor, tested on a JTAG console with an xbdm.xex plugin |
- [1] seen in one reference only.
- [≠] the references disagree; the alternatives are listed.
- [inf] inferred from what the code does, not stated by it.
- [none] not in any reference; only hardware can tell (section 6).
- Contract: what the app's client and mock do, where the references leave a choice.
Every target (devkits, retail consoles with an XBDM plugin, emulators) speaks the same XBDM protocol, so this document makes no per-target distinctions. Where a reference was written for one kind of console, the source key says so; that is evidence about the reference, not a difference in the protocol.
- TCP, port 730 (XC:42, NSx:21, ON xbdm.ts:73, MEt:110, XLs:23). Port 731 is the original Xbox's XBDM (NSg:14) and is out of scope.
- A devkit has two IP addresses, a debug one and a title one; XBDM answers on the
debug address: "if running a developer kernel, use the debug IP!" (NSt:21) [1].
altaddrreturns the title address (section 3.1). - IPv4 only in every reference (ON
utils.ts:40-42validates IPv4; ME discovery skips IPv6, MEi:96-100).
On accept the console sends exactly one line:
201- connected\r\n
XC compares the whole line byte for byte (XC:77-79); ME lowercases it first
(MEt:114-115, MEi:166-167); NSc skips to the first \n without checking (NSc:24).
ON writes its first command before reading the greeting (ON xbdm.ts:15-18), so a
console evidently buffers a command sent early [inf].
Contract: the client waits for the greeting before its first command, accepts
201- followed by any text (compared case-insensitively to connected only for a
log warning), and fails the connection on any other status. The mock sends the
greeting immediately after accept and must not lose bytes the client sends before
it.
command[ argument]*\r\n
argument = key=value | key="quoted value" | flag
- ASCII, terminated by CR LF (XC:676, NSc:64, MEc:690-694, XLs:66).
- Values: unquoted decimal or
0xhex for 32 bits,0qhex for 64 bits (NSd:85-110, MEp:52-60), quoted strings for names and paths (name="HDD:\x"), bare flags such asdir,running,cold,clear,override. - An empty line counts as a command: "Sending duplicate CRLF results in effectively two commands being sent" (MEc:690) [1]. Never send one.
- Key matching is case-insensitive in ME's parser (MEp:156-157). Command names are
sent in mixed case:
BYE(NSc:35) andbye(ON xbdm.ts:39, MEc:193);magicboot COLD(XC:366, NSx:103, ON consoles.ts:465) andmagicboot cold(ED:238, MEc:374). So command names and flags are probably case-insensitive [inf]. - Argument order and a trailing space appear not to matter: NeighborSharp emits flags,
then strings, then numbers, each followed by a space (NSd:132-151), so EmDbg sends
setmem data="0102" addr=0x82000000(ED:190-200) andbreak clear addr=0x...(ED:324-332), where ME sendsbreak addr=0x... clear(MEf:320) [inf]. - Lines have a maximum length on the console, value unknown [none]: XL writes
memory in 0x80-byte pieces "because xbdm caps the command line length" (XL:95,
XLs:110), ED splits
setcontext"due to the size limit" (ED:378), and the error table has406and446for over-long lines (MEr:53-54, MEr:168).
Contract: commands are ASCII; the client never pipelines (1.10), never sends an
empty line, puts the command name and flags in lower case, and keeps setmem data at
64 bytes or less per line (the smaller of ME's 64, MEc:666, and XL's 128, XL:96). The
mock accepts any argument order, tolerates trailing spaces, and refuses lines over a
configurable limit (default 512 bytes, a mock setting, not a console fact) with
406- line too long.
DDD- text\r\n
Three decimal digits, -, a space, then text. Parsers take the code from the first
three characters and the text from index 5 (NSd:12-16, MEr/XbdmResponse.cs:45,
ON xbdm.ts:91-102); XL takes text[4:].strip() (XL:57-62). ME requires more than 4
characters (XbdmResponse.cs:45).
The text is either a fixed phrase or the result itself:
| Code | Meaning | Text | Sources |
|---|---|---|---|
| 200 | OK, single-line result | OK, or the result (dbgname gives the name; bye gives bye) |
ON constants.ts:4, ON xbdm.ts:82-88, XC:122-125 |
| 201 | Connected (greeting only) | connected |
ON constants.ts:5 |
| 202 | Multi-line response follows (1.5) | multiline response follows |
ON constants.ts:6, XT:98 |
| 203 | Binary response follows (1.6) | binary response follows |
ON constants.ts:7, XC:404 |
| 204 | Ready for binary data from the client (1.7) | send binary data |
ON constants.ts:8, XC:517 |
| 205 | Connection dedicated, here to notifications (1.8) | not quoted by any reference | MEr:30, ED:403, MEc:1180 |
Errors are 4xx. The table is MemoryEngine360's, which names Microsoft's XBDM_*
constants (MEr:33-176) [1]; the codes the other references meet are marked.
| Code | XBDM name | Meaning | Also seen |
|---|---|---|---|
| 400 | UNDEFINED | Unspecified error | XT uses it for everything (not authoritative) |
| 401 | MAXCONNECT | Too many connections | ON consoles.ts:319 ("401- max number of connections exceeded") |
| 402 | NOSUCHFILE | No such file | modsections unknown module (MEf:430) |
| 403 | NOMODULE | No such module | |
| 404 | MEMUNMAPPED | Memory not mapped (setmem) |
MEc:670 tolerates it |
| 405 | NOTHREAD | No such thread | MEc:312, MEf:344 |
| 406 | CLOCKNOTSET | Clock not set; ME's note: "linetoolong or clocknotset" | |
| 407 | INVALIDCMD | Unknown command | |
| 408 | NOTSTOPPED | Not stopped (go when running) |
MEc:397, XL:204 |
| 409 | MUSTCOPY | File must be copied, not moved | |
| 410 | ALREADYEXISTS | Already exists (mkdir) |
XC:627, ON constants.ts:9 |
| 411 | DIRNOTEMPTY | Directory not empty | |
| 412 | BADFILENAME | Invalid file name | |
| 413 | CANNOTCREATE | Cannot create file (sendfile) |
MEf:232 |
| 414 | CANNOTACCESS | Access denied; also a directory given to a file command | MEf:149, MEf:174, MEf:234 |
| 415 | DEVICEFULL | Device full | |
| 416 | NOTDEBUGGABLE | Title not debuggable | |
| 417, 418 | BADCOUNTTYPE, COUNTUNAVAILABLE | Performance counters | |
| 420 | NOTLOCKED | Console not locked | |
| 421 | KEYXCHG | Key exchange required | |
| 422 | MUSTBEDEDICATED | Needs a dedicated connection | |
| 423 | INVALIDARG | Invalid argument | MEj (SendCommand, returns null on 423) |
| 424, 425 | PROFILENOTSTARTED, PROFILEALREADYSTARTED | Profiling | |
| 426 | ALREADYSTOPPED | Already stopped (stop) |
MEc:388, XL:204 |
| 427-445 | FASTCAPNOTENABLED ... PMCSESSIONNOTACTIVE | Profiling, NOMEMORY (428), TIMEOUT (429), NOSUCHPATH (430), screen formats (431, 432), FIELDNOTPRESENT (437, xexfield) |
MEr:111-165 |
| 446 | LINE_TOO_LONG | Command line too long | MEr:168 |
| 480, 481 | D3D_DEBUG_COMMAND_NOT_IMPLEMENTED, D3D_INVALID_SURFACE | Direct3D | |
| 496, 497 | (Vx) task pending, too many sessions | Voice |
- The error text is free-form and may carry the reason: ME detects a protected
directory by the substring
access deniedin thedirlisterror line (MEf:110) [1]. - No reference has seen a 5xx from a console; XT sends
500-for its own failure (XT:532). - DevTool's XDevkit calls return HRESULT
0x02DA0000for success (DTClasses/XDKUtilities.cs:117), so XBDM's facility is 0x2DA [1]. The mapping of 4xx codes to HRESULTs is not in the references.
Contract: the client parses DDD- and an optional space, keeps the code and the
text, and classifies: 2xx success, 4xx error (connection still usable, as XL treats it,
XL:147-148), anything else (5xx, 1xx, 3xx, non-digits) a protocol error that drops the
connection. An unexpected 2xx for a command (for example 203 where 200 was expected)
also drops the connection, because its payload framing cannot be known (1.6).
After the 202- line come zero or more lines, ended by a line that is exactly .
(NSc:86-91, MEc:272-281, ON xbdm.ts:25-36). An empty listing is
202- multiline response follows\r\n.\r\n; ON's "opening empty folders was throwing"
fix handles exactly that (ON commit 5128cb0).
- Body lines are usually
key=valuelists (dirlist, drivelist, walkmem, modules). Others are bare values: decimal thread ids (threads, MEc:339-341), hex strings (getmem,xexfield's value line, MEf:423), orKey: Valuepairs (hwinfo, MEc:283-290, MEc:459). - No reference handles dot-stuffing (a body line beginning with
.). None of the commands used here can produce one [inf].
There is no generic length. Each command frames its own payload, and the client must know the framing before it sends the command:
| Command | Framing after 203- |
Sources |
|---|---|---|
getfile |
4-byte little-endian unsigned length, then exactly that many bytes, no trailer | XC:430-466, NSc:118-123, ON consoles.ts:279-283, MEf:179-200 |
getmemex |
Blocks: 2-byte little-endian header (bits 0-14 byte count, bit 15 last block), then that many bytes; repeat | XL:79-92, XLs:78-101, MEc:597-627 |
screenshot |
One text line of key=value geometry, then framebuffersize raw bytes |
ON consoles.ts:548-562 |
ME's generic "show me any 203" path assumes the getmemex block framing (MEc:640-661,
used from SendCmdCommand.cs); that is a guess, not a rule.
For sendfile the console answers 204- send binary data; the client then sends
exactly the announced number of raw bytes, and the console answers once with a status
line, 200- OK on success (XC:566, MEf:215-218, MEf:224-227). NSx:133-137 and
ON consoles.ts:342-350 never read that last line, which leaves it in the stream for
the next command [≠]. Details in 3.6.
notify turns its connection into a one-way notification stream and answers 205
(ED:397-408, MEc:1176-1188, ME TestDebuggerEventReceiver.cs:52-55). From then on the
console sends notification lines (3.18) and the connection takes no further commands
in any reference [inf]. Notification lines may arrive before the 205 line on that
connection when a debugger is attached, because the console sends events in the
background (MEc:1168-1188) [1].
Both directions use CR LF. Readers differ: NSc reads to CR and discards one more byte
(NSc:48-60), XLs ends a line at LF and drops CRs (XLs:28-39), ME ends at the LF after
a CR (MEc:794-823), ON splits on CR LF (ON reader.ts via LINE_DELIMITER,
constants.ts:1).
Contract: the client ends a line at LF and strips one trailing CR; the mock always sends CR LF.
byeis answered200- bye, then the console closes the connection (ON xbdm.ts:38-45). ME and XLs sendbyeand close without reading (MEc:193, MEi:176, XLs:128).- ON first sent every command as
command\r\nbye\r\nin one write; that failed on a devkit and was changed to sendbyeonly after the response was read (ON commit 7a33519, "not working on devkit because of the bye command is handled") [1].
Contract: one command in flight per connection. The client writes the next command only after the previous response has been read completely (including any binary payload and trailing status). The mock still reads robustly: a command split over several segments, or two commands in one segment, are each handled in order.
| Reference | Value |
|---|---|
| XC | SO_RCVTIMEO 10 ms on Linux, 5 s on Windows, used to detect the end of a response (XC:55-63, XC:646-672). Do not copy |
| NSc | 1 s connect, send and receive (NSc:15-17) |
| ON | 3 s socket timeout (ON xbdm.ts:60) |
| ME | 4 s for the greeting (MEt:108); 5 s without progress while reading a line or binary data, then the connection is closed (MEc:843-874, MEc:981-1047). One reader loop says 5 s but computes 5000 s (MEc:932) |
| XL | 8 s per request (XL:40, XLs:18); 30 s for screenshot, "the console can take a few seconds to hand over a frame" (XL:262-272) |
Contract: connect 5 s; greeting 5 s; afterwards an idle timeout (no byte
received) of 10 s per response, 30 s for screenshot and for the status line after
sendfile data; there is no total time limit, so a large getfile may take minutes
as long as bytes keep arriving. All three are settings of the client, injectable by
tests. Short answers are also bounded as a whole, because a console that sends one
byte just inside the idle timeout would otherwise hold a call for the idle timeout
times the line limit: the greeting by its 5 s, the answer to bye by its own timeout,
and every command's answer, the status after sendfile data included, by a command
timeout; only file data (getfile and sendfile bodies) is exempt. The greeting, the
answer to bye and the status after sendfile data are read with a line limit of 512
bytes.
- Consoles accept several connections at once: ED keeps a command connection and a notification connection and opens a third for every memory read or write (ED:152, ED:180, ED:193, ED:205, ED:399); ME keeps a command connection and an event connection (MEc:1148-1160); ON opens a connection per command (ON xbdm.ts:12).
- There is a limit: opening a connection per file of a large folder led to
401- max number of connections exceeded(ON consoles.ts:319-320) [1]. The number is [none]. - XL deliberately uses one connection with a request queue (XL:100-110, XLr:103) [1].
Contract: the client opens one command connection per open console (the app's
per-mount worker serialises its use) and at most one notification connection. It opens
no connection per file. On 401 it reports "too many connections" and retries once
after closing its own idle connections.
notify reconnectport=Nis the only reconnect-related argument in the references: ED sendsreconnectport=0x00000001(ED:20, ED:401), ME sendsreconnectport=12345withreverse(MEc:1176) or without (TestDebuggerEventReceiver.cs:52). Both get 205 and notifications on the same socket. ME's comment: "no idea what reconnectport does" (MEc:1175). Its meaning, and that ofreverse, are [none].boxid,dedicate(as a command) and any keepalive command appear in no reference [none].- No reference sends keepalives. ME checks its own socket state every 2 s (MEc:115-123), which detects nothing on an idle TCP connection.
- State on the console survives a dropped connection: XL remembers it stopped the game
and sends
goon the next connection (XL:120, XL:142-145, XL:227-228) [1].
Contract: no keepalive traffic. A command connection found dead when the next command is sent is reopened once, transparently, unless a transfer was in progress (5.2). The client remembers whether it stopped execution and offers to resume after a reconnect.
debugger connect override name="<tool>" user="<host>" answers 200 (ED:417-426,
MEc:1163-1166). ME sends it before every break (MEf:318-337) and before notify;
ED once after connecting. override takes over an existing debugger session (ED:419).
DevTool uses XDevkit's ConnectAsDebugger(..., Force) (DT Forms/MainForm.cs:89). The
text of the matching disconnect is [none] (DevTool calls the COM method
DisconnectAsDebugger, Forms/MainForm.cs:529).
All on UDP port 730, one datagram each. ON points to xboxdevwiki's "Name Answering Protocol" for the format (ON consoles.ts:573).
| Type | Direction | Bytes | Sources |
|---|---|---|---|
| 1, forward lookup | client to broadcast | 01, name length (1 byte), name (ASCII, no terminator) |
NSu:42, ON consoles.ts:574-578 |
| 2, reply | console to client | 02, name length (1 byte), name (ASCII) |
NSd:206-213, MEd:43-54, ON consoles.ts:590 |
| 3, wildcard | client to broadcast (or one address) | 03 00 |
NSu:19, NSu:59, MEi:102 (BitConverter.GetBytes((short)3), little-endian) |
- Replies come from the console's address, which is the address to connect to (ON consoles.ts:599-601, MEi:126-137).
- Validation differs [≠]: ME's discovery requires
len + 2 == datagram size(MEi:131); its parser only requireslen <= size - 2(MEd:48-50); ON takes everything after byte 2 as the name (ON consoles.ts:590). NSd:208-212 checks only the type. - ON checks that the reply to a type-1 lookup carries the name it asked for (ON consoles.ts:593-597). Whether the console matches names case-insensitively is [none].
- Destination
255.255.255.255:730(ONxbdm/utils.ts:294, NSu:22, MEi:118). ME binds one socket per IPv4 interface address so the broadcast leaves on every network (MEi:96-118) [1]. - Waiting: NSu 1 s (NSu:21); ON 500 ms, 3 tries (ON
xbdm/utils.ts:276-277); ME 1.5 s per interface, 3 s overall (MEi:83, MEi:111). - Several consoles answer a wildcard; NSu collects until the timeout (NSu:24-36); ON
keeps only the first (TODO at ON
xbdm/utils.ts:302). - A unicast type 3 to one address checks that a console is there (NSu:57-77) [1].
- A reply gives the name only. ME then connects over TCP and asks
dbgname(MEi:134, MEi:161-185). - Whether every console answers UDP name queries is [none].
Every reference also accepts a typed IP address (ON consoles.ts:64-73, MEi:62-69 with the last address remembered, NSt:36-75). No reference scans a subnet over TCP. DevTool resolves names through the XDK's Neighborhood default console (DevTool README), which is Windows-only and out of scope.
Contract: Discovery broadcasts type 3 on every IPv4 interface, collects replies
for 1.5 s, retries twice, de-duplicates by address, then asks each console dbgname
over TCP (its answer wins over the UDP name). Anyone on the network can send replies,
so the client keeps at most 256 addresses and gives all dbgname queries of one search
10 s together; consoles not asked by then keep their UDP names. Adding a console by name sends type 1
and accepts a reply whose name matches case-insensitively. When nothing answers, the
user types an address; the app remembers consoles it has reached. No subnet scanning.
The mock answers type 3 always, type 1 only for its own name, and can be told to stay
silent (blocked UDP).
| Command | Seen in | Section |
|---|---|---|
dbgname |
XC, NSx, ON, ME, XL | 3.1 |
consoletype |
XC, ON | 3.1 |
getconsoleid |
ME | 3.1 |
xbeinfo |
XC, NSx, ED, ON, ME | 3.1 |
getexecstate |
ED, ME | 3.15 |
altaddr, getpid, hwinfo |
ME | 3.1 |
whomadethis |
ED | 3.1 |
help |
ME | 3.1 |
consolefeatures |
ME | 3.1 |
systeminfo, dmversion |
none | 3.1 |
drivelist |
XC, NSx, ON, ME | 3.2 |
drivefreespace |
XC, NSx, ON, ME | 3.2 |
dirlist |
XC, NSx, ON, ME | 3.3 |
getfileattributes |
XC only | 3.4 |
getfile |
XC, NSx, ON, ME | 3.5 |
sendfile |
XC, NSx, ON, ME | 3.6 |
sendvfile |
none | 3.7 |
mkdir |
XC, ON, ME | 3.8 |
delete |
XC, ON, ME | 3.9 |
rename |
XC, ONO, ON, ME | 3.10 |
setfileattributes |
none | 3.11 |
setsystime |
XC, ON | 3.12 |
screenshot |
ON, XL (through py-xbdm, not in references) | 3.13 |
magicboot |
XC, NSx, ED, ON, DT, ME | 3.14 |
shutdown |
NSx, ON, ME | 3.14 |
dvdeject |
ME | 3.14 |
stop, go |
ED, ME, XL | 3.15 |
suspend, resume |
ED, ME | 3.15 |
halt, continue, step |
none (only the singlestep notification) |
3.15 |
getmem |
ED, ME | 3.16 |
getmemex |
ME, XL | 3.16 |
setmem |
ED, ME, XL | 3.16 |
setmemex |
none | 3.16 |
walkmem |
ED, ME, XL | 3.16 |
modules, modsections, xexfield |
ME | 3.16 |
threads, threadinfo |
ME | 3.16 |
getcontext, setcontext |
ED, ME | 3.16 |
break, stopon |
ED, ME | 3.17 |
debugger |
ED, ME | 1.14 |
notify |
ED, ME | 3.18 |
bye |
NSc, ON, ME, XLs | 1.10 |
Each subsection gives syntax, response, errors and quirks. Examples use > for
client lines and < for console lines; CR LF is implied.
dbgname returns the console's debug name as the text of a 200 line.
> dbgname
< 200- MyDevkit
XC:113-125 (text from index 5), NSx:14, ON consoles.ts:53, MEc:405-407, MEi:171.
dbgname name=<new> sets it, unquoted in ME (MEc:497-499) [1].
consoletype returns the type as text. XT answers 200- reviewerkit (XT:287), XC
takes the text after 200- (XC:318-330), ON stores it (ON consoles.ts:61). The set of
values is not in the references [none]; XDevkit's numeric types are development
kit, test kit and reviewer kit (DT Classes/XDKUtilities.cs:121-126).
getconsoleid: 200- consoleid=<id>; ME strips the first 10 characters
(MEc:401-403) [1]. The id's format (hex digits, length) is [none].
xbeinfo running describes the running title; xbeinfo name="<path>" a file
(MEc:409-418). Response 202 with key=value lines; the name value is the path:
> xbeinfo running
< 202- multiline response follows
< timestamp=0x00000000 checksum=0x00000000
< name="\Device\Harddisk0\SystemExtPartition\20449700\dash.xex"
< .
The body above is XT's (XT:270-274); the real field set and whether name is a device
path or a drive path are [none]. NSx:42, ED:223, ON consoles.ts:529-534 and
MEc:409-418 read only name. ME's author notes xbeinfo did not work for modules
(MEf:407) [1].
getexecstate: 200 with one word: start, stop, pending, reboot,
pending_title, reboot_title (MEc:445-456); ED tests for start (ED:246-249).
altaddr: 200- addr=0x<hex>, the title IP address (MEc:485-491) [1]. ME
byte-swaps the number into .NET's IPAddress, so the value reads as the address in
network order: 0xC0A80102 is 192.168.1.2 [inf].
getpid: 200- pid=0x<hex> (MEc:478-483) [1]. ME byte-swaps it for display;
why is unclear.
hwinfo: 202 with Key: Value lines, in order flags, number of processors, PCI
bridge revision, reserved bytes, boot loader magic, boot loader flags (MEc:458-476)
[1]. Exact text [none].
whomadethis: its text contains Natelx on the RGH XBDM; EmDbg treats any other
answer as devkit XBDM (ED:415, ED:218) [1]. What a console that does not know the
command answers (perhaps 407) is [none].
help: 202 listing the commands the console knows (ME
Commands/ListHelpCommand.cs, SendCommandAndReceiveLines("help")) [1]. The line
format is [none]. It is the only capability probe in the references.
consolefeatures ver=2 type=<n> params="A\0\A\..." is JRPC2's extension
(MEj throughout): type 15 reads a temperature, 10 the CPU key, 13 the dashboard
version, 16 the title id, 17 the motherboard. ME probes JRPC2 with type 15 and, if the
answer is not 200, considers the connection "corrupted" and closes it (MEc:133-154)
[1]. Out of scope for the app; never sent on the command connection.
systeminfo, dmversion: not in any reference [none]. DevTool reads system
information (kernel, XDK version, flags, console revision) through XDRPC calls into
xbdm.xex ordinals 161 and 140 (DT Classes/XDKUtilities.cs:113-133), not through text
commands.
Contract: the console page shows dbgname, consoletype, getconsoleid and
xbeinfo running when they answer, and hides each field that errors. systeminfo and
dmversion are tried once per connection; a 4xx hides them.
drivelist: 202, one drivename="<NAME>" line per drive, name without colon or
backslash (XC:134-165, XT:95-103, ON consoles.ts:134-140, MEf:69-78).
> drivelist
< 202- multiline response follows
< drivename="DEVKIT"
< drivename="HDD"
< drivename="Z"
< .
The original Xbox instead answers one 200 line of drive letters (NSg:33-34); not a 360 behaviour.
drivefreespace name="<NAME>:\" with the trailing backslash (XC:182, NSx:56-59,
ON consoles.ts:147, MEf:81). Fields, each a 32-bit hex half of a 64-bit byte count:
freetocallerhi/lo, totalbyteshi/lo, totalfreebyteshi/lo.
> drivefreespace name="HDD:\"
< 202- multiline response follows
< freetocallerhi=0x00000003 freetocallerlo=0x1a2b0000 totalbyteshi=0x00000004 totalbyteslo=0x00000000 totalfreebyteshi=0x00000003 totalfreebyteslo=0x1a2b0000
< .
[≠] NSx, ON and ME read a 202 with one line (NSx:59, ON consoles.ts:144-148,
MEf:81-82); XT answers a single 200- line (XT:122-128) and XC parses whatever it gets
(XC:189-191). The real form is 202 by majority [inf]. NSx records a drive without
sizes when the command fails (NSx:61-63), so it may fail on some drives.
Friendly names, "from neighborhood" (XC:167-179, ON xbdm/utils.ts:84-101): DEVKIT and
E Game Development Volume, HDD Retail Hard Drive Emulation, Y Xbox360 Dashboard
Volume, Z Devkit Drive, D and GAME Active Title Media, anything else Volume.
DevTool shows that XBDM keeps a table of drive names (up to 35 characters, A-Z and 0-9)
mapped to device paths starting with \, at most 42 entries, each browsable or not
(DT Classes/NeighborhoodDrives.cs:46-112, :161) [1].
Contract: total = totalbytes, free = freetocaller, used = total - free (XC:192,
ON consoles.ts:184). The client accepts 200 or 202. A drive whose drivefreespace
fails is still listed, without sizes. drivelist names that repeat (compared
case-insensitively) and names after the 64th are ignored.
> dirlist name="HDD:\Content\"
< 202- multiline response follows
< name="0000000000000000" sizehi=0x0 sizelo=0x0 createhi=0x01d11fb5 createlo=0x59683c00 changehi=0x01d11fb5 changelo=0x59683c00 directory
< name="default.xex" sizehi=0x0 sizelo=0x0004f000 createhi=0x01d11fb5 createlo=0x59683c00 changehi=0x01d11fb5 changelo=0x59683c00
< .
- One line per entry; no
.or..entries in any example [inf]. - Fields:
name(quoted),sizehi/sizelo(64-bit size in two 32-bit hex halves),createhi/createloandchangehi/changelo(FILETIME halves, 4.3), and a baredirectoryflag for folders (XC:232-253, NSd:186-194, ON consoles.ts:220-238, MEf:118-138). Field order in the example follows the order clients read them; XT writesname, times, then size (XT:166-179). - ON detects folders with
line.endsWith(" directory")(ON consoles.ts:225), so on its console the flag ends the line [inf]; ME searches for the flag anywhere (MEf:138). - Other flags (
readonly,hidden, ...) appear in no reference [none]. - Errors: a path that does not exist is any non-202 (MEf:112-113); a protected path
carries
access deniedin its text (MEf:110). The exact codes (402? 430? 414?) are [none]. - Trailing backslash: XC and ME always add one (XC:209, MEf:106-107); ON sends drive
roots with one (
HDD:\) and subfolders without (ON commit fce4704,drive-button.tsx:42) [≠]. A drive root without the backslash (HDD:) is untested anywhere.
Contract: the client always sends dirlist with exactly one trailing backslash,
parses fields by name in any order, treats a missing size as 0 (reported as unknown) and
missing times as "unknown", and ignores flags it does not know. Entries named . or ..
are dropped; entries whose name the client could not send back (4.1: :, *, ?, a
control character, a byte above 0x7E, ...) are skipped and counted, like unreadable ones.
getfileattributes name="<path>" (XC:266-290) [1]. XC reads the line after the
first as sizehi sizelo createhi createlo changehi changelo (XC:279-287); XT answers
202 with one such line (XT:213-222). Whether the real reply is 200 or 202, carries the
directory flag, and what a missing path returns (XT says 404, which is wrong for a
file, 3.5) are [none]. XC uses it to test existence before uploading a folder
(XC:572-585).
Contract: the client parses 200 or 202 alike and treats any 4xx as "does not
exist or not accessible". FileSystem::stat() uses it when available and falls back to
listing the parent folder. The library does the same where it needs the answer: the
replace step of an upload lists the parent when getfileattributes answers 407, and
downloadToFile also when the answer has no sizehi/sizelo; a missing size is never
taken as 0.
> getfile name="HDD:\file.bin"
< 203- binary response follows
< [4 bytes: length, unsigned 32-bit little-endian]
< [length bytes]
- The length prefix is little-endian in every reference (XC:430-436 reads a native
inton x86, NSc:118-120, ON consoles.ts:279-280, MEf:179-183 "this value is always LE...???"), although the console is big-endian [inf]. - Nothing follows the data: the console neither closes the connection nor sends a
status line (ON consoles.ts:307-308, ON
app/[ipAddress]/files/download/route.ts:53-62, MEf:185-200 reads exactlylengthand carries on) [≠] XC drains the socket after it "just in case" (XC:473-474). - No checksum in any reference.
- No chunking in the protocol: the whole file follows as one stream. Clients read it in pieces of 1 KiB (XC:440), 64 KiB (MEf:186) or the stream's own (ON).
- Errors come instead of the 203: 414 for a directory or access denied (MEf:174-176), 402 for no such file [inf] (XT says 404, XT:342).
- Files of 4 GiB or more cannot be described by the 32-bit length; what the console does then is [none]. FATX files are smaller than 4 GiB, so this matters only for other filesystems.
- Partial transfer and cancel: there is no way to stop a download in the protocol. The
only way out is to close the connection (ON destroys the stream,
ON
app/[ipAddress]/files/download/route.ts:39-45).
Contract: the client reads exactly length bytes into the sink and only then
returns the connection to the idle state. Cancelling a download closes the connection
(it is reopened for the next command) and discards the partial local file, which the
app's sinks already do on a failed transfer. A length larger than the size dirlist
reported, or larger than the sink allows, is refused by closing the connection before
reading the body (5.2). When no listing gave the size, the client asks getfileattributes
first (3.4) and refuses a file of 4 GiB or more before sending getfile: the mock, as the
worst case, announces such a size modulo 2^32, and the download would end early looking
complete. A console that refuses getfileattributes with 407 is asked for the size by listing the
parent folder (3.4); when neither gives one, the file is asked for without that bound.
> sendfile name="HDD:\file.bin" length=0x2a
< 204- send binary data
> [0x2a raw bytes]
< 200- OK
lengthis hex with0x(XC:512, ON consoles.ts:342, MEf:231 as0x%08X); NSd emits0x%08Xtoo (NSd:145). Decimal is untested.- Errors before 204: 413 cannot create (ME's note: "Does a directory with the same name exist?", MEf:232-233); 414 when the path is a directory (MEf:234-235).
- After the data the console answers one status line; 200 on success (XC:566, MEf:215-218). Errors after the data (a full device mid-way: 415?) are [none].
- Data is sent as fast as TCP allows (MEf:206-212 writes 64 KiB pieces; ON pipes a stream, ON consoles.ts:346-349). XC sleeps 20 ms after every 1 KiB (XC:544-562); that is a workaround for its own receive loop, not a protocol need [inf].
- An existing file of the same name is replaced: ON's upload deletes only folders that
clash, not files (ON
components/upload-dropzone.tsx:61-80, commit 0514255 asks the user before "replacing" a file) [inf]. - Lengths of 4 GiB or more: [none]. ME passes an
int(MEf:203, MEf:230). - Cancel and drop: ON removed cancelling an upload because it "corrupted the file on the console" (ON commit acc1dc4) [1]. What remains after a dropped connection (a truncated file under the final name? nothing?) is [none].
- No checksum.
Contract: an upload goes to a temporary name in the target folder
(<name>.<random>.part, a FATX-valid name of at most 42 characters), then is renamed
to the final name after 200. The client looks the final name up before sendfile
(a folder there fails before the data) and again after 200. It replaces a file only
if one existed at the first lookup, deleting it right before the rename (the app's
Replace confirmation has already been given); a file that appeared during the upload is
left alone, the temporary file is deleted and the upload fails. A file that appears
between the second lookup and the rename cannot be detected on a console whose rename
replaces (question 40). From that delete on, the temporary file may be the only copy of either version: if the rename
fails, or the delete's answer is lost after its line went out, the temporary file is
kept, never queued for deletion, and the client reports its name. A cancel or
a drop closes the connection; on the next connection the client deletes the temporary
file, best effort. That includes a connection that fails after the sendfile line was sent
but before its 204 was read, because the console may already have created the file (the mock
creates it before answering; question 32). The client never sends more or fewer bytes than
announced.
Not in any reference [none]. Not used.
mkdir name="<path>", 200 on success, 410 if it exists (XC:619-632, ON consoles.ts:404-424,
MEf:168-170). XT answers 400 for an existing folder (XT:517-520), contradicting XC:627
[≠]; 410 wins (XC, ON). ME's interface claims "all subdirectories will be created"
(ME Connections/Features/IFeatureFileSystemInfo.cs:106) [1],
while ON creates each level itself when uploading a tree
(ON components/upload-dropzone.tsx:97-118).
Contract: the client creates one level per mkdir and maps 410 to "already exists".
delete name="<path>" for a file; delete name="<path>" dir for a folder
(XC:609, ON consoles.ts:373-377, MEf:147-154).
- Not recursive: every reference empties folders itself, depth first (XC:599-617,
ON consoles.ts:357-378, ME
Connections/Features/IFeatureFileSystemInfo.cs:66-88). - A folder without
dirgives 414; ME tries without and retries withdir(MEf:147-154). A non-empty folder presumably gives 411 [inf] (MEr:68-69). - Deleting a missing path: XT says 404 (XT:483-486), real code [none].
Contract: the client knows from the listing whether a path is a folder and sends
dir accordingly; remove() of a folder deletes its contents first, depth first, and
stops at the first error.
rename name="<old full path>" newname="<new full path>", 200 on success
(XC:634-644, ON consoles.ts:390-394, MEf:164-166). It also moves between folders: ONO's
cut and paste and ME's MoveFile are rename to another folder
(ONO src/Panels/ContentsPanel.cpp:161-175, MEf:164).
- Renaming onto an existing name: ON deletes the target first
(ON
components/files-page-context-menu.tsx:55-70), suggesting the console refuses [inf]; XT refuses with 400 (XT:568-572). Real behaviour [none]. - Across drives: the error table has 409 "must copy, not moved" (MEr:62-63), so a cross-volume rename is probably refused with 409 [inf].
Contract: rename() stays inside one folder and one drive. The client checks that
the new name does not exist before sending. A new name that differs from the old one only
in case is sent without that check (FATX ignores case, so the check would find the file
itself); if the console refuses it with 410 or 400, the client renames through an
intermediate <name>.<random>.ren in the same folder, renames back when the second step
fails, and says where the file is when that fails too (question 41).
Not in any reference [none]. Not used; the app has no attribute editing.
setsystime clockhi=0x<hex> clocklo=0x<hex>, a FILETIME (XC:340-362, ON consoles.ts:506-521).
A tz argument exists but its format is unknown (XC:346-348) [1] [none]. Errors:
406 "clock not set" belongs to reading the clock [inf]. Not needed by the app's
milestone; listed for the mock.
> screenshot
< 203- binary response follows
< pitch=0x00000c00 width=0x00000500 height=0x000002d0 format=0x... offsetx=0x0 offsety=0x0, framebuffersize=0x...
< [framebuffersize bytes]
ON consoles.ts:537-570 [1] for the framing (a port of a Go client, ON
xbdm/utils.ts:111-113). Fields pitch width height format offsetx offsety framebuffersize; a value may be followed by a comma, which the parser strips
(ON xbdm/utils.ts:69-73). The pixels are the GPU's tiled 32x32 layout and must be
untiled; ON supports only format 8888, 8-in-32 endianness, tiled, unsigned
(ON xbdm/utils.ts:125-174), and refuses others. The console can take seconds to answer
(XL:264). The exact field order and spacing are [none].
Contract: not in the first M3 deliverable; when added, the client reads the line,
checks framebuffersize against pitch * height (with tiling slack) and a cap of
64 MiB, then reads the bytes.
| Command | Effect | Response |
|---|---|---|
magicboot |
Warm reboot. XC and ON call it "go to dashboard" (XC:377-392, ON consoles.ts:440-447); ED and ME call it "restart title" / "soft reboot" (ED:241-244, MEt:64) [≠] | 200 expected by XC, ON and ME; XC waits 20 ms first because the answer is slow (XC:381-383, XBDM commit b8487a4) |
magicboot cold / COLD |
Cold reboot | [≠] ON expects the console to close the connection with no answer (ON consoles.ts:461-479); XC expects 200 (XC:364-375); ME reads a response, then closes (MEc:373-376) |
magicboot title="<path>" directory="<dir>" |
Launch an executable | [≠] ON expects 200 (ON consoles.ts:437); XC reads nothing ("Nothing should be received but just in case", XC:296-299); directory given without trailing backslash (XC:294, ON consoles.ts:432) or with one (MEf:157-161). A file in the root of FLASH: is launched with title only (DT Classes/XDKUtilities.cs:1051-1064) |
shutdown |
Power off | Connection closed with no answer (ON consoles.ts:482-505, MEc:378-381) |
dvdeject |
Open the tray | 200 [1] (MEf:58) |
XT's author removed argument checks from its magicboot "because the actual XBDM
server on an Xbox 360 doesn't do any" (XBDM commit 15d6b6e) [1]. After any reboot all
connections drop; ED treats an execution rebooting notification as the signal to
disconnect (ED:59-65).
Contract: these are user actions with a confirmation. The client sends the command and accepts either a 2xx or the connection closing within the idle timeout as success; afterwards the console is shown as rebooting and the command connection is reopened on demand.
stop: 200, or 426 when already stopped (MEc:383-390, XL:200-206).go: 200, or 408 when not stopped (MEc:392-399, XL:204).suspend thread=0x<id>/resume thread=0x<id>, 200 (MEf:376-378). ED has the two swapped (ED:251-263), a bug.getexecstate(3.1).halt,continue,step: in no reference [none]; ME'sStepThreadis a TODO (MEf:380-383). Single-stepping shows only as thesinglestepnotification (MEe:41).- Speed: while a title runs, XBDM gets little CPU time (about 100 KB/s, 28 ms per
request); stopped, about 1-1.4 MB/s (XL:107-109, XLr:103-108) [1]. XL, ME's scanner,
dumper and viewer stop the console around large reads and resume it afterwards, but
only if they stopped it (XL:208-228, ME
Engine/Scanners/FirstTypedScanTask.cs:103-111, MECommands/DumpMemoryCommand.cs:191-224).
Contract: the memory viewer offers "pause while reading" (default off for small
reads, on above 256 KiB, as XL's 0x40000 threshold, XL:112). The client remembers if it
sent the stop and sends go afterwards, also after a reconnect.
Addresses are 32-bit. ME's default viewer address is 0x82000000, where titles load
(ME Engine/HexEditing/MemoryViewer.cs:43); XL reads the emulator there too (XL
guest.py:18).
getmem addr=0x<hex> length=0x<hex> (ED:177-188, MEc:565-595):
> getmem addr=0x82000000 length=0x8
< 202- multiline response follows
< 4D5A9000????????
< .
- 202, then lines of hex digit pairs, two per byte, in ascending address order; lines are concatenated (ED:184-185, MEc:572-574).
- An unreadable byte is
??; ME zero-fills it (MEc:585-587) and uses a?anywhere to call a range protected (MEc:501-524). Whether?comes in pairs is [inf]. - Bytes per line: ME's comment says "typically 128 when reading big chunks" (MEc:582) [1]. Clients must not depend on it.
- The total must equal
length; ME throws otherwise (MEc:576-578). - Hex digit case [none]; parse both.
- Slow: about 7 KB/s according to XL (XL:3-4) [1]. ME uses it only up to 128 bytes,
where it is faster than
getmemex(MEc:553-563).
getmemex addr=0x<hex> length=0x<hex> (XL:78-92, XLs:73-104, MEc:597-627) [≠]
in the details:
> getmemex addr=0x82000000 length=0x800
< 203- binary response follows
< [2 bytes LE: 0x0400] [0x400 bytes]
< [2 bytes LE: 0x8400] [0x400 bytes]
- Blocks of "up to 0x400 bytes" (XL:79, XLs:78) [1], each after a 2-byte little-endian header: bits 0-14 the byte count, bit 15 set on the last block (XL's reading) or "status" with "most likely reading invalid/protected memory" (ME's reading, MEc:605-616).
- When bit 15 comes before
lengthbytes have arrived, the rest is unreadable: XL reports the first unreadable address (XL:90-91), ME zero-fills the rest (MEc:607-610). - A block larger than what remains: ME throws (MEc:617-619); XLs reads it and keeps only what was asked (XLs:87-93) [≠].
- The end: every reader stops as soon as
lengthbytes have arrived, whether or not that block has bit 15 set, and reads no further header (XL:88, XLs:99, MEc:626). A console that sent an empty last block after the data would leave it for the next command. - Sizes used successfully: up to 0xC000 per request in ME (MEc:126-128, "3/4th of the
receive buffer"), 0x10000 in ME's dumper (ME
Commands/DumpMemoryCommand.cs:204-205), 0x8000 while running and 0x20000 while stopped in XL (XL:238). The console's maximum is [none].
setmem addr=0x<hex> data=<hex digits> (MEc:663-677, XL:94-97, XLs:106-119, ED:190-200):
> setmem addr=0x82000000 data=DEADBEEF
< 200- set 4 bytes
- 200 on success; 404 when memory is not mapped, which ME accepts without error (MEc:670-672). The 200 text is not quoted by any reference (shown as an example only).
- Data per command: 64 bytes in ME (MEc:666), 128 in XL (XL:96) [≠]; both under the
line limit (1.3). ED sends the data quoted (
data="...", ED:196) [1]. - Hex case: ME uppercase (MEc:723-728 from a table), XL lowercase (XL:97) [inf]: both accepted.
- Whether a partly unmapped range is partly written, and what the 200 text says about it, is [none].
setmemex: not in any reference [none].
walkmem: 202, one line per committed region (ED:149-166, MEc:420-443, XL:193-198):
< base=0x82000000 size=0x00a40000 protect=0x00000004 phys=0x00000000
ME skips regions it must not touch, because touching them can freeze the console:
for reading, protections with 0x280 (NoCache, ExecuteWriteCopy); for writing, those
with 0x1222 (MEc:426-433, comment "we cannot clear memory caches via XBDM ... without
risking freezing the console") [1].
modules: 202, lines with name="..." base=0x.. size=0x.. check=0x.. timestamp=0x.. osize=0x.. (MEf:385-413, ME PluginXbox360Xbdm.cs FillModuleManager) [1]; ME reads
timestamp as Unix seconds (MEf:411).
modsections name="<module>": 202, lines name="..." base= size= index= flags=; 402
when unknown (MEf:429-447) [1].
xexfield module="<module>" field=0x10100: 202, fieldsize=0x.. then the value as
hex; 437 when the field is absent (MEf:415-426) [1].
threads: 202, one decimal thread id per line (MEc:332-341) [1].
threadinfo thread=0x<id>: 202 with one line suspend= priority= tlsbase= base= limit= slack= nameaddr= namelen= proc= lasterr=, or 405 (MEc:292-330) [1]. The name is read
from memory at nameaddr.
getcontext thread=0x<id> control int fp: 202, lines Name=0x... (32-bit) or
Name=0q... (64-bit): Cr Msr Xer Iar Lr Ctr Gpr0..Gpr31 and floating point (MEf:339-374,
ED:344-364). ED reads them as one argument list, ME line by line [≠] in layout
only. setcontext thread=0x<id> Name=value ..., split over several commands because
of the line limit (ED:366-395).
getpid (3.1).
Pointer chains and scans (what the memory tools build from the above): read 4 bytes,
interpret big-endian, add an offset, repeat (ME Connections/BaseConsoleConnection.cs:124-150; 0 or above 0xFFFFFFFF ends the chain); scans read walkmem regions in
64 KiB chunks with an overlap for values crossing a chunk border
(ME Engine/Scanners/DataTypedScanningContext.cs:46, FirstTypedScanTask.cs:116-190),
often with the console stopped (3.15).
Contract: the memory viewer reads with getmemex in requests of at most 0x8000 bytes
(0x20000 while stopped), falls back to getmem (at most 0x400 bytes per request) when
getmemex answers 407, shows unreadable bytes as ??, and starts read-only. Writes use
setmem with at most 64 bytes per line, are refused for regions whose walkmem
protection has 0x200 (NoCache), and need the explicit unlock from the design goals.
break addr=0x<hex>sets an execution breakpoint;break addr=0x<hex> clear(ME) orbreak clear addr=0x<hex>(ED) removes it (MEf:318-321, ED:311-332).break read|write|readwrite|execute=0x<hex> size=0x<hex> [clear]for data breakpoints (ED:273-309, MEf:323-337). ME sendsreadfor read-or-write (MEf:330), EDreadwrite[≠].break clearall(ED:265-271).stopon fcemakes the console stop on exceptions; ED sends it only to the Natelx XBDM because it "causes exceptions frequently on dk xbdm" (ED:427-434, EmDbg commit a3aa2bf) [1].
Not in the first M3 deliverable; the mock implements break only to answer 200.
Opened on a separate connection (ED:397-408, MEc:1148-1188):
> debugger connect override name="UnnamedGpl3QtApp" user="host"
< 200- connected
> notify reconnectport=0x3039 reverse
< execution stopped (may come before the 205)
< 205- now a notification channel
< debugstr thread=0xfb000010 lf string=Hello world
(Texts after the codes are illustrative; no reference quotes them.)
One line per event, the first word naming it (MEe:28-215, ED:36-135):
| Event | Fields | Source |
|---|---|---|
execution |
flag started, stopped, pending, rebooting, reboot_title, pending_title |
MEe:116-131, ED:55-69 |
debugstr |
thread=, flags lf, stop, then string= running to the end of the line, unquoted |
MEe:133-145, NSd:60-64 ("the 'string' value in debug logs is weird") |
break, singlestep |
addr=, thread=, flag stop |
MEe:40-50, ED:89-100 |
data (ME) / databreak (ED) [≠] |
addr=, thread=, one of read= write= readwrite= execute= |
MEe:42-62, ED:102-127 |
exception |
code= thread= address=, read= or write=, flags first, noncont |
MEe:67-88, ED:70-87 |
rip |
thread= |
MEe:90-94 |
assert |
thread=, then string= or flag prompt |
MEe:96-114 |
create / terminate |
thread=, start= |
MEe:147-160 |
modload |
name=" " base= size= timestamp= checksum=, flags tls, xbe |
MEe:162-186 |
sectload / sectunload |
name= base= size= index= flags= |
MEe:188-206 |
fiber |
id=, start= or flag delete |
MEe:208-224 |
a first word containing ! |
external, from a plugin | MEe:33-35 |
Not in the first M3 deliverable. When used: the client treats a notification connection
as receive-only, parses known events, keeps unknown ones as raw text, and tears down all
connections on execution rebooting.
DRIVE:\folder\file, backslash separators (all references). Drive names have one or more letters (HDD,DEVKIT,GAME,FLASH,E,Z), which is why ON wrote its own path join after Node stopped accepting multi-letter drives (ONutils.ts:85-113).- A drive root is
DRIVE:\(ONdrive-button.tsx:42,drivefreespaceeverywhere). - Executables may be named by device path, e.g.
\Device\Harddisk0\SystemExtPartition\...in XT'sxbeinfo(XT:273) and DevTool's drive table (DTClasses/NeighborhoodDrives.cs:112). - Whether forward slashes are accepted, whether names compare case-insensitively, and the maximum path length are [none].
Contract: the app's virtual paths map as /HDD/a/b to HDD:\a\b; drives are the
top-level folders. The client never sends /. Names are compared case-insensitively
for clash checks (FATX rules).
String values are wrapped in "; no reference escapes anything inside (XC:209,
ON consoles.ts:216, MEf:109). Parsers differ on embedded quotes [≠]: XC and ON end the
value at the next " (XC:704-716, ON xbdm/utils.ts:3-38); NSd treats \" as escaped
(NSd:115-123), which would break on a path ending in \ before the closing quote; ME
treats "" as a literal quote (MEp:130-134). Whether the console understands any escape
is [none]. Spaces inside quotes are fine.
Contract: the client refuses, before sending, any name or path containing ", CR,
LF, NUL or a byte above 0x7E; FATX forbids " in names anyway (the app's FATX name
check, src/core/FatxFileSystem.cpp:405-423). Parsers end a quoted value at the next ".
64-bit count of 100 ns intervals since 1601-01-01, sent as two 32-bit hex halves
(createhi/createlo, changehi/changelo, clockhi/clocklo). Unix seconds =
FILETIME / 10,000,000 - 11,644,473,600 (XC:6-7, ON xbdm/utils.ts:103-109). NSd and ME treat
it as UTC (NSd:191-192, MEf:130-135). Whether the console reports UTC or local time is
[none]; the app's FATX notes say FATX stores local time (docs/ROADMAP.md, M1).
0xhex for 32-bit values (all),0qhex for 64-bit (NSd:90-93, MEp:60, MEf:365-369), plain decimal (thread ids, JRPC2type=). ME parses unprefixed values as decimal (MEp:44-46); NS also (NSd:94-109).- Leading zeros optional: XC writes
0x2a(XC:512), NS and ME0x0000002A(NSd:145, MEf:231). - Hex digits in either case.
Contract: the client writes 0x with lower-case digits, no padding; parsers accept
0x, 0X, 0q, and decimal, upper and lower case, and refuse values that overflow
their width.
- Console memory is big-endian: ME defaults to big-endian (MEi:43-46, "xbox is BE by
default"), DevTool wraps memory in a big-endian reader (DT
Forms/MainForm.cs:90).getmemandgetmemexreturn bytes in address order, so a 32-bit value reads big-endian [inf] (every decoder copies bytes in order). - Binary length headers are little-endian (
getfile,getmemex; 1.6). - Values inside an emulated original-Xbox title are little-endian (xefu, XL
sdk/cpp360/ogxbox.h:9); out of scope.
| What | Value | Status |
|---|---|---|
getfile length |
32-bit; file under 4 GiB | all references |
sendfile length |
announced in hex; no maximum known | [none] |
getmem per request |
ME uses up to 128 bytes | client choice: at most 0x400 |
getmemex per request |
0x8000 to 0x20000 used; block size up to 0x400 | [1], maximum [none] |
setmem per line |
64 (ME) or 128 (XL) bytes | [≠] |
| Command line | limited, value unknown | [none] |
| Connections | limited, value unknown | [none] |
| Drive table | 42 entries, names up to 35 characters | [1] DevTool |
| UDP name | 1-byte length | all |
The mock runs in the test process on a loopback port of its choosing (the client's port is configurable; 730 is the default only for real consoles), or on an in-memory transport if sockets are unavailable. It serves a virtual tree of drives and files and a sparse memory map. It must:
- Send
201- connected\r\non accept; accept bytes that arrive before it. - Read commands as CR LF lines (also bare LF), in order, one at a time, however they
are split across segments; answer
407- unknown commandfor unknown commands and ignore nothing. - Parse arguments in any order, keys and flags case-insensitively, quoted values up to
the next
", tolerate trailing spaces; refuse a line longer than its limit with 406. - Answer every command in this document that the client uses, with the shapes given:
200 single lines, 202 bodies ended by
.,getfilewith a 4-byte little-endian length and no trailer,getmemexin blocks of at most 0x400 bytes with the last-block bit,sendfilewith 204 then exactlylengthbytes then 200. - Errors as the references show them: 410
mkdiron an existing name; 414deleteof a folder withoutdir,getfileof a folder,sendfileonto a folder; 404 forsetmemon unmapped memory;??pairs fromgetmemand an early last block fromgetmemexfor unreadable memory; 405 unknown thread; 408 and 426 forgoandstop. Where the references leave the code open, the mock uses these choices, and tests may rely only on "some 4xx" for them: 411 deleting a non-empty folder; 402 for missing files and paths; 414 withaccess deniedin the text for paths marked protected; 413 when the parent folder is missing; 410 forrenameonto an existing name. bye: answer200- byeand close.magicboot coldandshutdown: close without an answer (the strictest reading); plainmagicboot: answer 200, then close; a setting switches cold reboot to "answer 200 first".- Treat a dropped connection during
sendfilelike a console might: keep the bytes received so far under the target name (the worst case, so the client's clean-up is tested). - Enforce a connection limit (default 4) with
401- max number of connections exceededfollowed by close. - Offer fault injection, each a test switch: silence after the greeting; silence in the
middle of a 202 body or a binary payload; close in the middle of a line, a body or a
payload; a status line split byte by byte; an unknown status code (
299-,500-,abc); a 203 where 200 was expected; agetfilelength larger than the file (the console then stalls) and one above 4 GiB - 1 (0xFFFFFFFF); agetmemexblock larger than requested; notifications interleaved before 205; UDP silent; UDP replies malformed (wrong type byte, length beyond the datagram). - Answer UDP type 3 with type 2 carrying its name, type 1 only when the name matches (case-insensitive), and ignore other datagrams.
- Never depend on timing: no sleeps in the protocol path, so tests are deterministic and sanitizer runs stay fast.
- Framing. Read lines to LF, strip one CR, cap a line at 64 KiB (longer is a protocol error) and a 202 body at 16 MiB, each line counting at least 64 bytes, the memory it costs once parsed. Never interpret a byte stream as lines while a binary payload is pending, and never leave payload bytes in the buffer for the next command.
- One command at a time per connection (1.10); a mutex or the per-mount worker guarantees it.
- Status handling as in 1.4: 2xx expected by the command proceeds; 4xx becomes a
Statusfailure with the code and text, connection kept; anything else drops the connection. - Timeouts as in 1.11, idle-based, with a cancel that works at any point (the app's
Statuscancel). Cancel during a response drops the connection, because the rest of the response cannot be skipped reliably. - Huge or wrong lengths. Refuse before reading: a
getfilelength above the size from the listing (when known) or above the sink's limit; a screenshot above 64 MiB; agetmemexblock above 0x7FFF (impossible) or above what remains (drop, as ME does; do not read past). Never allocate from a length the console sent: stream into the sink in pieces of at most 64 KiB. - Silence. A console that stops sending trips the idle timeout; the connection is closed, the job fails with "the console stopped responding", and queued commands for that console fail at once instead of each waiting for its own timeout (XL:162-174 does the same).
- Drops mid-transfer.
getfile: the partial local file is discarded (the sink is not finished).sendfile: the temporary remote file is deleted on the next connection, best effort; the final name is never touched until the upload has its 200 (3.6). - Reconnect. A drop while idle is repaired once on the next command. The greeting is required again; no state is assumed to persist except what the client itself changed (execution stopped, debugger attached).
- Input checks before sending (4.2): no
", CR, LF, NUL or non-ASCII in names; onemkdirper level;renamewithin one folder;setmemat most 64 bytes per line. - No pipelining, no keepalive, no per-file connections (1.10, 1.12, 1.13).
- Parsing. Fields by key in any order; unknown keys and flags ignored; missing optional fields default sensibly; values that do not parse fail that entry, not the listing (the listing reports how many entries were skipped).
- Portability. Sockets live behind a small transport interface (POSIX and Winsock), which also lets the tests run the client against the mock without sockets.
Each needs a real console (or an emulator that implements XBDM) to settle.
- What exactly does
getfileattributesreturn (200 or 202, which fields, thedirectoryflag), and which error for a missing path? - Which codes does
dirlistgive for a missing folder, a file instead of a folder, and a protected folder? Doesdirlist name="HDD:"(no backslash) work? - Does
dirlisthave flags besidesdirectory(read-only, hidden, ...), and is the field order always name, size, times, flags? - Is
drivefreespace200 or 202, and which drives refuse it? - Does
sendfileonto an existing file replace it? Does it need the parent folder to exist (413)? - After a connection drops during
sendfile, what is left on the console: nothing, a truncated file under the final name, or something else? Does a later command on a new connection work at once? - What does the console answer after
sendfiledata when the device fills up, and when does it send it? - Can
getfileandsendfilehandle 4 GiB or more on any console (DEVKIT:and other non-FATX volumes)? - Does
renameonto an existing name fail, and with which code? Does a rename across drives give 409? - Does
mkdircreate missing parent folders (ME's claim)? - Which code does deleting a non-empty folder give (411?), and deleting a missing path?
- Are path names compared case-insensitively? Are forward slashes accepted? What is the maximum path length?
- Is there any escape for
"in quoted values? - What is the maximum command line length (406 or 446 when exceeded)?
- How many connections does a console allow at once (401)? Does it count the notification connection?
- Does the console close idle connections, and after how long? Is a keepalive needed?
- What do
reconnectportandreversedo onnotify? Doboxidor adedicatecommand exist? - What text does the 205 line carry, and does a dedicated connection accept further commands?
- Are FILETIMEs from
dirlistUTC or local time? Doessetsystimetaketz, in what format? - What values does
consoletypereturn, and what dosysteminfo,dmversionandgetconsoleidreturn, if they exist? - What does
whomadethisreturn on a devkit? - What does
helplist, and in what format? Can it serve as a capability probe? - Does
magicboot coldanswer 200 before the connection closes, or close at once? Does plainmagicbootgo to the dashboard or restart the title? Doesmagicboot title=answer, and isdirectoryneeded with or without a trailing backslash? - Is
getmemexalways present? What is its largest acceptedlength, its block size, and the exact meaning of bit 15? getmemon unmapped memory: 202 with??, or an error? How many bytes per line?- Does
setmemon a partly unmapped range write the mapped part? What is its 200 text? - Do
setmemex,sendvfileandsetfileattributesexist, and in which form? - Which
walkmemprotections are unsafe to read (ME's NoCache warning)? - Does sending a command before the greeting, or two commands in one packet, break any
target (ON's devkit
byeproblem)? Does it depend on the console? - Does a type-1 lookup match names case-insensitively?
- Is the
getfilelength really little-endian?
Added when the client first ran against the mock (docs/HARDWARE_TEST_PLAN.md, N1 to N8):
- Does the console create the
sendfiletarget before it answers 204, so that a drop right after the 204 leaves an empty file? - Does every target implement
getfileattributes? The client'sdownloadToFilesends it before everygetfile, and lists the parent folder instead after a 407 or an answer without a size. - Is
magicboot title=... directory="DRIVE:\"accepted for a file in a drive root, or istitlealone needed there, as DevTool sends forFLASH:? - Are the formats the client sends accepted everywhere: lower-case
0xhex without padding,setmemdata as upper-case hex without quotes? - Is
byealways answered200- byebefore the close? - Is a connection over the limit refused instead of the greeting, or after it?
- Does a reboot drop every connection, or only the one that asked for it?
- Screenshot: the field order and separators of the geometry line, and how
framebuffersizerelates topitch * height.
Added with the upload replace rule and case-only renames (docs/GAP_FIX_PLAN.md):
- Does
renameonto an existing name answer 410, or replace the file? - Is a
renamethat changes only the case of a name accepted, and does it change the stored case?
- XC finds the end of a response by a 10 ms receive timeout on Linux and 5 s on Windows
(XC:55-63, XC:646-672), sleeps after every command and every 1 KiB of upload
(XC:681, XC:561), and truncates after the first
.\r\n(XC:665-669). - XC compares the exact
203-and204-header text and reads it with a fixed-sizerecv(XC:403-428, XC:516-542), and reads the 4-byte length with onerecvthat may return fewer bytes (XC:431-436). - XT:
dirlistsends.after every entry (XT:181);sizehi/sizeloaresize & 0xffff0000andsize & 0xffff(XT:174-175, XT:216-217);magicbootanswers without CR LF (XT:253); missing files are 404 (memory not mapped) instead of a file error (XT:156, XT:342);sendfilesends 204 before it knows it can create the file (XT:418-427);mkdiron an existing name is 400, not 410 (XT:517-520). - ONO's paste uses
XboxPath::FileName(), which drops the extension (ONOsrc/Panels/ContentsPanel.cpp:169,XBDM/src/XboxPath.cpp:29-45). - NSc and ON never read the
200aftersendfiledata (NSx:133-137, ON consoles.ts:342-350); NSc reads binary bodies with singleStream.Readcalls that may return short (NSc:118-123). - ED swaps
suspendandresume(ED:251-263). - ME's reader loop computes a 5000-second timeout where it means 5 s (MEc:932).