diff --git a/src/libretro/api/_utils.py b/src/libretro/api/_utils.py index 8a27a45..497fa40 100644 --- a/src/libretro/api/_utils.py +++ b/src/libretro/api/_utils.py @@ -129,7 +129,7 @@ def addressof_buffer(buffer: Buffer) -> int: def memoryview_at( - address: c_char_p | c_void_p | int, size: c_ssize_t | int, readonly=False + address: c_char_p | c_void_p | int | bytes, size: c_ssize_t | int, readonly=False ) -> memoryview: flags = c_int(0x100 if readonly else 0x200) return pythonapi.PyMemoryView_FromMemory(cast(address, c_char_p), c_ssize_t(size), flags) diff --git a/src/libretro/core.py b/src/libretro/core.py index 39758e3..6c2ba08 100644 --- a/src/libretro/core.py +++ b/src/libretro/core.py @@ -513,7 +513,13 @@ def unserialize(self, data: bytes | bytearray | memoryview | Buffer) -> bool: """ buf: memoryview match data: - case bytes() | bytearray() | Buffer(): + case bytes(): + buf = memoryview_at(data, len(data), readonly=False) + # HACK! ctypes.Array.from_buffer requires a writable buffer, + # but bytes objects are read-only. + # retro_unserialize isn't supposed to modify the buffer, + # so we can blame undefined behavior if the core tries to write to it anyway. + case bytearray() | Buffer(): buf = memoryview(data) case memoryview(): buf = data @@ -525,6 +531,7 @@ def unserialize(self, data: bytes | bytearray | memoryview | Buffer) -> bool: buflen = len(buf) arraytype: Array = c_char * buflen + # TODO: Validate that the buffer wasn't written to, and raise a warning if it was. (Use zlib.crc32) return self._core.retro_unserialize(byref(arraytype.from_buffer(buf)), buflen) def cheat_reset(self):