diff --git a/rewind-alcaro.h b/rewind-alcaro.h index cc88e812af..8925f88792 100644 --- a/rewind-alcaro.h +++ b/rewind-alcaro.h @@ -1,6 +1,8 @@ //#define NO_UNALIGNED_MEM //Uncomment the above if alignment is enforced. +#include "boolean.h" + //A compressing, lossy stack. Optimized for large, mostly similar, blocks of data; optimized for // writing, less so for reading. Will discard old data if its capacity is exhausted. struct rewindstack { diff --git a/rewind.c b/rewind.c index 5a7229f422..ae3bae30ae 100644 --- a/rewind.c +++ b/rewind.c @@ -1,194 +1,64 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2014 - Hans-Kristian Arntzen - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ +/* RetroArch - A frontend for libretro. +* Copyright (C) 2010-2014 - Hans-Kristian Arntzen +* +* RetroArch is free software: you can redistribute it and/or modify it under the terms +* of the GNU General Public License as published by the Free Software Found- +* ation, either version 3 of the License, or (at your option) any later version. +* +* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with RetroArch. +* If not, see . +*/ #include "rewind.h" +#include "rewind-alcaro.h" #include -#include -#include "boolean.h" #include -#include -#include "general.h" -struct state_manager -{ - uint64_t *buffer; - size_t buf_size; - size_t buf_size_mask; - uint32_t *tmp_state; - size_t top_ptr; - size_t bottom_ptr; - size_t state_size; - bool first_pop; +struct state_manager { + struct rewindstack * core; + unsigned int state_size; }; -static inline size_t nearest_pow2_size(size_t v) -{ - size_t orig = v; - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; -#if SIZE_MAX >= UINT16_C(0xffff) - v |= v >> 8; -#endif -#if SIZE_MAX >= UINT32_C(0xffffffff) - v |= v >> 16; -#endif -#if SIZE_MAX >= UINT64_C(0xffffffffffffffff) - v |= v >> 32; -#endif - v++; - - size_t next = v; - size_t prev = v >> 1; - - if ((next - orig) < (orig - prev)) - return next; - else - return prev; -} - state_manager_t *state_manager_new(size_t state_size, size_t buffer_size, void *init_buffer) { - if (buffer_size <= state_size * 4) // Need a sufficient buffer size. - return NULL; - - state_manager_t *state = (state_manager_t*)calloc(1, sizeof(*state)); - if (!state) - return NULL; - - // We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's! - rarch_assert(state_size % 4 == 0); - state->top_ptr = 1; - - state->state_size = state_size / sizeof(uint32_t); // Works in multiple of 4. - state->buf_size = nearest_pow2_size(buffer_size) / sizeof(uint64_t); // Works in multiple of 8. - state->buf_size_mask = state->buf_size - 1; - RARCH_LOG("Readjusted rewind buffer size to %u MiB\n", (unsigned)(sizeof(uint64_t) * (state->buf_size >> 20))); - - if (!(state->buffer = (uint64_t*)calloc(1, state->buf_size * sizeof(uint64_t)))) - goto error; - if (!(state->tmp_state = (uint32_t*)calloc(1, state->state_size * sizeof(uint32_t)))) - goto error; - - memcpy(state->tmp_state, init_buffer, state_size); - - return state; - -error: - if (state) - { - free(state->buffer); - free(state->tmp_state); - free(state); - } - return NULL; + state_manager_t *state = (state_manager_t*)calloc(1, sizeof(*state)); + if (!state) + return NULL; + + state->state_size=state_size; + + state->core=rewindstack_create(state_size, buffer_size); + if (!state->core) + { + free(state); + return NULL; + } + + void* first_state=state->core->push_begin(state->core); + memcpy(first_state, init_buffer, state_size); + state->core->push_end(state->core); } void state_manager_free(state_manager_t *state) { - free(state->buffer); - free(state->tmp_state); - free(state); + state->core->free(state->core); + free(state); } bool state_manager_pop(state_manager_t *state, void **data) -{ - *data = state->tmp_state; - if (state->first_pop) - { - state->first_pop = false; - return true; - } - - state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask; - - if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v - { - state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask; - return false; - } - - while (state->buffer[state->top_ptr]) - { - // Apply the xor patch. - uint32_t addr = state->buffer[state->top_ptr] >> 32; - uint32_t xor_ = state->buffer[state->top_ptr] & 0xFFFFFFFFU; - state->tmp_state[addr] ^= xor_; - - state->top_ptr = (state->top_ptr - 1) & state->buf_size_mask; - } - - if (state->top_ptr == state->bottom_ptr) // Our stack is completely empty... :v - { - state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask; - return true; - } - - return true; -} - -static void reassign_bottom(state_manager_t *state) { - state->bottom_ptr = (state->top_ptr + 1) & state->buf_size_mask; - while (state->buffer[state->bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta). - state->bottom_ptr = (state->bottom_ptr + 1) & state->buf_size_mask; -} - -static void generate_delta(state_manager_t *state, const void *data) -{ - uint64_t i; - bool crossed = false; - const uint32_t *old_state = state->tmp_state; - const uint32_t *new_state = (const uint32_t*)data; - - state->buffer[state->top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between. - state->top_ptr &= state->buf_size_mask; - - // Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft. - if (state->top_ptr == state->bottom_ptr) - crossed = true; - - for (i = 0; i < state->state_size; i++) - { - uint64_t xor_ = old_state[i] ^ new_state[i]; - - // If the data differs (xor != 0), we push that xor on the stack with index and xor. - // This can be reversed by reapplying the xor. - // This, if states don't really differ much, we'll save lots of space :) - // Hopefully this will work really well with save states. - if (xor_) - { - state->buffer[state->top_ptr] = (i << 32) | xor_; - state->top_ptr = (state->top_ptr + 1) & state->buf_size_mask; - - if (state->top_ptr == state->bottom_ptr) - crossed = true; - } - } - - if (crossed) - reassign_bottom(state); + *data=(void*)state->core->pull(state->core); + return (*data); } bool state_manager_push(state_manager_t *state, const void *data) { - generate_delta(state, data); - memcpy(state->tmp_state, data, state->state_size * sizeof(uint32_t)); - state->first_pop = true; - - return true; + void* next_state=state->core->push_begin(state->core); + memcpy(next_state, data, state->state_size); + state->core->push_end(state->core); + return true; } -