all repos — mgba @ 27c70880385877006cbc85e3d7345be694110a1f

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	} 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}