retroarch_read_ram
Read memory from libretro cores that do not advertise a memory map, using the CHEEVOS address space as a fallback.
Instructions
Read memory via the CHEEVOS (achievements) address space. Use this if retroarch_read_memory reports 'no memory map defined' — some libretro cores don't advertise a memory map but DO support the older CHEEVOS read API.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| address | Yes | ||
| length | Yes |
Implementation Reference
- src/tools.ts:72-83 (registration)Tool registration entry for retroarch_read_ram, defined in the TOOLS array with its description (CHEEVOS address space) and input schema requiring address (integer) and length (integer, 1-4096).
{ name: "retroarch_read_ram", description: "Read memory via the CHEEVOS (achievements) address space. Use this if `retroarch_read_memory` reports 'no memory map defined' — some libretro cores don't advertise a memory map but DO support the older CHEEVOS read API.", inputSchema: { type: "object", required: ["address", "length"], properties: { address: { type: "integer" }, length: { type: "integer", minimum: 1, maximum: 4096 }, }, }, }, - src/tools.ts:216-220 (handler)Handler for retroarch_read_ram in the CallToolRequestSchema switch. Calls ra.readRam() on the RetroArchClient instance, formats the returned bytes as hex, and returns them with a CHEEVOS label.
case "retroarch_read_ram": { const bytes = await ra.readRam(p.address as number, p.length as number); const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0").toUpperCase()).join(" "); return ok(`${addrHex(p.address as number)} [${bytes.length} bytes, CHEEVOS]:\n${hex}`); } - src/retroarch.ts:158-164 (helper)RetroArchClient.readRam() — the underlying helper that sends the READ_CORE_RAM command over UDP to RetroArch's NCI, then parses the response via parseMemoryReply().
async readRam(addr: number, length: number): Promise<Uint8Array> { if (length <= 0) throw new Error("length must be positive"); if (length > 4096) throw new Error("length exceeds 4096 byte limit"); const cmd = `READ_CORE_RAM 0x${addr.toString(16)} ${length}`; const r = (await this.query(cmd)).toString().trim(); return parseMemoryReply(r, "READ_CORE_RAM", length); } - src/retroarch.ts:230-257 (helper)parseMemoryReply — shared helper used by readRam() to parse the READ_CORE_RAM UDP response. Handles success (hex bytes), failure (-1 error), and malformed data.
function parseMemoryReply(reply: string, expectedCmd: string, expectedLen: number): Uint8Array { const tokens = reply.split(/\s+/); if (tokens[0] !== expectedCmd) { throw new Error(`unexpected reply prefix (got "${tokens[0]}", expected "${expectedCmd}")`); } const tail = tokens.slice(2); if (tail.length === 0) { throw new Error(`${expectedCmd} returned no bytes`); } // Failure shape: "<cmd> <addr> -1 <error_msg>" if (tail[0] === "-1") { const err = tail.slice(1).join(" ") || "(no error message)"; throw new Error(`${expectedCmd} failed: ${err}`); } const out = new Uint8Array(tail.length); for (let i = 0; i < tail.length; i++) { const v = parseInt(tail[i], 16); if (Number.isNaN(v) || v < 0 || v > 255) { throw new Error(`${expectedCmd}: malformed byte at index ${i}: "${tail[i]}"`); } out[i] = v; } if (out.length !== expectedLen) { // Not strictly an error — RetroArch may clamp at memory-region boundaries. // Caller can decide what to do with a short read. } return out; }