all repos — mgba @ 60577e83948647d36a2e6a8b4ec8f8556df3f72f

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