all repos — mgba @ d52a7f3689c53298dc02bd2ebbd66b4213d12dc2

mGBA Game Boy Advance Emulator

src/gba/supervisor/thread.c (view raw)

  1/* Copyright (c) 2013-2015 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 "thread.h"
  7
  8#include "arm.h"
  9#include "gba/gba.h"
 10#include "gba/cheats.h"
 11#include "gba/serialize.h"
 12#include "gba/context/config.h"
 13#include "gba/rr/mgm.h"
 14#include "gba/rr/vbm.h"
 15
 16#include "debugger/debugger.h"
 17
 18#include "util/patch.h"
 19#include "util/vfs.h"
 20
 21#include "platform/commandline.h"
 22
 23#include <signal.h>
 24
 25static void _loadGameDir(struct GBAThread* threadContext);
 26
 27static const float _defaultFPSTarget = 60.f;
 28
 29#ifndef DISABLE_THREADING
 30#ifdef USE_PTHREADS
 31static pthread_key_t _contextKey;
 32static pthread_once_t _contextOnce = PTHREAD_ONCE_INIT;
 33
 34static void _createTLS(void) {
 35	pthread_key_create(&_contextKey, 0);
 36}
 37#elif _WIN32
 38static DWORD _contextKey;
 39static INIT_ONCE _contextOnce = INIT_ONCE_STATIC_INIT;
 40
 41static BOOL CALLBACK _createTLS(PINIT_ONCE once, PVOID param, PVOID* context) {
 42	UNUSED(once);
 43	UNUSED(param);
 44	UNUSED(context);
 45	_contextKey = TlsAlloc();
 46	return TRUE;
 47}
 48#endif
 49
 50static void _changeState(struct GBAThread* threadContext, enum ThreadState newState, bool broadcast) {
 51	MutexLock(&threadContext->stateMutex);
 52	threadContext->state = newState;
 53	if (broadcast) {
 54		ConditionWake(&threadContext->stateCond);
 55	}
 56	MutexUnlock(&threadContext->stateMutex);
 57}
 58
 59static void _waitOnInterrupt(struct GBAThread* threadContext) {
 60	while (threadContext->state == THREAD_INTERRUPTED) {
 61		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
 62	}
 63}
 64
 65static void _waitUntilNotState(struct GBAThread* threadContext, enum ThreadState oldState) {
 66	MutexLock(&threadContext->sync.videoFrameMutex);
 67	bool videoFrameWait = threadContext->sync.videoFrameWait;
 68	threadContext->sync.videoFrameWait = false;
 69	MutexUnlock(&threadContext->sync.videoFrameMutex);
 70
 71	while (threadContext->state == oldState) {
 72		MutexUnlock(&threadContext->stateMutex);
 73
 74		MutexLock(&threadContext->sync.videoFrameMutex);
 75		ConditionWake(&threadContext->sync.videoFrameRequiredCond);
 76		MutexUnlock(&threadContext->sync.videoFrameMutex);
 77
 78		MutexLock(&threadContext->sync.audioBufferMutex);
 79		ConditionWake(&threadContext->sync.audioRequiredCond);
 80		MutexUnlock(&threadContext->sync.audioBufferMutex);
 81
 82		MutexLock(&threadContext->stateMutex);
 83		ConditionWake(&threadContext->stateCond);
 84	}
 85
 86	MutexLock(&threadContext->sync.videoFrameMutex);
 87	threadContext->sync.videoFrameWait = videoFrameWait;
 88	MutexUnlock(&threadContext->sync.videoFrameMutex);
 89}
 90
 91static void _pauseThread(struct GBAThread* threadContext, bool onThread) {
 92	threadContext->state = THREAD_PAUSING;
 93	if (!onThread) {
 94		_waitUntilNotState(threadContext, THREAD_PAUSING);
 95	}
 96}
 97
 98struct GBAThreadStop {
 99	struct GBAStopCallback d;
100	struct GBAThread* p;
101};
102
103static void _stopCallback(struct GBAStopCallback* stop) {
104	struct GBAThreadStop* callback = (struct GBAThreadStop*) stop;
105	if (callback->p->stopCallback(callback->p)) {
106		_changeState(callback->p, THREAD_EXITING, false);
107	}
108}
109
110static THREAD_ENTRY _GBAThreadRun(void* context) {
111#ifdef USE_PTHREADS
112	pthread_once(&_contextOnce, _createTLS);
113#elif _WIN32
114	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
115#endif
116
117	struct GBA gba;
118	struct ARMCore cpu;
119	struct Patch patch;
120	struct GBACheatDevice cheatDevice;
121	struct GBAThread* threadContext = context;
122	struct ARMComponent* components[GBA_COMPONENT_MAX] = { 0 };
123	struct GBARRContext* movie = 0;
124	int numComponents = GBA_COMPONENT_MAX;
125
126	ThreadSetName("CPU Thread");
127
128#if !defined(_WIN32) && defined(USE_PTHREADS)
129	sigset_t signals;
130	sigemptyset(&signals);
131	pthread_sigmask(SIG_SETMASK, &signals, 0);
132#endif
133
134	GBACreate(&gba);
135	ARMSetComponents(&cpu, &gba.d, numComponents, components);
136	ARMInit(&cpu);
137	gba.sync = &threadContext->sync;
138	threadContext->gba = &gba;
139	threadContext->cpu = &cpu;
140	gba.logLevel = threadContext->logLevel;
141	gba.logHandler = threadContext->logHandler;
142	gba.stream = threadContext->stream;
143	gba.video.frameskip = threadContext->frameskip;
144
145	struct GBAThreadStop stop;
146	if (threadContext->stopCallback) {
147		stop.d.stop = _stopCallback;
148		stop.p = threadContext;
149		gba.stopCallback = &stop.d;
150	}
151
152	gba.idleOptimization = threadContext->idleOptimization;
153#ifdef USE_PTHREADS
154	pthread_setspecific(_contextKey, threadContext);
155#elif _WIN32
156	TlsSetValue(_contextKey, threadContext);
157#endif
158
159	if (threadContext->audioBuffers) {
160		GBAAudioResizeBuffer(&gba.audio, threadContext->audioBuffers);
161	} else {
162		threadContext->audioBuffers = GBA_AUDIO_SAMPLES;
163	}
164
165	if (threadContext->renderer) {
166		GBAVideoAssociateRenderer(&gba.video, threadContext->renderer);
167	}
168
169	if (threadContext->rom) {
170		if (GBAIsMB(threadContext->rom)) {
171			GBALoadMB(&gba, threadContext->rom, threadContext->fname);
172		} else {
173			GBALoadROM(&gba, threadContext->rom, threadContext->save, threadContext->fname);
174		}
175
176		struct GBACartridgeOverride override;
177		const struct GBACartridge* cart = (const struct GBACartridge*) gba.pristineRom;
178		memcpy(override.id, &cart->id, sizeof(override.id));
179		if (GBAOverrideFind(threadContext->overrides, &override)) {
180			GBAOverrideApply(&gba, &override);
181		}
182		if (threadContext->hasOverride) {
183			GBAOverrideApply(&gba, &threadContext->override);
184		}
185
186		if (threadContext->patch && loadPatch(threadContext->patch, &patch)) {
187			GBAApplyPatch(&gba, &patch);
188		}
189	}
190
191	if (threadContext->bios && GBAIsBIOS(threadContext->bios)) {
192		GBALoadBIOS(&gba, threadContext->bios);
193	}
194
195	if (threadContext->movie) {
196		struct VDir* movieDir = VDirOpen(threadContext->movie);
197#ifdef USE_LIBZIP
198		if (!movieDir) {
199			movieDir = VDirOpenZip(threadContext->movie, 0);
200		}
201#endif
202		if (movieDir) {
203			struct GBAMGMContext* mgm = malloc(sizeof(*mgm));
204			GBAMGMContextCreate(mgm);
205			if (!GBAMGMSetStream(mgm, movieDir)) {
206				mgm->d.destroy(&mgm->d);
207			} else {
208				movie = &mgm->d;
209			}
210		} else {
211			struct VFile* movieFile = VFileOpen(threadContext->movie, O_RDONLY);
212			if (movieFile) {
213				struct GBAVBMContext* vbm = malloc(sizeof(*vbm));
214				GBAVBMContextCreate(vbm);
215				if (!GBAVBMSetStream(vbm, movieFile)) {
216					vbm->d.destroy(&vbm->d);
217				} else {
218					movie = &vbm->d;
219				}
220			}
221		}
222	}
223
224	ARMReset(&cpu);
225
226	if (movie) {
227		gba.rr = movie;
228		movie->startPlaying(movie, false);
229		GBARRInitPlay(&gba);
230	}
231
232	if (threadContext->skipBios && gba.pristineRom) {
233		GBASkipBIOS(&gba);
234	}
235
236	if (!threadContext->cheats) {
237		GBACheatDeviceCreate(&cheatDevice);
238		threadContext->cheats = &cheatDevice;
239	}
240	if (threadContext->cheatsFile) {
241		GBACheatParseFile(threadContext->cheats, threadContext->cheatsFile);
242	}
243	GBACheatAttachDevice(&gba, threadContext->cheats);
244
245	if (threadContext->debugger) {
246		threadContext->debugger->log = GBADebuggerLogShim;
247		GBAAttachDebugger(&gba, threadContext->debugger);
248		ARMDebuggerEnter(threadContext->debugger, DEBUGGER_ENTER_ATTACHED, 0);
249	}
250
251	GBASIOSetDriverSet(&gba.sio, &threadContext->sioDrivers);
252
253	if (threadContext->volume == 0) {
254		threadContext->volume = GBA_AUDIO_VOLUME_MAX;
255	}
256	if (threadContext->mute) {
257		gba.audio.masterVolume = 0;
258	} else {
259		gba.audio.masterVolume = threadContext->volume;
260	}
261
262	gba.keySource = &threadContext->activeKeys;
263
264	if (threadContext->startCallback) {
265		threadContext->startCallback(threadContext);
266	}
267
268	_changeState(threadContext, THREAD_RUNNING, true);
269
270	while (threadContext->state < THREAD_EXITING) {
271		if (threadContext->debugger) {
272			struct ARMDebugger* debugger = threadContext->debugger;
273			ARMDebuggerRun(debugger);
274			if (debugger->state == DEBUGGER_SHUTDOWN) {
275				_changeState(threadContext, THREAD_EXITING, false);
276			}
277		} else {
278			while (threadContext->state == THREAD_RUNNING) {
279				ARMRunLoop(&cpu);
280			}
281		}
282
283		int resetScheduled = 0;
284		MutexLock(&threadContext->stateMutex);
285		while (threadContext->state > THREAD_RUNNING && threadContext->state < THREAD_EXITING) {
286			if (threadContext->state == THREAD_PAUSING) {
287				threadContext->state = THREAD_PAUSED;
288				ConditionWake(&threadContext->stateCond);
289			}
290			if (threadContext->state == THREAD_INTERRUPTING) {
291				threadContext->state = THREAD_INTERRUPTED;
292				ConditionWake(&threadContext->stateCond);
293			}
294			if (threadContext->state == THREAD_RUN_ON) {
295				if (threadContext->run) {
296					threadContext->run(threadContext);
297				}
298				threadContext->state = threadContext->savedState;
299				ConditionWake(&threadContext->stateCond);
300			}
301			if (threadContext->state == THREAD_RESETING) {
302				threadContext->state = THREAD_RUNNING;
303				resetScheduled = 1;
304			}
305			while (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_INTERRUPTED) {
306				ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
307			}
308		}
309		MutexUnlock(&threadContext->stateMutex);
310		if (resetScheduled) {
311			ARMReset(&cpu);
312			if (threadContext->skipBios && gba.pristineRom) {
313				GBASkipBIOS(&gba);
314			}
315		}
316	}
317
318	while (threadContext->state < THREAD_SHUTDOWN) {
319		_changeState(threadContext, THREAD_SHUTDOWN, false);
320	}
321
322	if (threadContext->cleanCallback) {
323		threadContext->cleanCallback(threadContext);
324	}
325
326	threadContext->gba = 0;
327	ARMDeinit(&cpu);
328	GBADestroy(&gba);
329	if (&cheatDevice == threadContext->cheats) {
330		GBACheatDeviceDestroy(&cheatDevice);
331	}
332
333	if (movie) {
334		movie->destroy(movie);
335		free(movie);
336	}
337
338	threadContext->sync.videoFrameOn = false;
339	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
340	ConditionWake(&threadContext->sync.audioRequiredCond);
341
342	return 0;
343}
344
345void GBAMapOptionsToContext(const struct GBAOptions* opts, struct GBAThread* threadContext) {
346	if (opts->useBios) {
347		threadContext->bios = VFileOpen(opts->bios, O_RDONLY);
348	} else {
349		threadContext->bios = 0;
350	}
351	threadContext->frameskip = opts->frameskip;
352	threadContext->volume = opts->volume;
353	threadContext->mute = opts->mute;
354	threadContext->logLevel = opts->logLevel;
355	if (opts->rewindEnable) {
356		threadContext->rewindBufferCapacity = opts->rewindBufferCapacity;
357		threadContext->rewindBufferInterval = opts->rewindBufferInterval;
358	} else {
359		threadContext->rewindBufferCapacity = 0;
360	}
361	threadContext->skipBios = opts->skipBios;
362	threadContext->sync.audioWait = opts->audioSync;
363	threadContext->sync.videoFrameWait = opts->videoSync;
364
365	if (opts->fpsTarget) {
366		threadContext->fpsTarget = opts->fpsTarget;
367	}
368
369	if (opts->audioBuffers) {
370		threadContext->audioBuffers = opts->audioBuffers;
371	}
372
373	threadContext->idleOptimization = opts->idleOptimization;
374}
375
376void GBAMapArgumentsToContext(const struct GBAArguments* args, struct GBAThread* threadContext) {
377	if (args->dirmode) {
378		threadContext->gameDir = VDirOpen(args->fname);
379		threadContext->stateDir = threadContext->gameDir;
380	} else {
381		GBAThreadLoadROM(threadContext, args->fname);
382	}
383	threadContext->fname = args->fname;
384	threadContext->patch = VFileOpen(args->patch, O_RDONLY);
385	threadContext->cheatsFile = VFileOpen(args->cheatsFile, O_RDONLY);
386	threadContext->movie = args->movie;
387}
388
389bool GBAThreadStart(struct GBAThread* threadContext) {
390	// TODO: error check
391	threadContext->activeKeys = 0;
392	threadContext->state = THREAD_INITIALIZED;
393	threadContext->sync.videoFrameOn = true;
394
395	threadContext->rewindBuffer = 0;
396	threadContext->rewindScreenBuffer = 0;
397	int newCapacity = threadContext->rewindBufferCapacity;
398	int newInterval = threadContext->rewindBufferInterval;
399	threadContext->rewindBufferCapacity = 0;
400	threadContext->rewindBufferInterval = 0;
401	GBARewindSettingsChanged(threadContext, newCapacity, newInterval);
402
403	if (!threadContext->fpsTarget) {
404		threadContext->fpsTarget = _defaultFPSTarget;
405	}
406
407	bool bootBios = threadContext->bootBios && threadContext->bios;
408
409	if (threadContext->rom && (!GBAIsROM(threadContext->rom) || bootBios)) {
410		threadContext->rom->close(threadContext->rom);
411		threadContext->rom = 0;
412	}
413
414	if (threadContext->gameDir) {
415		_loadGameDir(threadContext);
416	}
417
418	if (!threadContext->rom && !bootBios) {
419		threadContext->state = THREAD_SHUTDOWN;
420		return false;
421	}
422
423	threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR);
424
425	if (!threadContext->patch) {
426		threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".ups", O_RDONLY);
427	}
428	if (!threadContext->patch) {
429		threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".ips", O_RDONLY);
430	}
431	if (!threadContext->patch) {
432		threadContext->patch = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "patch", ".bps", O_RDONLY);
433	}
434
435	MutexInit(&threadContext->stateMutex);
436	ConditionInit(&threadContext->stateCond);
437
438	MutexInit(&threadContext->sync.videoFrameMutex);
439	ConditionInit(&threadContext->sync.videoFrameAvailableCond);
440	ConditionInit(&threadContext->sync.videoFrameRequiredCond);
441	MutexInit(&threadContext->sync.audioBufferMutex);
442	ConditionInit(&threadContext->sync.audioRequiredCond);
443
444	threadContext->interruptDepth = 0;
445
446#ifdef USE_PTHREADS
447	sigset_t signals;
448	sigemptyset(&signals);
449	sigaddset(&signals, SIGINT);
450	sigaddset(&signals, SIGTRAP);
451	pthread_sigmask(SIG_BLOCK, &signals, 0);
452#endif
453
454	MutexLock(&threadContext->stateMutex);
455	ThreadCreate(&threadContext->thread, _GBAThreadRun, threadContext);
456	while (threadContext->state < THREAD_RUNNING) {
457		ConditionWait(&threadContext->stateCond, &threadContext->stateMutex);
458	}
459	MutexUnlock(&threadContext->stateMutex);
460
461	return true;
462}
463
464bool GBAThreadHasStarted(struct GBAThread* threadContext) {
465	bool hasStarted;
466	MutexLock(&threadContext->stateMutex);
467	hasStarted = threadContext->state > THREAD_INITIALIZED;
468	MutexUnlock(&threadContext->stateMutex);
469	return hasStarted;
470}
471
472bool GBAThreadHasExited(struct GBAThread* threadContext) {
473	bool hasExited;
474	MutexLock(&threadContext->stateMutex);
475	hasExited = threadContext->state > THREAD_EXITING;
476	MutexUnlock(&threadContext->stateMutex);
477	return hasExited;
478}
479
480bool GBAThreadHasCrashed(struct GBAThread* threadContext) {
481	bool hasExited;
482	MutexLock(&threadContext->stateMutex);
483	hasExited = threadContext->state == THREAD_CRASHED;
484	MutexUnlock(&threadContext->stateMutex);
485	return hasExited;
486}
487
488void GBAThreadEnd(struct GBAThread* threadContext) {
489	MutexLock(&threadContext->stateMutex);
490	_waitOnInterrupt(threadContext);
491	threadContext->state = THREAD_EXITING;
492	if (threadContext->gba) {
493		threadContext->gba->cpu->halted = false;
494	}
495	ConditionWake(&threadContext->stateCond);
496	MutexUnlock(&threadContext->stateMutex);
497	MutexLock(&threadContext->sync.audioBufferMutex);
498	threadContext->sync.audioWait = 0;
499	ConditionWake(&threadContext->sync.audioRequiredCond);
500	MutexUnlock(&threadContext->sync.audioBufferMutex);
501
502	MutexLock(&threadContext->sync.videoFrameMutex);
503	threadContext->sync.videoFrameWait = false;
504	threadContext->sync.videoFrameOn = false;
505	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
506	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
507	MutexUnlock(&threadContext->sync.videoFrameMutex);
508}
509
510void GBAThreadReset(struct GBAThread* threadContext) {
511	MutexLock(&threadContext->stateMutex);
512	_waitOnInterrupt(threadContext);
513	threadContext->state = THREAD_RESETING;
514	ConditionWake(&threadContext->stateCond);
515	MutexUnlock(&threadContext->stateMutex);
516}
517
518void GBAThreadJoin(struct GBAThread* threadContext) {
519	ThreadJoin(threadContext->thread);
520
521	MutexDeinit(&threadContext->stateMutex);
522	ConditionDeinit(&threadContext->stateCond);
523
524	MutexDeinit(&threadContext->sync.videoFrameMutex);
525	ConditionWake(&threadContext->sync.videoFrameAvailableCond);
526	ConditionDeinit(&threadContext->sync.videoFrameAvailableCond);
527	ConditionWake(&threadContext->sync.videoFrameRequiredCond);
528	ConditionDeinit(&threadContext->sync.videoFrameRequiredCond);
529
530	ConditionWake(&threadContext->sync.audioRequiredCond);
531	ConditionDeinit(&threadContext->sync.audioRequiredCond);
532	MutexDeinit(&threadContext->sync.audioBufferMutex);
533
534	int i;
535	for (i = 0; i < threadContext->rewindBufferCapacity; ++i) {
536		if (threadContext->rewindBuffer[i]) {
537			GBADeallocateState(threadContext->rewindBuffer[i]);
538		}
539	}
540	free(threadContext->rewindBuffer);
541	free(threadContext->rewindScreenBuffer);
542
543	if (threadContext->rom) {
544		threadContext->rom->close(threadContext->rom);
545		threadContext->rom = 0;
546	}
547
548	if (threadContext->save) {
549		threadContext->save->close(threadContext->save);
550		threadContext->save = 0;
551	}
552
553	if (threadContext->bios) {
554		threadContext->bios->close(threadContext->bios);
555		threadContext->bios = 0;
556	}
557
558	if (threadContext->patch) {
559		threadContext->patch->close(threadContext->patch);
560		threadContext->patch = 0;
561	}
562
563	if (threadContext->gameDir) {
564		if (threadContext->stateDir == threadContext->gameDir) {
565			threadContext->stateDir = 0;
566		}
567		threadContext->gameDir->close(threadContext->gameDir);
568		threadContext->gameDir = 0;
569	}
570
571	if (threadContext->stateDir) {
572		threadContext->stateDir->close(threadContext->stateDir);
573		threadContext->stateDir = 0;
574	}
575}
576
577bool GBAThreadIsActive(struct GBAThread* threadContext) {
578	return threadContext->state >= THREAD_RUNNING && threadContext->state < THREAD_EXITING;
579}
580
581void GBAThreadInterrupt(struct GBAThread* threadContext) {
582	MutexLock(&threadContext->stateMutex);
583	++threadContext->interruptDepth;
584	if (threadContext->interruptDepth > 1 || !GBAThreadIsActive(threadContext)) {
585		MutexUnlock(&threadContext->stateMutex);
586		return;
587	}
588	threadContext->savedState = threadContext->state;
589	_waitOnInterrupt(threadContext);
590	threadContext->state = THREAD_INTERRUPTING;
591	threadContext->gba->cpu->nextEvent = 0;
592	ConditionWake(&threadContext->stateCond);
593	_waitUntilNotState(threadContext, THREAD_INTERRUPTING);
594	MutexUnlock(&threadContext->stateMutex);
595}
596
597void GBAThreadContinue(struct GBAThread* threadContext) {
598	MutexLock(&threadContext->stateMutex);
599	--threadContext->interruptDepth;
600	if (threadContext->interruptDepth < 1 && GBAThreadIsActive(threadContext)) {
601		threadContext->state = threadContext->savedState;
602		ConditionWake(&threadContext->stateCond);
603	}
604	MutexUnlock(&threadContext->stateMutex);
605}
606
607void GBARunOnThread(struct GBAThread* threadContext, void (*run)(struct GBAThread*)) {
608	MutexLock(&threadContext->stateMutex);
609	threadContext->run = run;
610	_waitOnInterrupt(threadContext);
611	threadContext->savedState = threadContext->state;
612	threadContext->state = THREAD_RUN_ON;
613	threadContext->gba->cpu->nextEvent = 0;
614	ConditionWake(&threadContext->stateCond);
615	_waitUntilNotState(threadContext, THREAD_RUN_ON);
616	MutexUnlock(&threadContext->stateMutex);
617}
618
619void GBAThreadPause(struct GBAThread* threadContext) {
620	bool frameOn = threadContext->sync.videoFrameOn;
621	MutexLock(&threadContext->stateMutex);
622	_waitOnInterrupt(threadContext);
623	if (threadContext->state == THREAD_RUNNING) {
624		_pauseThread(threadContext, false);
625		threadContext->frameWasOn = frameOn;
626		frameOn = false;
627	}
628	MutexUnlock(&threadContext->stateMutex);
629
630	GBASyncSetVideoSync(&threadContext->sync, frameOn);
631}
632
633void GBAThreadUnpause(struct GBAThread* threadContext) {
634	bool frameOn = threadContext->sync.videoFrameOn;
635	MutexLock(&threadContext->stateMutex);
636	_waitOnInterrupt(threadContext);
637	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
638		threadContext->state = THREAD_RUNNING;
639		ConditionWake(&threadContext->stateCond);
640		frameOn = threadContext->frameWasOn;
641	}
642	MutexUnlock(&threadContext->stateMutex);
643
644	GBASyncSetVideoSync(&threadContext->sync, frameOn);
645}
646
647bool GBAThreadIsPaused(struct GBAThread* threadContext) {
648	bool isPaused;
649	MutexLock(&threadContext->stateMutex);
650	_waitOnInterrupt(threadContext);
651	isPaused = threadContext->state == THREAD_PAUSED;
652	MutexUnlock(&threadContext->stateMutex);
653	return isPaused;
654}
655
656void GBAThreadTogglePause(struct GBAThread* threadContext) {
657	bool frameOn = threadContext->sync.videoFrameOn;
658	MutexLock(&threadContext->stateMutex);
659	_waitOnInterrupt(threadContext);
660	if (threadContext->state == THREAD_PAUSED || threadContext->state == THREAD_PAUSING) {
661		threadContext->state = THREAD_RUNNING;
662		ConditionWake(&threadContext->stateCond);
663		frameOn = threadContext->frameWasOn;
664	} else if (threadContext->state == THREAD_RUNNING) {
665		_pauseThread(threadContext, false);
666		threadContext->frameWasOn = frameOn;
667		frameOn = false;
668	}
669	MutexUnlock(&threadContext->stateMutex);
670
671	GBASyncSetVideoSync(&threadContext->sync, frameOn);
672}
673
674void GBAThreadPauseFromThread(struct GBAThread* threadContext) {
675	bool frameOn = true;
676	MutexLock(&threadContext->stateMutex);
677	_waitOnInterrupt(threadContext);
678	if (threadContext->state == THREAD_RUNNING) {
679		_pauseThread(threadContext, true);
680		frameOn = false;
681	}
682	MutexUnlock(&threadContext->stateMutex);
683
684	GBASyncSetVideoSync(&threadContext->sync, frameOn);
685}
686
687void GBAThreadLoadROM(struct GBAThread* threadContext, const char* fname) {
688	threadContext->rom = VFileOpen(fname, O_RDONLY);
689	threadContext->gameDir = 0;
690	if (!threadContext->gameDir) {
691		threadContext->gameDir = VDirOpenArchive(fname);
692	}
693}
694
695static void _loadGameDir(struct GBAThread* threadContext) {
696	threadContext->gameDir->rewind(threadContext->gameDir);
697	struct VDirEntry* dirent = threadContext->gameDir->listNext(threadContext->gameDir);
698	while (dirent) {
699		struct Patch patchTemp;
700		struct VFile* vf = threadContext->gameDir->openFile(threadContext->gameDir, dirent->name(dirent), O_RDONLY);
701		if (!vf) {
702			dirent = threadContext->gameDir->listNext(threadContext->gameDir);
703			continue;
704		}
705		if (!threadContext->rom && GBAIsROM(vf)) {
706			threadContext->rom = vf;
707		} else if (!threadContext->patch && loadPatch(vf, &patchTemp)) {
708			threadContext->patch = vf;
709		} else {
710			vf->close(vf);
711		}
712		dirent = threadContext->gameDir->listNext(threadContext->gameDir);
713	}
714}
715
716void GBAThreadReplaceROM(struct GBAThread* threadContext, const char* fname) {
717	GBAUnloadROM(threadContext->gba);
718
719	if (threadContext->rom) {
720		threadContext->rom->close(threadContext->rom);
721		threadContext->rom = 0;
722	}
723
724	if (threadContext->save) {
725		threadContext->save->close(threadContext->save);
726		threadContext->save = 0;
727	}
728
729	GBAThreadLoadROM(threadContext, fname);
730	if (threadContext->gameDir) {
731		_loadGameDir(threadContext);
732	}
733
734	threadContext->fname = fname;
735	threadContext->save = VDirOptionalOpenFile(threadContext->stateDir, threadContext->fname, "sram", ".sav", O_CREAT | O_RDWR);
736
737	GBARaiseIRQ(threadContext->gba, IRQ_GAMEPAK);
738	GBALoadROM(threadContext->gba, threadContext->rom, threadContext->save, threadContext->fname);
739}
740
741#ifdef USE_PTHREADS
742struct GBAThread* GBAThreadGetContext(void) {
743	pthread_once(&_contextOnce, _createTLS);
744	return pthread_getspecific(_contextKey);
745}
746#elif _WIN32
747struct GBAThread* GBAThreadGetContext(void) {
748	InitOnceExecuteOnce(&_contextOnce, _createTLS, NULL, 0);
749	return TlsGetValue(_contextKey);
750}
751#endif
752
753void GBAThreadTakeScreenshot(struct GBAThread* threadContext) {
754	GBATakeScreenshot(threadContext->gba, threadContext->stateDir);
755}
756
757#else
758struct GBAThread* GBAThreadGetContext(void) {
759	return 0;
760}
761#endif