all repos — mgba @ aff1486ec5fe3185de59d20665c4042b715853ae

mGBA Game Boy Advance Emulator

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}