From 2ce9806de5335fe08d82d0040bed1063802b475d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 22 Jul 2014 02:33:45 -0700 Subject: [PATCH 01/72] Fix ability to make audio buffers as big as they initially were --- src/gba/gba-audio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/gba-audio.c b/src/gba/gba-audio.c index 641b95550..f1bd249bd 100644 --- a/src/gba/gba-audio.c +++ b/src/gba/gba-audio.c @@ -71,7 +71,7 @@ void GBAAudioDeinit(struct GBAAudio* audio) { } void GBAAudioResizeBuffer(struct GBAAudio* audio, size_t samples) { - if (samples >= GBA_AUDIO_SAMPLES) { + if (samples > GBA_AUDIO_SAMPLES) { return; } From f55d0851628b44ee8fb0a475bba14c27c32f53a0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 22 Jul 2014 22:34:08 -0700 Subject: [PATCH 02/72] Change log handler API --- src/gba/gba-thread.c | 1 - src/gba/gba-thread.h | 5 ++++- src/gba/gba.c | 15 +++++++-------- src/gba/gba.h | 3 --- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 345a2c5bb..9f2eefceb 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -110,7 +110,6 @@ static THREAD_ENTRY _GBAThreadRun(void* context) { pthread_sigmask(SIG_SETMASK, &signals, 0); #endif - gba.logHandler = threadContext->logHandler; GBACreate(&gba); ARMSetComponents(&cpu, &gba.d, numComponents, components); ARMInit(&cpu); diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index 3e2c3007e..d8ea033b0 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -10,6 +10,7 @@ struct GBAThread; typedef void (*ThreadCallback)(struct GBAThread* threadContext); +typedef void (*LogHandler)(struct GBAThread*, enum GBALogLevel, const char* format, va_list args); enum ThreadState { THREAD_INITIALIZED = -1, @@ -55,6 +56,8 @@ struct GBAThread { struct VFile* patch; const char* fname; int activeKeys; + + // Run-time options int frameskip; float fpsTarget; size_t audioBuffers; @@ -66,7 +69,7 @@ struct GBAThread { Condition stateCond; enum ThreadState savedState; - GBALogHandler logHandler; + LogHandler logHandler; int logLevel; ThreadCallback startCallback; ThreadCallback cleanCallback; diff --git a/src/gba/gba.c b/src/gba/gba.c index 05f97a0d5..3d48df4a9 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -537,16 +537,15 @@ void GBAHalt(struct GBA* gba) { } static void _GBAVLog(struct GBA* gba, enum GBALogLevel level, const char* format, va_list args) { - if (!gba) { - struct GBAThread* threadContext = GBAThreadGetContext(); - if (threadContext) { + struct GBAThread* threadContext = GBAThreadGetContext(); + if (threadContext) { + if (!gba) { gba = threadContext->gba; } - } - - if (gba && gba->logHandler) { - gba->logHandler(gba, level, format, args); - return; + if (threadContext->logHandler) { + threadContext->logHandler(threadContext, level, format, args); + return; + } } if (gba && !(level & gba->logLevel) && level != GBA_LOG_FATAL) { diff --git a/src/gba/gba.h b/src/gba/gba.h index f4e20c340..3927d35bf 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -65,8 +65,6 @@ struct GBARotationSource; struct Patch; struct VFile; -typedef void (*GBALogHandler)(struct GBA*, enum GBALogLevel, const char* format, va_list args); - struct GBA { struct ARMComponent d; @@ -108,7 +106,6 @@ struct GBA { const char* activeFile; int logLevel; - GBALogHandler logHandler; }; struct GBACartridge { From 3051143fa3a1a458ac6c15af56ab3cd07ddc5e15 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 00:06:44 -0700 Subject: [PATCH 03/72] First pass at input mapping --- src/gba/gba-input.c | 84 ++++++++++++++++++++++++++++++++ src/gba/gba-input.h | 17 +++++++ src/gba/gba-thread.c | 2 + src/gba/gba-thread.h | 2 + src/gba/gba.h | 1 + src/platform/sdl/gl-main.c | 4 +- src/platform/sdl/sdl-events.c | 92 +++++++++++++---------------------- src/platform/sdl/sdl-events.h | 1 + 8 files changed, 144 insertions(+), 59 deletions(-) create mode 100644 src/gba/gba-input.c create mode 100644 src/gba/gba-input.h diff --git a/src/gba/gba-input.c b/src/gba/gba-input.c new file mode 100644 index 000000000..4641626c6 --- /dev/null +++ b/src/gba/gba-input.c @@ -0,0 +1,84 @@ +#include "gba-input.h" + +struct GBAInputMapImpl { + int* map; + uint32_t type; +}; + +void GBAInputMapInit(struct GBAInputMap* map) { + map->maps = 0; + map->numMaps = 0; +} + +void GBAInputMapDeinit(struct GBAInputMap* map) { + size_t m; + for (m = 0; m < map->numMaps; ++m) { + free(map->maps[m].map); + } + free(map->maps); + map->maps = 0; + map->numMaps = 0; +} + +enum GBAKey GBAInputMapKey(struct GBAInputMap* map, uint32_t type, int key) { + size_t m; + struct GBAInputMapImpl* impl = 0; + for (m = 0; m < map->numMaps; ++m) { + if (map->maps[m].type == type) { + impl = &map->maps[m]; + break; + } + } + if (!impl || !impl->map) { + return GBA_KEY_NONE; + } + + for (m = 0; m < GBA_KEY_MAX; ++m) { + if (impl->map[m] == key) { + return m; + } + } + return GBA_KEY_NONE; +} + +void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKey input) { + struct GBAInputMapImpl* impl = 0; + if (map->numMaps == 0) { + map->maps = malloc(sizeof(*map->maps)); + map->numMaps = 1; + impl = &map->maps[0]; + impl->type = type; + impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); + } else { + size_t m; + for (m = 0; m < map->numMaps; ++m) { + if (map->maps[m].type == type) { + impl = &map->maps[m]; + break; + } + } + } + if (!impl) { + size_t m; + for (m = 0; m < map->numMaps; ++m) { + if (!map->maps[m].type) { + impl = &map->maps[m]; + break; + } + } + if (impl) { + impl->type = type; + impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); + } else { + map->maps = realloc(map->maps, sizeof(*map->maps) * map->numMaps * 2); + for (m = map->numMaps * 2 - 1; m > map->numMaps; --m) { + map->maps[m].type = 0; + map->maps[m].map = 0; + } + impl = &map->maps[m]; + impl->type = type; + impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); + } + } + impl->map[input] = key; +} diff --git a/src/gba/gba-input.h b/src/gba/gba-input.h new file mode 100644 index 000000000..684598db2 --- /dev/null +++ b/src/gba/gba-input.h @@ -0,0 +1,17 @@ +#ifndef GBA_INPUT_H +#define GBA_INPUT_H + +#include "gba.h" + +struct GBAInputMap { + struct GBAInputMapImpl* maps; + size_t numMaps; +}; + +void GBAInputMapInit(struct GBAInputMap*); +void GBAInputMapDeinit(struct GBAInputMap*); + +enum GBAKey GBAInputMapKey(struct GBAInputMap*, uint32_t type, int key); +void GBAInputBindKey(struct GBAInputMap*, uint32_t type, int key, enum GBAKey input); + +#endif diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 9f2eefceb..37ccabdf7 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -390,6 +390,8 @@ void GBAThreadJoin(struct GBAThread* threadContext) { } free(threadContext->rewindBuffer); + GBAInputMapDeinit(&threadContext->inputMap); + if (threadContext->rom) { threadContext->rom->close(threadContext->rom); threadContext->rom = 0; diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index d8ea033b0..70468eb10 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -4,6 +4,7 @@ #include "common.h" #include "gba.h" +#include "gba-input.h" #include "util/threading.h" #include "platform/commandline.h" @@ -56,6 +57,7 @@ struct GBAThread { struct VFile* patch; const char* fname; int activeKeys; + struct GBAInputMap inputMap; // Run-time options int frameskip; diff --git a/src/gba/gba.h b/src/gba/gba.h index 3927d35bf..5825f9154 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -57,6 +57,7 @@ enum GBAKey { GBA_KEY_DOWN = 7, GBA_KEY_R = 8, GBA_KEY_L = 9, + GBA_KEY_MAX, GBA_KEY_NONE = -1 }; diff --git a/src/platform/sdl/gl-main.c b/src/platform/sdl/gl-main.c index 65e85c770..f494366ea 100644 --- a/src/platform/sdl/gl-main.c +++ b/src/platform/sdl/gl-main.c @@ -100,6 +100,9 @@ int main(int argc, char** argv) { renderer.audio.samples = context.audioBuffers; GBASDLInitAudio(&renderer.audio); + renderer.events.bindings = &context.inputMap; + GBASDLInitEvents(&renderer.events); + GBAThreadStart(&context); _GBASDLRunloop(&context, &renderer); @@ -118,7 +121,6 @@ static int _GBASDLInit(struct GLSoftwareRenderer* renderer) { return 0; } - GBASDLInitEvents(&renderer->events); #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 803f0fbd5..ab1e3a8d9 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -12,6 +12,9 @@ #define GUI_MOD KMOD_CTRL #endif +#define SDL_BINDING_KEY 0x53444C4B +#define SDL_BINDING_BUTTON 0x53444C42 + bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return false; @@ -21,6 +24,24 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) { #if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); #endif + + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_z, GBA_KEY_A); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_x, GBA_KEY_B); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_a, GBA_KEY_L); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_s, GBA_KEY_R); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_RETURN, GBA_KEY_START); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_BACKSPACE, GBA_KEY_SELECT); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_UP, GBA_KEY_UP); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_DOWN, GBA_KEY_DOWN); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT); + GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT); + + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 2, GBA_KEY_A); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 1, GBA_KEY_B); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 6, GBA_KEY_L); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 7, GBA_KEY_R); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 8, GBA_KEY_START); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 9, GBA_KEY_SELECT); return true; } @@ -29,64 +50,25 @@ void GBASDLDeinitEvents(struct GBASDLEvents* context) { SDL_QuitSubSystem(SDL_INIT_JOYSTICK); } -enum GBAKey GBASDLMapButtonToKey(int button) { - // Sorry, hardcoded to my gamepad for now - switch (button) { - case 2: - return GBA_KEY_A; - case 1: - return GBA_KEY_B; - case 6: - return GBA_KEY_L; - case 7: - return GBA_KEY_R; - case 8: - return GBA_KEY_START; - case 9: - return GBA_KEY_SELECT; - default: - return GBA_KEY_NONE; - } -} - static void _pauseAfterFrame(struct GBAThread* context) { context->frameCallback = 0; GBAThreadPauseFromThread(context); } static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents* sdlContext, const struct SDL_KeyboardEvent* event) { - enum GBAKey key = 0; + enum GBAKey key = GBA_KEY_NONE; + if (!event->keysym.mod) { + key = GBAInputMapKey(&context->inputMap, SDL_BINDING_KEY, event->keysym.sym); + } + if (key != GBA_KEY_NONE) { + if (event->type == SDL_KEYDOWN) { + context->activeKeys |= 1 << key; + } else { + context->activeKeys &= ~(1 << key); + } + return; + } switch (event->keysym.sym) { - case SDLK_z: - key = GBA_KEY_A; - break; - case SDLK_x: - key = GBA_KEY_B; - break; - case SDLK_a: - key = GBA_KEY_L; - break; - case SDLK_s: - key = GBA_KEY_R; - break; - case SDLK_RETURN: - key = GBA_KEY_START; - break; - case SDLK_BACKSPACE: - key = GBA_KEY_SELECT; - break; - case SDLK_UP: - key = GBA_KEY_UP; - break; - case SDLK_DOWN: - key = GBA_KEY_DOWN; - break; - case SDLK_LEFT: - key = GBA_KEY_LEFT; - break; - case SDLK_RIGHT: - key = GBA_KEY_RIGHT; - break; case SDLK_F11: if (event->type == SDL_KEYDOWN && context->debugger) { ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL); @@ -192,17 +174,11 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents } return; } - - if (event->type == SDL_KEYDOWN) { - context->activeKeys |= 1 << key; - } else { - context->activeKeys &= ~(1 << key); - } } static void _GBASDLHandleJoyButton(struct GBAThread* context, const struct SDL_JoyButtonEvent* event) { enum GBAKey key = 0; - key = GBASDLMapButtonToKey(event->button); + key = GBAInputMapKey(&context->inputMap, SDL_BINDING_BUTTON, event->button); if (key == GBA_KEY_NONE) { return; } diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index d8b9c22bb..1621aa052 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -8,6 +8,7 @@ #include struct GBASDLEvents { + struct GBAInputMap* bindings; SDL_Joystick* joystick; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; From 94001b113311cad1cce263f14afa2334a5ba4249 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 00:09:14 -0700 Subject: [PATCH 04/72] Fix crashes when GBARR is not initialized --- src/platform/sdl/sdl-events.c | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index ab1e3a8d9..c087a8b6d 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -84,8 +84,10 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents return; case SDLK_ESCAPE: GBAThreadInterrupt(context); - GBARRStopPlaying(context->gba->rr); - GBARRStopRecording(context->gba->rr); + if (context->gba->rr) { + GBARRStopPlaying(context->gba->rr); + GBARRStopRecording(context->gba->rr); + } GBAThreadContinue(context); return; default: @@ -111,22 +113,26 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBAThreadReset(context); break; case SDLK_t: - GBAThreadReset(context); - GBAThreadInterrupt(context); - GBARRContextCreate(context->gba); - GBARRSetStream(context->gba->rr, context->stateDir); - GBARRStopPlaying(context->gba->rr); - GBARRStartRecording(context->gba->rr); - GBAThreadContinue(context); + if (context->gba->rr) { + GBAThreadReset(context); + GBAThreadInterrupt(context); + GBARRContextCreate(context->gba); + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopPlaying(context->gba->rr); + GBARRStartRecording(context->gba->rr); + GBAThreadContinue(context); + } break; case SDLK_y: - GBAThreadReset(context); - GBAThreadInterrupt(context); - GBARRContextCreate(context->gba); - GBARRSetStream(context->gba->rr, context->stateDir); - GBARRStopRecording(context->gba->rr); - GBARRStartPlaying(context->gba->rr, event->keysym.mod & KMOD_SHIFT); - GBAThreadContinue(context); + if (context->gba->rr) { + GBAThreadReset(context); + GBAThreadInterrupt(context); + GBARRContextCreate(context->gba); + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopRecording(context->gba->rr); + GBARRStartPlaying(context->gba->rr, event->keysym.mod & KMOD_SHIFT); + GBAThreadContinue(context); + } break; default: break; From c7f7d0f752cf6a58b9dacebc8b3dd5385b58833e Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 00:19:50 -0700 Subject: [PATCH 05/72] Don't make empty savestate files when loading savestates --- src/gba/gba-serialize.c | 24 ++++++++---------------- src/gba/gba-serialize.h | 3 --- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index 7ad7aee8d..f18b76669 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -68,11 +68,11 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { GBAAudioDeserialize(&gba->audio, state); } -static struct VFile* _getStateVf(struct GBA* gba, int slot) { +static struct VFile* _getStateVf(struct GBA* gba, int slot, bool write) { char path[PATH_MAX]; path[PATH_MAX - 1] = '\0'; snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot); - struct VFile* vf = VFileOpen(path, O_CREAT | O_RDWR); + struct VFile* vf = VFileOpen(path, write ? (O_CREAT | O_RDWR) : O_RDONLY); if (vf) { vf->truncate(vf, sizeof(struct GBASerializedState)); } @@ -80,37 +80,29 @@ static struct VFile* _getStateVf(struct GBA* gba, int slot) { } bool GBASaveState(struct GBA* gba, int slot) { - struct VFile* vf = _getStateVf(gba, slot); + struct VFile* vf = _getStateVf(gba, slot, true); if (!vf) { return false; } - struct GBASerializedState* state = GBAMapState(vf); + struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); GBASerialize(gba, state); - GBAUnmapState(vf, state); + vf->unmap(vf, state, sizeof(struct GBASerializedState)); vf->close(vf); return true; } bool GBALoadState(struct GBA* gba, int slot) { - struct VFile* vf = _getStateVf(gba, slot); + struct VFile* vf = _getStateVf(gba, slot, false); if (!vf) { return false; } - struct GBASerializedState* state = GBAMapState(vf); + struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ); GBADeserialize(gba, state); - GBAUnmapState(vf, state); + vf->unmap(vf, state, sizeof(struct GBASerializedState)); vf->close(vf); return true; } -struct GBASerializedState* GBAMapState(struct VFile* vf) { - return vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); -} - -void GBAUnmapState(struct VFile* vf, struct GBASerializedState* state) { - vf->unmap(vf, state, sizeof(struct GBASerializedState)); -} - struct GBASerializedState* GBAAllocateState(void) { return anonymousMemoryMap(sizeof(struct GBASerializedState)); } diff --git a/src/gba/gba-serialize.h b/src/gba/gba-serialize.h index 86d3ce9a2..d354b83d9 100644 --- a/src/gba/gba-serialize.h +++ b/src/gba/gba-serialize.h @@ -266,9 +266,6 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state); bool GBASaveState(struct GBA* gba, int slot); bool GBALoadState(struct GBA* gba, int slot); -struct GBASerializedState* GBAMapState(struct VFile* vf); -void GBAUnmapState(struct VFile* vf, struct GBASerializedState* state); - struct GBASerializedState* GBAAllocateState(void); void GBADeallocateState(struct GBASerializedState* state); From f1a58c725457e7a64c7ebebbb272eead6f11f197 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 00:54:11 -0700 Subject: [PATCH 06/72] Fix ObjAffineSet over multiple inputs --- src/gba/gba-bios.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/gba-bios.c b/src/gba/gba-bios.c index c5738ef9b..f036bbb17 100644 --- a/src/gba/gba-bios.c +++ b/src/gba/gba-bios.c @@ -76,7 +76,7 @@ static void _ObjAffineSet(struct GBA* gba) { sx = cpu->memory.load16(cpu, offset, 0) / 256.f; sy = cpu->memory.load16(cpu, offset + 2, 0) / 256.f; theta = (cpu->memory.loadU16(cpu, offset + 4, 0) >> 8) / 128.f * M_PI; - offset += 6; + offset += 8; // Rotation a = d = cosf(theta); b = c = sinf(theta); From ec4eb253e5d940429195c9525018fb42d9c8e71a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 03:06:09 -0700 Subject: [PATCH 07/72] Unify swi state saving, keep I bit --- src/gba/hle-bios.c | 79 +++++++++++++++++++++++----------------------- src/gba/hle-bios.s | 28 +++++++--------- 2 files changed, 51 insertions(+), 56 deletions(-) diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index a4e779185..6129ef9ed 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -2,49 +2,48 @@ #include "gba-memory.h" -const size_t hleBiosLength = 516; +const size_t hleBiosLength = 500; const uint8_t hleBios[SIZE_BIOS] = { 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x05, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, - 0x1a, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, + 0x24, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x02, 0xf3, 0xa0, 0xe3, 0x00, 0x00, 0x5d, 0xe3, 0x01, 0xd3, 0xa0, 0x03, 0x20, 0xd0, 0x4d, 0x02, - 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x40, 0x5e, 0xe5, 0x54, 0x50, 0xa0, 0xe3, - 0x04, 0x41, 0x95, 0xe7, 0x00, 0x00, 0x54, 0xe3, 0x0f, 0xe0, 0xa0, 0xe1, - 0x14, 0xff, 0x2f, 0x11, 0x30, 0x40, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, + 0x30, 0x40, 0x2d, 0xe9, 0x02, 0x40, 0x5e, 0xe5, 0x7c, 0x50, 0xa0, 0xe3, + 0x04, 0x41, 0x95, 0xe7, 0x00, 0x00, 0x54, 0xe3, 0x00, 0x50, 0x4f, 0xe1, + 0x20, 0x00, 0x2d, 0xe9, 0x80, 0x50, 0x05, 0xe2, 0x1f, 0x50, 0x85, 0xe3, + 0x05, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x0f, 0xe0, 0xa0, 0xe1, + 0x14, 0xff, 0x2f, 0x11, 0x00, 0x40, 0xbd, 0xe8, 0x93, 0xf0, 0x29, 0xe3, + 0x20, 0x00, 0xbd, 0xe8, 0x05, 0xf0, 0x69, 0xe1, 0x30, 0x40, 0xbd, 0xe8, + 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa8, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, - 0x94, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, - 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, - 0x04, 0xf0, 0x5e, 0xe2, 0x01, 0x00, 0xa0, 0xe3, 0x01, 0x10, 0xa0, 0xe3, - 0x0c, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0x4f, 0xe1, 0x1f, 0xf0, 0x29, 0xe3, - 0x01, 0x43, 0xa0, 0xe3, 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, - 0x01, 0x20, 0xa0, 0xe3, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xc4, 0xe5, - 0x08, 0x02, 0xc4, 0xe5, 0xb8, 0x30, 0x54, 0xe1, 0x01, 0x30, 0x13, 0xe0, - 0x01, 0x30, 0x23, 0x10, 0xb8, 0x30, 0x44, 0x11, 0x08, 0x22, 0xc4, 0xe5, - 0xf7, 0xff, 0xff, 0x0a, 0x93, 0xf0, 0x29, 0xe3, 0x05, 0xf0, 0x69, 0xe1, - 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x00, 0x50, 0x4f, 0xe1, - 0x1f, 0xf0, 0x29, 0xe3, 0x02, 0x36, 0xa0, 0xe1, 0x01, 0x04, 0x12, 0xe3, - 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, - 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8, 0x03, 0x00, 0x51, 0xe1, - 0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x16, 0x00, 0x00, 0xea, - 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0xa3, 0x35, 0x81, 0xe0, - 0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xc1, 0xb0, - 0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea, 0x01, 0x03, 0x12, 0xe3, - 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x03, 0x00, 0x51, 0xe1, - 0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, - 0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0, 0x01, 0x00, 0xc0, 0xe3, - 0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1, 0xb2, 0x20, 0xd0, 0xb0, - 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, 0x93, 0xf0, 0x29, 0xe3, - 0x05, 0xf0, 0x69, 0xe1, 0x00, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, - 0x00, 0x50, 0x4f, 0xe1, 0x1f, 0xf0, 0x29, 0xe3, 0xf0, 0x07, 0x2d, 0xe9, - 0x01, 0x04, 0x12, 0xe3, 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, - 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, - 0x03, 0x50, 0xa0, 0xe1, 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, - 0x03, 0x80, 0xa0, 0xe1, 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, - 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, - 0x03, 0x00, 0x00, 0xea, 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, - 0xf8, 0x07, 0xa1, 0xb8, 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x07, 0xbd, 0xe8, - 0x93, 0xf0, 0x29, 0xe3, 0x05, 0xf0, 0x69, 0xe1, 0x00, 0x80, 0xbd, 0xe8 + 0x0c, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0f, 0x50, 0x2d, 0xe9, + 0x01, 0x03, 0xa0, 0xe3, 0x00, 0xe0, 0x8f, 0xe2, 0x04, 0xf0, 0x10, 0xe5, + 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x01, 0x00, 0xa0, 0xe3, + 0x01, 0x10, 0xa0, 0xe3, 0x0c, 0x40, 0x2d, 0xe9, 0x01, 0x43, 0xa0, 0xe3, + 0x00, 0x00, 0x50, 0xe3, 0x00, 0x00, 0xa0, 0xe3, 0x01, 0x20, 0xa0, 0xe3, + 0x00, 0x00, 0x00, 0x0a, 0x01, 0x03, 0xc4, 0xe5, 0x08, 0x02, 0xc4, 0xe5, + 0xb8, 0x30, 0x54, 0xe1, 0x01, 0x30, 0x13, 0xe0, 0x01, 0x30, 0x23, 0x10, + 0xb8, 0x30, 0x44, 0x11, 0x08, 0x22, 0xc4, 0xe5, 0xf7, 0xff, 0xff, 0x0a, + 0x0c, 0x80, 0xbd, 0xe8, 0x00, 0x40, 0x2d, 0xe9, 0x02, 0x36, 0xa0, 0xe1, + 0x01, 0x04, 0x12, 0xe3, 0x0f, 0x00, 0x00, 0x0a, 0x01, 0x03, 0x12, 0xe3, + 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, 0x04, 0x00, 0xb0, 0xe8, + 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, + 0x16, 0x00, 0x00, 0xea, 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, + 0xa3, 0x35, 0x81, 0xe0, 0xb0, 0x20, 0xd0, 0xe1, 0x03, 0x00, 0x51, 0xe1, + 0xb2, 0x20, 0xc1, 0xb0, 0xfc, 0xff, 0xff, 0xba, 0x0e, 0x00, 0x00, 0xea, + 0x01, 0x03, 0x12, 0xe3, 0x05, 0x00, 0x00, 0x0a, 0x23, 0x35, 0x81, 0xe0, + 0x03, 0x00, 0x51, 0xe1, 0x04, 0x00, 0xb0, 0xb8, 0x04, 0x00, 0xa1, 0xb8, + 0xfb, 0xff, 0xff, 0xba, 0x06, 0x00, 0x00, 0xea, 0xa3, 0x35, 0x81, 0xe0, + 0x01, 0x00, 0xc0, 0xe3, 0x01, 0x10, 0xc1, 0xe3, 0x03, 0x00, 0x51, 0xe1, + 0xb2, 0x20, 0xd0, 0xb0, 0xb2, 0x20, 0xc1, 0xb0, 0xfb, 0xff, 0xff, 0xba, + 0x00, 0x80, 0xbd, 0xe8, 0xf0, 0x47, 0x2d, 0xe9, 0x01, 0x04, 0x12, 0xe3, + 0x02, 0x36, 0xa0, 0xe1, 0x23, 0x25, 0x81, 0xe0, 0x0b, 0x00, 0x00, 0x0a, + 0x00, 0x30, 0x90, 0xe5, 0x03, 0x40, 0xa0, 0xe1, 0x03, 0x50, 0xa0, 0xe1, + 0x03, 0x60, 0xa0, 0xe1, 0x03, 0x70, 0xa0, 0xe1, 0x03, 0x80, 0xa0, 0xe1, + 0x03, 0x90, 0xa0, 0xe1, 0x03, 0xa0, 0xa0, 0xe1, 0x02, 0x00, 0x51, 0xe1, + 0xf8, 0x07, 0xa1, 0xb8, 0xfc, 0xff, 0xff, 0xba, 0x03, 0x00, 0x00, 0xea, + 0x02, 0x00, 0x51, 0xe1, 0xf8, 0x07, 0xb0, 0xb8, 0xf8, 0x07, 0xa1, 0xb8, + 0xfb, 0xff, 0xff, 0xba, 0xf0, 0x87, 0xbd, 0xe8 }; diff --git a/src/gba/hle-bios.s b/src/gba/hle-bios.s index 0084c641a..6849badfc 100644 --- a/src/gba/hle-bios.s +++ b/src/gba/hle-bios.s @@ -23,8 +23,18 @@ ldrb r4, [lr, #-2] mov r5, #swiTable ldr r4, [r5, r4, lsl #2] cmp r4, #0 +mrs r5, spsr +stmfd sp!, {r5} +and r5, #0x80 +orr r5, #0x1F +msr cpsr, r5 +stmfd sp!, {lr} mov lr, pc bxne r4 +ldmfd sp!, {lr} +msr cpsr, #0x93 +ldmfd sp!, {r5} +msr spsr, r5 ldmfd sp!, {r4-r5, lr} movs pc, lr @@ -57,8 +67,6 @@ mov r0, #1 mov r1, #1 IntrWait: stmfd sp!, {r2-r3, lr} -mrs r5, spsr -msr cpsr, #0x1F # Pull current interrupts enabled and add the ones we need mov r4, #0x04000000 # See if we want to return immediately @@ -78,14 +86,10 @@ eorne r3, r1 strneh r3, [r4, #-8] strb r2, [r4, #0x208] beq 0b -msr cpsr, #0x93 -msr spsr, r5 ldmfd sp!, {r2-r3, pc} CpuSet: stmfd sp!, {lr} -mrs r5, spsr -msr cpsr, #0x1F mov r3, r2, lsl #12 tst r2, #0x01000000 beq 0f @@ -134,15 +138,10 @@ ldrlth r2, [r0], #2 strlth r2, [r1], #2 blt 2b 3: -msr cpsr, #0x93 -msr spsr, r5 ldmfd sp!, {pc} CpuFastSet: -stmfd sp!, {lr} -mrs r5, spsr -msr cpsr, #0x1F -stmfd sp!, {r4-r10} +stmfd sp!, {r4-r10, lr} tst r2, #0x01000000 mov r3, r2, lsl #12 add r2, r1, r3, lsr #10 @@ -168,7 +167,4 @@ ldmltia r0!, {r3-r10} stmltia r1!, {r3-r10} blt 0b 2: -ldmfd sp!, {r4-r10} -msr cpsr, #0x93 -msr spsr, r5 -ldmfd sp!, {pc} +ldmfd sp!, {r4-r10, pc} From 6bbb1d5b43403985f4544207b76792e1484c43cd Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 22:57:25 -0700 Subject: [PATCH 08/72] Put back gnu99 standard, we use some of the features --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 78888eec8..74e2dfe70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 2.6) project(GBAc) set(BINARY_NAME gbac CACHE INTERNAL "Name of output binaries") -set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -std=c99") -set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=c99") +set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -std=gnu99") +set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=gnu99") set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger") set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger") set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") From 754d3f50ced2598d8adeff7fe032ca06b625ac7e Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 22:57:44 -0700 Subject: [PATCH 09/72] Check if we have a subparser before trying to use it --- src/platform/commandline.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/commandline.c b/src/platform/commandline.c index a97f209ad..d6b0e9acd 100644 --- a/src/platform/commandline.c +++ b/src/platform/commandline.c @@ -50,7 +50,7 @@ bool parseCommandArgs(struct StartupOptions* opts, int argc, char* const* argv, "g" #endif ; - if (subparser->extraOptions) { + if (subparser && subparser->extraOptions) { // TODO: modularize options to subparsers strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1); } From 6c9ee4c2127cc43585e3861ac2e97028e6d5f45e Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 22:57:58 -0700 Subject: [PATCH 10/72] Modernize egl-main --- src/platform/sdl/egl-main.c | 41 ++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/platform/sdl/egl-main.c b/src/platform/sdl/egl-main.c index a92d1f752..3d6b0e2bf 100644 --- a/src/platform/sdl/egl-main.c +++ b/src/platform/sdl/egl-main.c @@ -68,41 +68,46 @@ static void _GBASDLStart(struct GBAThread* context); static void _GBASDLClean(struct GBAThread* context); int main(int argc, char** argv) { - const char* fname = "test.rom"; - if (argc > 1) { - fname = argv[1]; - } - int fd = open(fname, O_RDONLY); - if (fd < 0) { + struct GBAVideoEGLRenderer renderer; + + struct StartupOptions opts; + if (!parseCommandArgs(&opts, argc, argv, 0)) { + usage(argv[0], 0); + freeOptions(&opts); return 1; } - struct GBAVideoEGLRenderer renderer; - if (!_GBAEGLInit(&renderer)) { return 1; } GBAVideoSoftwareRendererCreate(&renderer.d); struct GBAThread context = { - .fd = fd, - .fname = fname, - .biosFd = -1, - .useDebugger = 0, .renderer = &renderer.d.d, - .frameskip = 0, .sync.videoFrameWait = 0, - .sync.audioWait = 0, + .sync.audioWait = 1, .startCallback = _GBASDLStart, .cleanCallback = _GBASDLClean, .userData = &renderer }; + + context.debugger = createDebugger(&opts); + + GBAMapOptionsToContext(&opts, &context); + + renderer.audio.samples = context.audioBuffers; + GBASDLInitAudio(&renderer.audio); + + renderer.events.bindings = &context.inputMap; + GBASDLInitEvents(&renderer.events); + GBAThreadStart(&context); _GBAEGLRunloop(&context, &renderer); GBAThreadJoin(&context); - close(fd); + freeOptions(&opts); + free(context.debugger); _GBAEGLDeinit(&renderer); @@ -114,9 +119,6 @@ static int _GBAEGLInit(struct GBAVideoEGLRenderer* renderer) { return 0; } - GBASDLInitEvents(&renderer->events); - GBASDLInitAudio(&renderer->audio); - bcm_host_init(); renderer->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); int major, minor; @@ -238,7 +240,7 @@ static void _GBAEGLRunloop(struct GBAThread* context, struct GBAVideoEGLRenderer GBASyncWaitFrameEnd(&context->sync); while (SDL_PollEvent(&event)) { - GBASDLHandleEvent(context, &event); + GBASDLHandleEvent(context, &renderer->events, &event); } } } @@ -259,6 +261,7 @@ static void _GBAEGLDeinit(struct GBAVideoEGLRenderer* renderer) { static void _GBASDLStart(struct GBAThread* threadContext) { struct GBAVideoEGLRenderer* renderer = threadContext->userData; renderer->audio.audio = &threadContext->gba->audio; + renderer->audio.thread = threadContext; } static void _GBASDLClean(struct GBAThread* threadContext) { From 5eb729faf654e96ff6f223605af88f6b0f6dad56 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 23:05:17 -0700 Subject: [PATCH 11/72] Remove unused HLE BIOS length constant --- src/gba/hle-bios.c | 1 - src/gba/hle-bios.h | 1 - 2 files changed, 2 deletions(-) diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index 6129ef9ed..83de7ee00 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -2,7 +2,6 @@ #include "gba-memory.h" -const size_t hleBiosLength = 500; const uint8_t hleBios[SIZE_BIOS] = { 0x06, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x05, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, diff --git a/src/gba/hle-bios.h b/src/gba/hle-bios.h index e0772c186..b27196761 100644 --- a/src/gba/hle-bios.h +++ b/src/gba/hle-bios.h @@ -3,7 +3,6 @@ #include "common.h" -extern const size_t hleBiosLength; extern const uint8_t hleBios[]; #endif From 5477c891c5e235601b00601425df1fa8673a7929 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 23:12:28 -0700 Subject: [PATCH 12/72] Fix cmp and related opcodes in disassembler --- src/arm/decoder-arm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/arm/decoder-arm.c b/src/arm/decoder-arm.c index 163f35378..ee299369a 100644 --- a/src/arm/decoder-arm.c +++ b/src/arm/decoder-arm.c @@ -103,7 +103,9 @@ info->affectsCPSR = S; \ SHIFTER; \ if (SKIPPED == 1) { \ - info->operandFormat &= ~ARM_OPERAND_1; \ + info->operandFormat >>= 8; \ + info->op1 = info->op2; \ + info->op2 = info->op3; \ } else if (SKIPPED == 2) { \ info->operandFormat &= ~ARM_OPERAND_2; \ } \ From cbb522a702051cb0c39d73889a5affaeabfa97bc Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 23 Jul 2014 23:12:54 -0700 Subject: [PATCH 13/72] Fix *mull, *mlal instructions in disassembler --- src/arm/decoder.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/arm/decoder.c b/src/arm/decoder.c index 42c58057f..0ac1c612b 100644 --- a/src/arm/decoder.c +++ b/src/arm/decoder.c @@ -332,6 +332,10 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i written = _decodeRegister(info->op3.reg, buffer, blen); ADVANCE(written); } + if (info->operandFormat & ARM_OPERAND_4) { + strncpy(buffer, ", ", blen - 1); + ADVANCE(2); + } if (info->operandFormat & ARM_OPERAND_IMMEDIATE_4) { written = snprintf(buffer, blen - 1, "#%i", info->op4.immediate); ADVANCE(written); From a855f53355550ef6bbacb139cbdde0b013df7a74 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 01:13:14 -0700 Subject: [PATCH 14/72] Fix mosiac on edges of mode 0 regions --- src/gba/renderers/video-software.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 300e2bdd5..ba8b6d9df 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -1094,6 +1094,17 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re } #define DRAW_BACKGROUND_MODE_0(BPP, BLEND, OBJWIN) \ + uint32_t* pixel = &renderer->row[outX]; \ + if (background->mosaic && renderer->mosaic.bgH) { \ + int mosaicH = renderer->mosaic.bgH + 1; \ + int x; \ + int mosaicWait = outX % mosaicH; \ + int carryData = 0; \ + paletteData = 0; /* Quiets compiler warning */ \ + DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \ + return; \ + } \ + \ if (inX & 0x7) { \ int mod8 = inX & 0x7; \ BACKGROUND_TEXT_SELECT_CHARACTER; \ @@ -1119,17 +1130,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re outX = renderer->start + tileX * 8 - (inX & 0x7); \ } \ \ - uint32_t* pixel = &renderer->row[outX]; \ - if (background->mosaic && renderer->mosaic.bgH) { \ - int mosaicH = renderer->mosaic.bgH + 1; \ - int x; \ - int mosaicWait = outX % mosaicH; \ - int carryData = 0; \ - paletteData = 0; /* Quiets compiler warning */ \ - DRAW_BACKGROUND_MODE_0_MOSAIC_ ## BPP (BLEND, OBJWIN) \ - return; \ - } \ - \ + pixel = &renderer->row[outX]; \ DRAW_BACKGROUND_MODE_0_TILES_ ## BPP (BLEND, OBJWIN) static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* background, int y) { From 900d1684a9a762cd571e86241a32d4cfb0ab1039 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 02:34:42 -0700 Subject: [PATCH 15/72] Fix window edge case that might cause 0-width windows to draw --- src/gba/renderers/video-software.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index ba8b6d9df..b39eed92d 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -1114,6 +1114,9 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re /* TODO: ensure tiles are properly aligned from this*/ \ end = renderer->end; \ } \ + if (end == outX) { \ + return; \ + } \ DRAW_BACKGROUND_MODE_0_TILE_SUFFIX_ ## BPP (BLEND, OBJWIN) \ } \ if (inX & 0x7 || (renderer->end - renderer->start) & 0x7) { \ From 436c53b0a67c9676109550d4472ac53f3c7b458a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 02:37:04 -0700 Subject: [PATCH 16/72] Remove redefinition of COMPOSITE_16_NO_OBJWIN --- src/gba/renderers/video-software.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index b39eed92d..bb9bc12db 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -767,10 +767,6 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re #define COMPOSITE_256_NO_OBJWIN(BLEND) \ COMPOSITE_16_NO_OBJWIN(BLEND) - -#define COMPOSITE_16_NO_OBJWIN(BLEND) \ - _composite ## BLEND ## NoObjwin(renderer, pixel, palette[pixelData] | flags, current); - #define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN) \ pixelData = tileData & 0xF; \ current = *pixel; \ From 553f345bbd1b26241d3c68f07b983b22ab22d8a0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 02:38:36 -0700 Subject: [PATCH 17/72] Remember to initialize rr --- src/gba/gba.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gba/gba.c b/src/gba/gba.c index 3d48df4a9..007582300 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -135,6 +135,7 @@ static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component) { gba->keySource = 0; gba->rotationSource = 0; gba->rumble = 0; + gba->rr = 0; gba->romVf = 0; gba->biosVf = 0; From e2c3c4068db626f05ea4f013e62a72672ac66427 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 02:52:02 -0700 Subject: [PATCH 18/72] Pull out vram pointer --- src/gba/renderers/video-software.c | 35 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index bb9bc12db..403e6e751 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -790,7 +790,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re xBase += (localX & 0x100) << 5; \ } \ screenBase = yBase + (xBase >> 3); \ - mapData = renderer->d.vram[screenBase]; \ + mapData = vram[screenBase]; \ localY = inY & 0x7; \ if (GBA_TEXT_MAP_VFLIP(mapData)) { \ localY = 7 - localY; \ @@ -831,7 +831,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= 4 * mod8; \ for (; outX < end; ++outX) { \ @@ -847,7 +847,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re #define DRAW_BACKGROUND_MODE_0_TILE_PREFIX_16(BLEND, OBJWIN) \ charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ @@ -881,7 +881,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re if (!mosaicWait) { \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= x * 4; \ } else { \ @@ -910,7 +910,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ palette = &mainPalette[paletteData]; \ charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) >> 2) + localY; \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ if (tileData) { \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN); \ @@ -959,7 +959,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re int end2 = end - 4; \ int shift = inX & 0x3; \ if (end2 > 0) { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ tileData >>= 8 * shift; \ shift = 0; \ for (; outX < end2; ++outX) { \ @@ -968,7 +968,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re } \ } \ \ - tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ + tileData = ((uint32_t*) vram)[charBase + 1]; \ tileData >>= 8 * shift; \ for (; outX < end; ++outX) { \ uint32_t* pixel = &renderer->row[outX]; \ @@ -981,7 +981,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re outX = renderer->end - 8 + end; \ int end2 = 4 - end; \ if (end2 > 0) { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ for (; outX < renderer->end - end2; ++outX) { \ uint32_t* pixel = &renderer->row[outX]; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ @@ -989,7 +989,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re ++charBase; \ } \ \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ for (; outX < renderer->end; ++outX) { \ uint32_t* pixel = &renderer->row[outX]; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ @@ -1000,7 +1000,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re BACKGROUND_TEXT_SELECT_CHARACTER; \ charBase = ((background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 6)) >> 2) + (localY << 1); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ if (tileData) { \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ ++pixel; \ @@ -1013,7 +1013,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re } else { \ pixel += 4; \ } \ - tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ + tileData = ((uint32_t*) vram)[charBase + 1]; \ if (tileData) { \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ ++pixel; \ @@ -1027,7 +1027,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re pixel += 4; \ } \ } else { \ - uint32_t tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ + uint32_t tileData = ((uint32_t*) vram)[charBase + 1]; \ if (tileData) { \ pixel += 3; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ @@ -1039,7 +1039,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ } \ pixel += 4; \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ if (tileData) { \ pixel += 3; \ BACKGROUND_DRAW_PIXEL_256(BLEND, OBJWIN); \ @@ -1063,18 +1063,18 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re if (!mosaicWait) { \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ if (x >= 4) { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ + tileData = ((uint32_t*) vram)[charBase + 1]; \ tileData >>= (x - 4) * 8; \ } else { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ tileData >>= x * 8; \ } \ } else { \ if (x >= 4) { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase]; \ + tileData = ((uint32_t*) vram)[charBase]; \ tileData >>= (7 - x) * 8; \ } else { \ - tileData = ((uint32_t*)renderer->d.vram)[charBase + 1]; \ + tileData = ((uint32_t*) vram)[charBase + 1]; \ tileData >>= (3 - x) * 8; \ } \ } \ @@ -1176,6 +1176,7 @@ static void _drawBackgroundMode0(struct GBAVideoSoftwareRenderer* renderer, stru int paletteData; int tileX = 0; int tileEnd = (renderer->end - renderer->start + (inX & 0x7)) >> 3; + uint16_t* vram = renderer->d.vram; if (!objwinSlowPath) { if (!(flags & FLAG_TARGET_2)) { From 8e49360ffd340f84e16d2254ab56ffb4f992ce35 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 03:20:12 -0700 Subject: [PATCH 19/72] Add missing dummy video functions --- src/gba/gba-video.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/gba/gba-video.c b/src/gba/gba-video.c index a8ae1c7a2..46b7f9972 100644 --- a/src/gba/gba-video.c +++ b/src/gba/gba-video.c @@ -12,6 +12,8 @@ static void GBAVideoDummyRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoDummyRendererDeinit(struct GBAVideoRenderer* renderer); static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer); @@ -20,6 +22,8 @@ static struct GBAVideoRenderer dummyRenderer = { .reset = GBAVideoDummyRendererReset, .deinit = GBAVideoDummyRendererDeinit, .writeVideoRegister = GBAVideoDummyRendererWriteVideoRegister, + .writePalette = GBAVideoDummyRendererWritePalette, + .writeOAM = GBAVideoDummyRendererWriteOAM, .drawScanline = GBAVideoDummyRendererDrawScanline, .finishFrame = GBAVideoDummyRendererFinishFrame }; @@ -191,6 +195,19 @@ static uint16_t GBAVideoDummyRendererWriteVideoRegister(struct GBAVideoRenderer* return value; } +static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + UNUSED(renderer); + UNUSED(address); + UNUSED(value); + // Nothing to do +} + +static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { + UNUSED(renderer); + UNUSED(oam); + // Nothing to do +} + static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { UNUSED(renderer); UNUSED(y); From a701a6d9dd272be961149477170384c7c591eb2d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 03:23:41 -0700 Subject: [PATCH 20/72] Benchmark option to disable video rendering fully --- src/platform/perf-main.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/platform/perf-main.c b/src/platform/perf-main.c index 0873b26b9..3216bf187 100644 --- a/src/platform/perf-main.c +++ b/src/platform/perf-main.c @@ -7,18 +7,20 @@ #include #include -#define PERF_OPTIONS "S:" +#define PERF_OPTIONS "NS:" #define PERF_USAGE \ "\nBenchmark options:\n" \ + " -N Disable video rendering entirely" \ " -S SEC Run for SEC in-game seconds before exiting" struct PerfOpts { + bool noVideo; int duration; }; static void _GBAPerfRunloop(struct GBAThread* context, int* frames); static void _GBAPerfShutdown(int signal); -static int _parsePerfOpts(struct SubParser* parser, int option, const char* arg); +static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg); static struct GBAThread* _thread; @@ -28,7 +30,7 @@ int main(int argc, char** argv) { struct GBAVideoSoftwareRenderer renderer; GBAVideoSoftwareRendererCreate(&renderer); - struct PerfOpts perfOpts = { 0 }; + struct PerfOpts perfOpts = { false, 0 }; struct SubParser subparser = { .usage = PERF_USAGE, .parse = _parsePerfOpts, @@ -46,12 +48,15 @@ int main(int argc, char** argv) { renderer.outputBufferStride = 256; struct GBAThread context = { - .renderer = &renderer.d, .sync.videoFrameWait = 0, .sync.audioWait = 0 }; _thread = &context; + if (!perfOpts.noVideo) { + context.renderer = &renderer.d; + } + context.debugger = createDebugger(&opts); GBAMapOptionsToContext(&opts, &context); @@ -113,13 +118,16 @@ static void _GBAPerfShutdown(int signal) { pthread_mutex_unlock(&_thread->stateMutex); } -static int _parsePerfOpts(struct SubParser* parser, int option, const char* arg) { +static bool _parsePerfOpts(struct SubParser* parser, int option, const char* arg) { struct PerfOpts* opts = parser->opts; switch (option) { + case 'N': + opts->noVideo = true; + return true; case 'S': opts->duration = strtol(arg, 0, 10); return !errno; default: - return 0; + return false; } } From 5189d9afd4fe629b0ee52bc56f45834b549922b9 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 24 Jul 2014 04:21:33 -0700 Subject: [PATCH 21/72] Add mechanism for adding halts to busy loops --- src/gba/gba-memory.c | 4 ++ src/gba/gba.c | 89 ++++++++++++++++++++++++-------------------- src/gba/gba.h | 1 + 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/gba/gba-memory.c b/src/gba/gba-memory.c index 5993a7c71..740ead2dc 100644 --- a/src/gba/gba-memory.c +++ b/src/gba/gba-memory.c @@ -109,6 +109,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; + if (cpu->currentPC == gba->busyLoop) { + GBAHalt(gba); + } + int newRegion = address >> BASE_OFFSET; if (newRegion == memory->activeRegion) { return; diff --git a/src/gba/gba.c b/src/gba/gba.c index 007582300..0a6d982ff 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -26,72 +26,76 @@ struct GBACartridgeOverride { const char id[4]; enum SavedataType type; int gpio; + uint32_t busyLoop; }; static const struct GBACartridgeOverride _overrides[] = { // Boktai: The Sun is in Your Hand - { "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR }, - { "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR }, + { "U3IE", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + { "U3IP", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, // Boktai 2: Solar Boy Django - { "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR }, - { "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR }, + { "U32E", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, + { "U32P", SAVEDATA_EEPROM, GPIO_RTC | GPIO_LIGHT_SENSOR, -1 }, // Drill Dozer - { "V49J", SAVEDATA_SRAM, GPIO_RUMBLE }, - { "V49E", SAVEDATA_SRAM, GPIO_RUMBLE }, + { "V49J", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, + { "V49E", SAVEDATA_SRAM, GPIO_RUMBLE, -1 }, + + // Mega Man Battle Network + { "AREE", SAVEDATA_SRAM, GPIO_NONE, 0x8000338 }, // Pokemon Ruby - { "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXVE", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXVP", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXVI", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXVS", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXVD", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXVF", SAVEDATA_FLASH1M, GPIO_RTC }, + { "AXVJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVS", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVD", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXVF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, // Pokemon Sapphire - { "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXPE", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXPP", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXPI", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXPS", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXPD", SAVEDATA_FLASH1M, GPIO_RTC }, - { "AXPF", SAVEDATA_FLASH1M, GPIO_RTC }, + { "AXPJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPS", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPD", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "AXPF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, // Pokemon Emerald - { "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC }, - { "BPEE", SAVEDATA_FLASH1M, GPIO_RTC }, - { "BPEP", SAVEDATA_FLASH1M, GPIO_RTC }, - { "BPEI", SAVEDATA_FLASH1M, GPIO_RTC }, - { "BPES", SAVEDATA_FLASH1M, GPIO_RTC }, - { "BPED", SAVEDATA_FLASH1M, GPIO_RTC }, - { "BPEF", SAVEDATA_FLASH1M, GPIO_RTC }, + { "BPEJ", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEE", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEP", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEI", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPES", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPED", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, + { "BPEF", SAVEDATA_FLASH1M, GPIO_RTC, -1 }, // Pokemon FireRed - { "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE }, - { "BPRE", SAVEDATA_FLASH1M, GPIO_NONE }, - { "BPRP", SAVEDATA_FLASH1M, GPIO_NONE }, + { "BPRJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPRE", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPRP", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, // Pokemon LeafGreen - { "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE }, - { "BPGE", SAVEDATA_FLASH1M, GPIO_NONE }, - { "BPGP", SAVEDATA_FLASH1M, GPIO_NONE }, + { "BPGJ", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPGE", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "BPGP", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, // RockMan EXE 4.5 - Real Operation - { "BR4J", SAVEDATA_FLASH512, GPIO_RTC }, + { "BR4J", SAVEDATA_FLASH512, GPIO_RTC, -1 }, // Super Mario Advance 4 - { "AX4J", SAVEDATA_FLASH1M, GPIO_NONE }, - { "AX4E", SAVEDATA_FLASH1M, GPIO_NONE }, - { "AX4P", SAVEDATA_FLASH1M, GPIO_NONE }, + { "AX4J", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "AX4E", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, + { "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, // Wario Ware Twisted - { "RWZJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO }, - { "RWZE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO }, - { "RWZP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO }, + { "RWZJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RWZE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RWZP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, - { { 0, 0, 0, 0 }, 0, 0 } + { { 0, 0, 0, 0 }, 0, 0, -1 } }; static void GBAInit(struct ARMCore* cpu, struct ARMComponent* component); @@ -626,6 +630,7 @@ void GBAIllegal(struct ARMCore* cpu, uint32_t opcode) { void _checkOverrides(struct GBA* gba, uint32_t id) { int i; + gba->busyLoop = -1; for (i = 0; _overrides[i].id[0]; ++i) { const uint32_t* overrideId = (const uint32_t*) _overrides[i].id; if (*overrideId == id) { @@ -656,6 +661,8 @@ void _checkOverrides(struct GBA* gba, uint32_t id) { if (_overrides[i].gpio & GPIO_RUMBLE) { GBAGPIOInitRumble(&gba->memory.gpio); } + + gba->busyLoop = _overrides[i].busyLoop; return; } } diff --git a/src/gba/gba.h b/src/gba/gba.h index 5825f9154..a950bd9e2 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -95,6 +95,7 @@ struct GBA { int springIRQ; uint32_t biosChecksum; int* keySource; + uint32_t busyLoop; struct GBARotationSource* rotationSource; struct GBARumble* rumble; struct GBARRContext* rr; From 888d037b3d6103c2b13e17ee87659f75317385c2 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 25 Jul 2014 00:01:49 -0700 Subject: [PATCH 22/72] PGO options for GCC --- CMakeLists.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74e2dfe70..3ad9e89a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,21 @@ include_directories(${CMAKE_SOURCE_DIR}/src/arm) include_directories(${CMAKE_SOURCE_DIR}/src/gba) include_directories(${CMAKE_SOURCE_DIR}/src) +set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path") +mark_as_advanced(PGO_DIR) +set(PGO_PRE_FLAGS "-pg -fprofile-generate=${PGO_DIR}") +set(PGO_POST_FLAGS "-fprofile-use=${PGO_DIR}") + +if(PGO_STAGE EQUAL 1) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${PGO_PRE_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PGO_PRE_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${PGO_PRE_FLAGS}") +elseif(PGO_STAGE EQUAL 2) + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${PGO_POST_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PGO_POST_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${PGO_POST_FLAGS}") +endif() + add_definitions(-DBINARY_NAME="${BINARY_NAME}" -DPROJECT_NAME="${PROJECT_NAME}") include(FindPkgConfig) From 103bef17c93ee88a302b6eb857877eef5fe2e86d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 25 Jul 2014 03:45:54 -0700 Subject: [PATCH 23/72] Avoid compiler warnings --- src/gba/gba.c | 1 + src/util/vfs/vfs-zip.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/gba/gba.c b/src/gba/gba.c index 0a6d982ff..af374b3d1 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -580,6 +580,7 @@ void GBADebuggerLogShim(struct ARMDebugger* debugger, enum DebuggerLogLevel leve enum GBALogLevel gbaLevel; switch (level) { + default: // Avoids compiler warning case DEBUGGER_LOG_DEBUG: gbaLevel = GBA_LOG_DEBUG; break; diff --git a/src/util/vfs/vfs-zip.c b/src/util/vfs/vfs-zip.c index 2dbdaa1bc..0a5d34406 100644 --- a/src/util/vfs/vfs-zip.c +++ b/src/util/vfs/vfs-zip.c @@ -104,6 +104,8 @@ off_t _vfzSeek(struct VFile* vf, off_t offset, int whence) { } position = vfz->fileSize + offset; break; + default: + return -1; } if (position <= vfz->offset) { From bbe52bf632b75e0ea3e7d3a3027fe985ad5ebc29 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Fri, 25 Jul 2014 05:44:20 -0700 Subject: [PATCH 24/72] Add screenshot capability --- CMakeLists.txt | 2 +- src/platform/sdl/gl-main.c | 1 + src/platform/sdl/sdl-events.c | 21 ++++++++++ src/platform/sdl/sdl-events.h | 3 ++ src/util/png-io.c | 76 +++++++++++++++++++++++++++++++++++ src/util/png-io.h | 16 ++++++++ 6 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/util/png-io.c create mode 100644 src/util/png-io.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ad9e89a0..9508ac193 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ endif() source_group("ARM debugger" FILES ${DEBUGGER_SRC}) add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC}) -target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB}) +target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB} png) if(BUILD_SDL) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl) diff --git a/src/platform/sdl/gl-main.c b/src/platform/sdl/gl-main.c index f494366ea..23d358bea 100644 --- a/src/platform/sdl/gl-main.c +++ b/src/platform/sdl/gl-main.c @@ -77,6 +77,7 @@ int main(int argc, char** argv) { renderer.events.fullscreen = graphicsOpts.fullscreen; renderer.events.windowUpdated = 0; #endif + renderer.events.renderer = &renderer.d; if (!_GBASDLInit(&renderer)) { freeOptions(&opts); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index c087a8b6d..b0c15ce3a 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -5,6 +5,9 @@ #include "gba-rr.h" #include "gba-serialize.h" #include "gba-video.h" +#include "renderers/video-software.h" +#include "util/png-io.h" +#include "util/vfs.h" #if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__) #define GUI_MOD KMOD_GUI @@ -15,6 +18,8 @@ #define SDL_BINDING_KEY 0x53444C4B #define SDL_BINDING_BUTTON 0x53444C42 +static void _takeScreenshot(struct GBAThread*, struct GBASDLEvents*); + bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return false; @@ -74,6 +79,13 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents ARMDebuggerEnter(context->debugger, DEBUGGER_ENTER_MANUAL); } return; + case SDLK_F12: + if (event->type == SDL_KEYDOWN) { + GBAThreadInterrupt(context); + _takeScreenshot(context, sdlContext); + GBAThreadContinue(context); + } + return; case SDLK_TAB: context->sync.audioWait = event->type != SDL_KEYDOWN; return; @@ -249,3 +261,12 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContex _GBASDLHandleJoyHat(context, &event->jhat); } } + +static void _takeScreenshot(struct GBAThread* context, struct GBASDLEvents* sdlContext) { + struct VFile* vf = context->stateDir->openFile(context->stateDir, "screenshot.png", O_CREAT | O_WRONLY); + png_structp png = PNGWriteOpen(vf); + png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); + PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 256, sdlContext->renderer->outputBuffer); + PNGWriteClose(png, info); + vf->close(vf); +} diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 1621aa052..fe53a1b1a 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -7,8 +7,11 @@ #include +struct GBAVideoSoftwareRenderer; + struct GBASDLEvents { struct GBAInputMap* bindings; + struct GBAVideoSoftwareRenderer* renderer; SDL_Joystick* joystick; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; diff --git a/src/util/png-io.c b/src/util/png-io.c new file mode 100644 index 000000000..dd9439433 --- /dev/null +++ b/src/util/png-io.c @@ -0,0 +1,76 @@ +#include "util/png-io.h" + +#include "vfs.h" + +static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) { + struct VFile* vf = png_get_io_ptr(png); + size_t written = vf->write(vf, buffer, size); + if (written != size) { + png_error(png, "Could not write PNG"); + } +} + +png_structp PNGWriteOpen(struct VFile* source) { + png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png) { + return 0; + } + if (setjmp(png_jmpbuf(png))) { + png_destroy_write_struct(&png, 0); + return 0; + } + png_set_write_fn(png, source, _pngWrite, 0); + return png; +} + +png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height) { + png_infop info = png_create_info_struct(png); + if (!info) { + return 0; + } + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_write_info(png, info); + return info; +} + +bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels) { + png_bytep row = malloc(sizeof(png_bytep) * width * 3); + if (!row) { + return false; + } + png_bytep pixelData = pixels; + if (setjmp(png_jmpbuf(png))) { + free(row); + return false; + } + unsigned i; + for (i = 0; i < height; ++i) { + unsigned x; + for (x = 0; x < width; ++x) { + row[x * 3] = pixelData[stride * i * 4 + x * 4]; + row[x * 3 + 1] = pixelData[stride * i * 4 + x * 4 + 1]; + row[x * 3 + 2] = pixelData[stride * i * 4 + x * 4 + 2]; + } + png_write_row(png, row); + } + free(row); + return true; +} + +bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data) { + char realName[5]; + strncpy(realName, name, 4); + realName[0] = tolower(realName[0]); + realName[1] = tolower(realName[1]); + realName[4] = '\0'; + if (setjmp(png_jmpbuf(png))) { + return false; + } + png_write_chunk(png, (png_const_bytep) realName, data, size); + return true; +} + +void PNGWriteClose(png_structp png, png_infop info) { + png_write_end(png, info); + png_destroy_write_struct(&png, &info); +} diff --git a/src/util/png-io.h b/src/util/png-io.h new file mode 100644 index 000000000..2d3f0ebbc --- /dev/null +++ b/src/util/png-io.h @@ -0,0 +1,16 @@ +#ifndef PNG_IO_H +#define PNG_IO_H + +#include "common.h" + +#include + +struct VFile; + +png_structp PNGWriteOpen(struct VFile* source); +png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height); +bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels); +bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data); +void PNGWriteClose(png_structp png, png_infop info); + +#endif From deb278dc22f3d71c3961eeed857475f3e4af7efa Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 26 Jul 2014 00:03:54 -0700 Subject: [PATCH 25/72] Add function to GBAVideoRenderer for taking screenshot --- src/gba/gba-video.c | 12 +++++++++++- src/gba/gba-video.h | 2 ++ src/gba/renderers/video-software.c | 9 +++++++++ src/platform/sdl/gl-main.c | 1 - src/platform/sdl/sdl-events.c | 11 +++++++---- src/platform/sdl/sdl-events.h | 1 - 6 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/gba/gba-video.c b/src/gba/gba-video.c index 46b7f9972..fdd343c8c 100644 --- a/src/gba/gba-video.c +++ b/src/gba/gba-video.c @@ -16,6 +16,7 @@ static void GBAVideoDummyRendererWritePalette(struct GBAVideoRenderer* renderer, static void GBAVideoDummyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); static void GBAVideoDummyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer); +static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); static struct GBAVideoRenderer dummyRenderer = { .init = GBAVideoDummyRendererInit, @@ -25,7 +26,8 @@ static struct GBAVideoRenderer dummyRenderer = { .writePalette = GBAVideoDummyRendererWritePalette, .writeOAM = GBAVideoDummyRendererWriteOAM, .drawScanline = GBAVideoDummyRendererDrawScanline, - .finishFrame = GBAVideoDummyRendererFinishFrame + .finishFrame = GBAVideoDummyRendererFinishFrame, + .getPixels = GBAVideoDummyRendererGetPixels }; void GBAVideoInit(struct GBAVideo* video) { @@ -219,6 +221,14 @@ static void GBAVideoDummyRendererFinishFrame(struct GBAVideoRenderer* renderer) // Nothing to do } +static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) { + UNUSED(renderer); + UNUSED(stride); + UNUSED(pixels); + // Nothing to do +} + + void GBAVideoSerialize(struct GBAVideo* video, struct GBASerializedState* state) { memcpy(state->vram, video->renderer->vram, SIZE_VRAM); memcpy(state->oam, video->oam.raw, SIZE_OAM); diff --git a/src/gba/gba-video.h b/src/gba/gba-video.h index 053b60938..2dbfdfa8c 100644 --- a/src/gba/gba-video.h +++ b/src/gba/gba-video.h @@ -178,6 +178,8 @@ struct GBAVideoRenderer { void (*drawScanline)(struct GBAVideoRenderer* renderer, int y); void (*finishFrame)(struct GBAVideoRenderer* renderer); + void (*getPixels)(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); + uint16_t* palette; uint16_t* vram; union GBAOAM* oam; diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 403e6e751..f53e8dd67 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -29,6 +29,7 @@ static void GBAVideoSoftwareRendererWritePalette(struct GBAVideoRenderer* render static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer); +static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer); static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value); @@ -67,6 +68,7 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { renderer->d.writePalette = GBAVideoSoftwareRendererWritePalette; renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline; renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame; + renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels; } static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) { @@ -501,6 +503,13 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy; } +static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels) { + struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + + *stride = softwareRenderer->outputBufferStride; + *pixels = softwareRenderer->outputBuffer; +} + static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) { renderer->bg[0].enabled = renderer->dispcnt.bg0Enable; renderer->bg[1].enabled = renderer->dispcnt.bg1Enable; diff --git a/src/platform/sdl/gl-main.c b/src/platform/sdl/gl-main.c index 23d358bea..f494366ea 100644 --- a/src/platform/sdl/gl-main.c +++ b/src/platform/sdl/gl-main.c @@ -77,7 +77,6 @@ int main(int argc, char** argv) { renderer.events.fullscreen = graphicsOpts.fullscreen; renderer.events.windowUpdated = 0; #endif - renderer.events.renderer = &renderer.d; if (!_GBASDLInit(&renderer)) { freeOptions(&opts); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index b0c15ce3a..1ee61e861 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -18,7 +18,7 @@ #define SDL_BINDING_KEY 0x53444C4B #define SDL_BINDING_BUTTON 0x53444C42 -static void _takeScreenshot(struct GBAThread*, struct GBASDLEvents*); +static void _takeScreenshot(struct GBAThread*); bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { @@ -82,7 +82,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_F12: if (event->type == SDL_KEYDOWN) { GBAThreadInterrupt(context); - _takeScreenshot(context, sdlContext); + _takeScreenshot(context); GBAThreadContinue(context); } return; @@ -262,11 +262,14 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContex } } -static void _takeScreenshot(struct GBAThread* context, struct GBASDLEvents* sdlContext) { +static void _takeScreenshot(struct GBAThread* context) { + unsigned stride; + void* pixels = 0; struct VFile* vf = context->stateDir->openFile(context->stateDir, "screenshot.png", O_CREAT | O_WRONLY); + context->gba->video.renderer->getPixels(context->gba->video.renderer, &stride, &pixels); png_structp png = PNGWriteOpen(vf); png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); - PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, 256, sdlContext->renderer->outputBuffer); + PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels); PNGWriteClose(png, info); vf->close(vf); } diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index fe53a1b1a..a280aae69 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -11,7 +11,6 @@ struct GBAVideoSoftwareRenderer; struct GBASDLEvents { struct GBAInputMap* bindings; - struct GBAVideoSoftwareRenderer* renderer; SDL_Joystick* joystick; #if SDL_VERSION_ATLEAST(2, 0, 0) SDL_Window* window; From 89bb9c7eac58f3b7ded18b108b69e51952a14697 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 26 Jul 2014 00:04:24 -0700 Subject: [PATCH 26/72] Code for embedding savestates in PNG chunk, loading not yet implemented --- src/gba/gba-serialize.c | 63 +++++++++++++++++++++++++++-------- src/gba/gba-serialize.h | 6 ++-- src/platform/sdl/sdl-events.c | 4 +-- 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index f18b76669..ecf6b69a1 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -3,11 +3,14 @@ #include "gba-audio.h" #include "gba-io.h" #include "gba-thread.h" +#include "gba-video.h" #include "util/memory.h" +#include "util/png-io.h" #include "util/vfs.h" #include +#include const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; @@ -68,31 +71,65 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { GBAAudioDeserialize(&gba->audio, state); } -static struct VFile* _getStateVf(struct GBA* gba, int slot, bool write) { +static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bool write) { char path[PATH_MAX]; path[PATH_MAX - 1] = '\0'; - snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot); - struct VFile* vf = VFileOpen(path, write ? (O_CREAT | O_RDWR) : O_RDONLY); - if (vf) { - vf->truncate(vf, sizeof(struct GBASerializedState)); + struct VFile* vf; + if (!dir) { + snprintf(path, PATH_MAX - 1, "%s.ss%d", gba->activeFile, slot); + vf = VFileOpen(path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); + } else { + snprintf(path, PATH_MAX - 1, "savestate.ss%d", slot); + vf = dir->openFile(dir, path, write ? (O_CREAT | O_TRUNC | O_RDWR) : O_RDONLY); } return vf; } -bool GBASaveState(struct GBA* gba, int slot) { - struct VFile* vf = _getStateVf(gba, slot, true); +static bool _savePNGState(struct GBA* gba, struct VFile* vf) { + unsigned stride; + void* pixels = 0; + gba->video.renderer->getPixels(gba->video.renderer, &stride, &pixels); + if (!pixels) { + return false; + } + + struct GBASerializedState* state = GBAAllocateState(); + png_structp png = PNGWriteOpen(vf); + png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); + uLongf len = compressBound(sizeof(*state)); + void* buffer = malloc(len); + if (state && png && info && buffer) { + GBASerialize(gba, state); + compress(buffer, &len, (const Bytef*) state, sizeof(*state)); + PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels); + PNGWriteCustomChunk(png, "gbAs", len, buffer); + } + PNGWriteClose(png, info); + free(buffer); + GBADeallocateState(state); + return state && png && info && buffer; +} + +bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) { + struct VFile* vf = _getStateVf(gba, dir, slot, true); if (!vf) { return false; } - struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); - GBASerialize(gba, state); - vf->unmap(vf, state, sizeof(struct GBASerializedState)); + bool success = true; + if (!screenshot) { + vf->truncate(vf, sizeof(struct GBASerializedState)); + struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); + GBASerialize(gba, state); + vf->unmap(vf, state, sizeof(struct GBASerializedState)); + } else { + _savePNGState(gba, vf); + } vf->close(vf); - return true; + return success; } -bool GBALoadState(struct GBA* gba, int slot) { - struct VFile* vf = _getStateVf(gba, slot, false); +bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) { + struct VFile* vf = _getStateVf(gba, dir, slot, false); if (!vf) { return false; } diff --git a/src/gba/gba-serialize.h b/src/gba/gba-serialize.h index d354b83d9..6dd01869c 100644 --- a/src/gba/gba-serialize.h +++ b/src/gba/gba-serialize.h @@ -258,13 +258,13 @@ struct GBASerializedState { uint8_t wram[SIZE_WORKING_RAM]; }; -struct VFile; +struct VDir; void GBASerialize(struct GBA* gba, struct GBASerializedState* state); void GBADeserialize(struct GBA* gba, struct GBASerializedState* state); -bool GBASaveState(struct GBA* gba, int slot); -bool GBALoadState(struct GBA* gba, int slot); +bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot); +bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot); struct GBASerializedState* GBAAllocateState(void); void GBADeallocateState(struct GBASerializedState* state); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 1ee61e861..12689b65c 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -163,7 +163,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_F9: case SDLK_F10: GBAThreadInterrupt(context); - GBASaveState(context->gba, event->keysym.sym - SDLK_F1); + GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, false); GBAThreadContinue(context); break; default: @@ -182,7 +182,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_F9: case SDLK_F10: GBAThreadInterrupt(context); - GBALoadState(context->gba, event->keysym.sym - SDLK_F1); + GBALoadState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1); GBAThreadContinue(context); break; default: From 15ece309b7b5ddf6e113db82f1a84bcaa6fc85a9 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 26 Jul 2014 13:20:29 -0700 Subject: [PATCH 27/72] PNG savestate loading --- src/gba/gba-serialize.c | 38 ++++++++++++++++-- src/platform/sdl/sdl-events.c | 2 +- src/util/png-io.c | 74 ++++++++++++++++++++++++++++++++++- src/util/png-io.h | 14 +++++++ 4 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index ecf6b69a1..a07848c53 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -10,6 +10,7 @@ #include "util/vfs.h" #include +#include #include const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; @@ -110,6 +111,33 @@ static bool _savePNGState(struct GBA* gba, struct VFile* vf) { return state && png && info && buffer; } +static int _loadPNGChunkHandler(png_structp png, png_unknown_chunkp chunk) { + if (strcmp((const char*) chunk->name, "gbAs") != 0) { + return 0; + } + struct GBASerializedState state; + uLongf len = sizeof(state); + uncompress((Bytef*) &state, &len, chunk->data, chunk->size); + GBADeserialize(png_get_user_chunk_ptr(png), &state); + return 1; +} + +static bool _loadPNGState(struct GBA* gba, struct VFile* vf) { + png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES); + png_infop info = png_create_info_struct(png); + png_infop end = png_create_info_struct(png); + if (!png || !info || !end) { + PNGReadClose(png, info, end); + return false; + } + PNGInstallChunkHandler(png, gba, _loadPNGChunkHandler, "gbAs"); + PNGReadHeader(png, info); + PNGIgnorePixels(png, info); + PNGReadFooter(png, end); + PNGReadClose(png, info, end); + return true; +} + bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) { struct VFile* vf = _getStateVf(gba, dir, slot, true); if (!vf) { @@ -133,9 +161,13 @@ bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) { if (!vf) { return false; } - struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ); - GBADeserialize(gba, state); - vf->unmap(vf, state, sizeof(struct GBASerializedState)); + if (!isPNG(vf)) { + struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ); + GBADeserialize(gba, state); + vf->unmap(vf, state, sizeof(struct GBASerializedState)); + } else { + _loadPNGState(gba, vf); + } vf->close(vf); return true; } diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 12689b65c..21f89ed32 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -163,7 +163,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_F9: case SDLK_F10: GBAThreadInterrupt(context); - GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, false); + GBASaveState(context->gba, context->stateDir, event->keysym.sym - SDLK_F1, true); GBAThreadContinue(context); break; default: diff --git a/src/util/png-io.c b/src/util/png-io.c index dd9439433..fdc6239f0 100644 --- a/src/util/png-io.c +++ b/src/util/png-io.c @@ -10,6 +10,14 @@ static void _pngWrite(png_structp png, png_bytep buffer, png_size_t size) { } } +static void _pngRead(png_structp png, png_bytep buffer, png_size_t size) { + struct VFile* vf = png_get_io_ptr(png); + size_t read = vf->read(vf, buffer, size); + if (read != size) { + png_error(png, "Could not read PNG"); + } +} + png_structp PNGWriteOpen(struct VFile* source) { png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if (!png) { @@ -71,6 +79,70 @@ bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* d } void PNGWriteClose(png_structp png, png_infop info) { - png_write_end(png, info); + if (!setjmp(png_jmpbuf(png))) { + png_write_end(png, info); + } png_destroy_write_struct(&png, &info); } + +bool isPNG(struct VFile* source) { + png_byte header[PNG_HEADER_BYTES]; + source->read(source, header, PNG_HEADER_BYTES); + return !png_sig_cmp(header, 0, PNG_HEADER_BYTES); +} + +png_structp PNGReadOpen(struct VFile* source, unsigned offset) { + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); + if (!png) { + return 0; + } + if (setjmp(png_jmpbuf(png))) { + png_destroy_read_struct(&png, 0, 0); + return 0; + } + png_set_read_fn(png, source, _pngRead); + png_set_sig_bytes(png, offset); + return png; +} + +bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName) { + if (setjmp(png_jmpbuf(png))) { + return false; + } + png_set_read_user_chunk_fn(png, context, handler); + png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_const_bytep) chunkName, 1); + return true; +} + +bool PNGReadHeader(png_structp png, png_infop info) { + if (setjmp(png_jmpbuf(png))) { + return false; + } + png_read_info(png, info); + return true; +} + +bool PNGIgnorePixels(png_structp png, png_infop info) { + if (setjmp(png_jmpbuf(png))) { + return false; + } + + unsigned height = png_get_image_height(png, info); + unsigned i; + for (i = 0; i < height; ++i) { + png_read_row(png, 0, 0); + } + return true; +} + +bool PNGReadFooter(png_structp png, png_infop end) { + if (setjmp(png_jmpbuf(png))) { + return false; + } + png_read_end(png, end); + return true; +} + +void PNGReadClose(png_structp png, png_infop info, png_infop end) { + png_destroy_read_struct(&png, &info, &end); +} diff --git a/src/util/png-io.h b/src/util/png-io.h index 2d3f0ebbc..05bff7bc4 100644 --- a/src/util/png-io.h +++ b/src/util/png-io.h @@ -7,10 +7,24 @@ struct VFile; +enum { + PNG_HEADER_BYTES = 8 +}; + png_structp PNGWriteOpen(struct VFile* source); png_infop PNGWriteHeader(png_structp png, unsigned width, unsigned height); bool PNGWritePixels(png_structp png, unsigned width, unsigned height, unsigned stride, void* pixels); bool PNGWriteCustomChunk(png_structp png, const char* name, size_t size, void* data); void PNGWriteClose(png_structp png, png_infop info); +typedef int (*ChunkHandler)(png_structp, png_unknown_chunkp); + +bool isPNG(struct VFile* source); +png_structp PNGReadOpen(struct VFile* source, unsigned offset); +bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName); +bool PNGReadHeader(png_structp png, png_infop info); +bool PNGIgnorePixels(png_structp png, png_infop info); +bool PNGReadFooter(png_structp png, png_infop end); +void PNGReadClose(png_structp png, png_infop info, png_infop end); + #endif From a872bd36421a63b70f6d428dc1886b73ceb607ec Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 26 Jul 2014 13:28:44 -0700 Subject: [PATCH 28/72] Remove some ifdefed code by adding a constant for number of bytes per pixel --- src/gba/gba-video.h | 6 ++++++ src/platform/sdl/sw-main.c | 18 +++--------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/gba/gba-video.h b/src/gba/gba-video.h index 2dbfdfa8c..9f22b2412 100644 --- a/src/gba/gba-video.h +++ b/src/gba/gba-video.h @@ -5,6 +5,12 @@ #include "gba-memory.h" +#ifdef COLOR_16_BIT +#define BYTES_PER_PIXEL 2 +#else +#define BYTES_PER_PIXEL 4 +#endif + enum { VIDEO_CYCLES_PER_PIXEL = 4, diff --git a/src/platform/sdl/sw-main.c b/src/platform/sdl/sw-main.c index 99eed9e79..403c9863e 100644 --- a/src/platform/sdl/sw-main.c +++ b/src/platform/sdl/sw-main.c @@ -99,11 +99,7 @@ int main(int argc, char** argv) { #endif SDL_LockTexture(renderer.tex, 0, &renderer.d.outputBuffer, &renderer.d.outputBufferStride); -#ifdef COLOR_16_BIT - renderer.d.outputBufferStride /= 2; -#else - renderer.d.outputBufferStride /= 4; -#endif + renderer.d.outputBufferStride /= BYTES_PER_PIXEL; #else SDL_Surface* surface = SDL_GetVideoSurface(); SDL_LockSurface(surface); @@ -117,11 +113,7 @@ int main(int argc, char** argv) { renderer.d.outputBufferStride = surface->pitch / 4; #endif } else { -#ifdef COLOR_16_BIT - renderer.d.outputBuffer = malloc(240 * 160 * 2); -#else - renderer.d.outputBuffer = malloc(240 * 160 * 4); -#endif + renderer.d.outputBuffer = malloc(240 * 160 * BYTES_PER_PIXEL); renderer.d.outputBufferStride = 240; } #endif @@ -173,11 +165,7 @@ static void _GBASDLRunloop(struct GBAThread* context, struct SoftwareRenderer* r SDL_RenderCopy(renderer->sdlRenderer, renderer->tex, 0, 0); SDL_RenderPresent(renderer->sdlRenderer); SDL_LockTexture(renderer->tex, 0, &renderer->d.outputBuffer, &renderer->d.outputBufferStride); -#ifdef COLOR_16_BIT - renderer->d.outputBufferStride /= 2; -#else - renderer->d.outputBufferStride /= 4; -#endif + renderer->d.outputBufferStride /= BYTES_PER_PIXEL; #else switch (renderer->ratio) { #if defined(__ARM_NEON) && COLOR_16_BIT From b4d90e7e8455ecc29ea7380718b007ada083e7ee Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 26 Jul 2014 14:06:28 -0700 Subject: [PATCH 29/72] Restore pixels from PNG when loading savestate --- src/gba/gba-serialize.c | 6 +++++- src/gba/gba-thread.c | 10 ++++++++-- src/gba/gba-video.h | 1 + src/gba/renderers/video-software.c | 12 ++++++++++++ src/util/png-io.c | 31 ++++++++++++++++++++++++++++++ src/util/png-io.h | 1 + 6 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index a07848c53..a27136a9b 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -130,11 +130,15 @@ static bool _loadPNGState(struct GBA* gba, struct VFile* vf) { PNGReadClose(png, info, end); return false; } + uint32_t pixels[VIDEO_HORIZONTAL_PIXELS * VIDEO_VERTICAL_PIXELS * 4]; + PNGInstallChunkHandler(png, gba, _loadPNGChunkHandler, "gbAs"); PNGReadHeader(png, info); - PNGIgnorePixels(png, info); + PNGReadPixels(png, info, &pixels, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, VIDEO_HORIZONTAL_PIXELS); PNGReadFooter(png, end); PNGReadClose(png, info, end); + gba->video.renderer->putPixels(gba->video.renderer, VIDEO_HORIZONTAL_PIXELS, pixels); + GBASyncPostFrame(gba->sync); return true; } diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 37ccabdf7..6d7cbff83 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -535,6 +535,10 @@ void GBASyncPostFrame(struct GBASync* sync) { MutexUnlock(&sync->videoFrameMutex); struct GBAThread* thread = GBAThreadGetContext(); + if (!thread) { + return; + } + if (thread->rewindBuffer) { --thread->rewindBufferNext; if (thread->rewindBufferNext <= 0) { @@ -554,10 +558,12 @@ bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip) { MutexLock(&sync->videoFrameMutex); ConditionWake(&sync->videoFrameRequiredCond); - if (!sync->videoFrameOn) { + if (!sync->videoFrameOn && !sync->videoFramePending) { return false; } - ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); + if (!sync->videoFramePending) { + ConditionWait(&sync->videoFrameAvailableCond, &sync->videoFrameMutex); + } sync->videoFramePending = 0; sync->videoFrameSkip = frameskip; return true; diff --git a/src/gba/gba-video.h b/src/gba/gba-video.h index 9f22b2412..6e5838ce0 100644 --- a/src/gba/gba-video.h +++ b/src/gba/gba-video.h @@ -185,6 +185,7 @@ struct GBAVideoRenderer { void (*finishFrame)(struct GBAVideoRenderer* renderer); void (*getPixels)(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); + void (*putPixels)(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels); uint16_t* palette; uint16_t* vram; diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index f53e8dd67..46562f673 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -30,6 +30,7 @@ static uint16_t GBAVideoSoftwareRendererWriteVideoRegister(struct GBAVideoRender static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* renderer); static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, unsigned* stride, void** pixels); +static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels); static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer); static void GBAVideoSoftwareRendererWriteBGCNT(struct GBAVideoSoftwareRenderer* renderer, struct GBAVideoSoftwareBackground* bg, uint16_t value); @@ -69,6 +70,7 @@ void GBAVideoSoftwareRendererCreate(struct GBAVideoSoftwareRenderer* renderer) { renderer->d.drawScanline = GBAVideoSoftwareRendererDrawScanline; renderer->d.finishFrame = GBAVideoSoftwareRendererFinishFrame; renderer->d.getPixels = GBAVideoSoftwareRendererGetPixels; + renderer->d.putPixels = GBAVideoSoftwareRendererPutPixels; } static void GBAVideoSoftwareRendererInit(struct GBAVideoRenderer* renderer) { @@ -510,6 +512,16 @@ static void GBAVideoSoftwareRendererGetPixels(struct GBAVideoRenderer* renderer, *pixels = softwareRenderer->outputBuffer; } +static void GBAVideoSoftwareRendererPutPixels(struct GBAVideoRenderer* renderer, unsigned stride, void* pixels) { + struct GBAVideoSoftwareRenderer* softwareRenderer = (struct GBAVideoSoftwareRenderer*) renderer; + + uint32_t* colorPixels = pixels; + unsigned i; + for (i = 0; i < VIDEO_VERTICAL_PIXELS; ++i) { + memmove(&softwareRenderer->outputBuffer[softwareRenderer->outputBufferStride * i], &colorPixels[stride * i], VIDEO_HORIZONTAL_PIXELS * BYTES_PER_PIXEL); + } +} + static void GBAVideoSoftwareRendererUpdateDISPCNT(struct GBAVideoSoftwareRenderer* renderer) { renderer->bg[0].enabled = renderer->dispcnt.bg0Enable; renderer->bg[1].enabled = renderer->dispcnt.bg1Enable; diff --git a/src/util/png-io.c b/src/util/png-io.c index fdc6239f0..b6408c912 100644 --- a/src/util/png-io.c +++ b/src/util/png-io.c @@ -135,6 +135,37 @@ bool PNGIgnorePixels(png_structp png, png_infop info) { return true; } +bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride) { + if (setjmp(png_jmpbuf(png))) { + return false; + } + + uint8_t* pixelData = pixels; + unsigned pngHeight = png_get_image_height(png, info); + if (height > pngHeight) { + height = pngHeight; + } + + unsigned pngWidth = png_get_image_width(png, info); + if (width > pngWidth) { + width = pngWidth; + } + + unsigned i; + png_bytep row = malloc(png_get_rowbytes(png, info)); + for (i = 0; i < height; ++i) { + png_read_row(png, row, 0); + unsigned x; + for (x = 0; x < width; ++x) { + pixelData[stride * i * 4 + x * 4] = row[x * 3]; + pixelData[stride * i * 4 + x * 4 + 1] = row[x * 3 + 1]; + pixelData[stride * i * 4 + x * 4 + 2] = row[x * 3 + 2]; + } + } + free(row); + return true; +} + bool PNGReadFooter(png_structp png, png_infop end) { if (setjmp(png_jmpbuf(png))) { return false; diff --git a/src/util/png-io.h b/src/util/png-io.h index 05bff7bc4..257368090 100644 --- a/src/util/png-io.h +++ b/src/util/png-io.h @@ -23,6 +23,7 @@ bool isPNG(struct VFile* source); png_structp PNGReadOpen(struct VFile* source, unsigned offset); bool PNGInstallChunkHandler(png_structp png, void* context, ChunkHandler handler, const char* chunkName); bool PNGReadHeader(png_structp png, png_infop info); +bool PNGReadPixels(png_structp png, png_infop info, void* pixels, unsigned width, unsigned height, unsigned stride); bool PNGIgnorePixels(png_structp png, png_infop info); bool PNGReadFooter(png_structp png, png_infop end); void PNGReadClose(png_structp png, png_infop info, png_infop end); From f39d7e364057a2eb014fc0c63e5eb285344d2a97 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 27 Jul 2014 18:21:58 -0700 Subject: [PATCH 30/72] Move screenshot function to gba-thread.h --- src/gba/gba-thread.c | 13 +++++++++++++ src/gba/gba-thread.h | 2 ++ src/platform/sdl/sdl-events.c | 17 +---------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index 6d7cbff83..cc20d7766 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -7,6 +7,7 @@ #include "debugger/debugger.h" #include "util/patch.h" +#include "util/png-io.h" #include "util/vfs.h" #include @@ -518,6 +519,18 @@ struct GBAThread* GBAThreadGetContext(void) { } #endif +void GBAThreadTakeScreenshot(struct GBAThread* threadContext) { + unsigned stride; + void* pixels = 0; + struct VFile* vf = threadContext->stateDir->openFile(threadContext->stateDir, "screenshot.png", O_CREAT | O_WRONLY); + threadContext->gba->video.renderer->getPixels(threadContext->gba->video.renderer, &stride, &pixels); + png_structp png = PNGWriteOpen(vf); + png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); + PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels); + PNGWriteClose(png, info); + vf->close(vf); +} + void GBASyncPostFrame(struct GBASync* sync) { if (!sync) { return; diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index 70468eb10..813cf0c74 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -106,6 +106,8 @@ void GBAThreadTogglePause(struct GBAThread* threadContext); void GBAThreadPauseFromThread(struct GBAThread* threadContext); struct GBAThread* GBAThreadGetContext(void); +void GBAThreadTakeScreenshot(struct GBAThread* threadContext); + void GBASyncPostFrame(struct GBASync* sync); bool GBASyncWaitFrameStart(struct GBASync* sync, int frameskip); void GBASyncWaitFrameEnd(struct GBASync* sync); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 21f89ed32..1da64660d 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -6,7 +6,6 @@ #include "gba-serialize.h" #include "gba-video.h" #include "renderers/video-software.h" -#include "util/png-io.h" #include "util/vfs.h" #if SDL_VERSION_ATLEAST(2, 0, 0) && defined(__APPLE__) @@ -18,8 +17,6 @@ #define SDL_BINDING_KEY 0x53444C4B #define SDL_BINDING_BUTTON 0x53444C42 -static void _takeScreenshot(struct GBAThread*); - bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return false; @@ -82,7 +79,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_F12: if (event->type == SDL_KEYDOWN) { GBAThreadInterrupt(context); - _takeScreenshot(context); + GBAThreadTakeScreenshot(context); GBAThreadContinue(context); } return; @@ -261,15 +258,3 @@ void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContex _GBASDLHandleJoyHat(context, &event->jhat); } } - -static void _takeScreenshot(struct GBAThread* context) { - unsigned stride; - void* pixels = 0; - struct VFile* vf = context->stateDir->openFile(context->stateDir, "screenshot.png", O_CREAT | O_WRONLY); - context->gba->video.renderer->getPixels(context->gba->video.renderer, &stride, &pixels); - png_structp png = PNGWriteOpen(vf); - png_infop info = PNGWriteHeader(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); - PNGWritePixels(png, VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS, stride, pixels); - PNGWriteClose(png, info); - vf->close(vf); -} From 45b8ffb9cfae7dc1e44756cdd0192fb8978bdf3d Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 27 Jul 2014 18:22:39 -0700 Subject: [PATCH 31/72] New movie format --- src/gba/gba-rr.c | 110 ++++++++++++++++++++++++++++++++++++++++------- src/gba/gba-rr.h | 16 ++++++- 2 files changed, 109 insertions(+), 17 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 91450a374..1a037c9cf 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -5,6 +5,10 @@ #define FILE_INPUTS "input.log" +static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); +static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); +static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); + void GBARRContextCreate(struct GBA* gba) { if (gba->rr) { return; @@ -23,12 +27,12 @@ void GBARRContextDestroy(struct GBA* gba) { } bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { - if (rr->inputsStream && !rr->inputsStream->close(rr->inputsStream)) { + if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { return false; } rr->streamDir = stream; - rr->inputsStream = stream->openFile(stream, FILE_INPUTS, O_CREAT | O_RDWR); - return !!rr->inputsStream; + rr->movieStream = 0; + return true; } bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { @@ -36,19 +40,30 @@ bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { return false; } + rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_RDONLY); rr->autorecord = autorecord; - if (rr->inputsStream->seek(rr->inputsStream, 0, SEEK_SET) < 0) { - return false; - } - if (rr->inputsStream->read(rr->inputsStream, &rr->nextInput, sizeof(rr->nextInput)) != sizeof(rr->nextInput)) { + rr->peekedTag = TAG_INVALID; + _readTag(rr, rr->movieStream); // Discard the buffer + enum GBARRTag tag = _readTag(rr, rr->movieStream); + if (tag != TAG_BEGIN) { + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; return false; } + rr->isPlaying = true; return true; } void GBARRStopPlaying(struct GBARRContext* rr) { + if (!GBARRIsPlaying(rr)) { + return; + } rr->isPlaying = false; + if (rr->movieStream) { + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; + } } bool GBARRStartRecording(struct GBARRContext* rr) { @@ -56,12 +71,27 @@ bool GBARRStartRecording(struct GBARRContext* rr) { return false; } + rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_TRUNC | O_CREAT | O_WRONLY); + if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; + return false; + } + rr->isRecording = true; return true; } void GBARRStopRecording(struct GBARRContext* rr) { + if (!GBARRIsRecording(rr)) { + return; + } rr->isRecording = false; + if (rr->movieStream) { + _emitTag(rr, rr->movieStream, TAG_END); + rr->movieStream->close(rr->movieStream); + rr->movieStream = 0; + } } bool GBARRIsPlaying(struct GBARRContext* rr) { @@ -73,7 +103,7 @@ bool GBARRIsRecording(struct GBARRContext* rr) { } void GBARRNextFrame(struct GBARRContext* rr) { - if (!GBARRIsRecording(rr)) { + if (!GBARRIsRecording(rr) && !GBARRIsPlaying(rr)) { return; } @@ -82,7 +112,18 @@ void GBARRNextFrame(struct GBARRContext* rr) { ++rr->lagFrames; } - rr->inputThisFrame = false; + if (GBARRIsRecording(rr)) { + if (!rr->inputThisFrame) { + _emitTag(rr, rr->movieStream, TAG_LAG); + } + _emitTag(rr, rr->movieStream, TAG_FRAME); + + rr->inputThisFrame = false; + } else { + if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { + GBARRStopPlaying(rr); + } + } } void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { @@ -90,7 +131,11 @@ void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { return; } - rr->inputsStream->write(rr->inputsStream, &keys, sizeof(keys)); + if (keys != rr->currentInput) { + _emitTag(rr, rr->movieStream, TAG_INPUT); + rr->movieStream->write(rr->movieStream, &keys, sizeof(keys)); + rr->currentInput = keys; + } rr->inputThisFrame = true; } @@ -99,10 +144,45 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) { return 0; } - uint16_t keys = rr->nextInput; - rr->isPlaying = rr->inputsStream->read(rr->inputsStream, &rr->nextInput, sizeof(rr->nextInput)) == sizeof(rr->nextInput); - if (!rr->isPlaying && rr->autorecord) { - rr->isRecording = true; + if (rr->peekedTag == TAG_INPUT) { + _readTag(rr, rr->movieStream); } - return keys; + return rr->currentInput; +} + +enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { + enum GBARRTag tag = rr->peekedTag; + switch (tag) { + case TAG_INPUT: + vf->read(vf, &rr->currentInput, sizeof(uint16_t)); + break; + case TAG_FRAME: + case TAG_LAG: + case TAG_BEGIN: + case TAG_END: + case TAG_INVALID: + break; + } + + uint8_t tagBuffer; + if (vf->read(vf, &tagBuffer, 1) != 1) { + tagBuffer = TAG_END; + } + rr->peekedTag = tagBuffer; + return tag; +} + +bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { + enum GBARRTag readTag; + while ((readTag = _readTag(rr, vf)) != tag) { + if (readTag == TAG_END) { + return false; + } + } + return true; +} + +bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { + UNUSED(rr); + return vf->write(vf, &tag, 1) == 1; } diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index eba3300f6..ad9817d9a 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -7,11 +7,21 @@ struct GBA; struct VDir; struct VFile; +enum GBARRTag { + TAG_INVALID = 0x00, + TAG_INPUT = 0x01, + TAG_FRAME = 0x02, + TAG_LAG = 0x03, + TAG_BEGIN = 0x10, + TAG_END = 0x11, + TAG_PREVIOUSLY = 0x12, + TAG_NEXT_TIME = 0x13 +}; + struct GBARRContext { // Playback state bool isPlaying; bool autorecord; - uint16_t nextInput; // Recording state bool isRecording; @@ -23,7 +33,9 @@ struct GBARRContext { // Streaming state struct VDir* streamDir; - struct VFile* inputsStream; + struct VFile* movieStream; + uint16_t currentInput; + enum GBARRTag peekedTag; }; void GBARRContextCreate(struct GBA*); From b115cb564d8a3058303ec9a91aaf9696b09071f5 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 27 Jul 2014 18:22:53 -0700 Subject: [PATCH 32/72] Fix GBARR not starting in SDL port --- src/platform/sdl/sdl-events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 1da64660d..e8633c537 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -122,7 +122,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBAThreadReset(context); break; case SDLK_t: - if (context->gba->rr) { + if (context->stateDir) { GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); @@ -133,7 +133,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents } break; case SDLK_y: - if (context->gba->rr) { + if (context->stateDir) { GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); From 9873da0eb7d1b167de686a37d9e5d7902ef8f98f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 27 Jul 2014 19:06:30 -0700 Subject: [PATCH 33/72] Ability to chunk movie streams into multiple files --- src/gba/gba-rr.c | 66 +++++++++++++++++++++++++++++++++++++---- src/gba/gba-rr.h | 4 +++ src/gba/gba-serialize.c | 8 +++++ src/gba/gba-serialize.h | 10 +++++-- 4 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 1a037c9cf..afc785a6f 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -3,8 +3,6 @@ #include "gba.h" #include "util/vfs.h" -#define FILE_INPUTS "input.log" - static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); @@ -32,15 +30,55 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { } rr->streamDir = stream; rr->movieStream = 0; + rr->streamId = 1; return true; } +bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { + if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { + return false; + } + rr->movieStream = 0; + rr->streamId = streamId; + char buffer[14]; + snprintf(buffer, sizeof(buffer), "%u.log", streamId); + if (GBARRIsRecording(rr)) { + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); + } else if (GBARRIsPlaying(rr)) { + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); + rr->peekedTag = TAG_INVALID; + if (!rr->movieStream || !_seekTag(rr, rr->movieStream, TAG_BEGIN)) { + GBARRStopPlaying(rr); + } + } + return true; +} + +uint32_t GBARRIncrementStream(struct GBARRContext* rr) { + uint32_t newStreamId = rr->streamId + 1; + uint32_t oldStreamId = rr->streamId; + if (GBARRIsRecording(rr) && rr->movieStream) { + _emitTag(rr, rr->movieStream, TAG_END); + _emitTag(rr, rr->movieStream, TAG_NEXT_TIME); + rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId)); + } + if (!GBARRLoadStream(rr, newStreamId)) { + return 0; + } + _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); + rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); + _emitTag(rr, rr->movieStream, TAG_BEGIN); + return rr->streamId; +} + bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { if (GBARRIsRecording(rr) || GBARRIsPlaying(rr)) { return false; } - rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_RDONLY); + char buffer[14]; + snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId); + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); rr->autorecord = autorecord; rr->peekedTag = TAG_INVALID; _readTag(rr, rr->movieStream); // Discard the buffer @@ -71,7 +109,9 @@ bool GBARRStartRecording(struct GBARRContext* rr) { return false; } - rr->movieStream = rr->streamDir->openFile(rr->streamDir, FILE_INPUTS, O_TRUNC | O_CREAT | O_WRONLY); + char buffer[14]; + snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId); + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { rr->movieStream->close(rr->movieStream); rr->movieStream = 0; @@ -156,10 +196,14 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { case TAG_INPUT: vf->read(vf, &rr->currentInput, sizeof(uint16_t)); break; + case TAG_NEXT_TIME: + vf->read(vf, &rr->nextTime, sizeof(rr->nextTime)); + break; case TAG_FRAME: case TAG_LAG: case TAG_BEGIN: case TAG_END: + case TAG_PREVIOUSLY: case TAG_INVALID: break; } @@ -176,7 +220,19 @@ bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { enum GBARRTag readTag; while ((readTag = _readTag(rr, vf)) != tag) { if (readTag == TAG_END) { - return false; + if (rr->peekedTag == TAG_NEXT_TIME) { + while (_readTag(rr, vf) != TAG_END) { + if (!rr->nextTime) { + return false; + } + } + if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + return false; + } + vf = rr->movieStream; + } else { + return false; + } } } return true; diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index ad9817d9a..2c2339f75 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -30,18 +30,22 @@ struct GBARRContext { // Metadata uint32_t frames; uint32_t lagFrames; + uint32_t streamId; // Streaming state struct VDir* streamDir; struct VFile* movieStream; uint16_t currentInput; enum GBARRTag peekedTag; + uint32_t nextTime; }; void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); bool GBARRSetStream(struct GBARRContext*, struct VDir*); +bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); +uint32_t GBARRIncrementStream(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); void GBARRStopPlaying(struct GBARRContext*); diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index a27136a9b..11a979c07 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -2,6 +2,7 @@ #include "gba-audio.h" #include "gba-io.h" +#include "gba-rr.h" #include "gba-thread.h" #include "gba-video.h" @@ -35,6 +36,13 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { GBAIOSerialize(gba, state); GBAVideoSerialize(&gba->video, state); GBAAudioSerialize(&gba->audio, state); + + if (GBARRIsRecording(gba->rr)) { + state->associatedStreamId = gba->rr->streamId; + GBARRIncrementStream(gba->rr); + } else { + state->associatedStreamId = 0; + } } void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { diff --git a/src/gba/gba-serialize.h b/src/gba/gba-serialize.h index 6dd01869c..af73c45f9 100644 --- a/src/gba/gba-serialize.h +++ b/src/gba/gba-serialize.h @@ -139,7 +139,9 @@ const uint32_t GBA_SAVESTATE_MAGIC; * | bit 0: Is read enabled * | bit 1: Gyroscope sample is edge * | bits 2 - 15: Reserved - * 0x002C0 - 0x003FF: Reserved (leave zero) + * 0x002C0 - 0x002FF: Reserved (leave zero) + * 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) + * 0x00304 - 0x003FF: Reserved (leave zero) * 0x00400 - 0x007FF: I/O memory * 0x00800 - 0x00BFF: Palette * 0x00C00 - 0x00FFF: OAM @@ -248,7 +250,11 @@ struct GBASerializedState { unsigned reserved : 14; } gpio; - uint32_t reserved[80]; + uint32_t reservedGpio[16]; + + uint32_t associatedStreamId; + + uint32_t reserved[63]; uint16_t io[SIZE_IO >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1]; From dbc6567317605cf03d9c400b97ee3e03a960e04f Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 22:48:55 -0700 Subject: [PATCH 34/72] Only make GUI_MOD SDL events happen when no other modifiers are active --- src/platform/sdl/sdl-events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index e8633c537..03f398864 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -101,7 +101,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents return; default: if (event->type == SDL_KEYDOWN) { - if (event->keysym.mod & GUI_MOD) { + if ((event->keysym.mod & GUI_MOD) && (event->keysym.mod & GUI_MOD) == event->keysym.mod) { switch (event->keysym.sym) { #if SDL_VERSION_ATLEAST(2, 0, 0) case SDLK_f: From 00236136250bcdf0eb745a74401f13220053b501 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 22:49:24 -0700 Subject: [PATCH 35/72] Clean up movie state when exiting --- src/gba/gba-rr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index afc785a6f..b65bcb6d5 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -20,6 +20,13 @@ void GBARRContextDestroy(struct GBA* gba) { return; } + if (GBARRIsPlaying(gba->rr)) { + GBARRStopPlaying(gba->rr); + } + if (GBARRIsRecording(gba->rr)) { + GBARRStopRecording(gba->rr); + } + free(gba->rr); gba->rr = 0; } From 35bf1f399021919b456a3f78646e0ad9a3017ea0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 22:50:19 -0700 Subject: [PATCH 36/72] Keep max stream ID separate from current stream ID --- src/gba/gba-rr.c | 16 ++++++++++++---- src/gba/gba-rr.h | 3 ++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index b65bcb6d5..331a018de 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -38,6 +38,7 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { rr->streamDir = stream; rr->movieStream = 0; rr->streamId = 1; + rr->maxStreamId = 1; return true; } @@ -58,24 +59,31 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { GBARRStopPlaying(rr); } } + rr->frames = 0; + rr->lagFrames = 0; return true; } -uint32_t GBARRIncrementStream(struct GBARRContext* rr) { - uint32_t newStreamId = rr->streamId + 1; +bool GBARRIncrementStream(struct GBARRContext* rr) { + uint32_t newStreamId = rr->maxStreamId + 1; uint32_t oldStreamId = rr->streamId; if (GBARRIsRecording(rr) && rr->movieStream) { _emitTag(rr, rr->movieStream, TAG_END); + _emitTag(rr, rr->movieStream, TAG_FRAME_COUNT); + rr->movieStream->write(rr->movieStream, &rr->frames, sizeof(rr->frames)); + _emitTag(rr, rr->movieStream, TAG_LAG_COUNT); + rr->movieStream->write(rr->movieStream, &rr->lagFrames, sizeof(rr->lagFrames)); _emitTag(rr, rr->movieStream, TAG_NEXT_TIME); rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId)); } if (!GBARRLoadStream(rr, newStreamId)) { - return 0; + return false; } + rr->maxStreamId = newStreamId; _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); _emitTag(rr, rr->movieStream, TAG_BEGIN); - return rr->streamId; + return true; } bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 2c2339f75..9c7e23a93 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -31,6 +31,7 @@ struct GBARRContext { uint32_t frames; uint32_t lagFrames; uint32_t streamId; + uint32_t maxStreamId; // Streaming state struct VDir* streamDir; @@ -45,7 +46,7 @@ void GBARRContextDestroy(struct GBA*); bool GBARRSetStream(struct GBARRContext*, struct VDir*); bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); -uint32_t GBARRIncrementStream(struct GBARRContext*); +bool GBARRIncrementStream(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); void GBARRStopPlaying(struct GBARRContext*); From 68454549690cb0d4bb60d2fd200b275b5f602571 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 22:50:46 -0700 Subject: [PATCH 37/72] Add more rr tags, some implemented --- src/gba/gba-rr.c | 22 +++++++++++++++++++++- src/gba/gba-rr.h | 17 ++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 331a018de..0d586a747 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -211,14 +211,34 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { case TAG_INPUT: vf->read(vf, &rr->currentInput, sizeof(uint16_t)); break; + case TAG_PREVIOUSLY: + vf->read(vf, &rr->previously, sizeof(rr->previously)); + break; case TAG_NEXT_TIME: vf->read(vf, &rr->nextTime, sizeof(rr->nextTime)); break; + case TAG_MAX_STREAM: + vf->read(vf, &rr->maxStreamId, sizeof(rr->maxStreamId)); + break; + case TAG_FRAME_COUNT: + vf->read(vf, &rr->frames, sizeof(rr->frames)); + break; + case TAG_LAG_COUNT: + vf->read(vf, &rr->lagFrames, sizeof(rr->lagFrames)); + break; + + // To be spec'd + case TAG_RR_COUNT: + case TAG_INIT_TYPE: + case TAG_AUTHOR: + case TAG_COMMENT: + break; + + // Empty markers case TAG_FRAME: case TAG_LAG: case TAG_BEGIN: case TAG_END: - case TAG_PREVIOUSLY: case TAG_INVALID: break; } diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 9c7e23a93..d0f1c975f 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -8,14 +8,28 @@ struct VDir; struct VFile; enum GBARRTag { + // Playback tags TAG_INVALID = 0x00, TAG_INPUT = 0x01, TAG_FRAME = 0x02, TAG_LAG = 0x03, + + // Stream chunking tags TAG_BEGIN = 0x10, TAG_END = 0x11, TAG_PREVIOUSLY = 0x12, - TAG_NEXT_TIME = 0x13 + TAG_NEXT_TIME = 0x13, + TAG_MAX_STREAM = 0x14, + + // Recording information tags + TAG_FRAME_COUNT = 0x20, + TAG_LAG_COUNT = 0x21, + TAG_RR_COUNT = 0x22, + TAG_INIT_TYPE = 0x23, + + // User metadata tags + TAG_AUTHOR = 0x30, + TAG_COMMENT = 0x31 }; struct GBARRContext { @@ -39,6 +53,7 @@ struct GBARRContext { uint16_t currentInput; enum GBARRTag peekedTag; uint32_t nextTime; + uint32_t previously; }; void GBARRContextCreate(struct GBA*); From fade9941370ae801487c8ab39822f41fdd6a6f75 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 23:13:16 -0700 Subject: [PATCH 38/72] Clean up logic with an EOF tag --- src/gba/gba-rr.c | 26 ++++++++++++-------------- src/gba/gba-rr.h | 4 +++- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 0d586a747..a1de55426 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -3,6 +3,8 @@ #include "gba.h" #include "util/vfs.h" +#define BINEXT ".log" + static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); @@ -49,7 +51,7 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { rr->movieStream = 0; rr->streamId = streamId; char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u.log", streamId); + snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId); if (GBARRIsRecording(rr)) { rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); } else if (GBARRIsPlaying(rr)) { @@ -240,14 +242,16 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { case TAG_BEGIN: case TAG_END: case TAG_INVALID: + case TAG_EOF: break; } uint8_t tagBuffer; if (vf->read(vf, &tagBuffer, 1) != 1) { - tagBuffer = TAG_END; + rr->peekedTag = TAG_EOF; + } else { + rr->peekedTag = tagBuffer; } - rr->peekedTag = tagBuffer; return tag; } @@ -255,19 +259,13 @@ bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { enum GBARRTag readTag; while ((readTag = _readTag(rr, vf)) != tag) { if (readTag == TAG_END) { - if (rr->peekedTag == TAG_NEXT_TIME) { - while (_readTag(rr, vf) != TAG_END) { - if (!rr->nextTime) { - return false; - } - } - if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { - return false; - } - vf = rr->movieStream; - } else { + while (_readTag(rr, vf) != TAG_EOF); + if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { return false; } + vf = rr->movieStream; + } else if (readTag == TAG_EOF) { + return false; } } return true; diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index d0f1c975f..9629512fc 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -29,7 +29,9 @@ enum GBARRTag { // User metadata tags TAG_AUTHOR = 0x30, - TAG_COMMENT = 0x31 + TAG_COMMENT = 0x31, + + TAG_EOF = INT_MAX }; struct GBARRContext { From e3410fc457b57b6d3145ca5a4ab190adcd517c57 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 23:15:08 -0700 Subject: [PATCH 39/72] Fix _readTag to not crash if the VFile has been closed --- src/gba/gba-rr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index a1de55426..911dc873f 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -208,6 +208,10 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) { } enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { + if (!rr || !vf) { + return TAG_EOF; + } + enum GBARRTag tag = rr->peekedTag; switch (tag) { case TAG_INPUT: From 9cc97410d3804f85390d08313c92d089dec6d3b0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 29 Jul 2014 23:39:55 -0700 Subject: [PATCH 40/72] Make sure not to reload an old rr stream chunk when reaching the end of a file --- src/gba/gba-rr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 911dc873f..0c9ad267c 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -263,6 +263,7 @@ bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { enum GBARRTag readTag; while ((readTag = _readTag(rr, vf)) != tag) { if (readTag == TAG_END) { + rr->nextTime = 0; while (_readTag(rr, vf) != TAG_EOF); if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { return false; From 28218b24655fa2aeaaecf87525d349f87924ad66 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 30 Jul 2014 00:03:19 -0700 Subject: [PATCH 41/72] Fix .log -> .dat for the first chunk, and make sure it bails if the load fails --- src/gba/gba-rr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 0c9ad267c..d6b04d5a4 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -94,8 +94,12 @@ bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { } char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId); + snprintf(buffer, sizeof(buffer), "%u" BINEXT, rr->streamId); rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); + if (!rr->movieStream) { + return false; + } + rr->autorecord = autorecord; rr->peekedTag = TAG_INVALID; _readTag(rr, rr->movieStream); // Discard the buffer @@ -127,7 +131,7 @@ bool GBARRStartRecording(struct GBARRContext* rr) { } char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u.log", rr->streamId); + snprintf(buffer, sizeof(buffer), "%u" BINEXT, rr->streamId); rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { rr->movieStream->close(rr->movieStream); From 5ca6888840e010552b9f38501fc5594f8ccdbd73 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 30 Jul 2014 00:13:11 -0700 Subject: [PATCH 42/72] Savestate loading during recording and replaying --- src/gba/gba-rr.c | 69 ++++++++++++++++++++++++++++++++++++----- src/gba/gba-rr.h | 3 ++ src/gba/gba-serialize.c | 8 +++++ 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index d6b04d5a4..3f6af1141 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -3,11 +3,13 @@ #include "gba.h" #include "util/vfs.h" -#define BINEXT ".log" +#define BINEXT ".dat" +#define METADATA_FILENAME "metadata" BINEXT static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); +static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf); void GBARRContextCreate(struct GBA* gba) { if (gba->rr) { @@ -28,6 +30,9 @@ void GBARRContextDestroy(struct GBA* gba) { if (GBARRIsRecording(gba->rr)) { GBARRStopRecording(gba->rr); } + if (gba->rr->metadataFile) { + gba->rr->metadataFile->close(gba->rr->metadataFile); + } free(gba->rr); gba->rr = 0; @@ -37,7 +42,19 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { return false; } + + if (rr->metadataFile && !rr->metadataFile->close(rr->metadataFile)) { + return false; + } + rr->streamDir = stream; + rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR); + if (!_parseMetadata(rr, rr->metadataFile)) { + rr->metadataFile->close(rr->metadataFile); + rr->streamDir = 0; + rr->metadataFile = 0; + return false; + } rr->movieStream = 0; rr->streamId = 1; rr->maxStreamId = 1; @@ -53,7 +70,13 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { char buffer[14]; snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId); if (GBARRIsRecording(rr)) { - rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); + int flags = O_CREAT | O_WRONLY; + if (streamId > rr->maxStreamId) { + flags |= O_TRUNC; + } else { + flags |= O_APPEND; + } + rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, flags); } else if (GBARRIsPlaying(rr)) { rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); rr->peekedTag = TAG_INVALID; @@ -85,6 +108,9 @@ bool GBARRIncrementStream(struct GBARRContext* rr) { _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); _emitTag(rr, rr->movieStream, TAG_BEGIN); + + rr->metadataFile->seek(rr->metadataFile, rr->maxStreamIdOffset, SEEK_SET); + rr->metadataFile->write(rr->movieStream, &rr->maxStreamId, sizeof(rr->maxStreamId)); return true; } @@ -139,6 +165,12 @@ bool GBARRStartRecording(struct GBARRContext* rr) { return false; } + if (!rr->maxStreamIdOffset) { + _emitTag(rr, rr->metadataFile, TAG_MAX_STREAM); + rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); + rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId)); + } + rr->isRecording = true; return true; } @@ -182,7 +214,13 @@ void GBARRNextFrame(struct GBARRContext* rr) { rr->inputThisFrame = false; } else { if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { + uint32_t endStreamId = rr->streamId; GBARRStopPlaying(rr); + if (rr->autorecord) { + rr->isRecording = true; + GBARRLoadStream(rr, endStreamId); + GBARRIncrementStream(rr); + } } } } @@ -211,6 +249,15 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) { return rr->currentInput; } +bool GBARRSkipSegment(struct GBARRContext* rr) { + rr->nextTime = 0; + while (_readTag(rr, rr->movieStream) != TAG_EOF); + if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + return false; + } + return true; +} + enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { if (!rr || !vf) { return TAG_EOF; @@ -266,10 +313,8 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { enum GBARRTag readTag; while ((readTag = _readTag(rr, vf)) != tag) { - if (readTag == TAG_END) { - rr->nextTime = 0; - while (_readTag(rr, vf) != TAG_EOF); - if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + if (vf == rr->movieStream && readTag == TAG_END) { + if (!GBARRSkipSegment(rr)) { return false; } vf = rr->movieStream; @@ -282,5 +327,15 @@ bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { UNUSED(rr); - return vf->write(vf, &tag, 1) == 1; + return vf->write(vf, &tag, sizeof(tag)) == sizeof(tag); +} + +bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { + while (_readTag(rr, vf) != TAG_EOF) { + if (rr->peekedTag == TAG_MAX_STREAM) { + rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_CUR); + } + } + rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_SET); + return true; } diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 9629512fc..2817de569 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -48,9 +48,11 @@ struct GBARRContext { uint32_t lagFrames; uint32_t streamId; uint32_t maxStreamId; + off_t maxStreamIdOffset; // Streaming state struct VDir* streamDir; + struct VFile* metadataFile; struct VFile* movieStream; uint16_t currentInput; enum GBARRTag peekedTag; @@ -64,6 +66,7 @@ void GBARRContextDestroy(struct GBA*); bool GBARRSetStream(struct GBARRContext*, struct VDir*); bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); bool GBARRIncrementStream(struct GBARRContext*); +bool GBARRSkipSegment(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); void GBARRStopPlaying(struct GBARRContext*); diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index 11a979c07..2ce09963a 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -78,6 +78,14 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { GBAIODeserialize(gba, state); GBAVideoDeserialize(&gba->video, state); GBAAudioDeserialize(&gba->audio, state); + + if (GBARRIsRecording(gba->rr)) { + GBARRLoadStream(gba->rr, state->associatedStreamId); + GBARRIncrementStream(gba->rr); + } else if (GBARRIsPlaying(gba->rr)) { + GBARRLoadStream(gba->rr, state->associatedStreamId); + GBARRSkipSegment(gba->rr); + } } static struct VFile* _getStateVf(struct GBA* gba, struct VDir* dir, int slot, bool write) { From 4534574f509f2fa1b1bf3af008921e3a11283a20 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 30 Jul 2014 01:50:22 -0700 Subject: [PATCH 43/72] Fix game ID for Wario Ware Twisted override --- src/gba/gba.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index af374b3d1..04ffe0629 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -91,9 +91,9 @@ static const struct GBACartridgeOverride _overrides[] = { { "AX4P", SAVEDATA_FLASH1M, GPIO_NONE, -1 }, // Wario Ware Twisted - { "RWZJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, - { "RWZE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, - { "RWZP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RZWJ", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RZWE", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, + { "RZWP", SAVEDATA_SRAM, GPIO_RUMBLE | GPIO_GYRO, -1 }, { { 0, 0, 0, 0 }, 0, 0, -1 } }; From a09bb6d51ad83085f1c4e04a30a19a744a83c749 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 3 Aug 2014 19:47:02 -0700 Subject: [PATCH 44/72] Detect basic desync in rr --- src/gba/gba-rr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 3f6af1141..4b7057cc4 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -213,6 +213,9 @@ void GBARRNextFrame(struct GBARRContext* rr) { rr->inputThisFrame = false; } else { + if (rr->peekedTag == TAG_INPUT) { + GBALog(0, GBA_LOG_WARN, "RR desync detected!"); + } if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { uint32_t endStreamId = rr->streamId; GBARRStopPlaying(rr); From fabdfc86f3c944d3d61ee728d002f1c014694aaa Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 3 Aug 2014 20:15:34 -0700 Subject: [PATCH 45/72] Add magic numbers to movie files --- src/gba/gba-rr.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 4b7057cc4..f94a4da8e 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -3,9 +3,12 @@ #include "gba.h" #include "util/vfs.h" -#define BINEXT ".dat" -#define METADATA_FILENAME "metadata" BINEXT +#define BINARY_EXT ".dat" +#define BINARY_MAGIC "GBAb" +#define METADATA_FILENAME "metadata" BINARY_EXT +static bool _emitMagic(struct GBARRContext* rr, struct VFile* vf); +static bool _verifyMagic(struct GBARRContext* rr, struct VFile* vf); static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); @@ -68,7 +71,7 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { rr->movieStream = 0; rr->streamId = streamId; char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u" BINEXT, streamId); + snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId); if (GBARRIsRecording(rr)) { int flags = O_CREAT | O_WRONLY; if (streamId > rr->maxStreamId) { @@ -80,7 +83,7 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { } else if (GBARRIsPlaying(rr)) { rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); rr->peekedTag = TAG_INVALID; - if (!rr->movieStream || !_seekTag(rr, rr->movieStream, TAG_BEGIN)) { + if (!rr->movieStream || !_verifyMagic(rr, rr->movieStream) || !_seekTag(rr, rr->movieStream, TAG_BEGIN)) { GBARRStopPlaying(rr); } } @@ -104,6 +107,7 @@ bool GBARRIncrementStream(struct GBARRContext* rr) { if (!GBARRLoadStream(rr, newStreamId)) { return false; } + _emitMagic(rr, rr->movieStream); rr->maxStreamId = newStreamId; _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); rr->movieStream->write(rr->movieStream, &oldStreamId, sizeof(oldStreamId)); @@ -120,13 +124,16 @@ bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { } char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u" BINEXT, rr->streamId); + snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, rr->streamId); rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); if (!rr->movieStream) { return false; } rr->autorecord = autorecord; + if (!_verifyMagic(rr, rr->movieStream)) { + return false; + } rr->peekedTag = TAG_INVALID; _readTag(rr, rr->movieStream); // Discard the buffer enum GBARRTag tag = _readTag(rr, rr->movieStream); @@ -157,8 +164,12 @@ bool GBARRStartRecording(struct GBARRContext* rr) { } char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u" BINEXT, rr->streamId); + snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, rr->streamId); rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); + if (!rr->movieStream) { + return false; + } + _emitMagic(rr, rr->movieStream); if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { rr->movieStream->close(rr->movieStream); rr->movieStream = 0; @@ -261,6 +272,23 @@ bool GBARRSkipSegment(struct GBARRContext* rr) { return true; } +bool _emitMagic(struct GBARRContext* rr, struct VFile* vf) { + UNUSED(rr); + return vf->write(vf, BINARY_MAGIC, 4) == 4; +} + +bool _verifyMagic(struct GBARRContext* rr, struct VFile* vf) { + UNUSED(rr); + char buffer[4]; + if (vf->read(vf, buffer, sizeof(buffer)) != sizeof(buffer)) { + return false; + } + if (memcmp(buffer, BINARY_MAGIC, sizeof(buffer)) != 0) { + return false; + } + return true; +} + enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { if (!rr || !vf) { return TAG_EOF; From ff200093cab7f555f97cd28dda3c5091ba4c7f71 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sun, 3 Aug 2014 20:19:19 -0700 Subject: [PATCH 46/72] Fix crashing when trying to start recording while recording --- src/platform/sdl/sdl-events.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 03f398864..5fbbb88e7 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -126,9 +126,11 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); - GBARRSetStream(context->gba->rr, context->stateDir); - GBARRStopPlaying(context->gba->rr); - GBARRStartRecording(context->gba->rr); + if (!GBARRIsRecording(context->gba->rr)) { + GBARRSetStream(context->gba->rr, context->stateDir); + GBARRStopPlaying(context->gba->rr); + GBARRStartRecording(context->gba->rr); + } GBAThreadContinue(context); } break; From 220d836f13a99f81166217b8a498863c2e31d709 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 4 Aug 2014 00:52:58 -0700 Subject: [PATCH 47/72] Save initialization type and metadata magic number --- CMakeLists.txt | 8 +++-- src/gba/gba-rr.c | 55 +++++++++++++++++++++++++++++++---- src/gba/gba-rr.h | 20 +++++++++++-- src/platform/sdl/sdl-events.c | 5 ++-- 4 files changed, 76 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9508ac193..b0fc3c808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,16 +20,18 @@ include_directories(${CMAKE_SOURCE_DIR}/src/arm) include_directories(${CMAKE_SOURCE_DIR}/src/gba) include_directories(${CMAKE_SOURCE_DIR}/src) +set(BUILD_PGO CACHE BOOL "Build with profiling-guided optimization") +set(PGO_STAGE_2 CACHE BOOL "Rebuild for profiling-guided optimization after profiles have been generated") set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path") -mark_as_advanced(PGO_DIR) +mark_as_advanced(BUILD_PGO PGO_STAGE_2 PGO_DIR) set(PGO_PRE_FLAGS "-pg -fprofile-generate=${PGO_DIR}") set(PGO_POST_FLAGS "-fprofile-use=${PGO_DIR}") -if(PGO_STAGE EQUAL 1) +if(BUILD_PGO AND NOT PGO_STAGE_2) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${PGO_PRE_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PGO_PRE_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${PGO_PRE_FLAGS}") -elseif(PGO_STAGE EQUAL 2) +elseif(BUILD_PGO AND PGO_STAGE_2) set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${PGO_POST_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PGO_POST_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${PGO_POST_FLAGS}") diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index f94a4da8e..3e8cd6b3d 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -41,7 +41,7 @@ void GBARRContextDestroy(struct GBA* gba) { gba->rr = 0; } -bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { +bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) { if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { return false; } @@ -54,13 +54,34 @@ bool GBARRSetStream(struct GBARRContext* rr, struct VDir* stream) { rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR); if (!_parseMetadata(rr, rr->metadataFile)) { rr->metadataFile->close(rr->metadataFile); - rr->streamDir = 0; rr->metadataFile = 0; + rr->maxStreamId = 1; + } + rr->streamId = 1; + rr->movieStream = 0; + return true; +} + +bool GBARRReinitStream(struct GBARRContext* rr, enum GBARRInitFrom initFrom) { + if (!rr) { return false; } - rr->movieStream = 0; + + if (rr->metadataFile) { + rr->metadataFile->truncate(rr->metadataFile, 0); + } else { + rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR); + } + _emitMagic(rr, rr->metadataFile); + + rr->initFrom = initFrom; + rr->initFromOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); + _emitTag(rr, rr->metadataFile, TAG_INIT | initFrom); + rr->streamId = 1; rr->maxStreamId = 1; + rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); + _emitTag(rr, rr->metadataFile, 1); return true; } @@ -315,9 +336,20 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { vf->read(vf, &rr->lagFrames, sizeof(rr->lagFrames)); break; + case TAG_INIT_EX_NIHILO: + rr->initFrom = INIT_EX_NIHILO; + break; + case TAG_INIT_FROM_SAVEGAME: + rr->initFrom = INIT_FROM_SAVEGAME; + break; + case TAG_INIT_FROM_SAVESTATE: + rr->initFrom = INIT_FROM_SAVESTATE; + case TAG_INIT_FROM_BOTH: + rr->initFrom = INIT_FROM_BOTH; + break; + // To be spec'd case TAG_RR_COUNT: - case TAG_INIT_TYPE: case TAG_AUTHOR: case TAG_COMMENT: break; @@ -362,9 +394,22 @@ bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag) { } bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { + if (!_verifyMagic(rr, vf)) { + return false; + } while (_readTag(rr, vf) != TAG_EOF) { - if (rr->peekedTag == TAG_MAX_STREAM) { + switch (rr->peekedTag) { + case TAG_MAX_STREAM: rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_CUR); + break; + case TAG_INIT_EX_NIHILO: + case TAG_INIT_FROM_SAVEGAME: + case TAG_INIT_FROM_SAVESTATE: + case TAG_INIT_FROM_BOTH: + rr->initFromOffset = vf->seek(vf, 0, SEEK_CUR); + break; + default: + break; } } rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_SET); diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 2817de569..0fe96caff 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -7,6 +7,13 @@ struct GBA; struct VDir; struct VFile; +enum GBARRInitFrom { + INIT_EX_NIHILO = 0, + INIT_FROM_SAVEGAME = 1, + INIT_FROM_SAVESTATE = 2, + INIT_FROM_BOTH = 3, +}; + enum GBARRTag { // Playback tags TAG_INVALID = 0x00, @@ -25,7 +32,11 @@ enum GBARRTag { TAG_FRAME_COUNT = 0x20, TAG_LAG_COUNT = 0x21, TAG_RR_COUNT = 0x22, - TAG_INIT_TYPE = 0x23, + TAG_INIT = 0x24, + TAG_INIT_EX_NIHILO = 0x24 | INIT_EX_NIHILO, + TAG_INIT_FROM_SAVEGAME = 0x24 | INIT_FROM_SAVEGAME, + TAG_INIT_FROM_SAVESTATE = 0x24 | INIT_FROM_SAVESTATE, + TAG_INIT_FROM_BOTH = 0x24 | INIT_FROM_BOTH, // User metadata tags TAG_AUTHOR = 0x30, @@ -47,9 +58,13 @@ struct GBARRContext { uint32_t frames; uint32_t lagFrames; uint32_t streamId; + uint32_t maxStreamId; off_t maxStreamIdOffset; + enum GBARRInitFrom initFrom; + off_t initFromOffset; + // Streaming state struct VDir* streamDir; struct VFile* metadataFile; @@ -63,7 +78,8 @@ struct GBARRContext { void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); -bool GBARRSetStream(struct GBARRContext*, struct VDir*); +bool GBARRInitStream(struct GBARRContext*, struct VDir*); +bool GBARRReinitStream(struct GBARRContext*, enum GBARRInitFrom); bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); bool GBARRIncrementStream(struct GBARRContext*); bool GBARRSkipSegment(struct GBARRContext*); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 5fbbb88e7..d13e82493 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -127,7 +127,8 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBAThreadInterrupt(context); GBARRContextCreate(context->gba); if (!GBARRIsRecording(context->gba->rr)) { - GBARRSetStream(context->gba->rr, context->stateDir); + GBARRInitStream(context->gba->rr, context->stateDir); + GBARRReinitStream(context->gba->rr, INIT_FROM_SAVEGAME); GBARRStopPlaying(context->gba->rr); GBARRStartRecording(context->gba->rr); } @@ -139,7 +140,7 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); - GBARRSetStream(context->gba->rr, context->stateDir); + GBARRInitStream(context->gba->rr, context->stateDir); GBARRStopRecording(context->gba->rr); GBARRStartPlaying(context->gba->rr, event->keysym.mod & KMOD_SHIFT); GBAThreadContinue(context); From f8fff828e3d9e0c27e0852eae6895db00d4ad83c Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 4 Aug 2014 01:11:46 -0700 Subject: [PATCH 48/72] Start working on ability to bundle save data with movies --- src/gba/gba-rr.c | 12 ++++++++++++ src/gba/gba-rr.h | 1 + src/gba/gba-savedata.c | 5 +++++ src/gba/gba-savedata.h | 1 + src/platform/sdl/sdl-events.c | 9 +++++++-- 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 3e8cd6b3d..bc52423d6 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -41,6 +41,18 @@ void GBARRContextDestroy(struct GBA* gba) { gba->rr = 0; } +void GBARRAlterSavedata(struct GBA* gba) { + if (!gba || !gba->rr) { + return; + } + + if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { + // TOOD + } else { + GBASavedataMask(&gba->memory.savedata, 0); + } +} + bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) { if (rr->movieStream && !rr->movieStream->close(rr->movieStream)) { return false; diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 0fe96caff..c8533c55d 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -77,6 +77,7 @@ struct GBARRContext { void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); +void GBARRAlterSavedata(struct GBA*); bool GBARRInitStream(struct GBARRContext*, struct VDir*); bool GBARRReinitStream(struct GBARRContext*, enum GBARRInitFrom); diff --git a/src/gba/gba-savedata.c b/src/gba/gba-savedata.c index e3332b517..beb42a384 100644 --- a/src/gba/gba-savedata.c +++ b/src/gba/gba-savedata.c @@ -60,6 +60,11 @@ void GBASavedataDeinit(struct GBASavedata* savedata) { savedata->type = SAVEDATA_NONE; } +void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) { + GBASavedataDeinit(savedata); + GBASavedataInit(savedata, vf); +} + void GBASavedataInitFlash(struct GBASavedata* savedata) { if (savedata->type == SAVEDATA_NONE) { savedata->type = SAVEDATA_FLASH512; diff --git a/src/gba/gba-savedata.h b/src/gba/gba-savedata.h index a9969c850..0c5f07323 100644 --- a/src/gba/gba-savedata.h +++ b/src/gba/gba-savedata.h @@ -71,6 +71,7 @@ struct GBASavedata { void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataDeinit(struct GBASavedata* savedata); +void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataInitFlash(struct GBASavedata* savedata); void GBASavedataInitEEPROM(struct GBASavedata* savedata); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index d13e82493..130effdf3 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -128,9 +128,10 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBARRContextCreate(context->gba); if (!GBARRIsRecording(context->gba->rr)) { GBARRInitStream(context->gba->rr, context->stateDir); - GBARRReinitStream(context->gba->rr, INIT_FROM_SAVEGAME); + GBARRReinitStream(context->gba->rr, INIT_EX_NIHILO); GBARRStopPlaying(context->gba->rr); GBARRStartRecording(context->gba->rr); + GBARRAlterSavedata(context->gba); } GBAThreadContinue(context); } @@ -142,7 +143,11 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents GBARRContextCreate(context->gba); GBARRInitStream(context->gba->rr, context->stateDir); GBARRStopRecording(context->gba->rr); - GBARRStartPlaying(context->gba->rr, event->keysym.mod & KMOD_SHIFT); + GBARRStartPlaying(context->gba->rr, false); + if (context->gba->rr->initFrom & INIT_FROM_SAVESTATE) { + // TODO + } + GBARRAlterSavedata(context->gba); GBAThreadContinue(context); } break; From d979e04606f54be7636e9db1869dab0cfcdb10c4 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 4 Aug 2014 22:24:43 -0700 Subject: [PATCH 49/72] Unmask savedata if reseting while a movie is not recording or replaying --- src/gba/gba-savedata.c | 9 ++++++++- src/gba/gba-savedata.h | 3 +++ src/gba/gba.c | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/gba/gba-savedata.c b/src/gba/gba-savedata.c index beb42a384..01a49de06 100644 --- a/src/gba/gba-savedata.c +++ b/src/gba/gba-savedata.c @@ -18,6 +18,7 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->command = EEPROM_COMMAND_NULL; savedata->flashState = FLASH_STATE_RAW; savedata->vf = vf; + savedata->realVf = vf; } void GBASavedataDeinit(struct GBASavedata* savedata) { @@ -57,12 +58,18 @@ void GBASavedataDeinit(struct GBASavedata* savedata) { break; } } + savedata->data = 0; savedata->type = SAVEDATA_NONE; } void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) { GBASavedataDeinit(savedata); - GBASavedataInit(savedata, vf); + savedata->vf = vf; +} + +void GBASavedataUnmask(struct GBASavedata* savedata) { + GBASavedataDeinit(savedata); + savedata->vf = savedata->realVf; } void GBASavedataInitFlash(struct GBASavedata* savedata) { diff --git a/src/gba/gba-savedata.h b/src/gba/gba-savedata.h index 0c5f07323..e3acec6da 100644 --- a/src/gba/gba-savedata.h +++ b/src/gba/gba-savedata.h @@ -57,6 +57,7 @@ struct GBASavedata { uint8_t* data; enum SavedataCommand command; struct VFile* vf; + struct VFile* realVf; int readBitsRemaining; int readAddress; @@ -71,7 +72,9 @@ struct GBASavedata { void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataDeinit(struct GBASavedata* savedata); + void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf); +void GBASavedataUnmask(struct GBASavedata* savedata); void GBASavedataInitFlash(struct GBASavedata* savedata); void GBASavedataInitEEPROM(struct GBASavedata* savedata); diff --git a/src/gba/gba.c b/src/gba/gba.c index 04ffe0629..3decc7023 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -187,6 +187,9 @@ void GBAReset(struct ARMCore* cpu) { cpu->gprs[ARM_SP] = SP_BASE_SYSTEM; struct GBA* gba = (struct GBA*) cpu->master; + if (!GBARRIsPlaying(gba->rr) && !GBARRIsRecording(gba->rr)) { + GBASavedataUnmask(&gba->memory.savedata); + } GBAMemoryReset(gba); GBAVideoReset(&gba->video); GBAAudioReset(&gba->audio); From 86a2edbdf1b79b0a6a55f3b1e9452d90a4c67999 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 4 Aug 2014 22:40:11 -0700 Subject: [PATCH 50/72] Save/load state with file parameter --- src/gba/gba-serialize.c | 37 ++++++++++++++++++++++++++----------- src/gba/gba-serialize.h | 3 +++ 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index 2ce09963a..df07688f0 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -163,15 +163,7 @@ bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot) if (!vf) { return false; } - bool success = true; - if (!screenshot) { - vf->truncate(vf, sizeof(struct GBASerializedState)); - struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); - GBASerialize(gba, state); - vf->unmap(vf, state, sizeof(struct GBASerializedState)); - } else { - _savePNGState(gba, vf); - } + bool success = GBASaveStateNamed(gba, vf, screenshot); vf->close(vf); return success; } @@ -181,14 +173,37 @@ bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot) { if (!vf) { return false; } + bool success = GBALoadStateNamed(gba, vf); + vf->close(vf); + return success; +} + +bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot) { + if (!screenshot) { + vf->truncate(vf, sizeof(struct GBASerializedState)); + struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_WRITE); + if (!state) { + return false; + } + GBASerialize(gba, state); + vf->unmap(vf, state, sizeof(struct GBASerializedState)); + } else { + return _savePNGState(gba, vf); + } + return true; +} + +bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf) { if (!isPNG(vf)) { struct GBASerializedState* state = vf->map(vf, sizeof(struct GBASerializedState), MAP_READ); + if (!state) { + return false; + } GBADeserialize(gba, state); vf->unmap(vf, state, sizeof(struct GBASerializedState)); } else { - _loadPNGState(gba, vf); + return _loadPNGState(gba, vf); } - vf->close(vf); return true; } diff --git a/src/gba/gba-serialize.h b/src/gba/gba-serialize.h index af73c45f9..0f294f5fc 100644 --- a/src/gba/gba-serialize.h +++ b/src/gba/gba-serialize.h @@ -272,6 +272,9 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state); bool GBASaveState(struct GBA* gba, struct VDir* dir, int slot, bool screenshot); bool GBALoadState(struct GBA* gba, struct VDir* dir, int slot); +bool GBASaveStateNamed(struct GBA* gba, struct VFile* vf, bool screenshot); +bool GBALoadStateNamed(struct GBA* gba, struct VFile* vf); + struct GBASerializedState* GBAAllocateState(void); void GBADeallocateState(struct GBASerializedState* state); From 82b31c46f11c81193809e26479d8699b771b1be1 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 4 Aug 2014 23:37:37 -0700 Subject: [PATCH 51/72] RR starting from savegame (currently broken) and savestate --- src/gba/gba-rr.c | 58 +++++++++++++++++++++++++++++++++-- src/gba/gba-rr.h | 5 ++- src/gba/gba-savedata.c | 26 ++++++++++++++++ src/gba/gba-savedata.h | 1 + src/platform/sdl/sdl-events.c | 13 +++----- 5 files changed, 91 insertions(+), 12 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index bc52423d6..be3c8c80a 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -1,6 +1,7 @@ #include "gba-rr.h" #include "gba.h" +#include "gba-serialize.h" #include "util/vfs.h" #define BINARY_EXT ".dat" @@ -14,6 +15,9 @@ static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag ta static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf); +static struct VFile* _openSavedata(struct GBARRContext* rr, int flags); +static struct VFile* _openSavestate(struct GBARRContext* rr, int flags); + void GBARRContextCreate(struct GBA* gba) { if (gba->rr) { return; @@ -36,21 +40,63 @@ void GBARRContextDestroy(struct GBA* gba) { if (gba->rr->metadataFile) { gba->rr->metadataFile->close(gba->rr->metadataFile); } + if (gba->rr->savedata) { + gba->rr->savedata->close(gba->rr->savedata); + } free(gba->rr); gba->rr = 0; } -void GBARRAlterSavedata(struct GBA* gba) { +void GBARRSaveState(struct GBA* gba) { if (!gba || !gba->rr) { return; } if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { - // TOOD + if (gba->rr->savedata) { + gba->rr->savedata->close(gba->rr->savedata); + } + gba->rr->savedata = _openSavedata(gba->rr, O_TRUNC | O_CREAT | O_WRONLY); + GBASavedataClone(&gba->memory.savedata, gba->rr->savedata); + gba->rr->savedata->close(gba->rr->savedata); + gba->rr->savedata = _openSavedata(gba->rr, O_RDONLY); + GBASavedataMask(&gba->memory.savedata, gba->rr->savedata); } else { GBASavedataMask(&gba->memory.savedata, 0); } + + if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { + struct VFile* vf = _openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR); + GBASaveStateNamed(gba, vf, false); + vf->close(vf); + } else { + ARMReset(gba->cpu); + } +} + +void GBARRLoadState(struct GBA* gba) { + if (!gba || !gba->rr) { + return; + } + + if (gba->rr->initFrom & INIT_FROM_SAVEGAME) { + if (gba->rr->savedata) { + gba->rr->savedata->close(gba->rr->savedata); + } + gba->rr->savedata = _openSavedata(gba->rr, O_RDONLY); + GBASavedataMask(&gba->memory.savedata, gba->rr->savedata); + } else { + GBASavedataMask(&gba->memory.savedata, 0); + } + + if (gba->rr->initFrom & INIT_FROM_SAVESTATE) { + struct VFile* vf = _openSavestate(gba->rr, O_RDONLY); + GBALoadStateNamed(gba, vf); + vf->close(vf); + } else { + ARMReset(gba->cpu); + } } bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) { @@ -427,3 +473,11 @@ bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_SET); return true; } + +struct VFile* _openSavedata(struct GBARRContext* rr, int flags) { + return rr->streamDir->openFile(rr->streamDir, "movie.sav", flags); +} + +struct VFile* _openSavestate(struct GBARRContext* rr, int flags) { + return rr->streamDir->openFile(rr->streamDir, "movie.ssm", flags); +} diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index c8533c55d..f0c40550f 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -65,6 +65,8 @@ struct GBARRContext { enum GBARRInitFrom initFrom; off_t initFromOffset; + struct VFile* savedata; + // Streaming state struct VDir* streamDir; struct VFile* metadataFile; @@ -77,7 +79,8 @@ struct GBARRContext { void GBARRContextCreate(struct GBA*); void GBARRContextDestroy(struct GBA*); -void GBARRAlterSavedata(struct GBA*); +void GBARRSaveState(struct GBA*); +void GBARRLoadState(struct GBA*); bool GBARRInitStream(struct GBARRContext*, struct VDir*); bool GBARRReinitStream(struct GBARRContext*, enum GBARRInitFrom); diff --git a/src/gba/gba-savedata.c b/src/gba/gba-savedata.c index 01a49de06..02fadf7a8 100644 --- a/src/gba/gba-savedata.c +++ b/src/gba/gba-savedata.c @@ -72,6 +72,32 @@ void GBASavedataUnmask(struct GBASavedata* savedata) { savedata->vf = savedata->realVf; } +bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { + if (savedata->data) { + switch (savedata->type) { + case SAVEDATA_SRAM: + return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM; + case SAVEDATA_FLASH512: + return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512; + case SAVEDATA_FLASH1M: + return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M; + case SAVEDATA_EEPROM: + return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM; + case SAVEDATA_NONE: + return true; + } + } else if (savedata->vf) { + off_t read = 0; + uint8_t buffer[2048]; + do { + read = savedata->vf->read(savedata->vf, buffer, sizeof(buffer)); + out->write(out, buffer, read); + } while (read == sizeof(buffer)); + return read >= 0; + } + return true; +} + void GBASavedataInitFlash(struct GBASavedata* savedata) { if (savedata->type == SAVEDATA_NONE) { savedata->type = SAVEDATA_FLASH512; diff --git a/src/gba/gba-savedata.h b/src/gba/gba-savedata.h index e3acec6da..5cb5c517b 100644 --- a/src/gba/gba-savedata.h +++ b/src/gba/gba-savedata.h @@ -75,6 +75,7 @@ void GBASavedataDeinit(struct GBASavedata* savedata); void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf); void GBASavedataUnmask(struct GBASavedata* savedata); +bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out); void GBASavedataInitFlash(struct GBASavedata* savedata); void GBASavedataInitEEPROM(struct GBASavedata* savedata); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 130effdf3..47c4a95a6 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -123,31 +123,26 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents break; case SDLK_t: if (context->stateDir) { - GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); if (!GBARRIsRecording(context->gba->rr)) { + GBARRStopPlaying(context->gba->rr); GBARRInitStream(context->gba->rr, context->stateDir); GBARRReinitStream(context->gba->rr, INIT_EX_NIHILO); - GBARRStopPlaying(context->gba->rr); GBARRStartRecording(context->gba->rr); - GBARRAlterSavedata(context->gba); + GBARRSaveState(context->gba); } GBAThreadContinue(context); } break; case SDLK_y: if (context->stateDir) { - GBAThreadReset(context); GBAThreadInterrupt(context); GBARRContextCreate(context->gba); - GBARRInitStream(context->gba->rr, context->stateDir); GBARRStopRecording(context->gba->rr); + GBARRInitStream(context->gba->rr, context->stateDir); GBARRStartPlaying(context->gba->rr, false); - if (context->gba->rr->initFrom & INIT_FROM_SAVESTATE) { - // TODO - } - GBARRAlterSavedata(context->gba); + GBARRLoadState(context->gba); GBAThreadContinue(context); } break; From 5ee336d274fd481c9eb4eb0ee9b49d598aa4340a Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Mon, 4 Aug 2014 23:46:33 -0700 Subject: [PATCH 52/72] Fix masking savegames so it can write to a masked savegame --- src/gba/gba-savedata.c | 9 ++++++--- src/gba/gba-savedata.h | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/gba/gba-savedata.c b/src/gba/gba-savedata.c index 02fadf7a8..4e3e6cf98 100644 --- a/src/gba/gba-savedata.c +++ b/src/gba/gba-savedata.c @@ -19,6 +19,7 @@ void GBASavedataInit(struct GBASavedata* savedata, struct VFile* vf) { savedata->flashState = FLASH_STATE_RAW; savedata->vf = vf; savedata->realVf = vf; + savedata->mapMode = MAP_WRITE; } void GBASavedataDeinit(struct GBASavedata* savedata) { @@ -65,11 +66,13 @@ void GBASavedataDeinit(struct GBASavedata* savedata) { void GBASavedataMask(struct GBASavedata* savedata, struct VFile* vf) { GBASavedataDeinit(savedata); savedata->vf = vf; + savedata->mapMode = MAP_READ; } void GBASavedataUnmask(struct GBASavedata* savedata) { GBASavedataDeinit(savedata); savedata->vf = savedata->realVf; + savedata->mapMode = MAP_WRITE; } bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { @@ -115,7 +118,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) { if (end < SIZE_CART_FLASH512) { savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, savedata->mapMode); } savedata->currentBank = savedata->data; @@ -140,7 +143,7 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) { if (end < SIZE_CART_EEPROM) { savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, MAP_WRITE); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode); } if (end < SIZE_CART_EEPROM) { memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM - end); @@ -163,7 +166,7 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) { if (end < SIZE_CART_SRAM) { savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, MAP_WRITE); + savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode); } if (end < SIZE_CART_SRAM) { diff --git a/src/gba/gba-savedata.h b/src/gba/gba-savedata.h index 5cb5c517b..08654ca6a 100644 --- a/src/gba/gba-savedata.h +++ b/src/gba/gba-savedata.h @@ -57,6 +57,8 @@ struct GBASavedata { uint8_t* data; enum SavedataCommand command; struct VFile* vf; + + int mapMode; struct VFile* realVf; int readBitsRemaining; From 0389237fc6a43382aa1fce96af2d6a031d7e2ff9 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 5 Aug 2014 23:52:08 -0700 Subject: [PATCH 53/72] Loading savestates now changes the active branch --- src/gba/gba-rr.c | 112 +++++++++++++++++++++++++++++----------- src/gba/gba-rr.h | 3 +- src/gba/gba-serialize.c | 10 ++-- 3 files changed, 92 insertions(+), 33 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index be3c8c80a..0d685c510 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -13,8 +13,12 @@ static bool _verifyMagic(struct GBARRContext* rr, struct VFile* vf); static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); static bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag); static bool _emitTag(struct GBARRContext* rr, struct VFile* vf, uint8_t tag); +static bool _emitEnd(struct GBARRContext* rr, struct VFile* vf); + static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf); +static bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursive); + static struct VFile* _openSavedata(struct GBARRContext* rr, int flags); static struct VFile* _openSavestate(struct GBARRContext* rr, int flags); @@ -113,7 +117,7 @@ bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) { if (!_parseMetadata(rr, rr->metadataFile)) { rr->metadataFile->close(rr->metadataFile); rr->metadataFile = 0; - rr->maxStreamId = 1; + rr->maxStreamId = 0; } rr->streamId = 1; rr->movieStream = 0; @@ -136,8 +140,8 @@ bool GBARRReinitStream(struct GBARRContext* rr, enum GBARRInitFrom initFrom) { rr->initFromOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); _emitTag(rr, rr->metadataFile, TAG_INIT | initFrom); - rr->streamId = 1; - rr->maxStreamId = 1; + rr->streamId = 0; + rr->maxStreamId = 0; rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); _emitTag(rr, rr->metadataFile, 1); return true; @@ -152,11 +156,9 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { char buffer[14]; snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId); if (GBARRIsRecording(rr)) { - int flags = O_CREAT | O_WRONLY; + int flags = O_CREAT | O_RDWR; if (streamId > rr->maxStreamId) { flags |= O_TRUNC; - } else { - flags |= O_APPEND; } rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, flags); } else if (GBARRIsPlaying(rr)) { @@ -171,17 +173,13 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { return true; } -bool GBARRIncrementStream(struct GBARRContext* rr) { +bool GBARRIncrementStream(struct GBARRContext* rr, bool recursive) { uint32_t newStreamId = rr->maxStreamId + 1; uint32_t oldStreamId = rr->streamId; if (GBARRIsRecording(rr) && rr->movieStream) { - _emitTag(rr, rr->movieStream, TAG_END); - _emitTag(rr, rr->movieStream, TAG_FRAME_COUNT); - rr->movieStream->write(rr->movieStream, &rr->frames, sizeof(rr->frames)); - _emitTag(rr, rr->movieStream, TAG_LAG_COUNT); - rr->movieStream->write(rr->movieStream, &rr->lagFrames, sizeof(rr->lagFrames)); - _emitTag(rr, rr->movieStream, TAG_NEXT_TIME); - rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId)); + if (!_markStreamNext(rr, newStreamId, recursive)) { + return false; + } } if (!GBARRLoadStream(rr, newStreamId)) { return false; @@ -194,6 +192,7 @@ bool GBARRIncrementStream(struct GBARRContext* rr) { rr->metadataFile->seek(rr->metadataFile, rr->maxStreamIdOffset, SEEK_SET); rr->metadataFile->write(rr->movieStream, &rr->maxStreamId, sizeof(rr->maxStreamId)); + rr->previously = oldStreamId; return true; } @@ -216,6 +215,12 @@ bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { rr->peekedTag = TAG_INVALID; _readTag(rr, rr->movieStream); // Discard the buffer enum GBARRTag tag = _readTag(rr, rr->movieStream); + if (tag == TAG_PREVIOUSLY) { + if (rr->previously != 0) { + return false; + } + tag = _readTag(rr, rr->movieStream); + } if (tag != TAG_BEGIN) { rr->movieStream->close(rr->movieStream); rr->movieStream = 0; @@ -242,19 +247,6 @@ bool GBARRStartRecording(struct GBARRContext* rr) { return false; } - char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, rr->streamId); - rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_TRUNC | O_CREAT | O_WRONLY); - if (!rr->movieStream) { - return false; - } - _emitMagic(rr, rr->movieStream); - if (!_emitTag(rr, rr->movieStream, TAG_BEGIN)) { - rr->movieStream->close(rr->movieStream); - rr->movieStream = 0; - return false; - } - if (!rr->maxStreamIdOffset) { _emitTag(rr, rr->metadataFile, TAG_MAX_STREAM); rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); @@ -262,7 +254,7 @@ bool GBARRStartRecording(struct GBARRContext* rr) { } rr->isRecording = true; - return true; + return GBARRIncrementStream(rr, false); } void GBARRStopRecording(struct GBARRContext* rr) { @@ -312,7 +304,7 @@ void GBARRNextFrame(struct GBARRContext* rr) { if (rr->autorecord) { rr->isRecording = true; GBARRLoadStream(rr, endStreamId); - GBARRIncrementStream(rr); + GBARRIncrementStream(rr, false); } } } @@ -342,6 +334,15 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) { return rr->currentInput; } +bool GBARRFinishSegment(struct GBARRContext* rr) { + if (rr->movieStream) { + if (!_emitEnd(rr, rr->movieStream)) { + return false; + } + } + return GBARRIncrementStream(rr, false); +} + bool GBARRSkipSegment(struct GBARRContext* rr) { rr->nextTime = 0; while (_readTag(rr, rr->movieStream) != TAG_EOF); @@ -474,6 +475,59 @@ bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { return true; } +bool _emitEnd(struct GBARRContext* rr, struct VFile* vf) { + // TODO: Error check + _emitTag(rr, vf, TAG_END); + _emitTag(rr, vf, TAG_FRAME_COUNT); + vf->write(vf, &rr->frames, sizeof(rr->frames)); + _emitTag(rr, vf, TAG_LAG_COUNT); + vf->write(vf, &rr->lagFrames, sizeof(rr->lagFrames)); + _emitTag(rr, vf, TAG_NEXT_TIME); + + uint32_t newStreamId = 0; + vf->write(vf, &newStreamId, sizeof(newStreamId)); + return true; +} + +bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursive) { + if (rr->movieStream->seek(rr->movieStream, -sizeof(newStreamId) - 1, SEEK_END) < 0) { + return false; + } + + uint8_t tagBuffer; + if (rr->movieStream->read(rr->movieStream, &tagBuffer, 1) != 1) { + return false; + } + if (tagBuffer != TAG_NEXT_TIME) { + return false; + } + if (rr->movieStream->write(rr->movieStream, &newStreamId, sizeof(newStreamId)) != sizeof(newStreamId)) { + return false; + } + if (recursive) { + if (rr->movieStream->seek(rr->movieStream, 0, SEEK_SET) < 0) { + return false; + } + if (!_verifyMagic(rr, rr->movieStream)) { + return false; + } + _readTag(rr, rr->movieStream); + if (_readTag(rr, rr->movieStream) != TAG_PREVIOUSLY) { + return false; + } + if (rr->previously == 0) { + return true; + } + uint32_t currentStreamId = rr->streamId; + if (!GBARRLoadStream(rr, rr->previously)) { + return false; + } + return _markStreamNext(rr, currentStreamId, rr->previously); + } + return true; + +} + struct VFile* _openSavedata(struct GBARRContext* rr, int flags) { return rr->streamDir->openFile(rr->streamDir, "movie.sav", flags); } diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index f0c40550f..1f15cf78c 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -85,7 +85,8 @@ void GBARRLoadState(struct GBA*); bool GBARRInitStream(struct GBARRContext*, struct VDir*); bool GBARRReinitStream(struct GBARRContext*, enum GBARRInitFrom); bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); -bool GBARRIncrementStream(struct GBARRContext*); +bool GBARRIncrementStream(struct GBARRContext*, bool recursive); +bool GBARRFinishSegment(struct GBARRContext*); bool GBARRSkipSegment(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index df07688f0..ffcc31e91 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -39,7 +39,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { if (GBARRIsRecording(gba->rr)) { state->associatedStreamId = gba->rr->streamId; - GBARRIncrementStream(gba->rr); + GBARRFinishSegment(gba->rr); } else { state->associatedStreamId = 0; } @@ -80,8 +80,12 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { GBAAudioDeserialize(&gba->audio, state); if (GBARRIsRecording(gba->rr)) { - GBARRLoadStream(gba->rr, state->associatedStreamId); - GBARRIncrementStream(gba->rr); + if (state->associatedStreamId != gba->rr->streamId) { + GBARRLoadStream(gba->rr, state->associatedStreamId); + GBARRIncrementStream(gba->rr, true); + } else { + GBARRFinishSegment(gba->rr); + } } else if (GBARRIsPlaying(gba->rr)) { GBARRLoadStream(gba->rr, state->associatedStreamId); GBARRSkipSegment(gba->rr); From 06da7fcb0990a490c296d20e360a323f590c7962 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 6 Aug 2014 21:57:35 -0700 Subject: [PATCH 54/72] Fix rr writing to the wrong file --- src/gba/gba-rr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 0d685c510..6ab5520b8 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -191,7 +191,7 @@ bool GBARRIncrementStream(struct GBARRContext* rr, bool recursive) { _emitTag(rr, rr->movieStream, TAG_BEGIN); rr->metadataFile->seek(rr->metadataFile, rr->maxStreamIdOffset, SEEK_SET); - rr->metadataFile->write(rr->movieStream, &rr->maxStreamId, sizeof(rr->maxStreamId)); + rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId)); rr->previously = oldStreamId; return true; } From f0053268003218ffc5252198c0d44ac234b02ff9 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 7 Aug 2014 00:23:05 -0700 Subject: [PATCH 55/72] Add backslash as another frame advance key --- src/platform/sdl/sdl-events.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index 47c4a95a6..d664a52ef 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -86,6 +86,13 @@ static void _GBASDLHandleKeypress(struct GBAThread* context, struct GBASDLEvents case SDLK_TAB: context->sync.audioWait = event->type != SDL_KEYDOWN; return; + case SDLK_BACKSLASH: + if (event->type == SDL_KEYDOWN) { + GBAThreadPause(context); + context->frameCallback = _pauseAfterFrame; + GBAThreadUnpause(context); + } + return; case SDLK_LEFTBRACKET: GBAThreadInterrupt(context); GBARewind(context, 10); From ba993980dc31841d02fcd994e88c524c78317019 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 7 Aug 2014 00:24:25 -0700 Subject: [PATCH 56/72] Big cleanup to how RR handles separate segments, and adding lots of logging (fixes #106) --- src/gba/gba-rr.c | 76 +++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 6ab5520b8..f4985c21a 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -8,6 +8,10 @@ #define BINARY_MAGIC "GBAb" #define METADATA_FILENAME "metadata" BINARY_EXT +enum { + INVALID_INPUT = 0x8000 +}; + static bool _emitMagic(struct GBARRContext* rr, struct VFile* vf); static bool _verifyMagic(struct GBARRContext* rr, struct VFile* vf); static enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf); @@ -114,6 +118,7 @@ bool GBARRInitStream(struct GBARRContext* rr, struct VDir* stream) { rr->streamDir = stream; rr->metadataFile = rr->streamDir->openFile(rr->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR); + rr->currentInput = INVALID_INPUT; if (!_parseMetadata(rr, rr->metadataFile)) { rr->metadataFile->close(rr->metadataFile); rr->metadataFile = 0; @@ -153,6 +158,7 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { } rr->movieStream = 0; rr->streamId = streamId; + rr->currentInput = INVALID_INPUT; char buffer[14]; snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId); if (GBARRIsRecording(rr)) { @@ -168,6 +174,7 @@ bool GBARRLoadStream(struct GBARRContext* rr, uint32_t streamId) { GBARRStopPlaying(rr); } } + GBALog(0, GBA_LOG_DEBUG, "[RR] Loading segment: %u", streamId); rr->frames = 0; rr->lagFrames = 0; return true; @@ -184,6 +191,7 @@ bool GBARRIncrementStream(struct GBARRContext* rr, bool recursive) { if (!GBARRLoadStream(rr, newStreamId)) { return false; } + GBALog(0, GBA_LOG_DEBUG, "[RR] New segment: %u", newStreamId); _emitMagic(rr, rr->movieStream); rr->maxStreamId = newStreamId; _emitTag(rr, rr->movieStream, TAG_PREVIOUSLY); @@ -201,33 +209,12 @@ bool GBARRStartPlaying(struct GBARRContext* rr, bool autorecord) { return false; } - char buffer[14]; - snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, rr->streamId); - rr->movieStream = rr->streamDir->openFile(rr->streamDir, buffer, O_RDONLY); - if (!rr->movieStream) { - return false; - } - - rr->autorecord = autorecord; - if (!_verifyMagic(rr, rr->movieStream)) { - return false; - } - rr->peekedTag = TAG_INVALID; - _readTag(rr, rr->movieStream); // Discard the buffer - enum GBARRTag tag = _readTag(rr, rr->movieStream); - if (tag == TAG_PREVIOUSLY) { - if (rr->previously != 0) { - return false; - } - tag = _readTag(rr, rr->movieStream); - } - if (tag != TAG_BEGIN) { - rr->movieStream->close(rr->movieStream); - rr->movieStream = 0; - return false; - } - rr->isPlaying = true; + if (!GBARRLoadStream(rr, 1)) { + rr->isPlaying = false; + return false; + } + rr->autorecord = autorecord; return true; } @@ -282,9 +269,24 @@ void GBARRNextFrame(struct GBARRContext* rr) { return; } + if (GBARRIsPlaying(rr)) { + while (rr->peekedTag == TAG_INPUT) { + _readTag(rr, rr->movieStream); + GBALog(0, GBA_LOG_WARN, "[RR] Desync detected!"); + } + if (rr->peekedTag == TAG_LAG) { + GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame marked in stream"); + if (rr->inputThisFrame) { + GBALog(0, GBA_LOG_WARN, "[RR] Lag frame in stream does not match movie"); + } + } + } + ++rr->frames; + GBALog(0, GBA_LOG_DEBUG, "[RR] Frame: %u", rr->frames); if (!rr->inputThisFrame) { ++rr->lagFrames; + GBALog(0, GBA_LOG_DEBUG, "[RR] Lag frame: %u", rr->lagFrames); } if (GBARRIsRecording(rr)) { @@ -292,12 +294,8 @@ void GBARRNextFrame(struct GBARRContext* rr) { _emitTag(rr, rr->movieStream, TAG_LAG); } _emitTag(rr, rr->movieStream, TAG_FRAME); - rr->inputThisFrame = false; } else { - if (rr->peekedTag == TAG_INPUT) { - GBALog(0, GBA_LOG_WARN, "RR desync detected!"); - } if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { uint32_t endStreamId = rr->streamId; GBARRStopPlaying(rr); @@ -320,6 +318,7 @@ void GBARRLogInput(struct GBARRContext* rr, uint16_t keys) { rr->movieStream->write(rr->movieStream, &keys, sizeof(keys)); rr->currentInput = keys; } + GBALog(0, GBA_LOG_DEBUG, "[RR] Input log: %03X", rr->currentInput); rr->inputThisFrame = true; } @@ -331,6 +330,11 @@ uint16_t GBARRQueryInput(struct GBARRContext* rr) { if (rr->peekedTag == TAG_INPUT) { _readTag(rr, rr->movieStream); } + rr->inputThisFrame = true; + if (rr->currentInput == INVALID_INPUT) { + GBALog(0, GBA_LOG_WARN, "[RR] Stream did not specify input"); + } + GBALog(0, GBA_LOG_DEBUG, "[RR] Input replay: %03X", rr->currentInput); return rr->currentInput; } @@ -429,18 +433,17 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { } else { rr->peekedTag = tagBuffer; } + + if (rr->peekedTag == TAG_END) { + GBARRSkipSegment(rr); + } return tag; } bool _seekTag(struct GBARRContext* rr, struct VFile* vf, enum GBARRTag tag) { enum GBARRTag readTag; while ((readTag = _readTag(rr, vf)) != tag) { - if (vf == rr->movieStream && readTag == TAG_END) { - if (!GBARRSkipSegment(rr)) { - return false; - } - vf = rr->movieStream; - } else if (readTag == TAG_EOF) { + if (readTag == TAG_EOF) { return false; } } @@ -525,7 +528,6 @@ bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursi return _markStreamNext(rr, currentStreamId, rr->previously); } return true; - } struct VFile* _openSavedata(struct GBARRContext* rr, int flags) { From a4cd807b70992cdf11c9775087096b2faf7e993c Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 7 Aug 2014 01:13:03 -0700 Subject: [PATCH 57/72] Fix writing max strem ID in RR metadata --- src/gba/gba-rr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index f4985c21a..2300053ca 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -147,8 +147,9 @@ bool GBARRReinitStream(struct GBARRContext* rr, enum GBARRInitFrom initFrom) { rr->streamId = 0; rr->maxStreamId = 0; + _emitTag(rr, rr->metadataFile, TAG_MAX_STREAM); rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); - _emitTag(rr, rr->metadataFile, 1); + rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId)); return true; } From e732448aa4d7edb24095dd4c4ae2ad434c9effa2 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 7 Aug 2014 01:13:34 -0700 Subject: [PATCH 58/72] Write full finalization data when stopping recording --- src/gba/gba-rr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 2300053ca..79fcee9d2 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -251,7 +251,7 @@ void GBARRStopRecording(struct GBARRContext* rr) { } rr->isRecording = false; if (rr->movieStream) { - _emitTag(rr, rr->movieStream, TAG_END); + _emitEnd(rr, rr->movieStream); rr->movieStream->close(rr->movieStream); rr->movieStream = 0; } From 484a7f299a966cb8fbd90c9e06b332235073bae8 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 7 Aug 2014 01:13:50 -0700 Subject: [PATCH 59/72] Add rerecord count --- src/gba/gba-rr.c | 19 ++++++++++++++++++- src/gba/gba-rr.h | 4 ++++ src/gba/gba-serialize.c | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index 79fcee9d2..c47fc8684 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -150,6 +150,11 @@ bool GBARRReinitStream(struct GBARRContext* rr, enum GBARRInitFrom initFrom) { _emitTag(rr, rr->metadataFile, TAG_MAX_STREAM); rr->maxStreamIdOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); rr->metadataFile->write(rr->metadataFile, &rr->maxStreamId, sizeof(rr->maxStreamId)); + + rr->rrCount = 0; + _emitTag(rr, rr->metadataFile, TAG_RR_COUNT); + rr->rrCountOffset = rr->metadataFile->seek(rr->metadataFile, 0, SEEK_CUR); + rr->metadataFile->write(rr->metadataFile, &rr->rrCount, sizeof(rr->rrCount)); return true; } @@ -357,6 +362,13 @@ bool GBARRSkipSegment(struct GBARRContext* rr) { return true; } +bool GBARRMarkRerecord(struct GBARRContext* rr) { + ++rr->rrCount; + rr->metadataFile->seek(rr->metadataFile, rr->rrCountOffset, SEEK_SET); + rr->metadataFile->write(rr->metadataFile, &rr->rrCount, sizeof(rr->rrCount)); + return true; +} + bool _emitMagic(struct GBARRContext* rr, struct VFile* vf) { UNUSED(rr); return vf->write(vf, BINARY_MAGIC, 4) == 4; @@ -399,6 +411,9 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { case TAG_LAG_COUNT: vf->read(vf, &rr->lagFrames, sizeof(rr->lagFrames)); break; + case TAG_RR_COUNT: + vf->read(vf, &rr->rrCount, sizeof(rr->rrCount)); + break; case TAG_INIT_EX_NIHILO: rr->initFrom = INIT_EX_NIHILO; @@ -413,7 +428,6 @@ enum GBARRTag _readTag(struct GBARRContext* rr, struct VFile* vf) { break; // To be spec'd - case TAG_RR_COUNT: case TAG_AUTHOR: case TAG_COMMENT: break; @@ -471,6 +485,9 @@ bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { case TAG_INIT_FROM_BOTH: rr->initFromOffset = vf->seek(vf, 0, SEEK_CUR); break; + case TAG_RR_COUNT: + rr->rrCountOffset = vf->seek(vf, 0, SEEK_CUR); + break; default: break; } diff --git a/src/gba/gba-rr.h b/src/gba/gba-rr.h index 1f15cf78c..ad93616d5 100644 --- a/src/gba/gba-rr.h +++ b/src/gba/gba-rr.h @@ -65,6 +65,9 @@ struct GBARRContext { enum GBARRInitFrom initFrom; off_t initFromOffset; + uint32_t rrCount; + off_t rrCountOffset; + struct VFile* savedata; // Streaming state @@ -88,6 +91,7 @@ bool GBARRLoadStream(struct GBARRContext*, uint32_t streamId); bool GBARRIncrementStream(struct GBARRContext*, bool recursive); bool GBARRFinishSegment(struct GBARRContext*); bool GBARRSkipSegment(struct GBARRContext*); +bool GBARRMarkRerecord(struct GBARRContext*); bool GBARRStartPlaying(struct GBARRContext*, bool autorecord); void GBARRStopPlaying(struct GBARRContext*); diff --git a/src/gba/gba-serialize.c b/src/gba/gba-serialize.c index ffcc31e91..dd5aa35ba 100644 --- a/src/gba/gba-serialize.c +++ b/src/gba/gba-serialize.c @@ -86,6 +86,7 @@ void GBADeserialize(struct GBA* gba, struct GBASerializedState* state) { } else { GBARRFinishSegment(gba->rr); } + GBARRMarkRerecord(gba->rr); } else if (GBARRIsPlaying(gba->rr)) { GBARRLoadStream(gba->rr, state->associatedStreamId); GBARRSkipSegment(gba->rr); From 6291a62860376b1e4a5e0d3ee681b6c93eb10f68 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 7 Aug 2014 02:01:17 -0700 Subject: [PATCH 60/72] Fix stream recording resumption --- src/gba/gba-rr.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/gba/gba-rr.c b/src/gba/gba-rr.c index c47fc8684..f71e2f93b 100644 --- a/src/gba/gba-rr.c +++ b/src/gba/gba-rr.c @@ -22,6 +22,7 @@ static bool _emitEnd(struct GBARRContext* rr, struct VFile* vf); static bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf); static bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursive); +static void _streamEndReached(struct GBARRContext* rr); static struct VFile* _openSavedata(struct GBARRContext* rr, int flags); static struct VFile* _openSavestate(struct GBARRContext* rr, int flags); @@ -303,13 +304,7 @@ void GBARRNextFrame(struct GBARRContext* rr) { rr->inputThisFrame = false; } else { if (!_seekTag(rr, rr->movieStream, TAG_FRAME)) { - uint32_t endStreamId = rr->streamId; - GBARRStopPlaying(rr); - if (rr->autorecord) { - rr->isRecording = true; - GBARRLoadStream(rr, endStreamId); - GBARRIncrementStream(rr, false); - } + _streamEndReached(rr); } } } @@ -357,6 +352,7 @@ bool GBARRSkipSegment(struct GBARRContext* rr) { rr->nextTime = 0; while (_readTag(rr, rr->movieStream) != TAG_EOF); if (!rr->nextTime || !GBARRLoadStream(rr, rr->nextTime)) { + _streamEndReached(rr); return false; } return true; @@ -492,7 +488,6 @@ bool _parseMetadata(struct GBARRContext* rr, struct VFile* vf) { break; } } - rr->maxStreamIdOffset = vf->seek(vf, 0, SEEK_SET); return true; } @@ -548,6 +543,20 @@ bool _markStreamNext(struct GBARRContext* rr, uint32_t newStreamId, bool recursi return true; } +void _streamEndReached(struct GBARRContext* rr) { + if (!GBARRIsPlaying(rr)) { + return; + } + + uint32_t endStreamId = rr->streamId; + GBARRStopPlaying(rr); + if (rr->autorecord) { + rr->isRecording = true; + GBARRLoadStream(rr, endStreamId); + GBARRIncrementStream(rr, false); + } +} + struct VFile* _openSavedata(struct GBARRContext* rr, int flags) { return rr->streamDir->openFile(rr->streamDir, "movie.sav", flags); } From 88695a5ab10510abd143191d8ed69465990d0ecd Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 9 Aug 2014 03:59:31 -0700 Subject: [PATCH 61/72] Rough FFmpeg video encoder --- CMakeLists.txt | 10 ++ src/gba/gba-audio.c | 4 + src/gba/gba-thread.c | 3 + src/gba/gba-thread.h | 6 ++ src/platform/ffmpeg/ffmpeg-encoder.c | 134 +++++++++++++++++++++++++++ src/platform/ffmpeg/ffmpeg-encoder.h | 30 ++++++ 6 files changed, 187 insertions(+) create mode 100644 src/platform/ffmpeg/ffmpeg-encoder.c create mode 100644 src/platform/ffmpeg/ffmpeg-encoder.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b0fc3c808..a6298e494 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -std=gnu99") set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=gnu99") set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger") set(USE_GDB_STUB ON CACHE BOOL "Whether or not to enable the GDB stub ARM debugger") +set(USE_FFMPEG ON CACHE BOOL "Whether or not to enable FFmpeg support") set(BUILD_SDL ON CACHE BOOL "Build SDL frontend") set(BUILD_PERF ON CACHE BOOL "Build performance profiling tool") file(GLOB ARM_SRC ${CMAKE_SOURCE_DIR}/src/arm/*.c) @@ -84,6 +85,15 @@ if(USE_GDB_STUB) add_definitions(-DUSE_GDB_STUB) set(DEBUGGER_SRC "${DEBUGGER_SRC};${CMAKE_SOURCE_DIR}/src/debugger/gdb-stub.c") endif() + +if(USE_FFMPEG) + pkg_search_module(LIBAVCODEC libavcodec REQUIRED) + pkg_search_module(LIBAVFORMAT libavformat REQUIRED) + pkg_search_module(LIBAVUTIL libavutil REQUIRED) + add_definitions(-DUSE_FFMPEG) + list(APPEND UTIL_SRC "${CMAKE_SOURCE_DIR}/src/platform/ffmpeg/ffmpeg-encoder.c") + list(APPEND DEPENDENCY_LIB ${LIBAVCODEC_LIBRARIES} ${LIBAVFORMAT_LIBRARIES} ${LIBAVUTIL_LIBRARIES}) +endif() source_group("ARM debugger" FILES ${DEBUGGER_SRC}) add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC}) diff --git a/src/gba/gba-audio.c b/src/gba/gba-audio.c index f1bd249bd..db7ac79c0 100644 --- a/src/gba/gba-audio.c +++ b/src/gba/gba-audio.c @@ -704,6 +704,10 @@ static void _sample(struct GBAAudio* audio) { CircleBufferWrite32(&audio->left, sampleLeft); CircleBufferWrite32(&audio->right, sampleRight); unsigned produced = CircleBufferSize(&audio->left); + struct GBAThread* thread = GBAThreadGetContext(); + if (thread && thread->stream) { + thread->stream->postAudioFrame(thread->stream, sampleLeft, sampleRight); + } GBASyncProduceAudio(audio->p->sync, produced >= CircleBufferCapacity(&audio->left) / sizeof(int32_t) * 3); } diff --git a/src/gba/gba-thread.c b/src/gba/gba-thread.c index cc20d7766..da16be584 100644 --- a/src/gba/gba-thread.c +++ b/src/gba/gba-thread.c @@ -559,6 +559,9 @@ void GBASyncPostFrame(struct GBASync* sync) { GBARecordFrame(thread); } } + if (thread->stream) { + thread->stream->postVideoFrame(thread->stream, thread->renderer); + } if (thread->frameCallback) { thread->frameCallback(thread); } diff --git a/src/gba/gba-thread.h b/src/gba/gba-thread.h index 813cf0c74..8f09fc940 100644 --- a/src/gba/gba-thread.h +++ b/src/gba/gba-thread.h @@ -39,6 +39,11 @@ struct GBASync { Mutex audioBufferMutex; }; +struct GBAAVStream { + void (*postVideoFrame)(struct GBAAVStream*, struct GBAVideoRenderer* renderer); + void (*postAudioFrame)(struct GBAAVStream*, int32_t left, int32_t right); +}; + struct GBAThread { // Output enum ThreadState state; @@ -58,6 +63,7 @@ struct GBAThread { const char* fname; int activeKeys; struct GBAInputMap inputMap; + struct GBAAVStream* stream; // Run-time options int frameskip; diff --git a/src/platform/ffmpeg/ffmpeg-encoder.c b/src/platform/ffmpeg/ffmpeg-encoder.c new file mode 100644 index 000000000..e5e5838ed --- /dev/null +++ b/src/platform/ffmpeg/ffmpeg-encoder.c @@ -0,0 +1,134 @@ +#include "ffmpeg-encoder.h" + +#include "gba-video.h" + +#include + +static void _ffmpegPostVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer); +static void _ffmpegPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t right); + +bool FFmpegEncoderCreate(struct FFmpegEncoder* encoder) { + av_register_all(); + AVCodec* acodec = avcodec_find_encoder(AV_CODEC_ID_FLAC); + AVCodec* vcodec = avcodec_find_encoder(AV_CODEC_ID_HUFFYUV); + if (!acodec || !vcodec) { + return false; + } + + encoder->d.postVideoFrame = _ffmpegPostVideoFrame; + encoder->d.postAudioFrame = _ffmpegPostAudioFrame; + + encoder->currentAudioSample = 0; + encoder->currentAudioFrame = 0; + encoder->currentVideoFrame = 0; + + avformat_alloc_output_context2(&encoder->context, NULL, NULL, "test.mkv"); + + encoder->audioStream = avformat_new_stream(encoder->context, acodec); + encoder->audio = encoder->audioStream->codec; + encoder->audio->bit_rate = 128000; + encoder->audio->sample_rate = 0x8000; + encoder->audio->channels = 2; + encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO; + encoder->audio->sample_fmt = AV_SAMPLE_FMT_S16; + avcodec_open2(encoder->audio, acodec, 0); + encoder->audioFrame = av_frame_alloc(); + encoder->audioFrame->nb_samples = encoder->audio->frame_size; + encoder->audioFrame->format = encoder->audio->sample_fmt; + encoder->audioFrame->pts = 0; + encoder->audioBufferSize = av_samples_get_buffer_size(0, encoder->audio->channels, encoder->audio->frame_size, encoder->audio->sample_fmt, 0); + encoder->audioBuffer = av_malloc(encoder->audioBufferSize); + avcodec_fill_audio_frame(encoder->audioFrame, encoder->audio->channels, encoder->audio->sample_fmt, (const uint8_t*) encoder->audioBuffer, encoder->audioBufferSize, 0); + + encoder->videoStream = avformat_new_stream(encoder->context, vcodec); + encoder->video = encoder->videoStream->codec; + encoder->video->bit_rate = 4000000; + encoder->video->width = VIDEO_HORIZONTAL_PIXELS; + encoder->video->height = VIDEO_VERTICAL_PIXELS; + encoder->video->time_base = (AVRational) { VIDEO_TOTAL_LENGTH, GBA_ARM7TDMI_FREQUENCY }; + encoder->video->pix_fmt = AV_PIX_FMT_RGB24; + encoder->video->gop_size = 15; + encoder->video->max_b_frames = 0; + avcodec_open2(encoder->video, vcodec, 0); + encoder->videoFrame = av_frame_alloc(); + encoder->videoFrame->format = encoder->video->pix_fmt; + encoder->videoFrame->width = encoder->video->width; + encoder->videoFrame->height = encoder->video->height; + encoder->videoFrame->pts = 0; + av_image_alloc(encoder->videoFrame->data, encoder->videoFrame->linesize, encoder->video->width, encoder->video->height, encoder->video->pix_fmt, 32); + + if (encoder->context->oformat->flags & AVFMT_GLOBALHEADER) { + encoder->audio->flags |= CODEC_FLAG_GLOBAL_HEADER; + encoder->video->flags |= CODEC_FLAG_GLOBAL_HEADER; + } + + avio_open(&encoder->context->pb, "test.mkv", AVIO_FLAG_WRITE); + avformat_write_header(encoder->context, 0); + + return true; +} + +void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) { + struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream; + + av_frame_make_writable(encoder->audioFrame); + encoder->audioBuffer[encoder->currentAudioSample * 2] = left; + encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right; + encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base); + ++encoder->currentAudioFrame; + ++encoder->currentAudioSample; + + if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) { + return; + } + encoder->currentAudioSample = 0; + + AVPacket packet; + av_init_packet(&packet); + packet.data = 0; + packet.size = 0; + int gotData; + avcodec_encode_audio2(encoder->audio, &packet, encoder->audioFrame, &gotData); + if (gotData) { + packet.stream_index = encoder->audioStream->index; + av_interleaved_write_frame(encoder->context, &packet); + } + av_free_packet(&packet); +} + +void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* renderer) { + struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream; + uint32_t* pixels; + unsigned stride; + renderer->getPixels(renderer, &stride, (void**) &pixels); + + AVPacket packet; + + av_init_packet(&packet); + packet.data = 0; + packet.size = 0; + av_frame_make_writable(encoder->videoFrame); + encoder->videoFrame->pts = av_rescale_q(encoder->currentVideoFrame, encoder->video->time_base, encoder->videoStream->time_base); + ++encoder->currentVideoFrame; + + unsigned x, y; + for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { + for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { + uint32_t pixel = pixels[stride * y + x]; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3] = pixel; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 1] = pixel >> 8; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 2] = pixel >> 16; + } + } + + int gotData; + avcodec_encode_video2(encoder->video, &packet, encoder->videoFrame, &gotData); + if (gotData) { + if (encoder->videoStream->codec->coded_frame->key_frame) { + packet.flags |= AV_PKT_FLAG_KEY; + } + packet.stream_index = encoder->videoStream->index; + av_interleaved_write_frame(encoder->context, &packet); + } + av_free_packet(&packet); +} diff --git a/src/platform/ffmpeg/ffmpeg-encoder.h b/src/platform/ffmpeg/ffmpeg-encoder.h new file mode 100644 index 000000000..4e41fe3ad --- /dev/null +++ b/src/platform/ffmpeg/ffmpeg-encoder.h @@ -0,0 +1,30 @@ +#ifndef FFMPEG_ENCODER +#define FFMPEG_ENCODER + +#include "gba-thread.h" + +#include +#include + +struct FFmpegEncoder { + struct GBAAVStream d; + AVFormatContext* context; + + AVCodecContext* audio; + uint16_t* audioBuffer; + size_t audioBufferSize; + AVFrame* audioFrame; + size_t currentAudioSample; + int64_t currentAudioFrame; + AVStream* audioStream; + + AVCodecContext* video; + AVFrame* videoFrame; + int64_t currentVideoFrame; + AVStream* videoStream; +}; + +bool FFmpegEncoderCreate(struct FFmpegEncoder*); +void FFmpegEncoderDestroy(struct FFmpegEncoder*); + +#endif From 11c00d89589d2ecdd8c39ca684f3ee3a2fcfcf90 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Sat, 9 Aug 2014 04:17:34 -0700 Subject: [PATCH 62/72] Use FFV1 for reduced filesize --- src/platform/ffmpeg/ffmpeg-encoder.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/ffmpeg/ffmpeg-encoder.c b/src/platform/ffmpeg/ffmpeg-encoder.c index e5e5838ed..6656e07a3 100644 --- a/src/platform/ffmpeg/ffmpeg-encoder.c +++ b/src/platform/ffmpeg/ffmpeg-encoder.c @@ -10,7 +10,7 @@ static void _ffmpegPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t rig bool FFmpegEncoderCreate(struct FFmpegEncoder* encoder) { av_register_all(); AVCodec* acodec = avcodec_find_encoder(AV_CODEC_ID_FLAC); - AVCodec* vcodec = avcodec_find_encoder(AV_CODEC_ID_HUFFYUV); + AVCodec* vcodec = avcodec_find_encoder(AV_CODEC_ID_FFV1); if (!acodec || !vcodec) { return false; } @@ -46,7 +46,7 @@ bool FFmpegEncoderCreate(struct FFmpegEncoder* encoder) { encoder->video->width = VIDEO_HORIZONTAL_PIXELS; encoder->video->height = VIDEO_VERTICAL_PIXELS; encoder->video->time_base = (AVRational) { VIDEO_TOTAL_LENGTH, GBA_ARM7TDMI_FREQUENCY }; - encoder->video->pix_fmt = AV_PIX_FMT_RGB24; + encoder->video->pix_fmt = AV_PIX_FMT_BGR0; encoder->video->gop_size = 15; encoder->video->max_b_frames = 0; avcodec_open2(encoder->video, vcodec, 0); @@ -115,9 +115,9 @@ void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { uint32_t pixel = pixels[stride * y + x]; - encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3] = pixel; - encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 1] = pixel >> 8; - encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 2] = pixel >> 16; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4] = pixel >> 16; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 1] = pixel >> 8; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 2] = pixel; } } From 408223cfef5441b4f94f4f1b9bb091eaae909198 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 02:11:43 -0700 Subject: [PATCH 63/72] External support for changing codecs in FFmpeg encoder --- src/platform/ffmpeg/ffmpeg-encoder.c | 97 +++++++++++++++++++++++----- src/platform/ffmpeg/ffmpeg-encoder.h | 14 +++- 2 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/platform/ffmpeg/ffmpeg-encoder.c b/src/platform/ffmpeg/ffmpeg-encoder.c index 6656e07a3..d88caf2b1 100644 --- a/src/platform/ffmpeg/ffmpeg-encoder.c +++ b/src/platform/ffmpeg/ffmpeg-encoder.c @@ -7,26 +7,65 @@ static void _ffmpegPostVideoFrame(struct GBAAVStream*, struct GBAVideoRenderer* renderer); static void _ffmpegPostAudioFrame(struct GBAAVStream*, int32_t left, int32_t right); -bool FFmpegEncoderCreate(struct FFmpegEncoder* encoder) { +void FFmpegEncoderInit(struct FFmpegEncoder* encoder) { av_register_all(); - AVCodec* acodec = avcodec_find_encoder(AV_CODEC_ID_FLAC); - AVCodec* vcodec = avcodec_find_encoder(AV_CODEC_ID_FFV1); - if (!acodec || !vcodec) { - return false; - } encoder->d.postVideoFrame = _ffmpegPostVideoFrame; encoder->d.postAudioFrame = _ffmpegPostAudioFrame; + FFmpegEncoderSetAudio(encoder, "flac", 0); + FFmpegEncoderSetVideo(encoder, "png", 0); encoder->currentAudioSample = 0; encoder->currentAudioFrame = 0; encoder->currentVideoFrame = 0; +} - avformat_alloc_output_context2(&encoder->context, NULL, NULL, "test.mkv"); +bool FFmpegEncoderSetAudio(struct FFmpegEncoder* encoder, const char* acodec, unsigned abr) { + if (!avcodec_find_encoder_by_name(acodec)) { + return false; + } + encoder->audioCodec = acodec; + encoder->audioBitrate = abr; + return true; +} + +bool FFmpegEncoderSetVideo(struct FFmpegEncoder* encoder, const char* vcodec, unsigned vbr) { + AVCodec* codec = avcodec_find_encoder_by_name(vcodec); + if (!codec) { + return false; + } + + size_t i; + encoder->pixFormat = AV_PIX_FMT_NONE; + for (i = 0; codec->pix_fmts[i] != AV_PIX_FMT_NONE; ++i) { + if (codec->pix_fmts[i] == AV_PIX_FMT_RGB24) { + encoder->pixFormat = AV_PIX_FMT_RGB24; + break; + } + if (codec->pix_fmts[i] == AV_PIX_FMT_BGR0) { + encoder->pixFormat = AV_PIX_FMT_BGR0; + } + } + if (encoder->pixFormat == AV_PIX_FMT_NONE) { + return false; + } + encoder->videoCodec = vcodec; + encoder->videoBitrate = vbr; + return true; +} + +bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { + AVCodec* acodec = avcodec_find_encoder_by_name(encoder->audioCodec); + AVCodec* vcodec = avcodec_find_encoder_by_name(encoder->videoCodec); + if (!acodec || !vcodec) { + return false; + } + + avformat_alloc_output_context2(&encoder->context, 0, 0, outfile); encoder->audioStream = avformat_new_stream(encoder->context, acodec); encoder->audio = encoder->audioStream->codec; - encoder->audio->bit_rate = 128000; + encoder->audio->bit_rate = encoder->audioBitrate; encoder->audio->sample_rate = 0x8000; encoder->audio->channels = 2; encoder->audio->channel_layout = AV_CH_LAYOUT_STEREO; @@ -42,11 +81,11 @@ bool FFmpegEncoderCreate(struct FFmpegEncoder* encoder) { encoder->videoStream = avformat_new_stream(encoder->context, vcodec); encoder->video = encoder->videoStream->codec; - encoder->video->bit_rate = 4000000; + encoder->video->bit_rate = encoder->videoBitrate; encoder->video->width = VIDEO_HORIZONTAL_PIXELS; encoder->video->height = VIDEO_VERTICAL_PIXELS; encoder->video->time_base = (AVRational) { VIDEO_TOTAL_LENGTH, GBA_ARM7TDMI_FREQUENCY }; - encoder->video->pix_fmt = AV_PIX_FMT_BGR0; + encoder->video->pix_fmt = encoder->pixFormat; encoder->video->gop_size = 15; encoder->video->max_b_frames = 0; avcodec_open2(encoder->video, vcodec, 0); @@ -62,12 +101,25 @@ bool FFmpegEncoderCreate(struct FFmpegEncoder* encoder) { encoder->video->flags |= CODEC_FLAG_GLOBAL_HEADER; } - avio_open(&encoder->context->pb, "test.mkv", AVIO_FLAG_WRITE); + avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE); avformat_write_header(encoder->context, 0); return true; } +void FFmpegEncoderClose(struct FFmpegEncoder* encoder) { + av_write_trailer(encoder->context); + avio_close(encoder->context->pb); + + av_free(encoder->audioBuffer); + av_frame_free(&encoder->audioFrame); + avcodec_close(encoder->audio); + + av_frame_free(&encoder->videoFrame); + avcodec_close(encoder->video); + avformat_free_context(encoder->context); +} + void _ffmpegPostAudioFrame(struct GBAAVStream* stream, int32_t left, int32_t right) { struct FFmpegEncoder* encoder = (struct FFmpegEncoder*) stream; @@ -112,12 +164,23 @@ void _ffmpegPostVideoFrame(struct GBAAVStream* stream, struct GBAVideoRenderer* ++encoder->currentVideoFrame; unsigned x, y; - for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { - for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { - uint32_t pixel = pixels[stride * y + x]; - encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4] = pixel >> 16; - encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 1] = pixel >> 8; - encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 2] = pixel; + if (encoder->videoFrame->format == AV_PIX_FMT_BGR0) { + for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { + for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { + uint32_t pixel = pixels[stride * y + x]; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4] = pixel >> 16; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 1] = pixel >> 8; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 4 + 2] = pixel; + } + } + } else if (encoder->videoFrame->format == AV_PIX_FMT_RGB24) { + for (y = 0; y < VIDEO_VERTICAL_PIXELS; ++y) { + for (x = 0; x < VIDEO_HORIZONTAL_PIXELS; ++x) { + uint32_t pixel = pixels[stride * y + x]; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3] = pixel; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 1] = pixel >> 8; + encoder->videoFrame->data[0][y * encoder->videoFrame->linesize[0] + x * 3 + 2] = pixel >> 16; + } } } diff --git a/src/platform/ffmpeg/ffmpeg-encoder.h b/src/platform/ffmpeg/ffmpeg-encoder.h index 4e41fe3ad..89ff16eb9 100644 --- a/src/platform/ffmpeg/ffmpeg-encoder.h +++ b/src/platform/ffmpeg/ffmpeg-encoder.h @@ -10,6 +10,12 @@ struct FFmpegEncoder { struct GBAAVStream d; AVFormatContext* context; + unsigned audioBitrate; + const char* audioCodec; + + unsigned videoBitrate; + const char* videoCodec; + AVCodecContext* audio; uint16_t* audioBuffer; size_t audioBufferSize; @@ -19,12 +25,16 @@ struct FFmpegEncoder { AVStream* audioStream; AVCodecContext* video; + enum AVPixelFormat pixFormat; AVFrame* videoFrame; int64_t currentVideoFrame; AVStream* videoStream; }; -bool FFmpegEncoderCreate(struct FFmpegEncoder*); -void FFmpegEncoderDestroy(struct FFmpegEncoder*); +void FFmpegEncoderInit(struct FFmpegEncoder*); +bool FFmpegEncoderSetAudio(struct FFmpegEncoder*, const char* acodec, unsigned abr); +bool FFmpegEncoderSetVideo(struct FFmpegEncoder*, const char* vcodec, unsigned vbr); +bool FFmpegEncoderOpen(struct FFmpegEncoder*, const char* outfile); +void FFmpegEncoderClose(struct FFmpegEncoder*); #endif From 43de75c973bb314e45556d87fa528b1505754910 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 21:53:32 -0700 Subject: [PATCH 64/72] Detect libPNG and zlib --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b0fc3c808..b9db1f3ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,8 +86,12 @@ if(USE_GDB_STUB) endif() source_group("ARM debugger" FILES ${DEBUGGER_SRC}) +find_package(PNG REQUIRED) +find_package(ZLIB REQUIRED) +list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES}) + add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC}) -target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB} png) +target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB}) if(BUILD_SDL) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl) From 4fe48ad820afd0150b138b6495cc31c269e4ada9 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 21:58:48 -0700 Subject: [PATCH 65/72] Detect libedit --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9db1f3ef..a913f49b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,8 @@ if(USE_CLI_DEBUGGER AND NOT WIN32) add_definitions(-DUSE_CLI_DEBUGGER) list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c") list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/parser.c") - set(DEBUGGER_LIB "edit") + pkg_search_module(EDIT libedit REQUIRED) + set(DEBUGGER_LIB ${EDIT_LIBRARIES}) else() set(DEBUGGER_LIB "") endif() From e88aed2597ec1389c945d40eaff9302bfcc1eeb0 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 22:02:34 -0700 Subject: [PATCH 66/72] Detect libedit --- CMakeLists.txt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a913f49b1..05e21aac5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,13 +70,17 @@ endif() set(DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/debugger.c;${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c") -if(USE_CLI_DEBUGGER AND NOT WIN32) - # Win32 doesn't have a usable command line, nor libedit, so this is useless on Windows - add_definitions(-DUSE_CLI_DEBUGGER) - list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c") - list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/parser.c") - pkg_search_module(EDIT libedit REQUIRED) - set(DEBUGGER_LIB ${EDIT_LIBRARIES}) +if(USE_CLI_DEBUGGER) + pkg_search_module(EDIT libedit) + if(EDIT_FOUND) + add_definitions(-DUSE_CLI_DEBUGGER) + list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c") + list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/parser.c") + set(DEBUGGER_LIB ${EDIT_LIBRARIES}) + else() + message(WARNING "Could not find libedit for CLI debugger support") + set(USE_CLI_DEBUGGER OFF) + endif() else() set(DEBUGGER_LIB "") endif() From 09c49d5f3bb5cd9def44ec4c89d04caf8b6ffab1 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 22:06:03 -0700 Subject: [PATCH 67/72] Clean up list usage in CMakeLists --- CMakeLists.txt | 16 ++++++++-------- src/platform/sdl/CMakeLists.txt | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 05e21aac5..dae020b48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ file(GLOB GBA_SRC ${CMAKE_SOURCE_DIR}/src/gba/*.c) file(GLOB UTIL_SRC ${CMAKE_SOURCE_DIR}/src/util/*.[cS]) file(GLOB VFS_SRC ${CMAKE_SOURCE_DIR}/src/util/vfs/*.c) file(GLOB RENDERER_SRC ${CMAKE_SOURCE_DIR}/src/gba/renderers/video-software.c) -set(UTIL_SRC ${UTIL_SRC};${CMAKE_SOURCE_DIR}/src/platform/commandline.c) +list(APPEND UTIL_SRC ${CMAKE_SOURCE_DIR}/src/platform/commandline.c) source_group("ARM core" FILES ${ARM_SRC}) source_group("GBA board" FILES ${GBA_SRC} ${RENDERER_SRC}) source_group("Utilities" FILES ${UTIL_SRC} ${VFS_SRC}}) @@ -51,12 +51,12 @@ endif() if(WIN32) add_definitions(-D_WIN32_WINNT=0x0600) - set(OS_LIB "${OS_LIB};Ws2_32") + list(APPEND OS_LIB Ws2_32) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/windows/*.c) source_group("Windows-specific code" FILES ${OS_SRC}) else() add_definitions(-DUSE_PTHREADS) - set(OS_LIB "${OS_LIB};pthread") + list(APPEND OS_LIB pthread) file(GLOB OS_SRC ${CMAKE_SOURCE_DIR}/src/platform/posix/*.c) source_group("POSIX-specific code" FILES ${OS_SRC}) endif() @@ -68,14 +68,14 @@ if(BUILD_BBB OR BUILD_RASPI) endif() endif() -set(DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/debugger.c;${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c") +set(DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/debugger.c ${CMAKE_SOURCE_DIR}/src/debugger/memory-debugger.c) if(USE_CLI_DEBUGGER) pkg_search_module(EDIT libedit) if(EDIT_FOUND) add_definitions(-DUSE_CLI_DEBUGGER) - list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c") - list(APPEND DEBUGGER_SRC "${CMAKE_SOURCE_DIR}/src/debugger/parser.c") + list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/cli-debugger.c) + list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/parser.c) set(DEBUGGER_LIB ${EDIT_LIBRARIES}) else() message(WARNING "Could not find libedit for CLI debugger support") @@ -87,7 +87,7 @@ endif() if(USE_GDB_STUB) add_definitions(-DUSE_GDB_STUB) - set(DEBUGGER_SRC "${DEBUGGER_SRC};${CMAKE_SOURCE_DIR}/src/debugger/gdb-stub.c") + list(APPEND DEBUGGER_SRC ${CMAKE_SOURCE_DIR}/src/debugger/gdb-stub.c) endif() source_group("ARM debugger" FILES ${DEBUGGER_SRC}) @@ -105,7 +105,7 @@ endif() if(BUILD_PERF) set(PERF_SRC ${CMAKE_SOURCE_DIR}/src/platform/perf-main.c) if(UNIX AND NOT APPLE) - set(PERF_LIB "${PERF_LIB};rt") + list(APPEND PERF_LIB rt) endif() add_executable(${BINARY_NAME}-perf ${PERF_SRC}) diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 67c1c9b70..43f8d987d 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -17,7 +17,7 @@ if(SDL_VERSION EQUAL "1.2" OR NOT SDL2_FOUND) endif() file(GLOB PLATFORM_SRC ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-*.c) -set(PLATFORM_LIBRARY "${SDL_LIBRARY};${SDLMAIN_LIBRARY}") +set(PLATFORM_LIBRARY ${SDL_LIBRARY} ${SDLMAIN_LIBRARY}) include_directories(${CMAKE_SOURCE_DIR}/src/platform/sdl ${SDL_INCLUDE_DIR}) if(BUILD_RASPI) From 9ed4fad33cf1a9cef4f03ee5682bb50df6d9b428 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 22:18:06 -0700 Subject: [PATCH 68/72] Add library versions and installation targets --- CMakeLists.txt | 9 +++++++++ src/platform/sdl/CMakeLists.txt | 2 ++ 2 files changed, 11 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index dae020b48..60951b66d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,12 @@ include_directories(${CMAKE_SOURCE_DIR}/src/arm) include_directories(${CMAKE_SOURCE_DIR}/src/gba) include_directories(${CMAKE_SOURCE_DIR}/src) +set(LIB_VERSION_MAJOR 0) +set(LIB_VERSION_MINOR 1) +set(LIB_VERSION_PATCH 0) +set(LIB_VERSION_ABI 0.1) +set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) + set(BUILD_PGO CACHE BOOL "Build with profiling-guided optimization") set(PGO_STAGE_2 CACHE BOOL "Rebuild for profiling-guided optimization after profiles have been generated") set(PGO_DIR "/tmp/gba-pgo/" CACHE PATH "Profiling-guided optimization profiles path") @@ -97,6 +103,8 @@ list(APPEND DEPENDENCY_LIB ${PNG_LIBRARIES} ${ZLIB_LIBRARIES}) add_library(${BINARY_NAME} SHARED ${ARM_SRC} ${GBA_SRC} ${DEBUGGER_SRC} ${RENDERER_SRC} ${UTIL_SRC} ${VFS_SRC} ${OS_SRC}) target_link_libraries(${BINARY_NAME} m ${DEBUGGER_LIB} ${OS_LIB} ${DEPENDENCY_LIB}) +install(TARGETS ${BINARY_NAME} DESTINATION lib) +set_target_properties(${BINARY_NAME} PROPERTIES VERSION ${LIB_VERSION_STRING} SOVERSION ${LIB_VERSION_ABI}) if(BUILD_SDL) add_subdirectory(${CMAKE_SOURCE_DIR}/src/platform/sdl ${CMAKE_BINARY_DIR}/sdl) @@ -110,4 +118,5 @@ if(BUILD_PERF) add_executable(${BINARY_NAME}-perf ${PERF_SRC}) target_link_libraries(${BINARY_NAME}-perf ${BINARY_NAME} ${PERF_LIB}) + install(TARGETS ${BINARY_NAME}-perf DESTINATION bin) endif() diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 43f8d987d..ff01d4cbf 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -26,6 +26,7 @@ if(BUILD_RASPI) set(EGL_LIBRARY "-lEGL -lGLESv2 -lbcm_host") add_executable(${BINARY_NAME}-rpi ${PLATFORM_SRC} ${EGL_MAIN_SRC}) target_link_libraries(${BINARY_NAME}-rpi ${BINARY_NAME} ${PLATFORM_LIBRARY} ${EGL_LIBRARY}) + install(TARGETS ${BINARY_NAME}-rpi DESTINATION bin) endif() if(BUILD_BBB OR BUILD_RASPI OR NOT BUILD_GL) @@ -39,3 +40,4 @@ endif() add_executable(${BINARY_NAME}-sdl WIN32 ${PLATFORM_SRC} ${MAIN_SRC}) target_link_libraries(${BINARY_NAME}-sdl ${BINARY_NAME} ${PLATFORM_LIBRARY} ${OPENGL_LIBRARY}) set_target_properties(${BINARY_NAME}-sdl PROPERTIES OUTPUT_NAME ${BINARY_NAME}) +install(TARGETS ${BINARY_NAME}-sdl DESTINATION bin) From 9ac58c69968c2d4a166b74f486fd0b8086cdbc55 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 22:28:39 -0700 Subject: [PATCH 69/72] Rename project to mGBA --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be026fa28..ff71d11a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 2.6) -project(GBAc) -set(BINARY_NAME gbac CACHE INTERNAL "Name of output binaries") +project(mGBA) +set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries") set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Wextra -std=gnu99") set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -Wextra -std=gnu99") set(USE_CLI_DEBUGGER ON CACHE BOOL "Whether or not to enable the CLI-mode ARM debugger") From 2805cc5fea6605916d020a5bd7f7f448fd2bc553 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 22:50:24 -0700 Subject: [PATCH 70/72] Fix gamepad input --- src/gba/gba-input.c | 1 + src/platform/sdl/sdl-events.c | 3 --- src/platform/sdl/sdl-events.h | 3 +++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gba/gba-input.c b/src/gba/gba-input.c index 4641626c6..efa1faf2d 100644 --- a/src/gba/gba-input.c +++ b/src/gba/gba-input.c @@ -75,6 +75,7 @@ void GBAInputBindKey(struct GBAInputMap* map, uint32_t type, int key, enum GBAKe map->maps[m].type = 0; map->maps[m].map = 0; } + map->numMaps *= 2; impl = &map->maps[m]; impl->type = type; impl->map = calloc(GBA_KEY_MAX, sizeof(enum GBAKey)); diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index d664a52ef..bf581e2ad 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -14,9 +14,6 @@ #define GUI_MOD KMOD_CTRL #endif -#define SDL_BINDING_KEY 0x53444C4B -#define SDL_BINDING_BUTTON 0x53444C42 - bool GBASDLInitEvents(struct GBASDLEvents* context) { if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) { return false; diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index a280aae69..42010c7bf 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -7,6 +7,9 @@ #include +#define SDL_BINDING_KEY 0x53444C4B +#define SDL_BINDING_BUTTON 0x53444C42 + struct GBAVideoSoftwareRenderer; struct GBASDLEvents { From 94d091b6ed949fc117d8e0f95978ce720f97833b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 23:19:13 -0700 Subject: [PATCH 71/72] Use DualShock 3 button mapping for now --- src/platform/sdl/sdl-events.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/platform/sdl/sdl-events.c b/src/platform/sdl/sdl-events.c index bf581e2ad..346e98a37 100644 --- a/src/platform/sdl/sdl-events.c +++ b/src/platform/sdl/sdl-events.c @@ -35,12 +35,16 @@ bool GBASDLInitEvents(struct GBASDLEvents* context) { GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_LEFT, GBA_KEY_LEFT); GBAInputBindKey(context->bindings, SDL_BINDING_KEY, SDLK_RIGHT, GBA_KEY_RIGHT); - GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 2, GBA_KEY_A); - GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 1, GBA_KEY_B); - GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 6, GBA_KEY_L); - GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 7, GBA_KEY_R); - GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 8, GBA_KEY_START); - GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 9, GBA_KEY_SELECT); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 13, GBA_KEY_A); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 14, GBA_KEY_B); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 10, GBA_KEY_L); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 11, GBA_KEY_R); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 3, GBA_KEY_START); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 0, GBA_KEY_SELECT); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 4, GBA_KEY_UP); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 6, GBA_KEY_DOWN); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 7, GBA_KEY_LEFT); + GBAInputBindKey(context->bindings, SDL_BINDING_BUTTON, 5, GBA_KEY_RIGHT); return true; } From 30fa9d8675adbc9f0bb147949a77016f6e9ce11b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 12 Aug 2014 23:23:14 -0700 Subject: [PATCH 72/72] Remove prototype for removed function --- src/platform/sdl/sdl-events.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platform/sdl/sdl-events.h b/src/platform/sdl/sdl-events.h index 42010c7bf..05debc576 100644 --- a/src/platform/sdl/sdl-events.h +++ b/src/platform/sdl/sdl-events.h @@ -27,6 +27,4 @@ void GBASDLDeinitEvents(struct GBASDLEvents*); void GBASDLHandleEvent(struct GBAThread* context, struct GBASDLEvents* sdlContext, const union SDL_Event* event); -enum GBAKey GBASDLMapButtonToKey(int button); - #endif