1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
|
#include "statemanager.h"
#include "snapshot.h"
/* State Manager Class that records snapshot data for rewinding
mostly based on SSNES's rewind code by Themaister
*/
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 >= 0xffff
v |= v >> 8;
#endif
#if SIZE_MAX >= 0xffffffff
v |= v >> 16;
#endif
#if SIZE_MAX >= 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;
}
void StateManager::deallocate() {
if(buffer) {
delete [] buffer;
buffer = NULL;
}
if(tmp_state) {
delete [] tmp_state;
tmp_state = NULL;
}
if(in_state) {
delete [] in_state;
in_state = NULL;
}
}
StateManager::StateManager()
{
buffer = NULL;
tmp_state = NULL;
in_state = NULL;
init_done = false;
}
StateManager::~StateManager() {
deallocate();
}
bool StateManager::init(size_t buffer_size) {
init_done = false;
deallocate();
real_state_size = S9xFreezeSize();
state_size = real_state_size / sizeof(uint32_t); // Works in multiple of 4.
// We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's!
if(real_state_size % sizeof(uint32_t)) state_size ++;
if (buffer_size <= real_state_size) // Need a sufficient buffer size.
return false;
top_ptr = 1;
buf_size = nearest_pow2_size(buffer_size) / sizeof(uint64_t); // Works in multiple of 8.
buf_size_mask = buf_size - 1;
if (!(buffer = new uint64_t[buf_size]))
return false;
if (!(tmp_state = new uint32_t[state_size]))
return false;
if (!(in_state = new uint32_t[state_size]))
return false;
memset(tmp_state,0,state_size * sizeof(uint32_t));
memset(in_state,0,state_size * sizeof(uint32_t));
init_done = true;
return true;
}
int StateManager::pop()
{
if(!init_done)
return 0;
if (first_pop)
{
first_pop = false;
return S9xUnfreezeGameMem((uint8 *)tmp_state,real_state_size);
}
top_ptr = (top_ptr - 1) & buf_size_mask;
if (top_ptr == bottom_ptr) // Our stack is completely empty... :v
{
top_ptr = (top_ptr + 1) & buf_size_mask;
return 0;
}
while (buffer[top_ptr])
{
// Apply the xor patch.
uint32_t addr = buffer[top_ptr] >> 32;
uint32_t xor_ = buffer[top_ptr] & 0xFFFFFFFFU;
tmp_state[addr] ^= xor_;
top_ptr = (top_ptr - 1) & buf_size_mask;
}
if (top_ptr == bottom_ptr) // Our stack is completely empty... :v
{
top_ptr = (top_ptr + 1) & buf_size_mask;
}
return S9xUnfreezeGameMem((uint8 *)tmp_state,real_state_size);
}
void StateManager::reassign_bottom()
{
bottom_ptr = (top_ptr + 1) & buf_size_mask;
while (buffer[bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta).
bottom_ptr = (bottom_ptr + 1) & buf_size_mask;
}
void StateManager::generate_delta(const void *data)
{
bool crossed = false;
const uint32_t *old_state = tmp_state;
const uint32_t *new_state = (const uint32_t*)data;
buffer[top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between.
top_ptr &= buf_size_mask;
// Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft.
if (top_ptr == bottom_ptr)
crossed = true;
for (uint64_t i = 0; i < 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_)
{
buffer[top_ptr] = (i << 32) | xor_;
top_ptr = (top_ptr + 1) & buf_size_mask;
if (top_ptr == bottom_ptr)
crossed = true;
}
}
if (crossed)
reassign_bottom();
}
bool StateManager::push()
{
if(!init_done)
return false;
if(!S9xFreezeGameMem((uint8 *)in_state,real_state_size))
return false;
generate_delta(in_state);
uint32 *tmp = tmp_state;
tmp_state = in_state;
in_state = tmp;
first_pop = true;
return true;
}
|