all repos — mgba @ c657255009d8a3143e1a2ecd390f9f895374f60c

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 _rewindDiff(struct mCoreRewindContext* context);
 16
 17#ifndef DISABLE_THREADING
 18THREAD_ENTRY _rewindThread(void* context);
 19#endif
 20
 21void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, bool onThread) {
 22	if (context->currentState) {
 23		return;
 24	}
 25	mCoreRewindPatchesInit(&context->patchMemory, entries);
 26	size_t e;
 27	for (e = 0; e < entries; ++e) {
 28		initPatchFast(mCoreRewindPatchesAppend(&context->patchMemory));
 29	}
 30	context->previousState = VFileMemChunk(0, 0);
 31	context->currentState = VFileMemChunk(0, 0);
 32	context->size = 0;
 33	context->stateFlags = SAVESTATE_SAVEDATA;
 34#ifndef DISABLE_THREADING
 35	context->onThread = onThread;
 36	context->ready = false;
 37	if (onThread) {
 38		MutexInit(&context->mutex);
 39		ConditionInit(&context->cond);
 40		ThreadCreate(&context->thread, _rewindThread, context);
 41	}
 42#else
 43	UNUSED(onThread);
 44#endif
 45}
 46
 47void mCoreRewindContextDeinit(struct mCoreRewindContext* context) {
 48	if (!context->currentState) {
 49		return;
 50	}
 51#ifndef DISABLE_THREADING
 52	if (context->onThread) {
 53		MutexLock(&context->mutex);
 54		context->onThread = false;
 55		MutexUnlock(&context->mutex);
 56		ConditionWake(&context->cond);
 57		ThreadJoin(context->thread);
 58		MutexDeinit(&context->mutex);
 59		ConditionDeinit(&context->cond);
 60	}
 61#endif
 62	context->previousState->close(context->previousState);
 63	context->currentState->close(context->currentState);
 64	context->previousState = NULL;
 65	context->currentState = NULL;
 66	size_t s;
 67	for (s = 0; s < mCoreRewindPatchesSize(&context->patchMemory); ++s) {
 68		deinitPatchFast(mCoreRewindPatchesGetPointer(&context->patchMemory, s));
 69	}
 70	mCoreRewindPatchesDeinit(&context->patchMemory);
 71}
 72
 73void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) {
 74#ifndef DISABLE_THREADING
 75	if (context->onThread) {
 76		MutexLock(&context->mutex);
 77	}
 78#endif
 79	struct VFile* nextState = context->previousState;
 80	mCoreSaveStateNamed(core, nextState, context->stateFlags);
 81	context->previousState = context->currentState;
 82	context->currentState = nextState;
 83#ifndef DISABLE_THREADING
 84	if (context->onThread) {
 85		context->ready = true;
 86		ConditionWake(&context->cond);
 87		MutexUnlock(&context->mutex);
 88		return;
 89	}
 90#endif
 91	_rewindDiff(context);
 92}
 93
 94void _rewindDiff(struct mCoreRewindContext* context) {
 95	++context->current;
 96	if (context->size < mCoreRewindPatchesSize(&context->patchMemory)) {
 97		++context->size;
 98	}
 99	if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) {
100		context->current = 0;
101	}
102	struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
103	size_t size2 = context->currentState->size(context->currentState);
104	size_t size = context->previousState->size(context->previousState);
105	if (size2 > size) {
106		context->previousState->truncate(context->previousState, size2);
107		size = size2;
108	} else if (size > size2) {
109		context->currentState->truncate(context->currentState, size);
110	}
111	void* current = context->previousState->map(context->previousState, size, MAP_READ);
112	void* next = context->currentState->map(context->currentState, size, MAP_READ);
113	diffPatchFast(patch, current, next, size);
114	context->previousState->unmap(context->previousState, current, size);
115	context->currentState->unmap(context->currentState, next, size);
116}
117
118bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) {
119#ifndef DISABLE_THREADING
120	if (context->onThread) {
121		MutexLock(&context->mutex);
122	}
123#endif
124	if (!context->size) {
125#ifndef DISABLE_THREADING
126		if (context->onThread) {
127			MutexUnlock(&context->mutex);
128		}
129#endif
130		return false;
131	}
132	--context->size;
133
134	mCoreLoadStateNamed(core, context->previousState, context->stateFlags);
135	if (context->current == 0) {
136		context->current = mCoreRewindPatchesSize(&context->patchMemory);
137	}
138	--context->current;
139
140	struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current);
141	size_t size2 = context->previousState->size(context->previousState);
142	size_t size = context->currentState->size(context->currentState);
143	if (size2 < size) {
144		size = size2;
145	}
146	void* current = context->currentState->map(context->currentState, size, MAP_READ);
147	void* previous = context->previousState->map(context->previousState, size, MAP_WRITE);
148	patch->d.applyPatch(&patch->d, previous, size, current, size);
149	context->currentState->unmap(context->currentState, current, size);
150	context->previousState->unmap(context->previousState, previous, size);
151	struct VFile* nextState = context->previousState;
152	context->previousState = context->currentState;
153	context->currentState = nextState;
154#ifndef DISABLE_THREADING
155	if (context->onThread) {
156		MutexUnlock(&context->mutex);
157	}
158#endif
159	return true;
160}
161
162#ifndef DISABLE_THREADING
163THREAD_ENTRY _rewindThread(void* context) {
164	struct mCoreRewindContext* rewindContext = context;
165	ThreadSetName("Rewind Diff Thread");
166	MutexLock(&rewindContext->mutex);
167	while (rewindContext->onThread) {
168		while (!rewindContext->ready && rewindContext->onThread) {
169			ConditionWait(&rewindContext->cond, &rewindContext->mutex);
170		}
171		if (rewindContext->ready) {
172			_rewindDiff(rewindContext);
173		}
174		rewindContext->ready = false;
175	}
176	MutexUnlock(&rewindContext->mutex);
177	return 0;
178}
179#endif
180