7#if defined(SDL_LIBRETRO_IMPLEMENTATION) && !defined(SDL_LIBRETRO_SERIALIZE_IMPL_ONCE)
8#define SDL_LIBRETRO_SERIALIZE_IMPL_ONCE
21#define SDL_LIBRETRO_ENABLE_REWIND_DELTA
25#ifndef SDL_LIBRETRO_REWIND_DEFAULT_MAX_BYTES
31#define SDL_LIBRETRO_REWIND_DEFAULT_MAX_BYTES ((size_t)256 * 1024 * 1024)
36bool SDL_Libretro_SetCheat(SDL_Libretro* lr,
unsigned index,
bool enabled,
const char* code) {
37 if (!lr || !lr->core.gameLoaded)
return false;
38 lr->core.symbols.retro_cheat_set(index, enabled, code);
42void SDL_Libretro_ResetCheats(SDL_Libretro* lr) {
43 if (!lr || !lr->core.gameLoaded)
return;
44 lr->core.symbols.retro_cheat_reset();
53 if (!lr || !lr->core.gameLoaded)
return 0;
54 return lr->core.symbols.retro_serialize_size();
62 if (!lr || !lr->core.gameLoaded || !dst) {
63 SDL_SetError(
"[SDL_Libretro] Invalid SaveState_IO arguments");
65 size_t size = lr->core.symbols.retro_serialize_size();
67 SDL_SetError(
"[SDL_Libretro] Core does not support save states");
69 void* data = SDL_malloc(size);
71 if (lr->core.symbols.retro_serialize(data, size)) {
72 ok = (SDL_WriteIO(dst, data, size) == size);
78 if (closeio && dst) SDL_CloseIO(dst);
86 if (!lr || !lr->core.gameLoaded || !file) {
87 SDL_SetError(
"[SDL_Libretro] Invalid SaveState arguments");
91 SDL_IOStream* io = SDL_IOFromFile(file,
"wb");
92 if (!io)
return false;
96bool SDL_Libretro_LoadState_IO(SDL_Libretro* lr, SDL_IOStream* src,
bool closeio) {
97 if (!lr || !lr->core.gameLoaded || !src) {
98 SDL_SetError(
"[SDL_Libretro] Invalid LoadState_IO arguments");
99 if (closeio && src) SDL_CloseIO(src);
103 void* data = SDL_LoadFile_IO(src, &size, closeio);
104 if (!data)
return false;
105 bool ok = (size > 0) && lr->core.symbols.retro_unserialize(data, size);
108 if (ok && lr->rewindEnabled) SDL_Libretro_ClearRewind(lr);
116 if (!lr || !lr->core.gameLoaded || !file) {
117 SDL_SetError(
"[SDL_Libretro] Invalid LoadState arguments");
120 SDL_IOStream* io = SDL_IOFromFile(file,
"rb");
121 if (!io)
return false;
122 return SDL_Libretro_LoadState_IO(lr, io,
true);
140 if (!lr || !lr->core.gameLoaded || !lr->core.symbols.retro_get_memory_data || !lr->core.symbols.retro_get_memory_size) {
144 void* ptr = lr->core.symbols.retro_get_memory_data(memoryType);
145 if (size) *size = lr->core.symbols.retro_get_memory_size(memoryType);
164 SDL_SetError(
"[SDL_Libretro] Invalid SetMemoryData arguments");
169 if (!dst || capacity == 0) {
170 SDL_SetError(
"[SDL_Libretro] Memory type %u unavailable", memoryType);
173 size_t copySize = size < capacity ? size : capacity;
174 SDL_memcpy(dst, data, copySize);
184static void SDL_Libretro_FreeMemoryMap(SDL_Libretro* lr) {
185 if (lr->core.memoryMapDescriptors) {
186 for (
unsigned i = 0; i < lr->core.memoryMapDescriptorCount; i++) {
187 SDL_free((
void*)lr->core.memoryMapDescriptors[i].addrspace);
189 SDL_free(lr->core.memoryMapDescriptors);
190 lr->core.memoryMapDescriptors = NULL;
192 lr->core.memoryMapDescriptorCount = 0;
208 return (lr && lr->core.gameLoaded) ? lr->core.memoryMapDescriptorCount : 0;
233 Uint64* flags,
void** ptr,
size_t* offset,
size_t* start,
234 size_t* select,
size_t* disconnect,
size_t* len,
const char** addrspace) {
235 if (!lr || !lr->core.gameLoaded || index >= lr->core.memoryMapDescriptorCount) {
238 const struct retro_memory_descriptor* d = &lr->core.memoryMapDescriptors[index];
239 if (flags) *flags = d->flags;
240 if (ptr) *ptr = d->ptr;
241 if (offset) *offset = d->offset;
242 if (start) *start = d->start;
243 if (select) *select = d->select;
244 if (disconnect) *disconnect = d->disconnect;
245 if (len) *len = d->len;
246 if (addrspace) *addrspace = d->addrspace;
273 if (regionRemaining) *regionRemaining = 0;
274 if (!lr || !lr->core.gameLoaded)
return NULL;
276 for (
unsigned i = 0; i < lr->core.memoryMapDescriptorCount; i++) {
277 const struct retro_memory_descriptor* d = &lr->core.memoryMapDescriptors[i];
278 if (!d->ptr || d->len == 0)
continue;
281 if (d->select != 0) {
282 if (((address ^ d->start) & d->select) != 0)
continue;
283 }
else if (address < d->start || (address - d->start) >= d->len) {
288 size_t masked = address & ~d->disconnect;
289 if (masked < d->start)
continue;
290 size_t within = masked - d->start;
291 if (within >= d->len)
continue;
293 if (regionRemaining) *regionRemaining = d->len - within;
294 return (Uint8*)d->ptr + d->offset + within;
304static const char* SDL_Libretro_GetMemoryTypeName(
unsigned memoryType) {
305 switch (memoryType) {
306 case RETRO_MEMORY_SAVE_RAM:
return "SRAM";
307 case RETRO_MEMORY_RTC:
return "RTC";
308 case RETRO_MEMORY_SYSTEM_RAM:
return "system RAM";
309 case RETRO_MEMORY_VIDEO_RAM:
return "video RAM";
310 default:
return "memory";
327 if (!lr || !lr->core.gameLoaded || !dst) {
328 SDL_SetError(
"[SDL_Libretro] Invalid SaveMemory_IO arguments");
332 if (!mem || size == 0) {
335 ok = (SDL_WriteIO(dst, mem, size) == size);
338 if (closeio && dst) SDL_CloseIO(dst);
354 if (!lr || !lr->core.gameLoaded || !file) {
355 SDL_SetError(
"[SDL_Libretro] Invalid SaveMemory arguments");
362 if (!mem || size == 0)
return true;
364 SDL_IOStream* io = SDL_IOFromFile(file,
"wb");
365 if (!io)
return false;
368 SDL_Log(
"[SDL_Libretro] %s saved to %s (%zu bytes)",
369 SDL_Libretro_GetMemoryTypeName(memoryType), file, size);
389 if (!lr || !lr->core.gameLoaded || !src) {
390 SDL_SetError(
"[SDL_Libretro] Invalid LoadMemory_IO arguments");
391 if (closeio && src) SDL_CloseIO(src);
398 SDL_SetError(
"[SDL_Libretro] Core has no %s", SDL_Libretro_GetMemoryTypeName(memoryType));
399 if (closeio) SDL_CloseIO(src);
404 void* data = SDL_LoadFile_IO(src, &fileSize, closeio);
405 if (!data)
return false;
408 if (fileSize != capacity) {
409 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
410 "[SDL_Libretro] %s size mismatch (file %zu bytes, region %zu bytes); loading %zu",
411 SDL_Libretro_GetMemoryTypeName(memoryType), fileSize, capacity,
412 fileSize < capacity ? fileSize : capacity);
432 if (!lr || !lr->core.gameLoaded || !file) {
433 SDL_SetError(
"[SDL_Libretro] Invalid LoadMemory arguments");
436 SDL_IOStream* io = SDL_IOFromFile(file,
"rb");
437 if (!io)
return false;
440 SDL_Log(
"[SDL_Libretro] %s loaded from %s",
441 SDL_Libretro_GetMemoryTypeName(memoryType), file);
448bool SDL_Libretro_SaveSRAM_IO(SDL_Libretro* lr, SDL_IOStream* dst,
bool closeio) {
459bool SDL_Libretro_LoadSRAM_IO(SDL_Libretro* lr, SDL_IOStream* src,
bool closeio) {
472#ifdef SDL_LIBRETRO_ENABLE_REWIND_DELTA
487static size_t SDL_Libretro_RewindMaxEncodedSize(
size_t len) {
488 return len + (len / 128) + 1;
499static size_t SDL_Libretro_RewindMatchRun(
const unsigned char* a,
const unsigned char* b,
size_t max) {
501 for (; i +
sizeof(size_t) <= max; i +=
sizeof(size_t)) {
503 SDL_memcpy(&wa, a + i,
sizeof(
size_t));
504 SDL_memcpy(&wb, b + i,
sizeof(
size_t));
507 while (i < max && a[i] == b[i]) i++;
525static size_t SDL_Libretro_RewindEncodeDelta(
const unsigned char* cur,
const unsigned char* ref,
size_t len,
unsigned char* out,
size_t outCap) {
527 const size_t kMinSkip = 3;
529 size_t op = 0, i = 0;
531 if (cur[i] == ref[i]) {
532 size_t run = SDL_Libretro_RewindMatchRun(cur + i, ref + i, len - i);
536 if (out) {
if (op >= outCap)
return 0; out[op] = (
unsigned char)run; }
539 }
else if (run < 255) {
541 if (out) {
if (op >= outCap)
return 0; out[op] = 127; }
545 size_t chunk = run > 65535 ? 65535 : run;
547 if (op + 3 > outCap)
return 0;
549 out[op + 1] = (
unsigned char)(chunk & 0xFF);
550 out[op + 2] = (
unsigned char)(chunk >> 8);
562 while (j < len && cur[j] != ref[j]) j++;
566 j += SDL_Libretro_RewindMatchRun(cur + j, ref + j, len - j);
567 if ((j - gapStart) >= kMinSkip || j >= len)
break;
570 for (
size_t pos = segStart; pos < segEnd; ) {
571 size_t chunk = segEnd - pos;
572 if (chunk > 128) chunk = 128;
574 if (op + 1 + chunk > outCap)
return 0;
575 out[op] = (
unsigned char)(0x80 | (chunk - 1));
578 for (
size_t k = pos; k < pos + chunk; k++) {
579 if (out) out[op] = cur[k] ^ ref[k];
598static bool SDL_Libretro_RewindDecodeDelta(
const unsigned char* delta,
size_t deltaLen,
unsigned char* state,
size_t stateLen) {
599 size_t dp = 0, sp = 0;
600 while (dp < deltaLen && sp < stateLen) {
601 unsigned char tag = delta[dp++];
603 if (dp + 2 > deltaLen)
return false;
604 size_t skip = delta[dp] | ((size_t)delta[dp + 1] << 8);
607 }
else if (tag <= 0x7F) {
610 size_t count = (tag & 0x7F) + 1;
611 if (dp + count > deltaLen || sp + count > stateLen)
return false;
612 for (
size_t j = 0; j < count; j++)
613 state[sp++] ^= delta[dp++];
616 return (sp <= stateLen);
638 if (!lr)
return false;
640 SDL_Libretro_RewindFree(lr);
643 lr->rewindEnabled =
false;
648 if (bufferFrames == 0) bufferFrames = 300;
649 if (captureInterval == 0) captureInterval = 1;
652 if (!lr->core.loaded) {
653 lr->rewindEnabled =
true;
654 lr->rewindCapacity = bufferFrames;
655 lr->rewindCaptureInterval = captureInterval;
660 size_t slotSize = lr->core.symbols.retro_serialize_size();
662 SDL_SetError(
"[SDL_Libretro] Core does not support serialization");
663 lr->rewindEnabled =
false;
668 if (lr->core.serializationQuirks & RETRO_SERIALIZATION_QUIRK_INCOMPLETE) {
669 SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
670 "[SDL_Libretro] Core reports incomplete serialization, so rewind may be unreliable");
674 unsigned char* ref = (
unsigned char*)SDL_calloc(1, slotSize);
675 unsigned char* scratch = (
unsigned char*)SDL_malloc(slotSize);
676 SDL_LibretroRewindDelta* entries = (SDL_LibretroRewindDelta*)SDL_calloc(bufferFrames,
sizeof(*entries));
677 unsigned char* encScratch = NULL;
678 bool ok = (ref && scratch && entries);
679#ifdef SDL_LIBRETRO_ENABLE_REWIND_DELTA
681 encScratch = (
unsigned char*)SDL_malloc(SDL_Libretro_RewindMaxEncodedSize(slotSize));
682 ok = ok && (encScratch != NULL);
687 SDL_free(encScratch);
689 SDL_SetError(
"[SDL_Libretro] Failed to allocate rewind buffers");
690 lr->rewindEnabled =
false;
695 lr->rewindReference = ref;
696 lr->rewindScratch = scratch;
697 lr->rewindEncodeScratch = encScratch;
698 lr->rewindEntries = entries;
699 lr->rewindSlotSize = slotSize;
701 lr->rewindCapacity = bufferFrames;
702 lr->rewindCaptureInterval = captureInterval;
705 lr->rewindFrameCounter = 0;
706 lr->rewindEnabled =
true;
707 lr->rewindHasReference =
false;
708 lr->rewindActive =
false;
719 return lr && lr->rewindEnabled;
729 if (!lr || !lr->rewindEnabled || lr->rewindCount == 0)
return 0.0;
730 double fps = lr->core.fps > 0.0 ? lr->core.fps : 60.0;
731 return (
double)lr->rewindCount * (double)lr->rewindCaptureInterval / fps;
744 if (!lr || !lr->rewindEnabled)
return 0;
745 size_t total = lr->rewindBytes;
746 if (lr->rewindReference) total += lr->rewindSlotSize;
747 if (lr->rewindScratch) total += lr->rewindSlotSize;
748 total += (size_t)lr->rewindCapacity *
sizeof(SDL_LibretroRewindDelta);
765 lr->rewindMaxBytes = maxBytes;
766 SDL_Libretro_RewindEvictToBudget(lr);
776 return lr ? lr->rewindMaxBytes : 0;
796 if (!lr)
return false;
797 if (!(seconds > 0.0)) {
798 SDL_SetError(
"[SDL_Libretro] Rewind duration must be positive");
804 size_t slotSize = lr->rewindSlotSize;
805 if (slotSize == 0 && lr->core.loaded && lr->core.symbols.retro_serialize_size) {
806 slotSize = lr->core.symbols.retro_serialize_size();
809 SDL_SetError(
"[SDL_Libretro] Rewind state size unknown; load a serializable core first");
813 double fps = (lr->core.fps > 0.0) ? lr->core.fps : 60.0;
814 unsigned interval = (lr->rewindCaptureInterval > 0) ? lr->rewindCaptureInterval : 1;
818 double snapshots = (seconds * fps) / (
double)interval;
819 if (snapshots < 1.0) snapshots = 1.0;
820 size_t snaps = (size_t)snapshots;
821 if ((
double)snaps < snapshots) snaps++;
832static void SDL_Libretro_RewindCapture(SDL_Libretro* lr) {
833 if (!lr->rewindEnabled || !lr->rewindReference)
return;
835 lr->rewindFrameCounter++;
836 if (lr->rewindFrameCounter < lr->rewindCaptureInterval)
return;
837 lr->rewindFrameCounter = 0;
845 size_t curSize = lr->core.symbols.retro_serialize_size();
846 if (curSize == 0)
return;
847 if (curSize != lr->rewindSlotSize) {
848 unsigned char* nref = (
unsigned char*)SDL_calloc(1, curSize);
849 unsigned char* nscr = (
unsigned char*)SDL_malloc(curSize);
850 unsigned char* nenc = NULL;
851 bool ok = (nref && nscr);
852#ifdef SDL_LIBRETRO_ENABLE_REWIND_DELTA
853 nenc = (
unsigned char*)SDL_malloc(SDL_Libretro_RewindMaxEncodedSize(curSize));
854 ok = ok && (nenc != NULL);
862 SDL_free(lr->rewindReference);
863 SDL_free(lr->rewindScratch);
864 SDL_free(lr->rewindEncodeScratch);
865 lr->rewindReference = nref;
866 lr->rewindScratch = nscr;
867 lr->rewindEncodeScratch = nenc;
868 lr->rewindSlotSize = curSize;
869 SDL_Libretro_ClearRewind(lr);
872 if (!lr->core.symbols.retro_serialize(lr->rewindScratch, lr->rewindSlotSize))
return;
874 if (!lr->rewindHasReference) {
875 SDL_memcpy(lr->rewindReference, lr->rewindScratch, lr->rewindSlotSize);
876 lr->rewindHasReference =
true;
883 SDL_LibretroRewindDelta* slot = &lr->rewindEntries[lr->rewindHead];
884#ifdef SDL_LIBRETRO_ENABLE_REWIND_DELTA
891 size_t storeSize = SDL_Libretro_RewindEncodeDelta(
892 lr->rewindScratch, lr->rewindReference, lr->rewindSlotSize,
893 lr->rewindEncodeScratch, SDL_Libretro_RewindMaxEncodedSize(lr->rewindSlotSize));
894 if (storeSize == 0)
return;
899 if (slot->capacity < storeSize) {
900 unsigned char* data = (
unsigned char*)SDL_realloc(slot->data, storeSize);
902 lr->rewindBytes += storeSize - slot->capacity;
904 slot->capacity = storeSize;
906 SDL_memcpy(slot->data, lr->rewindEncodeScratch, storeSize);
907 slot->length = storeSize;
910 unsigned char* swap = lr->rewindReference;
911 lr->rewindReference = lr->rewindScratch;
912 lr->rewindScratch = swap;
915 unsigned char* recycled = slot->data;
916 size_t recycledCap = slot->capacity;
917 if (recycledCap < lr->rewindSlotSize) {
919 unsigned char* grown = (
unsigned char*)SDL_realloc(recycled, lr->rewindSlotSize);
922 recycledCap = lr->rewindSlotSize;
925 lr->rewindBytes += lr->rewindSlotSize - slot->capacity;
926 slot->data = lr->rewindReference;
927 slot->length = lr->rewindSlotSize;
928 slot->capacity = lr->rewindSlotSize;
929 lr->rewindReference = lr->rewindScratch;
930 lr->rewindScratch = recycled;
933 lr->rewindHead = (lr->rewindHead + 1) % lr->rewindCapacity;
934 if (lr->rewindCount < lr->rewindCapacity) {
939 SDL_Libretro_RewindEvictToBudget(lr);
947static void SDL_Libretro_RewindFreeEntry(SDL_Libretro* lr, SDL_LibretroRewindDelta* entry) {
948 lr->rewindBytes -= entry->capacity;
949 SDL_free(entry->data);
962static bool SDL_Libretro_RewindStepState(SDL_Libretro* lr) {
963 if (!lr->rewindEnabled || !lr->rewindReference || lr->rewindCount == 0)
return false;
965 lr->rewindHead = (lr->rewindHead == 0) ? (lr->rewindCapacity - 1) : (lr->rewindHead - 1);
968 SDL_LibretroRewindDelta* entry = &lr->rewindEntries[lr->rewindHead];
969 if (!entry->data || entry->length == 0)
return false;
971#ifdef SDL_LIBRETRO_ENABLE_REWIND_DELTA
972 bool reconstructed = SDL_Libretro_RewindDecodeDelta(entry->data, entry->length,
973 lr->rewindReference, lr->rewindSlotSize);
976 bool reconstructed = (entry->length == lr->rewindSlotSize);
977 if (reconstructed) SDL_memcpy(lr->rewindReference, entry->data, lr->rewindSlotSize);
980 SDL_Libretro_RewindFreeEntry(lr, entry);
988 if (!reconstructed || !lr->core.symbols.retro_unserialize(lr->rewindReference, lr->rewindSlotSize)) {
989 SDL_Libretro_ClearRewind(lr);
1003static bool SDL_Libretro_RewindStep(SDL_Libretro* lr) {
1004 if (!lr || !lr->core.gameLoaded || !lr->rewindEnabled) {
1005 SDL_SetError(
"[SDL_Libretro] Rewind is not enabled");
1008 if (!SDL_Libretro_RewindStepState(lr))
return false;
1010 lr->rewindActive =
true;
1011 lr->core.symbols.retro_run();
1012 lr->rewindActive =
false;
1023static void SDL_Libretro_RewindEvictToBudget(SDL_Libretro* lr) {
1024 if (!lr->rewindEnabled || lr->rewindMaxBytes == 0 || !lr->rewindEntries)
return;
1026 while (lr->rewindBytes > lr->rewindMaxBytes && lr->rewindCount > 1) {
1027 unsigned tail = (lr->rewindHead + lr->rewindCapacity - lr->rewindCount) % lr->rewindCapacity;
1028 SDL_Libretro_RewindFreeEntry(lr, &lr->rewindEntries[tail]);
1042static void SDL_Libretro_ClearRewind(SDL_Libretro* lr) {
1044 if (lr->rewindEntries) {
1045 for (
unsigned i = 0; i < lr->rewindCapacity; i++) {
1046 SDL_free(lr->rewindEntries[i].data);
1047 lr->rewindEntries[i].data = NULL;
1048 lr->rewindEntries[i].length = 0;
1049 lr->rewindEntries[i].capacity = 0;
1052 lr->rewindBytes = 0;
1054 lr->rewindCount = 0;
1055 lr->rewindFrameCounter = 0;
1056 lr->rewindHasReference =
false;
1064static void SDL_Libretro_RewindFree(SDL_Libretro* lr) {
1066 SDL_Libretro_ClearRewind(lr);
1069 SDL_free(lr->rewindEntries);
1070 lr->rewindEntries = NULL;
1071 SDL_free(lr->rewindReference);
1072 lr->rewindReference = NULL;
1073 SDL_free(lr->rewindScratch);
1074 lr->rewindScratch = NULL;
1075 SDL_free(lr->rewindEncodeScratch);
1076 lr->rewindEncodeScratch = NULL;
1077 lr->rewindSlotSize = 0;
1078 lr->rewindActive =
false;
bool SDL_Libretro_SaveState_IO(SDL_Libretro *lr, SDL_IOStream *dst, bool closeio)
Save the current libretro state to the given SDL_IOStream.
void SDL_Libretro_SetRewindMemoryLimit(SDL_Libretro *lr, size_t maxBytes)
Set the maximum number of bytes of encoded delta history to retain.
bool SDL_Libretro_SaveSRAM(SDL_Libretro *lr, const char *file)
Saves the current SRAM to the given file.
bool SDL_Libretro_GetMemoryMapDescriptor(const SDL_Libretro *lr, unsigned index, Uint64 *flags, void **ptr, size_t *offset, size_t *start, size_t *select, size_t *disconnect, size_t *len, const char **addrspace)
Retrieve one memory-map descriptor by index.
bool SDL_Libretro_LoadSRAM(SDL_Libretro *lr, const char *file)
Loads the current SRAM to the given file.
void * SDL_Libretro_GetMemoryData(const SDL_Libretro *lr, unsigned memoryType, size_t *size)
Get a pointer to a core memory region and its size.
bool SDL_Libretro_SetRewindEnabled(SDL_Libretro *lr, bool enabled, unsigned bufferFrames, unsigned captureInterval)
Enable or disable the rewind system.
bool SDL_Libretro_SetRewindMemoryDuration(SDL_Libretro *lr, double seconds)
Set the rewind memory budget by target duration instead of raw bytes.
size_t SDL_Libretro_GetRewindMemoryUsage(const SDL_Libretro *lr)
Get the approximate memory currently held by the rewind buffer, in bytes.
unsigned SDL_Libretro_GetMemoryMapCount(const SDL_Libretro *lr)
Get the number of memory-map descriptors the core has published.
bool SDL_Libretro_LoadState(SDL_Libretro *lr, const char *file)
Loads the libretro state from the given file.
size_t SDL_Libretro_GetStateSize(const SDL_Libretro *lr)
Retrieves the size of serialized states.
bool SDL_Libretro_SaveMemory(SDL_Libretro *lr, unsigned memoryType, const char *file)
Write a core memory region to a file.
bool SDL_Libretro_LoadMemory_IO(SDL_Libretro *lr, unsigned memoryType, SDL_IOStream *src, bool closeio)
Load a core memory region from a stream.
bool SDL_Libretro_LoadMemory(SDL_Libretro *lr, unsigned memoryType, const char *file)
Load a core memory region from a file.
bool SDL_Libretro_GetRewindEnabled(const SDL_Libretro *lr)
Check whether the rewind system is enabled (independent of current direction).
bool SDL_Libretro_SaveState(SDL_Libretro *lr, const char *file)
Saves the current libretro state to a file.
size_t SDL_Libretro_GetRewindMemoryLimit(const SDL_Libretro *lr)
Get the current rewind memory budget in bytes (0 if unbounded).
bool SDL_Libretro_SetMemoryData(SDL_Libretro *lr, unsigned memoryType, const void *data, size_t size)
Overwrite a core memory region with caller-provided bytes.
double SDL_Libretro_GetRewindRemaining(const SDL_Libretro *lr)
Calculates the amount of rewind time remaining in the buffer.
void * SDL_Libretro_GetMapAddress(const SDL_Libretro *lr, size_t address, size_t *regionRemaining)
Translate an emulated (guest) address to a live host pointer via the memory map.
bool SDL_Libretro_SaveMemory_IO(SDL_Libretro *lr, unsigned memoryType, SDL_IOStream *dst, bool closeio)
Write a core memory region to a stream.