v3.0.0
⚠️ Breaking change
read_process_memory(addr, int, N) with N in 3, 5, 6 or 7 and the value's
top bit set now returns a different number — on every platform, with no
exception raised.
bytes in memory: FF FF FF (a 24-bit unsigned field at its maximum)
2.2.1 -> 16777215
3.0.0 -> -1
Cheat Engine offers a "3 Bytes" type because games cap money and score in
24-bit fields, so this is reachable by following an ordinary tutorial. An
existing script, unchanged:
money = process.read_process_memory(addr, int, 3)
if money < 1000:
process.write_process_memory(addr, int, 3, 16777215) # "top up"| value read | money < 1000 |
result | |
|---|---|---|---|
| 2.2.1 | 16777215 | False |
does nothing — correct |
| 3.0.0 | −1 | True |
tops up a field already at maximum |
The guard inverts, and the branch that flips is the one that writes. Half
the value space of each affected width changes sign. Widths 1, 2, 4 and 8 are
untouched, and so is the int default of 4.
Why: int is signed everywhere else in this library — every C type it uses
is signed, the unsigned reads have their own family, and a scan already
compares signed on both of its sides. Widths 3/5/6/7 read unsigned only because
the padding byte happened to be zero, so a scan could match -1 at an address
that every read then reported as 16777215.
If you want the old value:
raw = process.read_process_memory(addr, bytes, 3)
value = int.from_bytes(raw, sys.byteorder, signed=False)Other behaviour changes
These reject what used to be accepted. Nothing that worked depends on them —
the previous behaviour was memory-unsafe or meaningless:
- A width wider than the type's largest C representation now raises
ValueError.get_c_type_of(bool, 8)sized a 1-byte buffer for an 8-byte
read, overflowing the calling process's memory;intat 16 overflowed 8. floataccepts only 4 and 8. IEEE-754 has no form between them, so other
widths returned a plausible-looking number decoded from bytes nobody read
(5.5e-318).- Linux reads and writes the width you asked for, not
sizeof(buffer). A
3-byte write used to touch 4 bytes, destroying a neighbouring byte, and the
same read answered differently per platform. search_by_addressesreturns values at unusualintwidths instead of
None, and raises for a width the type cannot represent instead of reporting
the address as unreadable.
str and bytes are unchanged.
New: MCP server
An optional Model Context Protocol server,
so an AI assistant can run the scan / refine / read loop itself.
pip install "PyMemoryEditor[mcp]"
claude mcp add pymemoryeditor -- pymemoryeditor-mcpFourteen tools cover the workflow, with results kept server-side behind a
handle so a 40 000-hit first scan costs a few tokens rather than a context
window. You approve every attach and your client confirms every write; OS and
credential processes are refused outright. The core library stays
dependency-free — only the protocol layer needs the SDK.
See the MCP guide.
Also
iter_processes()— a dependency-free counterpart topsutil.process_iter().decode_scan_targetandmake_predicatepromoted from private, so anything
that must agree with a scan can compare the way a scan does.