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 } else if (size > size2) {
54 nextState->truncate(nextState, size);
55 }
56 void* current = context->currentState->map(context->currentState, size, MAP_READ);
57 void* next = nextState->map(nextState, size, MAP_READ);
58 diffPatchFast(patch, current, next, size);
59 context->currentState->unmap(context->currentState, current, size);
60 nextState->unmap(next, nextState, size);
61 context->previousState = context->currentState;
62 context->currentState = nextState;
63}
64
65bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) {
66 if (!context->size) {
67 return false;
68 }
69 --context->size;
70
71 struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
72 size_t size2 = context->previousState->size(context->previousState);
73 size_t size = context->currentState->size(context->currentState);
74 if (size2 < size) {
75 size = size2;
76 }
77 void* current = context->currentState->map(context->currentState, size, MAP_READ);
78 void* previous = context->previousState->map(context->previousState, size, MAP_WRITE);
79 patch->d.applyPatch(&patch->d, current, size, previous, size);
80 context->currentState->unmap(context->currentState, current, size);
81 context->previousState->unmap(context->previousState, previous, size);
82 mCoreLoadStateNamed(core, context->previousState, context->stateFlags);
83 struct VFile* nextState = context->previousState;
84 context->previousState = context->currentState;
85 context->currentState = nextState;
86
87 if (context->current == 0) {
88 context->current = mCoreRewindPatchesSize(&context->patchMemory);
89 }
90 --context->current;
91 return true;
92}