diff --git a/Core/Debugger/WebSocket.cpp b/Core/Debugger/WebSocket.cpp index 4b0023bab9..7ff42a2d29 100644 --- a/Core/Debugger/WebSocket.cpp +++ b/Core/Debugger/WebSocket.cpp @@ -188,6 +188,13 @@ void HandleDebuggerRequest(const http::ServerRequest &request) { WebSocketClientInfo client_info; auto& disallowed_config = client_info.disallowed; + // Seed every broadcaster category. broadcast.config.set only accepts keys that already exist + // here (so a typo is rejected rather than silently ignored), and these otherwise only appear + // as a side effect of operator[] the first time each category actually broadcasts - which + // meant "game" and "stepping" were rejected as unsupported until one happened to fire, even + // though they're documented and valid. Keep in sync with the Broadcast calls further down. + for (const char *category : { "logger", "input", "game", "stepping" }) + disallowed_config[category] = false; LogBroadcaster logger; InputBroadcaster input; diff --git a/Core/Debugger/WebSocket/MemorySubscriber.cpp b/Core/Debugger/WebSocket/MemorySubscriber.cpp index 2f63cbf064..37e75816ed 100644 --- a/Core/Debugger/WebSocket/MemorySubscriber.cpp +++ b/Core/Debugger/WebSocket/MemorySubscriber.cpp @@ -103,6 +103,8 @@ AutoDisabledReplacements::~AutoDisabledReplacements() { // // Response (same event name): // - value: unsigned integer +// - uintValue: the same number under the name cpu.getReg/cpu.getAllRegs use, so a client can +// read either without special-casing which event it came from void WebSocketMemoryReadU8(DebuggerRequest &req) { uint32_t addr; if (!req.ParamU32("address", &addr, false)) { @@ -122,6 +124,8 @@ void WebSocketMemoryReadU8(DebuggerRequest &req) { AutoDisabledReplacements memLock = LockMemory(true); JsonWriter &json = req.Respond(); json.writeUint("value", Memory::ReadUnchecked_U8(addr)); + // Alias: cpu.getReg and cpu.getAllRegs call this uintValue. Same number, both names. + json.writeUint("uintValue", Memory::ReadUnchecked_U8(addr)); }); } @@ -132,6 +136,8 @@ void WebSocketMemoryReadU8(DebuggerRequest &req) { // // Response (same event name): // - value: unsigned integer +// - uintValue: the same number under the name cpu.getReg/cpu.getAllRegs use, so a client can +// read either without special-casing which event it came from void WebSocketMemoryReadU16(DebuggerRequest &req) { uint32_t addr; if (!req.ParamU32("address", &addr, false)) { @@ -151,6 +157,7 @@ void WebSocketMemoryReadU16(DebuggerRequest &req) { AutoDisabledReplacements memLock = LockMemory(true); JsonWriter &json = req.Respond(); json.writeUint("value", Memory::ReadUnchecked_U16(addr)); + json.writeUint("uintValue", Memory::ReadUnchecked_U16(addr)); }); } @@ -161,6 +168,8 @@ void WebSocketMemoryReadU16(DebuggerRequest &req) { // // Response (same event name): // - value: unsigned integer +// - uintValue: the same number under the name cpu.getReg/cpu.getAllRegs use, so a client can +// read either without special-casing which event it came from void WebSocketMemoryReadU32(DebuggerRequest &req) { uint32_t addr; if (!req.ParamU32("address", &addr, false)) { @@ -180,6 +189,7 @@ void WebSocketMemoryReadU32(DebuggerRequest &req) { AutoDisabledReplacements memLock = LockMemory(true); JsonWriter &json = req.Respond(); json.writeUint("value", Memory::ReadUnchecked_U32(addr)); + json.writeUint("uintValue", Memory::ReadUnchecked_U32(addr)); }); } @@ -330,6 +340,8 @@ void WebSocketMemoryWriteU8(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeUint("value", Memory::ReadUnchecked_U8(addr)); + // Alias: cpu.getReg and cpu.getAllRegs call this uintValue. Same number, both names. + json.writeUint("uintValue", Memory::ReadUnchecked_U8(addr)); }); } @@ -367,6 +379,7 @@ void WebSocketMemoryWriteU16(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeUint("value", Memory::ReadUnchecked_U16(addr)); + json.writeUint("uintValue", Memory::ReadUnchecked_U16(addr)); }); } @@ -404,6 +417,7 @@ void WebSocketMemoryWriteU32(DebuggerRequest &req) { JsonWriter &json = req.Respond(); json.writeUint("value", Memory::ReadUnchecked_U32(addr)); + json.writeUint("uintValue", Memory::ReadUnchecked_U32(addr)); }); } diff --git a/docs/WebSocketDebugger.md b/docs/WebSocketDebugger.md index 9b44ef17e1..38fee74c72 100644 --- a/docs/WebSocketDebugger.md +++ b/docs/WebSocketDebugger.md @@ -118,7 +118,7 @@ file - this is just an index. | CPU core | `cpu.stepping`, `cpu.resume`, `cpu.status` (reports `ticks` plus `us`, emulated microseconds, and `clockHz` - use `us` to line up with wall-clock timings, since games change the clock frequency and the ticks-per-second ratio isn't fixed), `cpu.getAllRegs`, `cpu.getReg`, `cpu.setReg`, `cpu.evaluate` | `CPUCoreSubscriber.cpp` | | Stepping | `cpu.stepInto`, `cpu.stepOver`, `cpu.stepOut`, `cpu.runUntil`, `cpu.runUntilTime` (run until a point in emulated time - `us` absolute or `relativeUs` from now - and break there; this is how to get a scripted repro reproducibly "N seconds into the game" instead of polling `cpu.status` in a loop), `cpu.nextHLE` | `SteppingSubscriber.cpp` | | Breakpoints | `cpu.breakpoint.add/update/remove/list`, `memory.breakpoint.add/update/remove/list`, `cpu.regBreakpoint.add/update/remove/list` (break when a register is written to, by any instruction anywhere - currently GPRs only; interpreter-only, no effect under a JIT backend) | `BreakpointSubscriber.cpp` | -| Memory read/write | `memory.read_u8/u16/u32`, `memory.read`, `memory.readString`, `memory.write_u8/u16/u32`, `memory.write` | `MemorySubscriber.cpp` | +| Memory read/write | `memory.read_u8/u16/u32`, `memory.read`, `memory.readString`, `memory.write_u8/u16/u32`, `memory.write`. The numeric ones report the result as both `value` and `uintValue` - the latter is what `cpu.getReg`/`cpu.getAllRegs` call it, so a client can read either without caring which event answered | `MemorySubscriber.cpp` | | Memory search | `memory.search` - scan a range for a `u8`/`u16`/`u32`/`float` value or a `bytes` pattern (with an optional wildcard mask), for narrowing down where an unknown value lives (Cheat Engine style) | `MemorySubscriber.cpp` | | Memory info/annotations | `memory.mapping`, `memory.info.config/set/list/search` | `MemoryInfoSubscriber.cpp` | | Disassembly | `memory.base`, `memory.disasm` (add `compact=true` for plain-text lines instead of full per-field objects), `memory.searchDisasm` (add `findAll=true` for every match instead of just the first - e.g. "every caller of this address"), `memory.assemble` | `DisasmSubscriber.cpp` |