Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2020-02-17 17:27:09 -08:00
commit 4546493039
56 changed files with 5686 additions and 1333 deletions

16
CHANGES
View File

@ -24,14 +24,30 @@ Emulation fixes:
- ARM: Fix STR storing PC after address calculation - ARM: Fix STR storing PC after address calculation
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320) - GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
- GBA Memory: Misaligned SRAM writes are ignored - GBA Memory: Misaligned SRAM writes are ignored
- GBA Memory: Improve gamepak prefetch timing
- GBA Serialize: Fix serializing DMA transfer register - GBA Serialize: Fix serializing DMA transfer register
- GBA Serialize: Fix audio serialization for desynced FIFOs - GBA Serialize: Fix audio serialization for desynced FIFOs
- GBA Serialize: Fix audio DMA timing deserialization - GBA Serialize: Fix audio DMA timing deserialization
- GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630) - GBA Video: Fix OAM not invalidating after reset (fixes mgba.io/i/1630)
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
- GBA Video: Fix Hblank timing
- GBA Video: Fix backdrop blending on lines without sprites (fixes mgba.io/i/1647)
- GBA Video: Fix OpenGL sprite flag priority
Other fixes: Other fixes:
- Core: Fix race condition initializing thread proxy
- Qt: Only dynamically reset video scale if a game is running - Qt: Only dynamically reset video scale if a game is running
- Qt: Fix race condition with proxied video events - Qt: Fix race condition with proxied video events
- Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642)
- Qt: Fix color selection in asset view (fixes mgba.io/i/1648)
- Qt: Fix missing OSD messages
- Qt: Fix crash unloading shaders
- Qt: Fix toggled actions on gamepads (fixes mgba.io/i/1650)
- Qt: Fix extraneous dialog (fixes mgba.io/i/1654)
- Util: Fix crash reading invalid ELFs
Misc:
- Qt: Renderer can be changed while a game is running
- Qt: Fix non-SDL build (fixes mgba.io/i/1656)
- Switch: Make OpenGL scale adjustable while running
0.8.0: (2020-01-21) 0.8.0: (2020-01-21)
Features: Features:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -91,7 +91,6 @@ struct GBA {
struct mRTCSource* rtcSource; struct mRTCSource* rtcSource;
struct mRumble* rumble; struct mRumble* rumble;
struct GBARRContext* rr;
bool isPristine; bool isPristine;
size_t pristineRomSize; size_t pristineRomSize;
size_t yankedRomSize; size_t yankedRomSize;

View File

@ -44,7 +44,6 @@ struct GBAVideoGLAffine {
struct GBAVideoGLBackground { struct GBAVideoGLBackground {
GLuint fbo; GLuint fbo;
GLuint tex; GLuint tex;
GLuint flags;
unsigned index; unsigned index;
int enabled; int enabled;
@ -96,7 +95,6 @@ enum {
GBA_GL_BG_CHARBASE, GBA_GL_BG_CHARBASE,
GBA_GL_BG_SIZE, GBA_GL_BG_SIZE,
GBA_GL_BG_OFFSET, GBA_GL_BG_OFFSET,
GBA_GL_BG_INFLAGS,
GBA_GL_BG_TRANSFORM, GBA_GL_BG_TRANSFORM,
GBA_GL_BG_RANGE, GBA_GL_BG_RANGE,
GBA_GL_BG_MOSAIC, GBA_GL_BG_MOSAIC,

View File

@ -1,87 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef RR_MGM_H
#define RR_MGM_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/internal/gba/rr/rr.h>
struct GBA;
struct VDir;
struct VFile;
enum GBAMGMTag {
// Playback tags
TAG_INVALID = 0x00,
TAG_INPUT = 0x01,
TAG_FRAME = 0x02,
TAG_LAG = 0x03,
TAG_RESET = 0x04,
// Stream chunking tags
TAG_BEGIN = 0x10,
TAG_END = 0x11,
TAG_PREVIOUSLY = 0x12,
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 = 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,
TAG_COMMENT = 0x31,
TAG_EOF = INT_MAX
};
struct GBAMGMContext {
struct GBARRContext d;
// Playback state
bool isPlaying;
bool autorecord;
// Recording state
bool isRecording;
bool inputThisFrame;
// Metadata
uint32_t streamId;
uint32_t maxStreamId;
off_t maxStreamIdOffset;
off_t initFromOffset;
off_t rrCountOffset;
// Streaming state
struct VDir* streamDir;
struct VFile* metadataFile;
struct VFile* movieStream;
uint16_t currentInput;
enum GBAMGMTag peekedTag;
uint32_t nextTime;
uint32_t previously;
};
void GBAMGMContextCreate(struct GBAMGMContext*);
bool GBAMGMSetStream(struct GBAMGMContext* mgm, struct VDir* stream);
bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom);
CXX_GUARD_END
#endif

View File

@ -1,65 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GBA_RR_H
#define GBA_RR_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/core/log.h>
#include <mgba/internal/gba/serialize.h>
struct VFile;
mLOG_DECLARE_CATEGORY(GBA_RR);
enum GBARRInitFrom {
INIT_EX_NIHILO = 0,
INIT_FROM_SAVEGAME = 1,
INIT_FROM_SAVESTATE = 2,
INIT_FROM_BOTH = 3,
};
struct GBARRContext {
void (*destroy)(struct GBARRContext*);
bool (*startPlaying)(struct GBARRContext*, bool autorecord);
void (*stopPlaying)(struct GBARRContext*);
bool (*startRecording)(struct GBARRContext*);
void (*stopRecording)(struct GBARRContext*);
bool (*isPlaying)(const struct GBARRContext*);
bool (*isRecording)(const struct GBARRContext*);
void (*nextFrame)(struct GBARRContext*);
void (*logInput)(struct GBARRContext*, uint16_t input);
uint16_t (*queryInput)(struct GBARRContext*);
bool (*queryReset)(struct GBARRContext*);
void (*stateSaved)(struct GBARRContext*, struct GBASerializedState*);
void (*stateLoaded)(struct GBARRContext*, const struct GBASerializedState*);
struct VFile* (*openSavedata)(struct GBARRContext* mgm, int flags);
struct VFile* (*openSavestate)(struct GBARRContext* mgm, int flags);
uint32_t frames;
uint32_t lagFrames;
enum GBARRInitFrom initFrom;
uint32_t rrCount;
struct VFile* savedata;
};
void GBARRDestroy(struct GBARRContext*);
void GBARRInitRecord(struct GBA*);
void GBARRInitPlay(struct GBA*);
CXX_GUARD_END
#endif

View File

@ -1,30 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GBA_VBM_H
#define GBA_VBM_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/internal/gba/rr/rr.h>
struct GBAVBMContext {
struct GBARRContext d;
bool isPlaying;
struct VFile* vbmFile;
int32_t inputOffset;
};
void GBAVBMContextCreate(struct GBAVBMContext*);
bool GBAVBMSetStream(struct GBAVBMContext*, struct VFile*);
CXX_GUARD_END
#endif

View File

@ -92,7 +92,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | bits 6 - 7: Reserved * | bits 6 - 7: Reserved
* 0x001E0 - 0x001FF: Video miscellaneous state * 0x001E0 - 0x001FF: Video miscellaneous state
* | 0x001E0 - 0x001E3: Next event * | 0x001E0 - 0x001E3: Next event
* | 0x001E4 - 0x001FB: Reserved * | 0x001E4 - 0x001F7: Reserved
* | 0x001F8 - 0x001FB: Miscellaneous flags
* | 0x001FC - 0x001FF: Frame counter * | 0x001FC - 0x001FF: Frame counter
* 0x00200 - 0x00213: Timer 0 * 0x00200 - 0x00213: Timer 0
* | 0x00200 - 0x00201: Reload value * | 0x00200 - 0x00201: Reload value
@ -192,8 +193,7 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x002F4 - 0x002F7: GBA BIOS bus prefetch * | 0x002F4 - 0x002F7: GBA BIOS bus prefetch
* | 0x002F8 - 0x002FB: CPU prefecth (decode slot) * | 0x002F8 - 0x002FB: CPU prefecth (decode slot)
* | 0x002FC - 0x002FF: CPU prefetch (fetch slot) * | 0x002FC - 0x002FF: CPU prefetch (fetch slot)
* 0x00300 - 0x00303: Associated movie stream ID for record/replay (or 0 if no stream) * 0x00300 - 0x00317: Reserved (leave zero)
* 0x00304 - 0x00317: Savestate creation time (usec since 1970)
* 0x00318 - 0x0031B: Last prefetched program counter * 0x00318 - 0x0031B: Last prefetched program counter
* 0x0031C - 0x0031F: Miscellaneous flags * 0x0031C - 0x0031F: Miscellaneous flags
* | bit 0: Is CPU halted? * | bit 0: Is CPU halted?
@ -210,6 +210,9 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* Total size: 0x61000 (397,312) bytes * Total size: 0x61000 (397,312) bytes
*/ */
DECL_BITFIELD(GBASerializedVideoFlags, uint32_t);
DECL_BITS(GBASerializedVideoFlags, Mode, 0, 2);
DECL_BITFIELD(GBASerializedHWFlags1, uint16_t); DECL_BITFIELD(GBASerializedHWFlags1, uint16_t);
DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0); DECL_BIT(GBASerializedHWFlags1, ReadWrite, 0);
DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1); DECL_BIT(GBASerializedHWFlags1, GyroEdge, 1);
@ -267,7 +270,8 @@ struct GBASerializedState {
struct { struct {
int32_t nextEvent; int32_t nextEvent;
int32_t reserved[6]; int32_t reserved[5];
GBASerializedVideoFlags flags;
int32_t frameCounter; int32_t frameCounter;
} video; } video;
@ -322,8 +326,7 @@ struct GBASerializedState {
uint32_t biosPrefetch; uint32_t biosPrefetch;
uint32_t cpuPrefetch[2]; uint32_t cpuPrefetch[2];
uint32_t associatedStreamId; uint32_t reservedCpu[6];
uint32_t reservedRr[5];
uint32_t lastPrefetchedPc; uint32_t lastPrefetchedPc;
GBASerializedMiscFlags miscFlags; GBASerializedMiscFlags miscFlags;

View File

@ -18,8 +18,9 @@ mLOG_DECLARE_CATEGORY(GBA_VIDEO);
enum { enum {
VIDEO_HBLANK_PIXELS = 68, VIDEO_HBLANK_PIXELS = 68,
VIDEO_HDRAW_LENGTH = 1006, VIDEO_HDRAW_LENGTH = 960,
VIDEO_HBLANK_LENGTH = 226, VIDEO_HBLANK_LENGTH = 272,
VIDEO_HBLANK_FLIP = 46,
VIDEO_HORIZONTAL_LENGTH = 1232, VIDEO_HORIZONTAL_LENGTH = 1232,
VIDEO_VBLANK_PIXELS = 68, VIDEO_VBLANK_PIXELS = 68,

View File

@ -489,16 +489,16 @@ static void DSVideoSoftwareRendererDrawGBAScanline(struct GBAVideoRenderer* rend
} }
} }
if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) { if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) {
++softwareRenderer->bg[0].enabled; ++softwareRenderer->bg[0].enabled;
} }
if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) { if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) {
++softwareRenderer->bg[1].enabled; ++softwareRenderer->bg[1].enabled;
} }
if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) { if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) {
++softwareRenderer->bg[2].enabled; ++softwareRenderer->bg[2].enabled;
} }
if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) { if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) {
++softwareRenderer->bg[3].enabled; ++softwareRenderer->bg[3].enabled;
} }

View File

@ -137,8 +137,10 @@ static void _postEvent(struct mVideoLogger* logger, enum mVideoLoggerEvent event
struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger;
MutexLock(&proxyRenderer->mutex); MutexLock(&proxyRenderer->mutex);
proxyRenderer->event = event; proxyRenderer->event = event;
ConditionWake(&proxyRenderer->toThreadCond); while (proxyRenderer->event) {
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
}
MutexUnlock(&proxyRenderer->mutex); MutexUnlock(&proxyRenderer->mutex);
} }
@ -179,6 +181,7 @@ static THREAD_ENTRY _proxyThread(void* logger) {
ThreadSetName("Proxy Renderer Thread"); ThreadSetName("Proxy Renderer Thread");
MutexLock(&proxyRenderer->mutex); MutexLock(&proxyRenderer->mutex);
ConditionWake(&proxyRenderer->fromThreadCond);
while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) {
ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex);
if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) {

View File

@ -39,10 +39,7 @@ set(SIO_FILES
set(EXTRA_FILES set(EXTRA_FILES
extra/audio-mixer.c extra/audio-mixer.c
extra/battlechip.c extra/battlechip.c
extra/proxy.c extra/proxy.c)
rr/mgm.c
rr/rr.c
rr/vbm.c)
set(DEBUGGER_FILES set(DEBUGGER_FILES
debugger/cli.c) debugger/cli.c)

View File

@ -355,8 +355,9 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
} }
return; return;
} }
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
struct GBACore* gbacore = (struct GBACore*) core; struct GBACore* gbacore = (struct GBACore*) core;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (strcmp("videoScale", option) == 0) { if (strcmp("videoScale", option) == 0) {
if (config != &core->config) { if (config != &core->config) {
mCoreConfigCopyValue(&core->config, config, "videoScale"); mCoreConfigCopyValue(&core->config, config, "videoScale");
@ -369,6 +370,31 @@ static void _GBACoreReloadConfigOption(struct mCore* core, const char* option, c
return; return;
} }
#endif #endif
if (strcmp("hwaccelVideo", option) == 0) {
struct GBAVideoRenderer* renderer = NULL;
if (gbacore->renderer.outputBuffer) {
renderer = &gbacore->renderer.d;
}
int fakeBool;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
renderer = &gbacore->glRenderer.d;
} else {
gbacore->glRenderer.scale = 1;
}
#endif
#ifndef MINIMAL_CORE
if (renderer && core->videoLogger) {
gbacore->proxyRenderer.logger = core->videoLogger;
GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer);
renderer = &gbacore->proxyRenderer.d;
}
#endif
if (renderer) {
GBAVideoAssociateRenderer(&gba->video, renderer);
}
}
} }
static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) { static void _GBACoreDesiredVideoDimensions(struct mCore* core, unsigned* width, unsigned* height) {
@ -540,8 +566,10 @@ static void _GBACoreReset(struct mCore* core) {
int fakeBool; int fakeBool;
#if defined(BUILD_GLES2) || defined(BUILD_GLES3) #if defined(BUILD_GLES2) || defined(BUILD_GLES3)
if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) { if (gbacore->glRenderer.outputTex != (unsigned) -1 && mCoreConfigGetIntValue(&core->config, "hwaccelVideo", &fakeBool) && fakeBool) {
renderer = &gbacore->glRenderer.d;
mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale); mCoreConfigGetIntValue(&core->config, "videoScale", &gbacore->glRenderer.scale);
renderer = &gbacore->glRenderer.d;
} else {
gbacore->glRenderer.scale = 1;
} }
#endif #endif
#ifndef DISABLE_THREADING #ifndef DISABLE_THREADING

View File

@ -157,8 +157,8 @@ void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
if (!proxyRenderer->logger->block) { if (!proxyRenderer->logger->block) {
proxyRenderer->backend->deinit(proxyRenderer->backend); proxyRenderer->backend->deinit(proxyRenderer->backend);
} else { } else {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
} }
mVideoLoggerRendererDeinit(proxyRenderer->logger); mVideoLoggerRendererDeinit(proxyRenderer->logger);

View File

@ -13,7 +13,6 @@
#include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/overrides.h> #include <mgba/internal/gba/overrides.h>
#include <mgba/internal/gba/rr/rr.h>
#include <mgba-util/patch.h> #include <mgba-util/patch.h>
#include <mgba-util/crc32.h> #include <mgba-util/crc32.h>
@ -96,7 +95,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->luminanceSource = 0; gba->luminanceSource = 0;
gba->rtcSource = 0; gba->rtcSource = 0;
gba->rumble = 0; gba->rumble = 0;
gba->rr = 0;
gba->romVf = 0; gba->romVf = 0;
gba->biosVf = 0; gba->biosVf = 0;
@ -170,7 +168,6 @@ void GBADestroy(struct GBA* gba) {
GBAVideoDeinit(&gba->video); GBAVideoDeinit(&gba->video);
GBAAudioDeinit(&gba->audio); GBAAudioDeinit(&gba->audio);
GBASIODeinit(&gba->sio); GBASIODeinit(&gba->sio);
gba->rr = 0;
mTimingDeinit(&gba->timing); mTimingDeinit(&gba->timing);
mCoreCallbacksListDeinit(&gba->coreCallbacks); mCoreCallbacksListDeinit(&gba->coreCallbacks);
} }
@ -196,10 +193,8 @@ void GBAReset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = SP_BASE_SYSTEM; cpu->gprs[ARM_SP] = SP_BASE_SYSTEM;
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
if (!gba->rr || (!gba->rr->isPlaying(gba->rr) && !gba->rr->isRecording(gba->rr))) { gba->memory.savedata.maskWriteback = false;
gba->memory.savedata.maskWriteback = false; GBASavedataUnmask(&gba->memory.savedata);
GBASavedataUnmask(&gba->memory.savedata);
}
gba->cpuBlocked = false; gba->cpuBlocked = false;
gba->earlyExit = false; gba->earlyExit = false;
@ -812,10 +807,6 @@ void GBAFrameStarted(struct GBA* gba) {
void GBAFrameEnded(struct GBA* gba) { void GBAFrameEnded(struct GBA* gba) {
GBASavedataClean(&gba->memory.savedata, gba->video.frameCounter); GBASavedataClean(&gba->memory.savedata, gba->video.frameCounter);
if (gba->rr) {
gba->rr->nextFrame(gba->rr);
}
if (gba->cpu->components && gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) { if (gba->cpu->components && gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]) {
struct mCheatDevice* device = (struct mCheatDevice*) gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE]; struct mCheatDevice* device = (struct mCheatDevice*) gba->cpu->components[CPU_COMPONENT_CHEAT_DEVICE];
size_t i; size_t i;

View File

@ -8,7 +8,6 @@
#include <mgba/internal/arm/macros.h> #include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/dma.h> #include <mgba/internal/gba/dma.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/rr/rr.h>
#include <mgba/internal/gba/serialize.h> #include <mgba/internal/gba/serialize.h>
mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O", "gba.io"); mLOG_DEFINE_CATEGORY(GBA_IO, "GBA I/O", "gba.io");
@ -734,31 +733,24 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
} }
} }
} }
if (gba->rr && gba->rr->isPlaying(gba->rr)) { uint16_t input = 0;
return 0x3FF ^ gba->rr->queryInput(gba->rr); if (gba->keyCallback) {
} else { input = gba->keyCallback->readKeys(gba->keyCallback);
uint16_t input = 0; if (gba->keySource) {
if (gba->keyCallback) { *gba->keySource = input;
input = gba->keyCallback->readKeys(gba->keyCallback);
if (gba->keySource) {
*gba->keySource = input;
}
} else if (gba->keySource) {
input = *gba->keySource;
if (!gba->allowOpposingDirections) {
unsigned rl = input & 0x030;
unsigned ud = input & 0x0C0;
input &= 0x30F;
if (rl != 0x030) {
input |= rl;
}
if (ud != 0x0C0) {
input |= ud;
}
}
} }
if (gba->rr && gba->rr->isRecording(gba->rr)) { } else if (gba->keySource) {
gba->rr->logInput(gba->rr, input); input = *gba->keySource;
if (!gba->allowOpposingDirections) {
unsigned rl = input & 0x030;
unsigned ud = input & 0x0C0;
input &= 0x30F;
if (rl != 0x030) {
input |= rl;
}
if (ud != 0x0C0) {
input |= ud;
}
} }
return 0x3FF ^ input; return 0x3FF ^ input;
} }

View File

@ -1626,17 +1626,19 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
maxLoads -= previousLoads; maxLoads -= previousLoads;
} }
int32_t s = cpu->memory.activeSeqCycles16 + 1; int32_t s = cpu->memory.activeSeqCycles16;
int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1; int32_t n2s = cpu->memory.activeNonseqCycles16 - cpu->memory.activeSeqCycles16 + 1;
// Figure out how many sequential loads we can jam in // Figure out how many sequential loads we can jam in
int32_t stall = s; int32_t stall = s + 1;
int32_t loads = 1; int32_t loads = 1;
while (stall < wait && loads < maxLoads) { while (stall < wait && loads < maxLoads) {
stall += s; stall += s;
++loads; ++loads;
} }
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1);
if (stall > wait) { if (stall > wait) {
// The wait cannot take less time than the prefetch stalls // The wait cannot take less time than the prefetch stalls
wait = stall; wait = stall;
@ -1645,10 +1647,9 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
// This instruction used to have an N, convert it to an S. // This instruction used to have an N, convert it to an S.
wait -= n2s; wait -= n2s;
memory->lastPrefetchedPc = cpu->gprs[ARM_PC] + WORD_SIZE_THUMB * (loads + previousLoads - 1);
// The next |loads|S waitstates disappear entirely, so long as they're all in a row // The next |loads|S waitstates disappear entirely, so long as they're all in a row
cpu->cycles -= (s - 1) * loads; wait -= stall - 1;
return wait; return wait;
} }

View File

@ -117,7 +117,6 @@ static const struct GBAVideoGLUniform _uniformsMode0[] = {
{ "charBase", GBA_GL_BG_CHARBASE, }, { "charBase", GBA_GL_BG_CHARBASE, },
{ "size", GBA_GL_BG_SIZE, }, { "size", GBA_GL_BG_SIZE, },
{ "offset", GBA_GL_BG_OFFSET, }, { "offset", GBA_GL_BG_OFFSET, },
{ "inflags", GBA_GL_BG_INFLAGS, },
{ "mosaic", GBA_GL_BG_MOSAIC, }, { "mosaic", GBA_GL_BG_MOSAIC, },
{ 0 } { 0 }
}; };
@ -130,10 +129,8 @@ static const char* const _renderMode0 =
"uniform int charBase;\n" "uniform int charBase;\n"
"uniform int size;\n" "uniform int size;\n"
"uniform int offset[160];\n" "uniform int offset[160];\n"
"uniform ivec4 inflags;\n"
"uniform ivec2 mosaic;\n" "uniform ivec2 mosaic;\n"
"OUT(0) out vec4 color;\n" "OUT(0) out vec4 color;\n"
"OUT(1) out ivec4 flags;\n"
"vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n" "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n"
@ -171,7 +168,6 @@ static const char* const _renderMode0 =
" }\n" " }\n"
" int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (tileFlags & 0x3) * 256;\n" " int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (tileFlags & 0x3) * 256;\n"
" color = renderTile(tile, int(map.r * 15.9), coord & 7);\n" " color = renderTile(tile, int(map.r * 15.9), coord & 7);\n"
" flags = inflags;\n"
"}"; "}";
static const char* const _fetchTileOverflow = static const char* const _fetchTileOverflow =
@ -199,7 +195,6 @@ static const struct GBAVideoGLUniform _uniformsMode2[] = {
{ "screenBase", GBA_GL_BG_SCREENBASE, }, { "screenBase", GBA_GL_BG_SCREENBASE, },
{ "charBase", GBA_GL_BG_CHARBASE, }, { "charBase", GBA_GL_BG_CHARBASE, },
{ "size", GBA_GL_BG_SIZE, }, { "size", GBA_GL_BG_SIZE, },
{ "inflags", GBA_GL_BG_INFLAGS, },
{ "offset", GBA_GL_BG_OFFSET, }, { "offset", GBA_GL_BG_OFFSET, },
{ "transform", GBA_GL_BG_TRANSFORM, }, { "transform", GBA_GL_BG_TRANSFORM, },
{ "range", GBA_GL_BG_RANGE, }, { "range", GBA_GL_BG_RANGE, },
@ -235,12 +230,10 @@ static const char* const _renderMode2 =
"uniform int screenBase;\n" "uniform int screenBase;\n"
"uniform int charBase;\n" "uniform int charBase;\n"
"uniform int size;\n" "uniform int size;\n"
"uniform ivec4 inflags;\n"
"uniform ivec4 transform[160];\n" "uniform ivec4 transform[160];\n"
"uniform ivec2 range;\n" "uniform ivec2 range;\n"
"uniform ivec2 mosaic;\n" "uniform ivec2 mosaic;\n"
"OUT(0) out vec4 color;\n" "OUT(0) out vec4 color;\n"
"OUT(1) out ivec4 flags;\n"
"vec4 fetchTile(ivec2 coord);\n" "vec4 fetchTile(ivec2 coord);\n"
"vec2 interpolate(ivec2 arr[4], float x);\n" "vec2 interpolate(ivec2 arr[4], float x);\n"
@ -284,7 +277,6 @@ static const char* const _renderMode2 =
" vec2 mixedTransform = interpolate(mat, lin);\n" " vec2 mixedTransform = interpolate(mat, lin);\n"
" vec2 mixedOffset = interpolate(offset, lin);\n" " vec2 mixedOffset = interpolate(offset, lin);\n"
" color = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n" " color = fetchTile(ivec2(mixedTransform * incoord.x + mixedOffset));\n"
" flags = inflags;\n"
"}"; "}";
static const struct GBAVideoGLUniform _uniformsMode35[] = { static const struct GBAVideoGLUniform _uniformsMode35[] = {
@ -293,7 +285,6 @@ static const struct GBAVideoGLUniform _uniformsMode35[] = {
{ "vram", GBA_GL_BG_VRAM, }, { "vram", GBA_GL_BG_VRAM, },
{ "charBase", GBA_GL_BG_CHARBASE, }, { "charBase", GBA_GL_BG_CHARBASE, },
{ "size", GBA_GL_BG_SIZE, }, { "size", GBA_GL_BG_SIZE, },
{ "inflags", GBA_GL_BG_INFLAGS, },
{ "offset", GBA_GL_BG_OFFSET, }, { "offset", GBA_GL_BG_OFFSET, },
{ "transform", GBA_GL_BG_TRANSFORM, }, { "transform", GBA_GL_BG_TRANSFORM, },
{ "range", GBA_GL_BG_RANGE, }, { "range", GBA_GL_BG_RANGE, },
@ -306,12 +297,10 @@ static const char* const _renderMode35 =
"uniform sampler2D vram;\n" "uniform sampler2D vram;\n"
"uniform int charBase;\n" "uniform int charBase;\n"
"uniform ivec2 size;\n" "uniform ivec2 size;\n"
"uniform ivec4 inflags;\n"
"uniform ivec4 transform[160];\n" "uniform ivec4 transform[160];\n"
"uniform ivec2 range;\n" "uniform ivec2 range;\n"
"uniform ivec2 mosaic;\n" "uniform ivec2 mosaic;\n"
"OUT(0) out vec4 color;\n" "OUT(0) out vec4 color;\n"
"OUT(1) out ivec4 flags;\n"
"vec2 interpolate(ivec2 arr[4], float x);\n" "vec2 interpolate(ivec2 arr[4], float x);\n"
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
@ -347,7 +336,6 @@ static const char* const _renderMode35 =
" ivec4 entry = ivec4(texelFetch(vram, ivec2(address & 255, address >> 8), 0) * 15.9);\n" " ivec4 entry = ivec4(texelFetch(vram, ivec2(address & 255, address >> 8), 0) * 15.9);\n"
" int sixteen = (entry.x << 12) | (entry.y << 8) | (entry.z << 4) | entry.w;\n" " int sixteen = (entry.x << 12) | (entry.y << 8) | (entry.z << 4) | entry.w;\n"
" color = vec4(float(sixteen & 0x1F) / 31., float((sixteen >> 5) & 0x1F) / 31., float((sixteen >> 10) & 0x1F) / 31., 1.);\n" " color = vec4(float(sixteen & 0x1F) / 31., float((sixteen >> 5) & 0x1F) / 31., float((sixteen >> 10) & 0x1F) / 31., 1.);\n"
" flags = inflags;\n"
"}"; "}";
static const struct GBAVideoGLUniform _uniformsMode4[] = { static const struct GBAVideoGLUniform _uniformsMode4[] = {
@ -357,7 +345,6 @@ static const struct GBAVideoGLUniform _uniformsMode4[] = {
{ "palette", GBA_GL_BG_PALETTE, }, { "palette", GBA_GL_BG_PALETTE, },
{ "charBase", GBA_GL_BG_CHARBASE, }, { "charBase", GBA_GL_BG_CHARBASE, },
{ "size", GBA_GL_BG_SIZE, }, { "size", GBA_GL_BG_SIZE, },
{ "inflags", GBA_GL_BG_INFLAGS, },
{ "offset", GBA_GL_BG_OFFSET, }, { "offset", GBA_GL_BG_OFFSET, },
{ "transform", GBA_GL_BG_TRANSFORM, }, { "transform", GBA_GL_BG_TRANSFORM, },
{ "range", GBA_GL_BG_RANGE, }, { "range", GBA_GL_BG_RANGE, },
@ -371,12 +358,10 @@ static const char* const _renderMode4 =
"uniform int palette[256];\n" "uniform int palette[256];\n"
"uniform int charBase;\n" "uniform int charBase;\n"
"uniform ivec2 size;\n" "uniform ivec2 size;\n"
"uniform ivec4 inflags;\n"
"uniform ivec4 transform[160];\n" "uniform ivec4 transform[160];\n"
"uniform ivec2 range;\n" "uniform ivec2 range;\n"
"uniform ivec2 mosaic;\n" "uniform ivec2 mosaic;\n"
"OUT(0) out vec4 color;\n" "OUT(0) out vec4 color;\n"
"OUT(1) out ivec4 flags;\n"
"vec2 interpolate(ivec2 arr[4], float x);\n" "vec2 interpolate(ivec2 arr[4], float x);\n"
"void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n" "void loadAffine(int y, out ivec2 mat[4], out ivec2 aff[4]);\n"
@ -413,7 +398,6 @@ static const char* const _renderMode4 =
" ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n" " ivec2 entry = ivec2(twoEntries[3 - 2 * (address & 1)] * 15.9, twoEntries[2 - 2 * (address & 1)] * 15.9);\n"
" int paletteEntry = palette[entry.y * 16 + entry.x];\n" " int paletteEntry = palette[entry.y * 16 + entry.x];\n"
" color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n" " color = vec4(PALETTE_ENTRY(paletteEntry), 1.);\n"
" flags = inflags;\n"
"}"; "}";
static const struct GBAVideoGLUniform _uniformsObj[] = { static const struct GBAVideoGLUniform _uniformsObj[] = {
@ -567,7 +551,7 @@ static const struct GBAVideoGLUniform _uniformsFinalize[] = {
{ "maxPos", GBA_GL_VS_MAXPOS, }, { "maxPos", GBA_GL_VS_MAXPOS, },
{ "scale", GBA_GL_FINALIZE_SCALE, }, { "scale", GBA_GL_FINALIZE_SCALE, },
{ "layers", GBA_GL_FINALIZE_LAYERS, }, { "layers", GBA_GL_FINALIZE_LAYERS, },
{ "flags", GBA_GL_FINALIZE_FLAGS, }, { "objFlags", GBA_GL_FINALIZE_FLAGS, },
{ "window", GBA_GL_FINALIZE_WINDOW, }, { "window", GBA_GL_FINALIZE_WINDOW, },
{ "backdrop", GBA_GL_FINALIZE_BACKDROP, }, { "backdrop", GBA_GL_FINALIZE_BACKDROP, },
{ "backdropFlags", GBA_GL_FINALIZE_BACKDROPFLAGS, }, { "backdropFlags", GBA_GL_FINALIZE_BACKDROPFLAGS, },
@ -578,7 +562,7 @@ static const char* const _finalize =
"in vec2 texCoord;\n" "in vec2 texCoord;\n"
"uniform int scale;\n" "uniform int scale;\n"
"uniform sampler2D layers[5];\n" "uniform sampler2D layers[5];\n"
"uniform isampler2D flags[5];\n" "uniform isampler2D objFlags;\n"
"uniform isampler2D window;\n" "uniform isampler2D window;\n"
"uniform sampler2D backdrop;\n" "uniform sampler2D backdrop;\n"
"uniform isampler2D backdropFlags;\n" "uniform isampler2D backdropFlags;\n"
@ -610,35 +594,35 @@ static const char* const _finalize =
" if ((layerWindow & 16) != 0) {\n" " if ((layerWindow & 16) != 0) {\n"
" vec4 pix = texelFetch(layers[4], coord, 0);\n" " vec4 pix = texelFetch(layers[4], coord, 0);\n"
" if (pix.a != 0.) {\n" " if (pix.a != 0.) {\n"
" ivec4 inflags = ivec4(texelFetch(flags[4], coord, 0));\n" " ivec4 inflags = ivec4(texelFetch(objFlags, coord, 0));\n"
" composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
" }\n" " }\n"
" }\n" " }\n"
" if ((layerWindow & 1) != 0) {\n" " if ((layerWindow & 1) != 0) {\n"
" vec4 pix = texelFetch(layers[0], coord, 0);\n" " vec4 pix = texelFetch(layers[0], coord, 0);\n"
" if (pix.a != 0.) {\n" " if (pix.a != 0.) {\n"
" ivec4 inflags = ivec4(texelFetch(flags[0], coord, 0).xyz, 0);\n" " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(1, texCoord.y), 0));\n"
" composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
" }\n" " }\n"
" }\n" " }\n"
" if ((layerWindow & 2) != 0) {\n" " if ((layerWindow & 2) != 0) {\n"
" vec4 pix = texelFetch(layers[1], coord, 0);\n" " vec4 pix = texelFetch(layers[1], coord, 0);\n"
" if (pix.a != 0.) {\n" " if (pix.a != 0.) {\n"
" ivec4 inflags = ivec4(texelFetch(flags[1], coord, 0).xyz, 0);\n" " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(2, texCoord.y), 0));\n"
" composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
" }\n" " }\n"
" }\n" " }\n"
" if ((layerWindow & 4) != 0) {\n" " if ((layerWindow & 4) != 0) {\n"
" vec4 pix = texelFetch(layers[2], coord, 0);\n" " vec4 pix = texelFetch(layers[2], coord, 0);\n"
" if (pix.a != 0.) {\n" " if (pix.a != 0.) {\n"
" ivec4 inflags = ivec4(texelFetch(flags[2], coord, 0).xyz, 0);\n" " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(3, texCoord.y), 0));\n"
" composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
" }\n" " }\n"
" }\n" " }\n"
" if ((layerWindow & 8) != 0) {\n" " if ((layerWindow & 8) != 0) {\n"
" vec4 pix = texelFetch(layers[3], coord, 0);\n" " vec4 pix = texelFetch(layers[3], coord, 0);\n"
" if (pix.a != 0.) {\n" " if (pix.a != 0.) {\n"
" ivec4 inflags = ivec4(texelFetch(flags[3], coord, 0).xyz, 0);\n" " ivec4 inflags = ivec4(texelFetch(backdropFlags, ivec2(4, texCoord.y), 0));\n"
" composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n" " composite(pix, inflags, topPixel, topFlags, bottomPixel, bottomFlags);\n"
" }\n" " }\n"
" }\n" " }\n"
@ -747,7 +731,7 @@ static void _initFramebufferTextureEx(GLuint tex, GLenum internalFormat, GLenum
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 1, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, type, 0); glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, scale > 0 ? GBA_VIDEO_HORIZONTAL_PIXELS * scale : 8, GBA_VIDEO_VERTICAL_PIXELS * (scale > 0 ? scale : 1), 0, format, type, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
} }
@ -764,7 +748,7 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) {
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_BACKDROP]);
_initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0); _initFramebufferTexture(glRenderer->layers[GBA_GL_TEX_BACKDROP_COLOR], GL_RGB, GL_COLOR_ATTACHMENT0, 0);
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale); _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_BACKDROP_FLAGS], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, 0);
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_WINDOW]);
_initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT0, glRenderer->scale); _initFramebufferTextureEx(glRenderer->layers[GBA_GL_TEX_WINDOW], GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT0, glRenderer->scale);
@ -777,7 +761,6 @@ static void _initFramebuffers(struct GBAVideoGLRenderer* glRenderer) {
struct GBAVideoGLBackground* bg = &glRenderer->bg[i]; struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo); glBindFramebuffer(GL_FRAMEBUFFER, bg->fbo);
_initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale); _initFramebufferTexture(bg->tex, GL_RGBA, GL_COLOR_ATTACHMENT0, glRenderer->scale);
_initFramebufferTextureEx(bg->flags, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, GL_COLOR_ATTACHMENT1, glRenderer->scale);
} }
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
@ -825,7 +808,6 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
bg->affine.sy = 0; bg->affine.sy = 0;
glGenFramebuffers(1, &bg->fbo); glGenFramebuffers(1, &bg->fbo);
glGenTextures(1, &bg->tex); glGenTextures(1, &bg->tex);
glGenTextures(1, &bg->flags);
} }
_initFramebuffers(glRenderer); _initFramebuffers(glRenderer);
@ -924,7 +906,6 @@ void GBAVideoGLRendererDeinit(struct GBAVideoRenderer* renderer) {
struct GBAVideoGLBackground* bg = &glRenderer->bg[i]; struct GBAVideoGLBackground* bg = &glRenderer->bg[i];
glDeleteFramebuffers(1, &bg->fbo); glDeleteFramebuffers(1, &bg->fbo);
glDeleteTextures(1, &bg->tex); glDeleteTextures(1, &bg->tex);
glDeleteTextures(1, &bg->flags);
} }
} }
@ -1400,12 +1381,12 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
#endif #endif
glClearStencil(0); glClearStencil(0);
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[GBA_GL_FBO_OBJ]);
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->bg[i].fbo);
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
} }
} }
@ -1428,11 +1409,21 @@ void _drawScanlines(struct GBAVideoGLRenderer* glRenderer, int y) {
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }); glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
glClearBufferfv(GL_COLOR, 0, (GLfloat[]) { ((backdrop >> 16) & 0xF8) / 248., ((backdrop >> 8) & 0xF8) / 248., (backdrop & 0xF8) / 248., 1.f }); glClearBufferfv(GL_COLOR, 0, (GLfloat[]) { ((backdrop >> 16) & 0xF8) / 248., ((backdrop >> 8) & 0xF8) / 248., (backdrop & 0xF8) / 248., 1.f });
glClearBufferiv(GL_COLOR, 1, (GLint[]) { 32, glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4), glRenderer->blda, 0 }); glClearBufferiv(GL_COLOR, 1, (GLint[]) { 32, glRenderer->target1Bd | (glRenderer->target2Bd * 2) | (glRenderer->blendEffect * 4), glRenderer->blda, 0 });
glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 });
int i;
for (i = 0; i < 4; ++i) {
glScissor(i + 1, glRenderer->firstY, i + 2, y - glRenderer->firstY + 1);
glClearBufferiv(GL_COLOR, 1, (GLint[]) { glRenderer->bg[i].priority,
glRenderer->bg[i].target1 | (glRenderer->bg[i].target2 << 1) | (glRenderer->blendEffect << 2),
glRenderer->blda, 0 });
}
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
GBAVideoGLRendererDrawWindow(glRenderer, y); GBAVideoGLRendererDrawWindow(glRenderer, y);
if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) { if (GBARegisterDISPCNTIsObjEnable(glRenderer->dispcnt) && !glRenderer->d.disableOBJ) {
int i;
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glEnable(GL_STENCIL_TEST); glEnable(GL_STENCIL_TEST);
glDepthFunc(GL_LESS); glDepthFunc(GL_LESS);
@ -1615,33 +1606,25 @@ void _finalizeLayers(struct GBAVideoGLRenderer* renderer) {
glActiveTexture(GL_TEXTURE0 + 3); glActiveTexture(GL_TEXTURE0 + 3);
glBindTexture(GL_TEXTURE_2D, renderer->bg[0].tex); glBindTexture(GL_TEXTURE_2D, renderer->bg[0].tex);
glActiveTexture(GL_TEXTURE0 + 4); glActiveTexture(GL_TEXTURE0 + 4);
glBindTexture(GL_TEXTURE_2D, renderer->bg[0].flags);
glActiveTexture(GL_TEXTURE0 + 5);
glBindTexture(GL_TEXTURE_2D, renderer->bg[1].tex); glBindTexture(GL_TEXTURE_2D, renderer->bg[1].tex);
glActiveTexture(GL_TEXTURE0 + 6); glActiveTexture(GL_TEXTURE0 + 5);
glBindTexture(GL_TEXTURE_2D, renderer->bg[1].flags);
glActiveTexture(GL_TEXTURE0 + 7);
glBindTexture(GL_TEXTURE_2D, renderer->bg[2].tex); glBindTexture(GL_TEXTURE_2D, renderer->bg[2].tex);
glActiveTexture(GL_TEXTURE0 + 8); glActiveTexture(GL_TEXTURE0 + 6);
glBindTexture(GL_TEXTURE_2D, renderer->bg[2].flags);
glActiveTexture(GL_TEXTURE0 + 9);
glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex); glBindTexture(GL_TEXTURE_2D, renderer->bg[3].tex);
glActiveTexture(GL_TEXTURE0 + 10); glActiveTexture(GL_TEXTURE0 + 7);
glBindTexture(GL_TEXTURE_2D, renderer->bg[3].flags);
glActiveTexture(GL_TEXTURE0 + 11);
glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]); glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_COLOR]);
glActiveTexture(GL_TEXTURE0 + 12); glActiveTexture(GL_TEXTURE0 + 8);
glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]); glBindTexture(GL_TEXTURE_2D, renderer->layers[GBA_GL_TEX_BACKDROP_FLAGS]);
glUniform2i(uniforms[GBA_GL_VS_LOC], GBA_VIDEO_VERTICAL_PIXELS, 0); glUniform2i(uniforms[GBA_GL_VS_LOC], GBA_VIDEO_VERTICAL_PIXELS, 0);
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); glUniform2i(uniforms[GBA_GL_VS_MAXPOS], GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
glUniform1i(uniforms[GBA_GL_FINALIZE_SCALE], renderer->scale); glUniform1i(uniforms[GBA_GL_FINALIZE_SCALE], renderer->scale);
glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 5, 7, 9, 1 }); glUniform1iv(uniforms[GBA_GL_FINALIZE_LAYERS], 5, (GLint[]) { 3, 4, 5, 6, 1 });
glUniform1iv(uniforms[GBA_GL_FINALIZE_FLAGS], 5, (GLint[]) { 4, 6, 8, 10, 2 }); glUniform1i(uniforms[GBA_GL_FINALIZE_FLAGS], 2);
glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0); glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0); glUniform1i(uniforms[GBA_GL_FINALIZE_WINDOW], 0);
glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 11); glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROP], 7);
glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 12); glUniform1i(uniforms[GBA_GL_FINALIZE_BACKDROPFLAGS], 8);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
} }
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -1734,13 +1717,13 @@ void GBAVideoGLRendererDrawSprite(struct GBAVideoGLRenderer* renderer, struct GB
glStencilFunc(GL_EQUAL, 1, 1); glStencilFunc(GL_EQUAL, 1, 1);
glUseProgram(shader->program); glUseProgram(shader->program);
glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 }); glDrawBuffers(2, (GLenum[]) { GL_NONE, GL_COLOR_ATTACHMENT1 });
glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
glBindVertexArray(shader->vao); glBindVertexArray(shader->vao);
glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0); glUniform2i(uniforms[GBA_GL_VS_LOC], totalHeight, 0);
glUniform2i(uniforms[GBA_GL_VS_MAXPOS], totalWidth, totalHeight); glUniform2i(uniforms[GBA_GL_VS_MAXPOS], totalWidth, totalHeight);
glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c), 0, 0, 0); glUniform4i(uniforms[GBA_GL_OBJ_INFLAGS], GBAObjAttributesCGetPriority(sprite->c),
(renderer->target1Obj || GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT) | (renderer->target2Obj * 2) | (renderer->blendEffect * 4),
renderer->blda, GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 }); glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
} }
@ -1758,10 +1741,7 @@ void _prepareBackground(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBa
} else { } else {
glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0); glUniform2i(uniforms[GBA_GL_BG_MOSAIC], 0, 0);
} }
glUniform4i(uniforms[GBA_GL_BG_INFLAGS], background->priority, glDrawBuffers(1, (GLenum[]) { GL_COLOR_ATTACHMENT0 });
background->target1 | (background->target2 * 2) | (renderer->blendEffect * 4),
renderer->blda, 0);
glDrawBuffers(2, (GLenum[]) { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 });
} }
void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {

View File

@ -234,7 +234,7 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re
} }
#define TEST_LAYER_ENABLED(X) \ #define TEST_LAYER_ENABLED(X) \
(softwareRenderer->bg[X].enabled == 4 && \ (softwareRenderer->bg[X].enabled == 3 && \
(GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \ (GBAWindowControlIsBg ## X ## Enable(softwareRenderer->currentWindow.packed) || \
(GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \ (GBARegisterDISPCNTIsObjwinEnable(softwareRenderer->dispcnt) && GBAWindowControlIsBg ## X ## Enable (softwareRenderer->objwin.packed))) && \
softwareRenderer->bg[X].priority == priority) softwareRenderer->bg[X].priority == priority)

View File

@ -691,19 +691,19 @@ static void GBAVideoSoftwareRendererDrawScanline(struct GBAVideoRenderer* render
softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy; softwareRenderer->bg[3].sy += softwareRenderer->bg[3].dmy;
} }
if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 4) { if (softwareRenderer->bg[0].enabled > 0 && softwareRenderer->bg[0].enabled < 3) {
++softwareRenderer->bg[0].enabled; ++softwareRenderer->bg[0].enabled;
DIRTY_SCANLINE(softwareRenderer, y); DIRTY_SCANLINE(softwareRenderer, y);
} }
if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 4) { if (softwareRenderer->bg[1].enabled > 0 && softwareRenderer->bg[1].enabled < 3) {
++softwareRenderer->bg[1].enabled; ++softwareRenderer->bg[1].enabled;
DIRTY_SCANLINE(softwareRenderer, y); DIRTY_SCANLINE(softwareRenderer, y);
} }
if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 4) { if (softwareRenderer->bg[2].enabled > 0 && softwareRenderer->bg[2].enabled < 3) {
++softwareRenderer->bg[2].enabled; ++softwareRenderer->bg[2].enabled;
DIRTY_SCANLINE(softwareRenderer, y); DIRTY_SCANLINE(softwareRenderer, y);
} }
if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 4) { if (softwareRenderer->bg[3].enabled > 0 && softwareRenderer->bg[3].enabled < 3) {
++softwareRenderer->bg[3].enabled; ++softwareRenderer->bg[3].enabled;
DIRTY_SCANLINE(softwareRenderer, y); DIRTY_SCANLINE(softwareRenderer, y);
} }
@ -737,16 +737,16 @@ static void GBAVideoSoftwareRendererFinishFrame(struct GBAVideoRenderer* rendere
softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy; softwareRenderer->bg[3].sy = softwareRenderer->bg[3].refy;
if (softwareRenderer->bg[0].enabled > 0) { if (softwareRenderer->bg[0].enabled > 0) {
softwareRenderer->bg[0].enabled = 4; softwareRenderer->bg[0].enabled = 3;
} }
if (softwareRenderer->bg[1].enabled > 0) { if (softwareRenderer->bg[1].enabled > 0) {
softwareRenderer->bg[1].enabled = 4; softwareRenderer->bg[1].enabled = 3;
} }
if (softwareRenderer->bg[2].enabled > 0) { if (softwareRenderer->bg[2].enabled > 0) {
softwareRenderer->bg[2].enabled = 4; softwareRenderer->bg[2].enabled = 3;
} }
if (softwareRenderer->bg[3].enabled > 0) { if (softwareRenderer->bg[3].enabled > 0) {
softwareRenderer->bg[3].enabled = 4; softwareRenderer->bg[3].enabled = 3;
} }
} }
@ -773,7 +773,7 @@ static void _enableBg(struct GBAVideoSoftwareRenderer* renderer, int bg, bool ac
} else if (!wasActive && active) { } else if (!wasActive && active) {
if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) { if (renderer->nextY == 0 || GBARegisterDISPCNTGetMode(renderer->dispcnt) > 2) {
// TODO: Investigate in more depth how switching background works in different modes // TODO: Investigate in more depth how switching background works in different modes
renderer->bg[bg].enabled = 4; renderer->bg[bg].enabled = 3;
} else { } else {
renderer->bg[bg].enabled = 1; renderer->bg[bg].enabled = 1;
} }

View File

@ -1,590 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/rr/mgm.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/serialize.h>
#include <mgba-util/vfs.h>
#define BINARY_EXT ".mgm"
#define BINARY_MAGIC "GBAb"
#define METADATA_FILENAME "metadata" BINARY_EXT
enum {
INVALID_INPUT = 0x8000
};
static void GBAMGMContextDestroy(struct GBARRContext*);
static bool GBAMGMStartPlaying(struct GBARRContext*, bool autorecord);
static void GBAMGMStopPlaying(struct GBARRContext*);
static bool GBAMGMStartRecording(struct GBARRContext*);
static void GBAMGMStopRecording(struct GBARRContext*);
static bool GBAMGMIsPlaying(const struct GBARRContext*);
static bool GBAMGMIsRecording(const struct GBARRContext*);
static void GBAMGMNextFrame(struct GBARRContext*);
static void GBAMGMLogInput(struct GBARRContext*, uint16_t input);
static uint16_t GBAMGMQueryInput(struct GBARRContext*);
static bool GBAMGMQueryReset(struct GBARRContext*);
static void GBAMGMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state);
static void GBAMGMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state);
static bool _loadStream(struct GBAMGMContext*, uint32_t streamId);
static bool _incrementStream(struct GBAMGMContext*, bool recursive);
static bool _finishSegment(struct GBAMGMContext*);
static bool _skipSegment(struct GBAMGMContext*);
static bool _markRerecord(struct GBAMGMContext*);
static bool _emitMagic(struct GBAMGMContext*, struct VFile* vf);
static bool _verifyMagic(struct GBAMGMContext*, struct VFile* vf);
static enum GBAMGMTag _readTag(struct GBAMGMContext*, struct VFile* vf);
static bool _seekTag(struct GBAMGMContext*, struct VFile* vf, enum GBAMGMTag tag);
static bool _emitTag(struct GBAMGMContext*, struct VFile* vf, uint8_t tag);
static bool _emitEnd(struct GBAMGMContext*, struct VFile* vf);
static bool _parseMetadata(struct GBAMGMContext*, struct VFile* vf);
static bool _markStreamNext(struct GBAMGMContext*, uint32_t newStreamId, bool recursive);
static void _streamEndReached(struct GBAMGMContext*);
static struct VFile* GBAMGMOpenSavedata(struct GBARRContext*, int flags);
static struct VFile* GBAMGMOpenSavestate(struct GBARRContext*, int flags);
void GBAMGMContextCreate(struct GBAMGMContext* mgm) {
memset(mgm, 0, sizeof(*mgm));
mgm->d.destroy = GBAMGMContextDestroy;
mgm->d.startPlaying = GBAMGMStartPlaying;
mgm->d.stopPlaying = GBAMGMStopPlaying;
mgm->d.startRecording = GBAMGMStartRecording;
mgm->d.stopRecording = GBAMGMStopRecording;
mgm->d.isPlaying = GBAMGMIsPlaying;
mgm->d.isRecording = GBAMGMIsRecording;
mgm->d.nextFrame = GBAMGMNextFrame;
mgm->d.logInput = GBAMGMLogInput;
mgm->d.queryInput = GBAMGMQueryInput;
mgm->d.queryReset = GBAMGMQueryReset;
mgm->d.stateSaved = GBAMGMStateSaved;
mgm->d.stateLoaded = GBAMGMStateLoaded;
mgm->d.openSavedata = GBAMGMOpenSavedata;
mgm->d.openSavestate = GBAMGMOpenSavestate;
}
void GBAMGMContextDestroy(struct GBARRContext* rr) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (mgm->metadataFile) {
mgm->metadataFile->close(mgm->metadataFile);
}
}
bool GBAMGMSetStream(struct GBAMGMContext* mgm, struct VDir* stream) {
if (mgm->movieStream && !mgm->movieStream->close(mgm->movieStream)) {
return false;
}
if (mgm->metadataFile && !mgm->metadataFile->close(mgm->metadataFile)) {
return false;
}
mgm->streamDir = stream;
mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_RDWR);
mgm->currentInput = INVALID_INPUT;
if (!_parseMetadata(mgm, mgm->metadataFile)) {
mgm->metadataFile->close(mgm->metadataFile);
mgm->metadataFile = 0;
mgm->maxStreamId = 0;
}
mgm->streamId = 1;
mgm->movieStream = 0;
return true;
}
bool GBAMGMCreateStream(struct GBAMGMContext* mgm, enum GBARRInitFrom initFrom) {
if (mgm->metadataFile) {
mgm->metadataFile->truncate(mgm->metadataFile, 0);
} else {
mgm->metadataFile = mgm->streamDir->openFile(mgm->streamDir, METADATA_FILENAME, O_CREAT | O_TRUNC | O_RDWR);
}
_emitMagic(mgm, mgm->metadataFile);
mgm->d.initFrom = initFrom;
mgm->initFromOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
_emitTag(mgm, mgm->metadataFile, TAG_INIT | initFrom);
mgm->streamId = 0;
mgm->maxStreamId = 0;
_emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM);
mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
mgm->d.rrCount = 0;
_emitTag(mgm, mgm->metadataFile, TAG_RR_COUNT);
mgm->rrCountOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount));
return true;
}
bool _loadStream(struct GBAMGMContext* mgm, uint32_t streamId) {
if (mgm->movieStream && !mgm->movieStream->close(mgm->movieStream)) {
return false;
}
mgm->movieStream = 0;
mgm->streamId = streamId;
mgm->currentInput = INVALID_INPUT;
char buffer[14];
snprintf(buffer, sizeof(buffer), "%u" BINARY_EXT, streamId);
if (mgm->d.isRecording(&mgm->d)) {
int flags = O_CREAT | O_RDWR;
if (streamId > mgm->maxStreamId) {
flags |= O_TRUNC;
}
mgm->movieStream = mgm->streamDir->openFile(mgm->streamDir, buffer, flags);
} else if (mgm->d.isPlaying(&mgm->d)) {
mgm->movieStream = mgm->streamDir->openFile(mgm->streamDir, buffer, O_RDONLY);
mgm->peekedTag = TAG_INVALID;
if (!mgm->movieStream || !_verifyMagic(mgm, mgm->movieStream) || !_seekTag(mgm, mgm->movieStream, TAG_BEGIN)) {
mgm->d.stopPlaying(&mgm->d);
}
}
mLOG(GBA_RR, DEBUG, "Loading segment: %u", streamId);
mgm->d.frames = 0;
mgm->d.lagFrames = 0;
return true;
}
bool _incrementStream(struct GBAMGMContext* mgm, bool recursive) {
uint32_t newStreamId = mgm->maxStreamId + 1;
uint32_t oldStreamId = mgm->streamId;
if (mgm->d.isRecording(&mgm->d) && mgm->movieStream) {
if (!_markStreamNext(mgm, newStreamId, recursive)) {
return false;
}
}
if (!_loadStream(mgm, newStreamId)) {
return false;
}
mLOG(GBA_RR, DEBUG, "New segment: %u", newStreamId);
_emitMagic(mgm, mgm->movieStream);
mgm->maxStreamId = newStreamId;
_emitTag(mgm, mgm->movieStream, TAG_PREVIOUSLY);
mgm->movieStream->write(mgm->movieStream, &oldStreamId, sizeof(oldStreamId));
_emitTag(mgm, mgm->movieStream, TAG_BEGIN);
mgm->metadataFile->seek(mgm->metadataFile, mgm->maxStreamIdOffset, SEEK_SET);
mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
mgm->previously = oldStreamId;
return true;
}
bool GBAMGMStartPlaying(struct GBARRContext* rr, bool autorecord) {
if (rr->isRecording(rr) || rr->isPlaying(rr)) {
return false;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
mgm->isPlaying = true;
if (!_loadStream(mgm, 1)) {
mgm->isPlaying = false;
return false;
}
mgm->autorecord = autorecord;
return true;
}
void GBAMGMStopPlaying(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
mgm->isPlaying = false;
if (mgm->movieStream) {
mgm->movieStream->close(mgm->movieStream);
mgm->movieStream = 0;
}
}
bool GBAMGMStartRecording(struct GBARRContext* rr) {
if (rr->isRecording(rr) || rr->isPlaying(rr)) {
return false;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (!mgm->maxStreamIdOffset) {
_emitTag(mgm, mgm->metadataFile, TAG_MAX_STREAM);
mgm->maxStreamIdOffset = mgm->metadataFile->seek(mgm->metadataFile, 0, SEEK_CUR);
mgm->metadataFile->write(mgm->metadataFile, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
}
mgm->isRecording = true;
return _incrementStream(mgm, false);
}
void GBAMGMStopRecording(struct GBARRContext* rr) {
if (!rr->isRecording(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
mgm->isRecording = false;
if (mgm->movieStream) {
_emitEnd(mgm, mgm->movieStream);
mgm->movieStream->close(mgm->movieStream);
mgm->movieStream = 0;
}
}
bool GBAMGMIsPlaying(const struct GBARRContext* rr) {
const struct GBAMGMContext* mgm = (const struct GBAMGMContext*) rr;
return mgm->isPlaying;
}
bool GBAMGMIsRecording(const struct GBARRContext* rr) {
const struct GBAMGMContext* mgm = (const struct GBAMGMContext*) rr;
return mgm->isRecording;
}
void GBAMGMNextFrame(struct GBARRContext* rr) {
if (!rr->isRecording(rr) && !rr->isPlaying(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (rr->isPlaying(rr)) {
while (mgm->peekedTag == TAG_INPUT) {
_readTag(mgm, mgm->movieStream);
mLOG(GBA_RR, WARN, "Desync detected!");
}
if (mgm->peekedTag == TAG_LAG) {
mLOG(GBA_RR, DEBUG, "Lag frame marked in stream");
if (mgm->inputThisFrame) {
mLOG(GBA_RR, WARN, "Lag frame in stream does not match movie");
}
}
}
++mgm->d.frames;
mLOG(GBA_RR, DEBUG, "Frame: %u", mgm->d.frames);
if (!mgm->inputThisFrame) {
++mgm->d.lagFrames;
mLOG(GBA_RR, DEBUG, "Lag frame: %u", mgm->d.lagFrames);
}
if (rr->isRecording(rr)) {
if (!mgm->inputThisFrame) {
_emitTag(mgm, mgm->movieStream, TAG_LAG);
}
_emitTag(mgm, mgm->movieStream, TAG_FRAME);
mgm->inputThisFrame = false;
} else {
if (!_seekTag(mgm, mgm->movieStream, TAG_FRAME)) {
_streamEndReached(mgm);
}
}
}
void GBAMGMLogInput(struct GBARRContext* rr, uint16_t keys) {
if (!rr->isRecording(rr)) {
return;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (keys != mgm->currentInput) {
_emitTag(mgm, mgm->movieStream, TAG_INPUT);
mgm->movieStream->write(mgm->movieStream, &keys, sizeof(keys));
mgm->currentInput = keys;
}
mLOG(GBA_RR, DEBUG, "Input log: %03X", mgm->currentInput);
mgm->inputThisFrame = true;
}
uint16_t GBAMGMQueryInput(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return 0;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (mgm->peekedTag == TAG_INPUT) {
_readTag(mgm, mgm->movieStream);
}
mgm->inputThisFrame = true;
if (mgm->currentInput == INVALID_INPUT) {
mLOG(GBA_RR, WARN, "Stream did not specify input");
}
mLOG(GBA_RR, DEBUG, "Input replay: %03X", mgm->currentInput);
return mgm->currentInput;
}
bool GBAMGMQueryReset(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return 0;
}
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
return mgm->peekedTag == TAG_RESET;
}
void GBAMGMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (rr->isRecording(rr)) {
state->associatedStreamId = mgm->streamId;
_finishSegment(mgm);
}
}
void GBAMGMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
if (rr->isRecording(rr)) {
if (state->associatedStreamId != mgm->streamId) {
_loadStream(mgm, state->associatedStreamId);
_incrementStream(mgm, true);
} else {
_finishSegment(mgm);
}
_markRerecord(mgm);
} else if (rr->isPlaying(rr)) {
_loadStream(mgm, state->associatedStreamId);
_skipSegment(mgm);
}
}
bool _finishSegment(struct GBAMGMContext* mgm) {
if (mgm->movieStream) {
if (!_emitEnd(mgm, mgm->movieStream)) {
return false;
}
}
return _incrementStream(mgm, false);
}
bool _skipSegment(struct GBAMGMContext* mgm) {
mgm->nextTime = 0;
while (_readTag(mgm, mgm->movieStream) != TAG_EOF);
if (!mgm->nextTime || !_loadStream(mgm, mgm->nextTime)) {
_streamEndReached(mgm);
return false;
}
return true;
}
bool _markRerecord(struct GBAMGMContext* mgm) {
++mgm->d.rrCount;
mgm->metadataFile->seek(mgm->metadataFile, mgm->rrCountOffset, SEEK_SET);
mgm->metadataFile->write(mgm->metadataFile, &mgm->d.rrCount, sizeof(mgm->d.rrCount));
return true;
}
bool _emitMagic(struct GBAMGMContext* mgm, struct VFile* vf) {
UNUSED(mgm);
return vf->write(vf, BINARY_MAGIC, 4) == 4;
}
bool _verifyMagic(struct GBAMGMContext* mgm, struct VFile* vf) {
UNUSED(mgm);
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 GBAMGMTag _readTag(struct GBAMGMContext* mgm, struct VFile* vf) {
if (!mgm || !vf) {
return TAG_EOF;
}
enum GBAMGMTag tag = mgm->peekedTag;
switch (tag) {
case TAG_INPUT:
vf->read(vf, &mgm->currentInput, sizeof(uint16_t));
break;
case TAG_PREVIOUSLY:
vf->read(vf, &mgm->previously, sizeof(mgm->previously));
break;
case TAG_NEXT_TIME:
vf->read(vf, &mgm->nextTime, sizeof(mgm->nextTime));
break;
case TAG_MAX_STREAM:
vf->read(vf, &mgm->maxStreamId, sizeof(mgm->maxStreamId));
break;
case TAG_FRAME_COUNT:
vf->read(vf, &mgm->d.frames, sizeof(mgm->d.frames));
break;
case TAG_LAG_COUNT:
vf->read(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames));
break;
case TAG_RR_COUNT:
vf->read(vf, &mgm->d.rrCount, sizeof(mgm->d.rrCount));
break;
case TAG_INIT_EX_NIHILO:
mgm->d.initFrom = INIT_EX_NIHILO;
break;
case TAG_INIT_FROM_SAVEGAME:
mgm->d.initFrom = INIT_FROM_SAVEGAME;
break;
case TAG_INIT_FROM_SAVESTATE:
mgm->d.initFrom = INIT_FROM_SAVESTATE;
break;
case TAG_INIT_FROM_BOTH:
mgm->d.initFrom = INIT_FROM_BOTH;
break;
// To be spec'd
case TAG_AUTHOR:
case TAG_COMMENT:
break;
// Empty markers
case TAG_FRAME:
case TAG_LAG:
case TAG_RESET:
case TAG_BEGIN:
case TAG_END:
case TAG_INVALID:
case TAG_EOF:
break;
}
uint8_t tagBuffer;
if (vf->read(vf, &tagBuffer, 1) != 1) {
mgm->peekedTag = TAG_EOF;
} else {
mgm->peekedTag = tagBuffer;
}
if (mgm->peekedTag == TAG_END) {
_skipSegment(mgm);
}
return tag;
}
bool _seekTag(struct GBAMGMContext* mgm, struct VFile* vf, enum GBAMGMTag tag) {
enum GBAMGMTag readTag;
while ((readTag = _readTag(mgm, vf)) != tag) {
if (readTag == TAG_EOF) {
return false;
}
}
return true;
}
bool _emitTag(struct GBAMGMContext* mgm, struct VFile* vf, uint8_t tag) {
UNUSED(mgm);
return vf->write(vf, &tag, sizeof(tag)) == sizeof(tag);
}
bool _parseMetadata(struct GBAMGMContext* mgm, struct VFile* vf) {
if (!_verifyMagic(mgm, vf)) {
return false;
}
while (_readTag(mgm, vf) != TAG_EOF) {
switch (mgm->peekedTag) {
case TAG_MAX_STREAM:
mgm->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:
mgm->initFromOffset = vf->seek(vf, 0, SEEK_CUR);
break;
case TAG_RR_COUNT:
mgm->rrCountOffset = vf->seek(vf, 0, SEEK_CUR);
break;
default:
break;
}
}
return true;
}
bool _emitEnd(struct GBAMGMContext* mgm, struct VFile* vf) {
// TODO: Error check
_emitTag(mgm, vf, TAG_END);
_emitTag(mgm, vf, TAG_FRAME_COUNT);
vf->write(vf, &mgm->d.frames, sizeof(mgm->d.frames));
_emitTag(mgm, vf, TAG_LAG_COUNT);
vf->write(vf, &mgm->d.lagFrames, sizeof(mgm->d.lagFrames));
_emitTag(mgm, vf, TAG_NEXT_TIME);
uint32_t newStreamId = 0;
vf->write(vf, &newStreamId, sizeof(newStreamId));
return true;
}
bool _markStreamNext(struct GBAMGMContext* mgm, uint32_t newStreamId, bool recursive) {
if (mgm->movieStream->seek(mgm->movieStream, -sizeof(newStreamId) - 1, SEEK_END) < 0) {
return false;
}
uint8_t tagBuffer;
if (mgm->movieStream->read(mgm->movieStream, &tagBuffer, 1) != 1) {
return false;
}
if (tagBuffer != TAG_NEXT_TIME) {
return false;
}
if (mgm->movieStream->write(mgm->movieStream, &newStreamId, sizeof(newStreamId)) != sizeof(newStreamId)) {
return false;
}
if (recursive) {
if (mgm->movieStream->seek(mgm->movieStream, 0, SEEK_SET) < 0) {
return false;
}
if (!_verifyMagic(mgm, mgm->movieStream)) {
return false;
}
_readTag(mgm, mgm->movieStream);
if (_readTag(mgm, mgm->movieStream) != TAG_PREVIOUSLY) {
return false;
}
if (mgm->previously == 0) {
return true;
}
uint32_t currentStreamId = mgm->streamId;
if (!_loadStream(mgm, mgm->previously)) {
return false;
}
return _markStreamNext(mgm, currentStreamId, mgm->previously);
}
return true;
}
void _streamEndReached(struct GBAMGMContext* mgm) {
if (!mgm->d.isPlaying(&mgm->d)) {
return;
}
uint32_t endStreamId = mgm->streamId;
mgm->d.stopPlaying(&mgm->d);
if (mgm->autorecord) {
mgm->isRecording = true;
_loadStream(mgm, endStreamId);
_incrementStream(mgm, false);
}
}
struct VFile* GBAMGMOpenSavedata(struct GBARRContext* rr, int flags) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
return mgm->streamDir->openFile(mgm->streamDir, "movie.sav", flags);
}
struct VFile* GBAMGMOpenSavestate(struct GBARRContext* rr, int flags) {
struct GBAMGMContext* mgm = (struct GBAMGMContext*) rr;
return mgm->streamDir->openFile(mgm->streamDir, "movie.ssm", flags);
}

View File

@ -1,77 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/rr/rr.h>
#include <mgba/core/log.h>
#include <mgba/core/serialize.h>
#include <mgba-util/vfs.h>
mLOG_DEFINE_CATEGORY(GBA_RR, "GBA RR", "gba.rr");
void GBARRInitRecord(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 = gba->rr->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 = gba->rr->openSavedata(gba->rr, O_RDONLY);
GBASavedataMask(&gba->memory.savedata, gba->rr->savedata, false);
} else {
GBASavedataMask(&gba->memory.savedata, 0, false);
}
if (gba->rr->initFrom & INIT_FROM_SAVESTATE) {
struct VFile* vf = gba->rr->openSavestate(gba->rr, O_TRUNC | O_CREAT | O_RDWR);
//GBASaveStateNamed(gba, vf, SAVESTATE_SAVEDATA);
vf->close(vf);
} else {
ARMReset(gba->cpu);
}
}
void GBARRInitPlay(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 = gba->rr->openSavedata(gba->rr, O_RDONLY);
GBASavedataMask(&gba->memory.savedata, gba->rr->savedata, false);
} else {
GBASavedataMask(&gba->memory.savedata, 0, false);
}
if (gba->rr->initFrom & INIT_FROM_SAVESTATE) {
struct VFile* vf = gba->rr->openSavestate(gba->rr, O_RDONLY);
//GBALoadStateNamed(gba, vf, SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA);
vf->close(vf);
} else {
ARMReset(gba->cpu);
}
}
void GBARRDestroy(struct GBARRContext* rr) {
if (rr->isPlaying(rr)) {
rr->stopPlaying(rr);
}
if (rr->isRecording(rr)) {
rr->stopRecording(rr);
}
if (rr->savedata) {
rr->savedata->close(rr->savedata);
rr->savedata = 0;
}
rr->destroy(rr);
}

View File

@ -1,274 +0,0 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <mgba/internal/gba/rr/vbm.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/serialize.h>
#include <mgba-util/vfs.h>
#ifdef USE_ZLIB
#include <zlib.h>
#endif
static const char VBM_MAGIC[] = "VBM\x1A";
static void GBAVBMContextDestroy(struct GBARRContext*);
static bool GBAVBMStartPlaying(struct GBARRContext*, bool autorecord);
static void GBAVBMStopPlaying(struct GBARRContext*);
static bool GBAVBMStartRecording(struct GBARRContext*);
static void GBAVBMStopRecording(struct GBARRContext*);
static bool GBAVBMIsPlaying(const struct GBARRContext*);
static bool GBAVBMIsRecording(const struct GBARRContext*);
static void GBAVBMNextFrame(struct GBARRContext*);
static uint16_t GBAVBMQueryInput(struct GBARRContext*);
static bool GBAVBMQueryReset(struct GBARRContext*);
static void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state);
static void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state);
static struct VFile* GBAVBMOpenSavedata(struct GBARRContext*, int flags);
static struct VFile* GBAVBMOpenSavestate(struct GBARRContext*, int flags);
void GBAVBMContextCreate(struct GBAVBMContext* vbm) {
memset(vbm, 0, sizeof(*vbm));
vbm->d.destroy = GBAVBMContextDestroy;
vbm->d.startPlaying = GBAVBMStartPlaying;
vbm->d.stopPlaying = GBAVBMStopPlaying;
vbm->d.startRecording = GBAVBMStartRecording;
vbm->d.stopRecording = GBAVBMStopRecording;
vbm->d.isPlaying = GBAVBMIsPlaying;
vbm->d.isRecording = GBAVBMIsRecording;
vbm->d.nextFrame = GBAVBMNextFrame;
vbm->d.logInput = 0;
vbm->d.queryInput = GBAVBMQueryInput;
vbm->d.queryReset = GBAVBMQueryReset;
vbm->d.stateSaved = GBAVBMStateSaved;
vbm->d.stateLoaded = GBAVBMStateLoaded;
vbm->d.openSavedata = GBAVBMOpenSavedata;
vbm->d.openSavestate = GBAVBMOpenSavestate;
}
bool GBAVBMStartPlaying(struct GBARRContext* rr, bool autorecord) {
if (rr->isRecording(rr) || rr->isPlaying(rr) || autorecord) {
return false;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
vbm->isPlaying = true;
vbm->vbmFile->seek(vbm->vbmFile, vbm->inputOffset, SEEK_SET);
return true;
}
void GBAVBMStopPlaying(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
vbm->isPlaying = false;
}
bool GBAVBMStartRecording(struct GBARRContext* rr) {
UNUSED(rr);
return false;
}
void GBAVBMStopRecording(struct GBARRContext* rr) {
UNUSED(rr);
}
bool GBAVBMIsPlaying(const struct GBARRContext* rr) {
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
return vbm->isPlaying;
}
bool GBAVBMIsRecording(const struct GBARRContext* rr) {
UNUSED(rr);
return false;
}
void GBAVBMNextFrame(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
vbm->vbmFile->seek(vbm->vbmFile, sizeof(uint16_t), SEEK_CUR);
}
uint16_t GBAVBMQueryInput(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return 0;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
uint16_t input;
vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input));
vbm->vbmFile->seek(vbm->vbmFile, -sizeof(input), SEEK_CUR);
return input & 0x3FF;
}
bool GBAVBMQueryReset(struct GBARRContext* rr) {
if (!rr->isPlaying(rr)) {
return false;
}
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
uint16_t input;
vbm->vbmFile->read(vbm->vbmFile, &input, sizeof(input));
vbm->vbmFile->seek(vbm->vbmFile, -sizeof(input), SEEK_CUR);
return input & 0x800;
}
void GBAVBMStateSaved(struct GBARRContext* rr, struct GBASerializedState* state) {
UNUSED(rr);
UNUSED(state);
}
void GBAVBMStateLoaded(struct GBARRContext* rr, const struct GBASerializedState* state) {
UNUSED(rr);
UNUSED(state);
}
struct VFile* GBAVBMOpenSavedata(struct GBARRContext* rr, int flags) {
UNUSED(flags);
#ifndef USE_ZLIB
UNUSED(rr);
return 0;
#else
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
off_t pos = vbm->vbmFile->seek(vbm->vbmFile, 0, SEEK_CUR);
uint32_t saveType, flashSize, sramOffset;
vbm->vbmFile->seek(vbm->vbmFile, 0x18, SEEK_SET);
vbm->vbmFile->read(vbm->vbmFile, &saveType, sizeof(saveType));
vbm->vbmFile->read(vbm->vbmFile, &flashSize, sizeof(flashSize));
vbm->vbmFile->seek(vbm->vbmFile, 0x38, SEEK_SET);
vbm->vbmFile->read(vbm->vbmFile, &sramOffset, sizeof(sramOffset));
if (!sramOffset) {
vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET);
return 0;
}
vbm->vbmFile->seek(vbm->vbmFile, sramOffset, SEEK_SET);
struct VFile* save = VFileMemChunk(0, 0);
size_t size;
switch (saveType) {
case 1:
size = SIZE_CART_SRAM;
break;
case 2:
size = flashSize;
if (size > SIZE_CART_FLASH1M) {
size = SIZE_CART_FLASH1M;
}
break;
case 3:
size = SIZE_CART_EEPROM;
break;
default:
size = SIZE_CART_FLASH1M;
break;
}
uLong zlen = vbm->inputOffset - sramOffset;
char buffer[8761];
char* zbuffer = malloc(zlen);
vbm->vbmFile->read(vbm->vbmFile, zbuffer, zlen);
z_stream zstr;
zstr.zalloc = Z_NULL;
zstr.zfree = Z_NULL;
zstr.opaque = Z_NULL;
zstr.avail_in = zlen;
zstr.next_in = (Bytef*) zbuffer;
zstr.avail_out = 0;
inflateInit2(&zstr, 31);
// Skip header, we know where the save file is
zstr.avail_out = sizeof(buffer);
zstr.next_out = (Bytef*) &buffer;
int err = inflate(&zstr, 0);
while (err != Z_STREAM_END && !zstr.avail_out) {
zstr.avail_out = sizeof(buffer);
zstr.next_out = (Bytef*) &buffer;
int err = inflate(&zstr, 0);
if (err < 0) {
break;
}
save->write(save, buffer, sizeof(buffer) - zstr.avail_out);
}
inflateEnd(&zstr);
vbm->vbmFile->seek(vbm->vbmFile, pos, SEEK_SET);
return save;
#endif
}
struct VFile* GBAVBMOpenSavestate(struct GBARRContext* rr, int flags) {
UNUSED(rr);
UNUSED(flags);
return 0;
}
void GBAVBMContextDestroy(struct GBARRContext* rr) {
struct GBAVBMContext* vbm = (struct GBAVBMContext*) rr;
if (vbm->vbmFile) {
vbm->vbmFile->close(vbm->vbmFile);
}
}
bool GBAVBMSetStream(struct GBAVBMContext* vbm, struct VFile* vf) {
vf->seek(vf, 0, SEEK_SET);
char magic[4];
vf->read(vf, magic, sizeof(magic));
if (memcmp(magic, VBM_MAGIC, sizeof(magic)) != 0) {
return false;
}
uint32_t id;
vf->read(vf, &id, sizeof(id));
if (id != 1) {
return false;
}
vf->seek(vf, 4, SEEK_CUR);
vf->read(vf, &vbm->d.frames, sizeof(vbm->d.frames));
vf->read(vf, &vbm->d.rrCount, sizeof(vbm->d.rrCount));
uint8_t flags;
vf->read(vf, &flags, sizeof(flags));
if (flags & 2) {
#ifdef USE_ZLIB
vbm->d.initFrom = INIT_FROM_SAVEGAME;
#else
// zlib is needed to parse the savegame
return false;
#endif
}
if (flags & 1) {
// Incompatible savestate format
return false;
}
vf->seek(vf, 1, SEEK_CUR);
vf->read(vf, &flags, sizeof(flags));
if ((flags & 0x7) != 1) {
// Non-GBA movie
return false;
}
// TODO: parse more flags
vf->seek(vf, 0x3C, SEEK_SET);
vf->read(vf, &vbm->inputOffset, sizeof(vbm->inputOffset));
vf->seek(vf, vbm->inputOffset, SEEK_SET);
vbm->vbmFile = vf;
return true;
}

View File

@ -7,7 +7,6 @@
#include <mgba/internal/arm/macros.h> #include <mgba/internal/arm/macros.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/rr/rr.h>
#include <mgba-util/memory.h> #include <mgba-util/memory.h>
#include <mgba-util/vfs.h> #include <mgba-util/vfs.h>
@ -73,11 +72,6 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBAVideoSerialize(&gba->video, state); GBAVideoSerialize(&gba->video, state);
GBAAudioSerialize(&gba->audio, state); GBAAudioSerialize(&gba->audio, state);
GBASavedataSerialize(&gba->memory.savedata, state); GBASavedataSerialize(&gba->memory.savedata, state);
state->associatedStreamId = 0;
if (gba->rr) {
gba->rr->stateSaved(gba->rr, state);
}
} }
bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
@ -195,10 +189,6 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
GBAAudioDeserialize(&gba->audio, state); GBAAudioDeserialize(&gba->audio, state);
GBASavedataDeserialize(&gba->memory.savedata, state); GBASavedataDeserialize(&gba->memory.savedata, state);
if (gba->rr) {
gba->rr->stateLoaded(gba->rr, state);
}
gba->timing.reroot = gba->timing.root; gba->timing.reroot = gba->timing.root;
gba->timing.root = NULL; gba->timing.root = NULL;

View File

@ -31,6 +31,7 @@ static void GBAVideoDummyRendererGetPixels(struct GBAVideoRenderer* renderer, si
static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels);
static void _startHblank(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHblank(struct mTiming*, void* context, uint32_t cyclesLate);
static void _midHblank(struct mTiming*, void* context, uint32_t cyclesLate);
static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate); static void _startHdraw(struct mTiming*, void* context, uint32_t cyclesLate);
static uint16_t _zeroes[0x2000] = {}; static uint16_t _zeroes[0x2000] = {};
@ -137,10 +138,17 @@ void GBAVideoAssociateRenderer(struct GBAVideo* video, struct GBAVideoRenderer*
video->renderer->init(video->renderer); video->renderer->init(video->renderer);
} }
void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _midHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBAVideo* video = context; struct GBAVideo* video = context;
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATClearInHblank(dispstat); dispstat = GBARegisterDISPSTATClearInHblank(dispstat);
video->p->memory.io[REG_DISPSTAT >> 1] = dispstat;
video->event.callback = _startHdraw;
mTimingSchedule(timing, &video->event, VIDEO_HBLANK_FLIP - cyclesLate);
}
void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBAVideo* video = context;
video->event.callback = _startHblank; video->event.callback = _startHblank;
mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate); mTimingSchedule(timing, &video->event, VIDEO_HDRAW_LENGTH - cyclesLate);
@ -150,6 +158,11 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
} }
video->p->memory.io[REG_VCOUNT >> 1] = video->vcount; video->p->memory.io[REG_VCOUNT >> 1] = video->vcount;
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
}
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) { if (video->vcount == GBARegisterDISPSTATGetVcountSetting(dispstat)) {
dispstat = GBARegisterDISPSTATFillVcounter(dispstat); dispstat = GBARegisterDISPSTATFillVcounter(dispstat);
if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) { if (GBARegisterDISPSTATIsVcounterIRQ(dispstat)) {
@ -190,16 +203,12 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBAVideo* video = context; struct GBAVideo* video = context;
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1]; video->event.callback = _midHblank;
dispstat = GBARegisterDISPSTATFillInHblank(dispstat); mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - VIDEO_HBLANK_FLIP - cyclesLate);
video->event.callback = _startHdraw;
mTimingSchedule(timing, &video->event, VIDEO_HBLANK_LENGTH - cyclesLate);
// Begin Hblank // Begin Hblank
GBARegisterDISPSTAT dispstat = video->p->memory.io[REG_DISPSTAT >> 1];
dispstat = GBARegisterDISPSTATFillInHblank(dispstat); dispstat = GBARegisterDISPSTATFillInHblank(dispstat);
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS && video->frameskipCounter <= 0) {
video->renderer->drawScanline(video->renderer, video->vcount);
}
if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) { if (video->vcount < GBA_VIDEO_VERTICAL_PIXELS) {
GBADMARunHblank(video->p, -cyclesLate); GBADMARunHblank(video->p, -cyclesLate);
@ -324,6 +333,15 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState*
memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->oam, video->oam.raw, SIZE_OAM);
memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); memcpy(state->pram, video->palette, SIZE_PALETTE_RAM);
STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent); STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent);
int32_t flags = 0;
if (video->event.callback == _startHdraw) {
flags = GBASerializedVideoFlagsSetMode(flags, 1);
} else if (video->event.callback == _startHblank) {
flags = GBASerializedVideoFlagsSetMode(flags, 2);
} else if (video->event.callback == _midHblank) {
flags = GBASerializedVideoFlagsSetMode(flags, 3);
}
STORE_32(flags, 0, &state->video.flags);
STORE_32(video->frameCounter, 0, &state->video.frameCounter); STORE_32(video->frameCounter, 0, &state->video.frameCounter);
} }
@ -341,14 +359,28 @@ void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState
} }
LOAD_32(video->frameCounter, 0, &state->video.frameCounter); LOAD_32(video->frameCounter, 0, &state->video.frameCounter);
int32_t flags;
LOAD_32(flags, 0, &state->video.flags);
GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
switch (GBASerializedVideoFlagsGetMode(flags)) {
case 0:
if (GBARegisterDISPSTATIsInHblank(dispstat)) {
video->event.callback = _startHdraw;
} else {
video->event.callback = _startHblank;
}
case 1:
video->event.callback = _startHdraw;
break;
case 2:
video->event.callback = _startHblank;
break;
case 3:
video->event.callback = _midHblank;
break;
}
uint32_t when; uint32_t when;
LOAD_32(when, 0, &state->video.nextEvent); LOAD_32(when, 0, &state->video.nextEvent);
GBARegisterDISPSTAT dispstat = state->io[REG_DISPSTAT >> 1];
if (GBARegisterDISPSTATIsInHblank(dispstat)) {
video->event.callback = _startHdraw;
} else {
video->event.callback = _startHblank;
}
mTimingSchedule(&video->p->timing, &video->event, when); mTimingSchedule(&video->p->timing, &video->event, when);
LOAD_16(video->vcount, REG_VCOUNT, state->io); LOAD_16(video->vcount, REG_VCOUNT, state->io);

View File

@ -137,9 +137,9 @@ void AssetTile::selectColor(int index) {
m_ui.color->setColor(0, color); m_ui.color->setColor(0, color);
m_ui.color->update(); m_ui.color->update();
uint32_t r = M_R8(color); uint32_t r = ((color & 0xF8) * 0x21) >> 5;
uint32_t g = M_G8(color); uint32_t g = (((color >> 8) & 0xF8) * 0x21) >> 5;
uint32_t b = M_B8(color); uint32_t b = (((color >> 16) & 0xF8) * 0x21) >> 5;
m_ui.r->setText(tr("0x%0 (%1)").arg(r, 2, 16, QChar('0')).arg(r, 2, 10, QChar('0'))); m_ui.r->setText(tr("0x%0 (%1)").arg(r, 2, 16, QChar('0')).arg(r, 2, 10, QChar('0')));
m_ui.g->setText(tr("0x%0 (%1)").arg(g, 2, 16, QChar('0')).arg(g, 2, 10, QChar('0'))); m_ui.g->setText(tr("0x%0 (%1)").arg(g, 2, 16, QChar('0')).arg(g, 2, 10, QChar('0')));
m_ui.b->setText(tr("0x%0 (%1)").arg(b, 2, 16, QChar('0')).arg(b, 2, 10, QChar('0'))); m_ui.b->setText(tr("0x%0 (%1)").arg(b, 2, 16, QChar('0')).arg(b, 2, 10, QChar('0')));

View File

@ -81,9 +81,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
controller->m_resetActions.clear(); controller->m_resetActions.clear();
if (!controller->m_hwaccel) { context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer.data()), controller->screenDimensions().width());
context->core->setVideoBuffer(context->core, reinterpret_cast<color_t*>(controller->m_activeBuffer.data()), controller->screenDimensions().width());
}
QMetaObject::invokeMethod(controller, "didReset"); QMetaObject::invokeMethod(controller, "didReset");
controller->finishFrame(); controller->finishFrame();
@ -363,14 +361,12 @@ void CoreController::setLogger(LogController* logger) {
} }
void CoreController::start() { void CoreController::start() {
if (!m_hwaccel) { QSize size(256, 384);
QSize size(256, 384); m_activeBuffer.resize(size.width() * size.height() * sizeof(color_t));
m_activeBuffer.resize(size.width() * size.height() * sizeof(color_t)); m_activeBuffer.fill(0xFF);
m_activeBuffer.fill(0xFF); m_completeBuffer = m_activeBuffer;
m_completeBuffer = m_activeBuffer;
m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer.data()), size.width()); m_threadContext.core->setVideoBuffer(m_threadContext.core, reinterpret_cast<color_t*>(m_activeBuffer.data()), size.width());
}
if (!m_patched) { if (!m_patched) {
mCoreAutoloadPatch(m_threadContext.core); mCoreAutoloadPatch(m_threadContext.core);
@ -848,11 +844,23 @@ void CoreController::endVideoLog(bool closeVf) {
void CoreController::setFramebufferHandle(int fb) { void CoreController::setFramebufferHandle(int fb) {
Interrupter interrupter(this); Interrupter interrupter(this);
if (fb < 0) { if (fb < 0) {
if (!m_hwaccel) {
return;
}
mCoreConfigSetIntValue(&m_threadContext.core->config, "hwaccelVideo", 0);
m_threadContext.core->setVideoGLTex(m_threadContext.core, -1);
m_hwaccel = false; m_hwaccel = false;
} else { } else {
mCoreConfigSetIntValue(&m_threadContext.core->config, "hwaccelVideo", 1);
m_threadContext.core->setVideoGLTex(m_threadContext.core, fb); m_threadContext.core->setVideoGLTex(m_threadContext.core, fb);
if (m_hwaccel) {
return;
}
m_hwaccel = true; m_hwaccel = true;
} }
if (hasStarted()) {
m_threadContext.core->reloadConfigOption(m_threadContext.core, "hwaccelVideo", NULL);
}
} }
void CoreController::updateKeys() { void CoreController::updateKeys() {

View File

@ -83,6 +83,7 @@ CoreController* CoreManager::loadGame(VFile* vf, const QString& path, const QStr
mCore* core = mCoreFindVF(vf); mCore* core = mCoreFindVF(vf);
if (!core) { if (!core) {
LOG(QT, ERROR) << tr("Could not load game. Are you sure it's in the correct format?");
return nullptr; return nullptr;
} }

View File

@ -578,10 +578,19 @@ void PainterGL::clearShaders() {
return; return;
} }
#ifdef BUILD_GLES2 #ifdef BUILD_GLES2
if (!m_started) {
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
}
if (m_shader.passes) { if (m_shader.passes) {
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend)); mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
mGLES2ShaderFree(&m_shader); mGLES2ShaderFree(&m_shader);
} }
if (!m_started) {
m_gl->doneCurrent();
}
#endif #endif
} }

View File

@ -45,6 +45,12 @@ LogController::Stream LogController::operator()(int category, int level) {
void LogController::load(const ConfigController* config) { void LogController::load(const ConfigController* config) {
mLogFilterLoad(&m_filter, config->config()); mLogFilterLoad(&m_filter, config->config());
if (!levels(mLogCategoryById("gba.bios"))) {
mLogFilterSet(&m_filter, "gba.bios", mLOG_STUB | mLOG_FATAL);
}
if (!levels(mLogCategoryById("core.status"))) {
mLogFilterSet(&m_filter, "core.status", mLOG_ALL & ~mLOG_DEBUG);
}
setLogFile(config->getOption("logFile")); setLogFile(config->getOption("logFile"));
logToStdout(config->getOption("logToStdout").toInt()); logToStdout(config->getOption("logToStdout").toInt());
logToFile(config->getOption("logToFile").toInt()); logToFile(config->getOption("logToFile").toInt());
@ -114,7 +120,7 @@ void LogController::disableLevels(int levels, int category) {
void LogController::clearLevels(int category) { void LogController::clearLevels(int category) {
auto id = mLogCategoryId(category); auto id = mLogCategoryId(category);
mLogFilterReset (&m_filter, id); mLogFilterReset(&m_filter, id);
} }
void LogController::logToFile(bool log) { void LogController::logToFile(bool log) {

View File

@ -487,12 +487,13 @@ void SettingsView::updateConfig() {
} }
int videoScale = m_controller->getOption("videoScale", 1).toInt(); int videoScale = m_controller->getOption("videoScale", 1).toInt();
saveSetting("videoScale", m_ui.videoScale);
int hwaccelVideo = m_controller->getOption("hwaccelVideo").toInt(); int hwaccelVideo = m_controller->getOption("hwaccelVideo").toInt();
saveSetting("hwaccelVideo", m_ui.hwaccelVideo->currentIndex());
if (hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) { if (hwaccelVideo != m_ui.hwaccelVideo->currentIndex()) {
emit videoRendererChanged(); emit videoRendererChanged();
} }
saveSetting("videoScale", m_ui.videoScale);
saveSetting("hwaccelVideo", m_ui.hwaccelVideo->currentIndex());
m_logModel.save(m_controller); m_logModel.save(m_controller);
m_logModel.logger()->setLogFile(m_ui.logFile->text()); m_logModel.logger()->setLogFile(m_ui.logFile->text());

View File

@ -159,7 +159,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
} }
Action* action = item.value()->action(); Action* action = item.value()->action();
if (action) { if (action) {
action->trigger(); if (m_actions->isHeld(action->name())) {
action->trigger(true);
} else {
action->trigger(!action->isActive());
}
} }
event->accept(); event->accept();
return true; return true;
@ -170,7 +174,7 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
return false; return false;
} }
Action* action = item.value()->action(); Action* action = item.value()->action();
if (action) { if (action && m_actions->isHeld(action->name())) {
action->trigger(false); action->trigger(false);
} }
event->accept(); event->accept();
@ -184,7 +188,15 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
} }
Action* action = item.value()->action(); Action* action = item.value()->action();
if (action) { if (action) {
action->trigger(gae->isNew()); if (gae->isNew()) {
if (m_actions->isHeld(action->name())) {
action->trigger(true);
} else {
action->trigger(!action->isActive());
}
} else if (m_actions->isHeld(action->name())) {
action->trigger(false);
}
} }
event->accept(); event->accept();
return true; return true;

View File

@ -510,7 +510,7 @@ void Window::openSettingsWindow() {
connect(settingsWindow, &SettingsView::audioDriverChanged, this, &Window::reloadAudioDriver); connect(settingsWindow, &SettingsView::audioDriverChanged, this, &Window::reloadAudioDriver);
connect(settingsWindow, &SettingsView::cameraDriverChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::cameraDriverChanged, this, &Window::mustRestart);
connect(settingsWindow, &SettingsView::cameraChanged, &m_inputController, &InputController::setCamera); connect(settingsWindow, &SettingsView::cameraChanged, &m_inputController, &InputController::setCamera);
connect(settingsWindow, &SettingsView::videoRendererChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::videoRendererChanged, this, &Window::changeRenderer);
connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart); connect(settingsWindow, &SettingsView::languageChanged, this, &Window::mustRestart);
connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig);
#ifdef USE_SQLITE3 #ifdef USE_SQLITE3
@ -926,8 +926,8 @@ void Window::gameCrashed(const QString& errorMessage) {
} }
void Window::gameFailed() { void Window::gameFailed() {
QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Load"), QMessageBox* fail = new QMessageBox(QMessageBox::Warning, tr("Couldn't Start"),
tr("Could not load game. Are you sure it's in the correct format?"), tr("Could not start game."),
QMessageBox::Ok, this, Qt::Sheet); QMessageBox::Ok, this, Qt::Sheet);
fail->setAttribute(Qt::WA_DeleteOnClose); fail->setAttribute(Qt::WA_DeleteOnClose);
fail->show(); fail->show();
@ -949,10 +949,6 @@ void Window::unimplementedBiosCall(int call) {
void Window::reloadDisplayDriver() { void Window::reloadDisplayDriver() {
if (m_controller) { if (m_controller) {
if (m_controller->hardwareAccelerated()) {
mustRestart();
return;
}
m_display->stopDrawing(); m_display->stopDrawing();
detachWidget(m_display.get()); detachWidget(m_display.get());
} }
@ -990,14 +986,7 @@ void Window::reloadDisplayDriver() {
#endif #endif
if (m_controller) { if (m_controller) {
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext); attachDisplay();
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::paused, m_display.get(), &Display::pauseDrawing);
connect(m_controller.get(), &CoreController::unpaused, m_display.get(), &Display::unpauseDrawing);
connect(m_controller.get(), &CoreController::frameAvailable, m_display.get(), &Display::framePosted);
connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage);
connect(m_controller.get(), &CoreController::didReset, m_display.get(), &Display::resizeContext);
attachWidget(m_display.get()); attachWidget(m_display.get());
m_display->startDrawing(m_controller); m_display->startDrawing(m_controller);
@ -1028,6 +1017,27 @@ void Window::reloadAudioDriver() {
connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged); connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged);
} }
void Window::changeRenderer() {
if (!m_controller) {
return;
}
if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && m_controller->supportsFeature(CoreController::Feature::OPENGL)) {
std::shared_ptr<VideoProxy> proxy = m_display->videoProxy();
if (!proxy) {
proxy = std::make_shared<VideoProxy>();
}
m_display->setVideoProxy(proxy);
proxy->attach(m_controller.get());
int fb = m_display->framebufferHandle();
if (fb >= 0) {
m_controller->setFramebufferHandle(fb);
}
} else {
m_controller->setFramebufferHandle(-1);
}
}
void Window::tryMakePortable() { void Window::tryMakePortable() {
QMessageBox* confirm = new QMessageBox(QMessageBox::Question, tr("Really make portable?"), QMessageBox* confirm = new QMessageBox(QMessageBox::Question, tr("Really make portable?"),
tr("This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?"), tr("This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?"),
@ -1267,10 +1277,10 @@ void Window::setupMenu(QMenuBar* menubar) {
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
m_actions.addSeparator("file"); m_actions.addSeparator("file");
Action* importShark = addGameAction(tr("Import GameShark Save"), "importShark", this, &Window::importSharkport, "file"); Action* importShark = addGameAction(tr("Import GameShark Save..."), "importShark", this, &Window::importSharkport, "file");
m_platformActions.insert(PLATFORM_GBA, importShark); m_platformActions.insert(PLATFORM_GBA, importShark);
Action* exportShark = addGameAction(tr("Export GameShark Save"), "exportShark", this, &Window::exportSharkport, "file"); Action* exportShark = addGameAction(tr("Export GameShark Save..."), "exportShark", this, &Window::exportSharkport, "file");
m_platformActions.insert(PLATFORM_GBA, exportShark); m_platformActions.insert(PLATFORM_GBA, exportShark);
#endif #endif
@ -1828,7 +1838,6 @@ void Window::updateFrame() {
void Window::setController(CoreController* controller, const QString& fname) { void Window::setController(CoreController* controller, const QString& fname) {
if (!controller) { if (!controller) {
gameFailed();
return; return;
} }
if (m_pendingClose) { if (m_pendingClose) {
@ -1851,17 +1860,6 @@ void Window::setController(CoreController* controller, const QString& fname) {
reloadDisplayDriver(); reloadDisplayDriver();
} }
if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && controller->supportsFeature(CoreController::Feature::OPENGL)) {
std::shared_ptr<VideoProxy> proxy = std::make_shared<VideoProxy>();
m_display->setVideoProxy(proxy);
proxy->attach(controller);
int fb = m_display->framebufferHandle();
if (fb >= 0) {
controller->setFramebufferHandle(fb);
}
}
m_controller = std::shared_ptr<CoreController>(controller); m_controller = std::shared_ptr<CoreController>(controller);
m_inputController.recalibrateAxes(); m_inputController.recalibrateAxes();
m_controller->setInputController(&m_inputController); m_controller->setInputController(&m_inputController);
@ -1901,14 +1899,7 @@ void Window::setController(CoreController* controller, const QString& fname) {
emit paused(false); emit paused(false);
}); });
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext); attachDisplay();
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::paused, m_display.get(), &Display::pauseDrawing);
connect(m_controller.get(), &CoreController::unpaused, m_display.get(), &Display::unpauseDrawing);
connect(m_controller.get(), &CoreController::frameAvailable, m_display.get(), &Display::framePosted);
connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage);
connect(m_controller.get(), &CoreController::didReset, m_display.get(), &Display::resizeContext);
connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver); connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver);
connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame); connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame);
@ -1965,6 +1956,18 @@ void Window::setController(CoreController* controller, const QString& fname) {
} }
} }
void Window::attachDisplay() {
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext);
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::paused, m_display.get(), &Display::pauseDrawing);
connect(m_controller.get(), &CoreController::unpaused, m_display.get(), &Display::unpauseDrawing);
connect(m_controller.get(), &CoreController::frameAvailable, m_display.get(), &Display::framePosted);
connect(m_controller.get(), &CoreController::statusPosted, m_display.get(), &Display::showMessage);
connect(m_controller.get(), &CoreController::didReset, m_display.get(), &Display::resizeContext);
changeRenderer();
}
WindowBackground::WindowBackground(QWidget* parent) WindowBackground::WindowBackground(QWidget* parent)
: QWidget(parent) : QWidget(parent)
{ {

View File

@ -130,6 +130,7 @@ private slots:
void reloadAudioDriver(); void reloadAudioDriver();
void reloadDisplayDriver(); void reloadDisplayDriver();
void changeRenderer();
void tryMakePortable(); void tryMakePortable();
void mustRestart(); void mustRestart();
@ -155,6 +156,7 @@ private:
void updateMRU(); void updateMRU();
void openView(QWidget* widget); void openView(QWidget* widget);
void attachDisplay();
template <typename T, typename... A> std::function<void()> openTView(A... arg); template <typename T, typename... A> std::function<void()> openTView(A... arg);
template <typename T, typename... A> std::function<void()> openControllerTView(A... arg); template <typename T, typename... A> std::function<void()> openControllerTView(A... arg);

View File

@ -387,9 +387,21 @@ void InputController::setPreferredGamepad(uint32_t type, int index) {
if (!m_config) { if (!m_config) {
return; return;
} }
#ifdef BUILD_SDL
char name[34] = {0}; char name[34] = {0};
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name)); SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name));
#else
const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick));
if (!name) {
return;
}
#endif
mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name); mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name);
#else
UNUSED(type);
UNUSED(index);
#endif
} }
mRumble* InputController::rumble() { mRumble* InputController::rumble() {

File diff suppressed because it is too large Load Diff

View File

@ -320,6 +320,11 @@ static void _gameLoaded(struct mGUIRunner* runner) {
} }
} }
int scale;
if (mCoreConfigGetUIntValue(&runner->config, "videoScale", &scale)) {
runner->core->reloadConfigOption(runner->core, "videoScale", &runner->config);
}
rumble.up = 0; rumble.up = 0;
rumble.down = 0; rumble.down = 0;
} }
@ -857,7 +862,7 @@ int main(int argc, char* argv[]) {
.nStates = 16 .nStates = 16
}, },
{ {
.title = "GPU-accelerated renderer (experimental, requires game reload)", .title = "GPU-accelerated renderer (requires game reload)",
.data = "hwaccelVideo", .data = "hwaccelVideo",
.submenu = 0, .submenu = 0,
.state = 0, .state = 0,

View File

@ -84,6 +84,9 @@ void ELFGetProgramHeaders(struct ELF* elf, struct ELFProgramHeaders* ph) {
ELFProgramHeadersClear(ph); ELFProgramHeadersClear(ph);
Elf32_Ehdr* hdr = elf32_getehdr(elf->e); Elf32_Ehdr* hdr = elf32_getehdr(elf->e);
Elf32_Phdr* phdr = elf32_getphdr(elf->e); Elf32_Phdr* phdr = elf32_getphdr(elf->e);
if (!hdr || !phdr) {
return;
}
ELFProgramHeadersResize(ph, hdr->e_phnum); ELFProgramHeadersResize(ph, hdr->e_phnum);
memcpy(ELFProgramHeadersGetPointer(ph, 0), phdr, sizeof(*phdr) * hdr->e_phnum); memcpy(ELFProgramHeadersGetPointer(ph, 0), phdr, sizeof(*phdr) * hdr->e_phnum);
} }