src/core/rewind.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/core/rewind.h>
7
8#include <mgba/core/core.h>
9#include <mgba/core/serialize.h>
10#include <mgba-util/patch/fast.h>
11#include <mgba-util/vfs.h>
12
13DEFINE_VECTOR(mCoreRewindPatches, struct PatchFast);
14
15void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) {
16 mCoreRewindPatchesInit(&context->patchMemory, entries);
17 size_t e;
18 for (e = 0; e < entries; ++e) {
19 initPatchFast(mCoreRewindPatchesAppend(&context->patchMemory));
20 }
21 context->previousState = VFileMemChunk(0, 0);
22 context->currentState = VFileMemChunk(0, 0);
23 context->size = 0;
24 context->stateFlags = SAVESTATE_SAVEDATA;
25}
26
27void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
28 context->previousState->close(context->previousState);
29 context->currentState->close(context->currentState);
30 size_t s;
31 for (s = 0; s < mCoreRewindPatchesSize(&context->patchMemory); ++s) {
32 deinitPatchFast(mCoreRewindPatchesGetPointer(&context->patchMemory, s));
33 }
34 mCoreRewindPatchesDeinit(&context->patchMemory);
35}
36
37void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
38 struct VFile* nextState = context->previousState;
39 ++context->current;
40 if (context->size < mCoreRewindPatchesSize(&context->patchMemory)) {
41 ++context->size;
42 }
43 if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) {
44 context->current = 0;
45 }
46 mCoreSaveStateNamed(core, nextState, context->stateFlags);
47 struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
48 size_t size2 = nextState->size(nextState);
49 size_t size = context->currentState->size(context->currentState);
50 if (size2 > size) {
51 context->currentState->truncate(context->currentState, size2);
52 size = size2;
53 }
54 void* current = context->currentState->map(context->currentState, size, MAP_READ);
55 void* next = nextState->map(nextState, size, MAP_READ);
56 diffPatchFast(patch, current, next, size);
57 context->currentState->unmap(context->currentState, current, size);
58 nextState->unmap(next, nextState, size);
59 context->previousState = context->currentState;
60 context->currentState = nextState;
61}
62
63bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) {
64 if (!context->size) {
65 return false;
66 }
67 --context->size;
68
69 struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
70 size_t size2 = context->previousState->size(context->previousState);
71 size_t size = context->currentState->size(context->currentState);
72 if (size2 < size) {
73 size = size2;
74 }
75 void* current = context->currentState->map(context->currentState, size, MAP_READ);
76 void* previous = context->previousState->map(context->previousState, size, MAP_WRITE);
77 patch->d.applyPatch(&patch->d, current, size, previous, size);
78 context->currentState->unmap(context->currentState, current, size);
79 context->previousState->unmap(context->previousState, previous, size);
80 mCoreLoadStateNamed(core, context->previousState, context->stateFlags);
81 struct VFile* nextState = context->previousState;
82 context->previousState = context->currentState;
83 context->currentState = nextState;
84
85 if (context->current == 0) {
86 context->current = mCoreRewindPatchesSize(&context->patchMemory);
87 }
88 --context->current;
89 return true;
90}