Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2019-09-28 15:25:08 -07:00
commit 8ca80d4a94
94 changed files with 2542 additions and 1028 deletions

12
CHANGES
View File

@ -28,6 +28,8 @@ Features:
- Support Discord Rich Presence - Support Discord Rich Presence
- Debugger: Add tracing to file - Debugger: Add tracing to file
- Map viewer supports bitmapped GBA modes - Map viewer supports bitmapped GBA modes
- OpenGL renderer with high-resolution upscaling support
- Experimental high level "XQ" audio for most GBA games
Emulation fixes: Emulation fixes:
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208) - GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
- GBA: Reset now reloads multiboot ROMs - GBA: Reset now reloads multiboot ROMs
@ -43,6 +45,9 @@ Emulation fixes:
- GBA Memory: Fix writing to OBJ memory in modes 3 and 5 - GBA Memory: Fix writing to OBJ memory in modes 3 and 5
- GBA: Fix RTC on non-standard sized ROMs (fixes mgba.io/i/1400) - GBA: Fix RTC on non-standard sized ROMs (fixes mgba.io/i/1400)
- GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743) - GBA Memory: Prevent writing to mirrored BG VRAM (fixes mgba.io/i/743)
- GBA Video: Fix sprite mosaic clamping (fixes mgba.io/i/1008)
- GB: Fix HALT when IE and IF unused bits are set (fixes mgba.io/i/1349)
- GBA Video: Implement mosaic on transformed sprites (fixes mgba.io/b/9)
Other fixes: Other fixes:
- Qt: More app metadata fixes - Qt: More app metadata fixes
- Qt: Fix load recent from archive (fixes mgba.io/i/1325) - Qt: Fix load recent from archive (fixes mgba.io/i/1325)
@ -60,6 +65,10 @@ Other fixes:
- Wii: Fix aspect ratio (fixes mgba.io/i/500) - Wii: Fix aspect ratio (fixes mgba.io/i/500)
- Qt: Fix some Qt display driver race conditions - Qt: Fix some Qt display driver race conditions
- FFmpeg: Fix audio conversion producing gaps - FFmpeg: Fix audio conversion producing gaps
- Core: Improved lockstep driver reliability (Le Hoang Quyen)
- GBA: Fix skipping BIOS on irregularly sized ROMs
- Qt: Fix bounded fast forward with Qt Multimedia
- Qt: Fix saving settings with native FPS target
Misc: Misc:
- GBA Savedata: EEPROM performance fixes - GBA Savedata: EEPROM performance fixes
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
@ -79,6 +88,9 @@ Misc:
- Qt: Open a message box for Qt frontend errors - Qt: Open a message box for Qt frontend errors
- GBA Video: Clean up dead code in sprite rendering loop - GBA Video: Clean up dead code in sprite rendering loop
- FFmpeg: Support audio-only recording - FFmpeg: Support audio-only recording
- Qt: Increase maximum magnifications and scaling
- Qt: Add native FPS button to settings view
- Qt: Improve sync code
0.7.1: (2019-02-24) 0.7.1: (2019-02-24)
Bugfixes: Bugfixes:

View File

@ -90,6 +90,7 @@ file(GLOB GB_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/*.c)
file(GLOB GB_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/test/*.c) file(GLOB GB_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/test/*.c)
file(GLOB DS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/*.c) file(GLOB DS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/ds/*.c)
file(GLOB GBA_CHEATS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/cheats/*.c) file(GLOB GBA_CHEATS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/cheats/*.c)
file(GLOB GBA_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/audio-mixer.c)
file(GLOB GBA_RR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/rr/*.c) file(GLOB GBA_RR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/rr/*.c)
file(GLOB CORE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/*.c) file(GLOB CORE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/*.c)
file(GLOB CORE_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/test/*.c) file(GLOB CORE_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/test/*.c)
@ -261,15 +262,10 @@ if(WIN32)
endif() endif()
elseif(UNIX) elseif(UNIX)
set(USE_PTHREADS ON) set(USE_PTHREADS ON)
add_definitions(-DUSE_PTHREADS)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_definitions(-D_GNU_SOURCE) add_definitions(-D_GNU_SOURCE)
endif() endif()
if(NOT APPLE AND NOT HAIKU)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-dirent.c) list(APPEND CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fd.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-dirent.c)
file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/posix/*.c) file(GLOB OS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/posix/*.c)
@ -355,6 +351,7 @@ if(3DS OR WII)
add_definitions(-D_GNU_SOURCE) add_definitions(-D_GNU_SOURCE)
endif() endif()
include(CheckCCompilerFlag)
include(CheckFunctionExists) include(CheckFunctionExists)
include(CheckIncludeFiles) include(CheckIncludeFiles)
check_function_exists(strdup HAVE_STRDUP) check_function_exists(strdup HAVE_STRDUP)
@ -406,6 +403,27 @@ endif()
check_function_exists(chmod HAVE_CHMOD) check_function_exists(chmod HAVE_CHMOD)
check_function_exists(umask HAVE_UMASK) check_function_exists(umask HAVE_UMASK)
if(USE_PTHREADS)
check_include_files("pthread.h" HAVE_PTHREAD_H)
if(HAVE_PTHREAD_H)
check_c_compiler_flag(-pthread HAVE_PTHREAD)
if(HAVE_PTHREAD AND NOT APPLE AND NOT HAIKU)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
endif()
check_function_exists(pthread_create HAVE_PTHREAD_CREATE)
if(HAVE_PTHREAD_CREATE)
add_definitions(-DUSE_PTHREADS)
check_include_files("pthread_np.h" HAVE_PTHREAD_NP_H)
check_function_exists(pthread_setname_np HAVE_PTHREAD_SETNAME_NP)
check_function_exists(pthread_set_name_np HAVE_PTHREAD_SET_NAME_NP)
endif()
endif()
endif()
set(FUNCTION_DEFINES) set(FUNCTION_DEFINES)
if(HAVE_STRDUP) if(HAVE_STRDUP)
@ -447,6 +465,18 @@ if(HAVE_UMASK)
list(APPEND FUNCTION_DEFINES HAVE_UMASK) list(APPEND FUNCTION_DEFINES HAVE_UMASK)
endif() endif()
if(HAVE_PTHREAD_NP_H)
list(APPEND FUNCTION_DEFINES HAVE_PTHREAD_NP_H)
endif()
if(HAVE_PTHREAD_SETNAME_NP)
list(APPEND FUNCTION_DEFINES HAVE_PTHREAD_SETNAME_NP)
endif()
if(HAVE_PTHREAD_SET_NAME_NP)
list(APPEND FUNCTION_DEFINES HAVE_PTHREAD_SET_NAME_NP)
endif()
# Feature dependencies # Feature dependencies
set(FEATURE_DEFINES) set(FEATURE_DEFINES)
set(FEATURE_FLAGS) set(FEATURE_FLAGS)

View File

@ -233,7 +233,6 @@ Footnotes
<a name="missing">[1]</a> Currently missing features on GBA are <a name="missing">[1]</a> Currently missing features on GBA are
- OBJ window for modes 3, 4 and 5 ([Bug #5](http://mgba.io/b/5)) - OBJ window for modes 3, 4 and 5 ([Bug #5](http://mgba.io/b/5))
- Mosaic for transformed OBJs ([Bug #9](http://mgba.io/b/9))
Missing features on DS are Missing features on DS are

View File

@ -215,7 +215,6 @@ Fußnoten
<a name="missing">[1]</a> Zurzeit fehlende Features sind <a name="missing">[1]</a> Zurzeit fehlende Features sind
- OBJ-Fenster für die Modi 3, 4 und 5 ([Bug #5](http://mgba.io/b/5)) - OBJ-Fenster für die Modi 3, 4 und 5 ([Bug #5](http://mgba.io/b/5))
- Mosaik-Effekt für umgewandelte OBJs ([Bug #9](http://mgba.io/b/9))
<a name="flashdetect">[2]</a> In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden. <a name="flashdetect">[2]</a> In manchen Fällen ist es nicht möglich, die Größe des Flash-Speichers automatisch zu ermitteln. Diese kann dann zur Laufzeit konfiguriert werden, es wird jedoch empfohlen, den Fehler zu melden.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -12,7 +12,7 @@ CXX_GUARD_START
#include <pthread.h> #include <pthread.h>
#include <sys/time.h> #include <sys/time.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__) #ifdef HAVE_PTHREAD_NP_H
#include <pthread_np.h> #include <pthread_np.h>
#elif defined(__HAIKU__) #elif defined(__HAIKU__)
#include <OS.h> #include <OS.h>
@ -85,20 +85,15 @@ static inline int ThreadJoin(Thread thread) {
} }
static inline int ThreadSetName(const char* name) { static inline int ThreadSetName(const char* name) {
#ifdef __APPLE__ #if defined(__APPLE__) && defined(HAVE_PTHREAD_SETNAME_NP)
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060
return pthread_setname_np(name); return pthread_setname_np(name);
#else #elif defined(HAVE_PTHREAD_SET_NAME_NP)
UNUSED(name);
return 0;
#endif
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name); pthread_set_name_np(pthread_self(), name);
return 0; return 0;
#elif defined(__HAIKU__) #elif defined(__HAIKU__)
rename_thread(find_thread(NULL), name); rename_thread(find_thread(NULL), name);
return 0; return 0;
#elif !defined(BUILD_PANDORA) // Pandora's glibc is too old #elif defined(HAVE_PTHREAD_SETNAME_NP)
return pthread_setname_np(pthread_self(), name); return pthread_setname_np(pthread_self(), name);
#else #else
UNUSED(name); UNUSED(name);

View File

@ -13,6 +13,10 @@ CXX_GUARD_START
enum mCPUComponentType { enum mCPUComponentType {
CPU_COMPONENT_DEBUGGER, CPU_COMPONENT_DEBUGGER,
CPU_COMPONENT_CHEAT_DEVICE, CPU_COMPONENT_CHEAT_DEVICE,
CPU_COMPONENT_MISC_1,
CPU_COMPONENT_MISC_2,
CPU_COMPONENT_MISC_3,
CPU_COMPONENT_MISC_4,
CPU_COMPONENT_MAX CPU_COMPONENT_MAX
}; };

View File

@ -10,10 +10,14 @@
CXX_GUARD_START CXX_GUARD_START
#include <mgba/core/cpu.h>
#include <mgba/core/log.h> #include <mgba/core/log.h>
#include <mgba/internal/gb/audio.h> #include <mgba/internal/gb/audio.h>
#include <mgba-util/circle-buffer.h> #include <mgba-util/circle-buffer.h>
#define MP2K_MAGIC 0x68736D53
#define MP2K_MAX_SOUND_CHANNELS 12
mLOG_DECLARE_CATEGORY(GBA_AUDIO); mLOG_DECLARE_CATEGORY(GBA_AUDIO);
struct GBADMA; struct GBADMA;
@ -44,6 +48,7 @@ DECL_BITFIELD(GBARegisterSOUNDBIAS, uint16_t);
DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10); DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10);
DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2); DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2);
struct GBAAudioMixer;
struct GBAAudio { struct GBAAudio {
struct GBA* p; struct GBA* p;
@ -71,6 +76,8 @@ struct GBAAudio {
GBARegisterSOUNDBIAS soundbias; GBARegisterSOUNDBIAS soundbias;
struct GBAAudioMixer* mixer;
bool externalMixing;
int32_t sampleInterval; int32_t sampleInterval;
bool forceDisableChA; bool forceDisableChA;
@ -85,6 +92,188 @@ struct GBAStereoSample {
int16_t right; int16_t right;
}; };
struct GBAMP2kADSR {
uint8_t attack;
uint8_t decay;
uint8_t sustain;
uint8_t release;
};
struct GBAMP2kSoundChannel {
uint8_t status;
uint8_t type;
uint8_t rightVolume;
uint8_t leftVolume;
struct GBAMP2kADSR adsr;
uint8_t ky;
uint8_t envelopeV;
uint8_t envelopeRight;
uint8_t envelopeLeft;
uint8_t echoVolume;
uint8_t echoLength;
uint8_t d1;
uint8_t d2;
uint8_t gt;
uint8_t midiKey;
uint8_t ve;
uint8_t pr;
uint8_t rp;
uint8_t d3[3];
uint32_t ct;
uint32_t fw;
uint32_t freq;
uint32_t waveData;
uint32_t cp;
uint32_t track;
uint32_t pp;
uint32_t np;
uint32_t d4;
uint16_t xpi;
uint16_t xpc;
};
struct GBAMP2kContext {
uint32_t magic;
uint8_t pcmDmaCounter;
uint8_t reverb;
uint8_t maxChans;
uint8_t masterVolume;
uint8_t freq;
uint8_t mode;
uint8_t c15;
uint8_t pcmDmaPeriod;
uint8_t maxLines;
uint8_t gap[3];
int32_t pcmSamplesPerVBlank;
int32_t pcmFreq;
int32_t divFreq;
uint32_t cgbChans;
uint32_t func;
uint32_t intp;
uint32_t cgbSound;
uint32_t cgbOscOff;
uint32_t midiKeyToCgbFreq;
uint32_t mPlayJumpTable;
uint32_t plynote;
uint32_t extVolPit;
uint8_t gap2[16];
struct GBAMP2kSoundChannel chans[MP2K_MAX_SOUND_CHANNELS];
};
struct GBAMP2kMusicPlayerInfo {
uint32_t songHeader;
uint32_t status;
uint8_t trackCount;
uint8_t priority;
uint8_t cmd;
uint8_t unk_B;
uint32_t clock;
uint8_t gap[8];
uint32_t memAccArea;
uint16_t tempoD;
uint16_t tempoU;
uint16_t tempoI;
uint16_t tempoC;
uint16_t fadeOI;
uint16_t fadeOC;
uint16_t fadeOV;
uint32_t tracks;
uint32_t tone;
uint32_t magic;
uint32_t func;
uint32_t intp;
};
struct GBAMP2kInstrument {
uint8_t type;
uint8_t key;
uint8_t length;
union {
uint8_t pan;
uint8_t sweep;
} ps;
union {
uint32_t waveData;
uint32_t subTable;
} data;
union {
struct GBAMP2kADSR adsr;
uint32_t map;
} extInfo;
};
struct GBAMP2kMusicPlayerTrack {
uint8_t flags;
uint8_t wait;
uint8_t patternLevel;
uint8_t repN;
uint8_t gateTime;
uint8_t key;
uint8_t velocity;
uint8_t runningStatus;
uint8_t keyM;
uint8_t pitM;
int8_t keyShift;
int8_t keyShiftX;
int8_t tune;
uint8_t pitX;
int8_t bend;
uint8_t bendRange;
uint8_t volMR;
uint8_t volML;
uint8_t vol;
uint8_t volX;
int8_t pan;
int8_t panX;
int8_t modM;
uint8_t mod;
uint8_t modT;
uint8_t lfoSpeed;
uint8_t lfoSpeedC;
uint8_t lfoDelay;
uint8_t lfoDelayC;
uint8_t priority;
uint8_t echoVolume;
uint8_t echoLength;
uint32_t chan;
struct GBAMP2kInstrument instrument;
uint8_t gap[10];
uint16_t unk_3A;
uint32_t unk_3C;
uint32_t cmdPtr;
uint32_t patternStack[3];
};
struct GBAMP2kTrack {
struct GBAMP2kMusicPlayerTrack track;
struct GBAMP2kSoundChannel* channel;
uint8_t lastCommand;
struct CircleBuffer buffer;
uint32_t samplePlaying;
float currentOffset;
bool waiting;
};
struct GBAAudioMixer {
struct mCPUComponent d;
struct GBAAudio* p;
uint32_t contextAddress;
bool (*engage)(struct GBAAudioMixer* mixer, uint32_t address);
void (*vblank)(struct GBAAudioMixer* mixer);
void (*step)(struct GBAAudioMixer* mixer);
struct GBAMP2kContext context;
struct GBAMP2kMusicPlayerInfo player;
struct GBAMP2kTrack activeTracks[MP2K_MAX_SOUND_CHANNELS];
double tempo;
double frame;
struct GBAStereoSample last;
};
void GBAAudioInit(struct GBAAudio* audio, size_t samples); void GBAAudioInit(struct GBAAudio* audio, size_t samples);
void GBAAudioReset(struct GBAAudio* audio); void GBAAudioReset(struct GBAAudio* audio);
void GBAAudioDeinit(struct GBAAudio* audio); void GBAAudioDeinit(struct GBAAudio* audio);

View File

@ -0,0 +1,19 @@
/* Copyright (c) 2013-2017 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_AUDIO_MIXER_H
#define GBA_AUDIO_MIXER_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/internal/gba/audio.h>
void GBAAudioMixerCreate(struct GBAAudioMixer* mixer);
CXX_GUARD_END
#endif

View File

@ -62,20 +62,25 @@ struct GBAVideoGLBackground {
int32_t refx; int32_t refx;
int32_t refy; int32_t refy;
struct GBAVideoGLAffine affine[4]; struct GBAVideoGLAffine affine;
}; };
enum { enum {
GBA_GL_FBO_OBJ = 0, GBA_GL_FBO_OBJ = 0,
GBA_GL_FBO_WINDOW = 1, GBA_GL_FBO_BACKDROP,
GBA_GL_FBO_OUTPUT = 2, GBA_GL_FBO_WINDOW,
GBA_GL_FBO_OUTPUT,
GBA_GL_FBO_MAX GBA_GL_FBO_MAX
}; };
enum { enum {
GBA_GL_TEX_OBJ_COLOR = 0, GBA_GL_TEX_OBJ_COLOR = 0,
GBA_GL_TEX_OBJ_FLAGS = 1, GBA_GL_TEX_OBJ_FLAGS,
GBA_GL_TEX_WINDOW = 6, GBA_GL_TEX_BACKDROP_COLOR,
GBA_GL_TEX_BACKDROP_FLAGS,
GBA_GL_TEX_WINDOW,
GBA_GL_TEX_AFFINE_2,
GBA_GL_TEX_AFFINE_3,
GBA_GL_TEX_MAX GBA_GL_TEX_MAX
}; };
@ -91,6 +96,8 @@ enum {
GBA_GL_BG_OFFSET, GBA_GL_BG_OFFSET,
GBA_GL_BG_INFLAGS, GBA_GL_BG_INFLAGS,
GBA_GL_BG_TRANSFORM, GBA_GL_BG_TRANSFORM,
GBA_GL_BG_RANGE,
GBA_GL_BG_MOSAIC,
GBA_GL_OBJ_VRAM = 2, GBA_GL_OBJ_VRAM = 2,
GBA_GL_OBJ_PALETTE, GBA_GL_OBJ_PALETTE,
@ -101,6 +108,7 @@ enum {
GBA_GL_OBJ_TRANSFORM, GBA_GL_OBJ_TRANSFORM,
GBA_GL_OBJ_DIMS, GBA_GL_OBJ_DIMS,
GBA_GL_OBJ_OBJWIN, GBA_GL_OBJ_OBJWIN,
GBA_GL_OBJ_MOSAIC,
GBA_GL_FINALIZE_SCALE = 2, GBA_GL_FINALIZE_SCALE = 2,
GBA_GL_FINALIZE_LAYERS, GBA_GL_FINALIZE_LAYERS,
@ -124,6 +132,7 @@ struct GBAVideoGLRenderer {
uint32_t* temporaryBuffer; uint32_t* temporaryBuffer;
struct GBAVideoGLBackground bg[4]; struct GBAVideoGLBackground bg[4];
struct GBAVideoGLAffine affine[2][GBA_VIDEO_VERTICAL_PIXELS];
int oamMax; int oamMax;
bool oamDirty; bool oamDirty;
@ -144,6 +153,9 @@ struct GBAVideoGLRenderer {
GLuint vramTex; GLuint vramTex;
unsigned vramDirty; unsigned vramDirty;
uint16_t shadowRegs[0x30];
uint64_t regsDirty;
struct GBAVideoGLShader bgShader[6]; struct GBAVideoGLShader bgShader[6];
struct GBAVideoGLShader objShader[2]; struct GBAVideoGLShader objShader[2];
struct GBAVideoGLShader finalizeShader; struct GBAVideoGLShader finalizeShader;
@ -162,7 +174,7 @@ struct GBAVideoGLRenderer {
GBAMosaicControl mosaic; GBAMosaicControl mosaic;
struct GBAVideoGLWindowN { struct GBAVideoGLWindowN {
struct GBAVideoWindowRegion h; struct GBAVideoWindowRegion h[2];
struct GBAVideoWindowRegion v; struct GBAVideoWindowRegion v;
GBAWindowControl control; GBAWindowControl control;
} winN[2]; } winN[2];
@ -171,6 +183,7 @@ struct GBAVideoGLRenderer {
GBAWindowControl objwin; GBAWindowControl objwin;
int firstAffine; int firstAffine;
int firstY;
int scale; int scale;
}; };

View File

@ -153,10 +153,12 @@ static void _wait(struct mVideoLogger* logger) {
_proxyThreadRecover(proxyRenderer); _proxyThreadRecover(proxyRenderer);
return; return;
} }
MutexLock(&proxyRenderer->mutex);
while (RingFIFOSize(&proxyRenderer->dirtyQueue)) { while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
ConditionWake(&proxyRenderer->toThreadCond); ConditionWake(&proxyRenderer->toThreadCond);
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
} }
MutexUnlock(&proxyRenderer->mutex);
} }
static void _unlock(struct mVideoLogger* logger) { static void _unlock(struct mVideoLogger* logger) {

View File

@ -250,31 +250,21 @@ void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y)
void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) { void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
}
if (!proxyRenderer->logger->block) { if (!proxyRenderer->logger->block) {
proxyRenderer->backend->finishFrame(proxyRenderer->backend); proxyRenderer->backend->finishFrame(proxyRenderer->backend);
} }
mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->unlock(proxyRenderer->logger);
}
} }
static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) { static void GBVideoProxyRendererEnableSGBBorder(struct GBVideoRenderer* renderer, bool enable) {
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed // Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger); proxyRenderer->logger->wait(proxyRenderer->logger);
} }
proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable); proxyRenderer->backend->enableSGBBorder(proxyRenderer->backend, enable);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->unlock(proxyRenderer->logger);
}
} }
static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) { static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) {
@ -297,9 +287,6 @@ static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->wait(proxyRenderer->logger);
} }
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {

View File

@ -723,7 +723,7 @@ static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cycle
void GBHalt(struct LR35902Core* cpu) { void GBHalt(struct LR35902Core* cpu) {
struct GB* gb = (struct GB*) cpu->master; struct GB* gb = (struct GB*) cpu->master;
if (!(gb->memory.ie & gb->memory.io[REG_IF])) { if (!(gb->memory.ie & gb->memory.io[REG_IF] & 0x1F)) {
cpu->cycles = cpu->nextEvent; cpu->cycles = cpu->nextEvent;
cpu->halted = true; cpu->halted = true;
} else if (gb->model < GB_MODEL_CGB) { } else if (gb->model < GB_MODEL_CGB) {

View File

@ -14,6 +14,8 @@
#include <mgba/internal/gba/serialize.h> #include <mgba/internal/gba/serialize.h>
#include <mgba/internal/gba/video.h> #include <mgba/internal/gba/video.h>
#define MP2K_LOCK_MAX 8
#ifdef _3DS #ifdef _3DS
#define blip_add_delta blip_add_delta_fast #define blip_add_delta blip_add_delta_fast
#endif #endif
@ -24,7 +26,7 @@ const unsigned GBA_AUDIO_SAMPLES = 2048;
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t); const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
const int GBA_AUDIO_VOLUME_MAX = 0x100; const int GBA_AUDIO_VOLUME_MAX = 0x100;
static const int CLOCKS_PER_FRAME = 0x400; static const int CLOCKS_PER_FRAME = 0x800;
static int _applyBias(struct GBAAudio* audio, int sample); static int _applyBias(struct GBAAudio* audio, int sample);
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate); static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
@ -49,9 +51,11 @@ void GBAAudioInit(struct GBAAudio* audio, size_t samples) {
CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chA.fifo, GBA_AUDIO_FIFO_SIZE);
CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE); CircleBufferInit(&audio->chB.fifo, GBA_AUDIO_FIFO_SIZE);
audio->externalMixing = false;
audio->forceDisableChA = false; audio->forceDisableChA = false;
audio->forceDisableChB = false; audio->forceDisableChB = false;
audio->masterVolume = GBA_AUDIO_VOLUME_MAX; audio->masterVolume = GBA_AUDIO_VOLUME_MAX;
audio->mixer = NULL;
} }
void GBAAudioReset(struct GBAAudio* audio) { void GBAAudioReset(struct GBAAudio* audio) {
@ -111,6 +115,20 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest);
return; return;
} }
uint32_t source = info->source;
uint32_t magic[2] = {
audio->p->cpu->memory.load32(audio->p->cpu, source - 0x350, NULL),
audio->p->cpu->memory.load32(audio->p->cpu, source - 0x980, NULL)
};
if (audio->mixer) {
if (magic[0] - MP2K_MAGIC <= MP2K_LOCK_MAX) {
audio->mixer->engage(audio->mixer, source - 0x350);
} else if (magic[1] - MP2K_MAGIC <= MP2K_LOCK_MAX) {
audio->mixer->engage(audio->mixer, source - 0x980);
} else {
audio->externalMixing = false;
}
}
info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED);
info->reg = GBADMARegisterSetWidth(info->reg, 1); info->reg = GBADMARegisterSetWidth(info->reg, 1);
} }
@ -265,23 +283,28 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
sampleLeft >>= psgShift; sampleLeft >>= psgShift;
sampleRight >>= psgShift; sampleRight >>= psgShift;
if (!audio->forceDisableChA) { if (audio->mixer) {
if (audio->chALeft) { audio->mixer->step(audio->mixer);
sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA;
}
if (audio->chARight) {
sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA;
}
} }
if (!audio->externalMixing) {
if (!audio->forceDisableChA) {
if (audio->chALeft) {
sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA;
}
if (!audio->forceDisableChB) { if (audio->chARight) {
if (audio->chBLeft) { sampleRight += (audio->chA.sample << 2) >> !audio->volumeChA;
sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB; }
} }
if (audio->chBRight) { if (!audio->forceDisableChB) {
sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB; if (audio->chBLeft) {
sampleLeft += (audio->chB.sample << 2) >> !audio->volumeChB;
}
if (audio->chBRight) {
sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB;
}
} }
} }

View File

@ -12,6 +12,7 @@
#include <mgba/internal/gba/cheats.h> #include <mgba/internal/gba/cheats.h>
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
#include <mgba/internal/gba/extra/audio-mixer.h>
#include <mgba/internal/gba/extra/cli.h> #include <mgba/internal/gba/extra/cli.h>
#include <mgba/internal/gba/overrides.h> #include <mgba/internal/gba/overrides.h>
#ifndef DISABLE_THREADING #ifndef DISABLE_THREADING
@ -127,6 +128,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
}; };
struct mVideoLogContext; struct mVideoLogContext;
#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1
struct GBACore { struct GBACore {
struct mCore d; struct mCore d;
struct GBAVideoSoftwareRenderer renderer; struct GBAVideoSoftwareRenderer renderer;
@ -144,6 +148,7 @@ struct GBACore {
const struct Configuration* overrides; const struct Configuration* overrides;
struct mDebuggerPlatform* debuggerPlatform; struct mDebuggerPlatform* debuggerPlatform;
struct mCheatDevice* cheatDevice; struct mCheatDevice* cheatDevice;
struct GBAAudioMixer* audioMixer;
}; };
static bool _GBACoreInit(struct mCore* core) { static bool _GBACoreInit(struct mCore* core) {
@ -166,6 +171,7 @@ static bool _GBACoreInit(struct mCore* core) {
gbacore->debuggerPlatform = NULL; gbacore->debuggerPlatform = NULL;
gbacore->cheatDevice = NULL; gbacore->cheatDevice = NULL;
gbacore->logContext = NULL; gbacore->logContext = NULL;
gbacore->audioMixer = NULL;
GBACreate(gba); GBACreate(gba);
// TODO: Restore cheats // TODO: Restore cheats
@ -217,6 +223,7 @@ static void _GBACoreDeinit(struct mCore* core) {
mCheatDeviceDestroy(gbacore->cheatDevice); mCheatDeviceDestroy(gbacore->cheatDevice);
} }
free(gbacore->cheatDevice); free(gbacore->cheatDevice);
free(gbacore->audioMixer);
mCoreConfigFreeOpts(&core->opts); mCoreConfigFreeOpts(&core->opts);
free(core); free(core);
} }
@ -280,6 +287,7 @@ static void _GBACoreLoadConfig(struct mCore* core, const struct mCoreConfig* con
mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections"); mCoreConfigCopyValue(&core->config, config, "allowOpposingDirections");
mCoreConfigCopyValue(&core->config, config, "gba.bios"); mCoreConfigCopyValue(&core->config, config, "gba.bios");
mCoreConfigCopyValue(&core->config, config, "gba.audioHle");
#ifndef DISABLE_THREADING #ifndef DISABLE_THREADING
mCoreConfigCopyValue(&core->config, config, "threadedVideo"); mCoreConfigCopyValue(&core->config, config, "threadedVideo");
@ -475,6 +483,16 @@ static void _GBACoreReset(struct mCore* core) {
GBAVideoAssociateRenderer(&gba->video, renderer); GBAVideoAssociateRenderer(&gba->video, renderer);
} }
#ifndef MINIMAL_CORE
int useAudioMixer;
if (!gbacore->audioMixer && mCoreConfigGetIntValue(&core->config, "gba.audioHle", &useAudioMixer) && useAudioMixer) {
gbacore->audioMixer = malloc(sizeof(*gbacore->audioMixer));
GBAAudioMixerCreate(gbacore->audioMixer);
((struct ARMCore*) core->cpu)->components[CPU_COMPONENT_AUDIO_MIXER] = &gbacore->audioMixer->d;
ARMHotplugAttach(core->cpu, CPU_COMPONENT_AUDIO_MIXER);
}
#endif
GBAOverrideApplyDefaults(gba, gbacore->overrides); GBAOverrideApplyDefaults(gba, gbacore->overrides);
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
@ -521,7 +539,7 @@ static void _GBACoreReset(struct mCore* core) {
#endif #endif
ARMReset(core->cpu); ARMReset(core->cpu);
if (core->opts.skipBios && gba->isPristine) { if (core->opts.skipBios && (gba->romVf || gba->memory.rom)) {
GBASkipBIOS(core->board); GBASkipBIOS(core->board);
} }
} }

307
src/gba/extra/audio-mixer.c Normal file
View File

@ -0,0 +1,307 @@
/* Copyright (c) 2013-2017 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/extra/audio-mixer.h>
#include <mgba/core/blip_buf.h>
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/video.h>
#define OVERSAMPLE 2
static void _mp2kInit(void* cpu, struct mCPUComponent* component);
static void _mp2kDeinit(struct mCPUComponent* component);
static bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address);
static void _mp2kVblank(struct GBAAudioMixer* mixer);
static void _mp2kStep(struct GBAAudioMixer* mixer);
void GBAAudioMixerCreate(struct GBAAudioMixer* mixer) {
mixer->d.init = _mp2kInit;
mixer->d.deinit = _mp2kDeinit;
mixer->engage = _mp2kEngage;
mixer->vblank = _mp2kVblank;
mixer->step = _mp2kStep;
}
void _mp2kInit(void* cpu, struct mCPUComponent* component) {
struct ARMCore* arm = cpu;
struct GBA* gba = (struct GBA*) arm->master;
struct GBAAudioMixer* mixer = (struct GBAAudioMixer*) component;
gba->audio.mixer = mixer;
mixer->p = &gba->audio;
mixer->contextAddress = 0;
mixer->tempo = 120.0 / 75.0;
mixer->frame = 0;
mixer->last.left = 0;
mixer->last.right = 0;
memset(&mixer->context, 0, sizeof(mixer->context));
memset(&mixer->activeTracks, 0, sizeof(mixer->activeTracks));
size_t i;
for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) {
mixer->activeTracks[i].channel = &mixer->context.chans[i];
CircleBufferInit(&mixer->activeTracks[i].buffer, 0x10000);
}
}
void _mp2kDeinit(struct mCPUComponent* component) {
struct GBAAudioMixer* mixer = (struct GBAAudioMixer*) component;
size_t i;
for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) {
CircleBufferDeinit(&mixer->activeTracks[i].buffer);
}
}
static void _loadInstrument(struct ARMCore* cpu, struct GBAMP2kInstrument* instrument, uint32_t base) {
struct ARMMemory* memory = &cpu->memory;
instrument->type = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, type), 0);
instrument->key = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, key), 0);
instrument->length = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, length), 0);
instrument->ps.pan = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, ps.pan), 0);
if (instrument->type == 0x40 || instrument->type == 0x80) {
instrument->data.subTable = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, data.subTable), 0);
instrument->extInfo.map = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.map), 0);
} else {
instrument->data.waveData = memory->load32(cpu, base + offsetof(struct GBAMP2kInstrument, data.waveData), 0);
instrument->extInfo.adsr.attack = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.attack), 0);
instrument->extInfo.adsr.decay = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.decay), 0);
instrument->extInfo.adsr.sustain = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.sustain), 0);
instrument->extInfo.adsr.release = memory->load8(cpu, base + offsetof(struct GBAMP2kInstrument, extInfo.adsr.release), 0);
}
}
static void _lookupInstrument(struct ARMCore* cpu, struct GBAMP2kInstrument* instrument, uint8_t key) {
struct ARMMemory* memory = &cpu->memory;
if (instrument->type == 0x40) {
uint32_t subInstrumentBase = instrument->data.subTable;
uint32_t keyTable = instrument->extInfo.map;
uint8_t id = memory->load8(cpu, keyTable + key, 0);
subInstrumentBase += 12 * id;
_loadInstrument(cpu, instrument, subInstrumentBase);
}
if (instrument->type == 0x80) {
uint32_t subInstrumentBase = instrument->data.subTable;
subInstrumentBase += 12 * key;
_loadInstrument(cpu, instrument, subInstrumentBase);
}
}
static void _stepSample(struct GBAAudioMixer* mixer, struct GBAMP2kTrack* track) {
struct ARMCore* cpu = mixer->p->p->cpu;
struct ARMMemory* memory = &cpu->memory;
uint32_t headerAddress;
struct GBAMP2kInstrument instrument = track->track.instrument;
uint8_t note = track->track.key;
_lookupInstrument(cpu, &instrument, note);
double freq;
switch (instrument.type) {
case 0x00:
case 0x08:
case 0x40:
case 0x80:
freq = GBA_ARM7TDMI_FREQUENCY / (double) track->channel->freq;
break;
default:
// We don't care about PSG channels
return;
}
headerAddress = instrument.data.waveData;
if (headerAddress < 0x20) {
mLOG(GBA_AUDIO, ERROR, "Audio track has invalid instrument");
return;
}
uint32_t loopOffset = memory->load32(cpu, headerAddress + 0x8, 0);
uint32_t endOffset = memory->load32(cpu, headerAddress + 0xC, 0);
uint32_t sampleBase = headerAddress + 0x10;
uint32_t sampleI = track->samplePlaying;
double sampleOffset = track->currentOffset;
double updates = VIDEO_TOTAL_LENGTH / (mixer->tempo * mixer->p->sampleInterval / OVERSAMPLE);
int nSample;
for (nSample = 0; nSample < updates; ++nSample) {
int8_t sample = memory->load8(cpu, sampleBase + sampleI, 0);
struct GBAStereoSample stereo = {
(sample * track->channel->leftVolume * track->channel->envelopeV) >> 9,
(sample * track->channel->rightVolume * track->channel->envelopeV) >> 9
};
CircleBufferWrite16(&track->buffer, stereo.left);
CircleBufferWrite16(&track->buffer, stereo.right);
sampleOffset += mixer->p->sampleInterval / OVERSAMPLE;
while (sampleOffset > freq) {
sampleOffset -= freq;
++sampleI;
if (sampleI >= endOffset) {
sampleI = loopOffset;
}
}
}
track->samplePlaying = sampleI;
track->currentOffset = sampleOffset;
}
static void _mp2kReload(struct GBAAudioMixer* mixer) {
struct ARMCore* cpu = mixer->p->p->cpu;
struct ARMMemory* memory = &cpu->memory;
mixer->context.magic = memory->load32(cpu, mixer->contextAddress + offsetof(struct GBAMP2kContext, magic), 0);
int i;
for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) {
struct GBAMP2kSoundChannel* ch = &mixer->context.chans[i];
struct GBAMP2kTrack* track = &mixer->activeTracks[i];
track->waiting = false;
uint32_t base = mixer->contextAddress + offsetof(struct GBAMP2kContext, chans[i]);
ch->status = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, status), 0);
ch->type = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, type), 0);
ch->rightVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, rightVolume), 0);
ch->leftVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, leftVolume), 0);
ch->adsr.attack = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.attack), 0);
ch->adsr.decay = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.decay), 0);
ch->adsr.sustain = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.sustain), 0);
ch->adsr.release = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, adsr.release), 0);
ch->ky = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, ky), 0);
ch->envelopeV = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeV), 0);
ch->envelopeRight = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeRight), 0);
ch->envelopeLeft = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, envelopeLeft), 0);
ch->echoVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, echoVolume), 0);
ch->echoLength = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, echoLength), 0);
ch->d1 = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d1), 0);
ch->d2 = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d2), 0);
ch->gt = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, gt), 0);
ch->midiKey = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, midiKey), 0);
ch->ve = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, ve), 0);
ch->pr = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, pr), 0);
ch->rp = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, rp), 0);
ch->d3[0] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[0]), 0);
ch->d3[1] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[1]), 0);
ch->d3[2] = memory->load8(cpu, base + offsetof(struct GBAMP2kSoundChannel, d3[2]), 0);
ch->ct = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, ct), 0);
ch->fw = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, fw), 0);
ch->freq = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, freq), 0);
ch->waveData = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, waveData), 0);
ch->cp = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, cp), 0);
ch->track = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, track), 0);
ch->pp = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, pp), 0);
ch->np = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, np), 0);
ch->d4 = memory->load32(cpu, base + offsetof(struct GBAMP2kSoundChannel, d4), 0);
ch->xpi = memory->load16(cpu, base + offsetof(struct GBAMP2kSoundChannel, xpi), 0);
ch->xpc = memory->load16(cpu, base + offsetof(struct GBAMP2kSoundChannel, xpc), 0);
base = ch->track;
if (base) {
track->track.flags = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, flags), 0);
track->track.wait = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, wait), 0);
track->track.patternLevel = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternLevel), 0);
track->track.repN = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, repN), 0);
track->track.gateTime = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, gateTime), 0);
track->track.key = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, key), 0);
track->track.velocity = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, velocity), 0);
track->track.runningStatus = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, runningStatus), 0);
track->track.keyM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyM), 0);
track->track.pitM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pitM), 0);
track->track.keyShift = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyShift), 0);
track->track.keyShiftX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, keyShiftX), 0);
track->track.tune = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, tune), 0);
track->track.pitX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pitX), 0);
track->track.bend = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, bend), 0);
track->track.bendRange = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, bendRange), 0);
track->track.volMR = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volMR), 0);
track->track.volML = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volML), 0);
track->track.vol = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, vol), 0);
track->track.volX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, volX), 0);
track->track.pan = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, pan), 0);
track->track.panX = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, panX), 0);
track->track.modM = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, modM), 0);
track->track.mod = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, mod), 0);
track->track.modT = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, modT), 0);
track->track.lfoSpeed = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoSpeed), 0);
track->track.lfoSpeedC = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoSpeedC), 0);
track->track.lfoDelay = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoDelay), 0);
track->track.lfoDelayC = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, lfoDelayC), 0);
track->track.priority = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, priority), 0);
track->track.echoVolume = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, echoVolume), 0);
track->track.echoLength = memory->load8(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, echoLength), 0);
track->track.chan = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, chan), 0);
_loadInstrument(cpu, &track->track.instrument, base + offsetof(struct GBAMP2kMusicPlayerTrack, instrument));
track->track.cmdPtr = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, cmdPtr), 0);
track->track.patternStack[0] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[0]), 0);
track->track.patternStack[1] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[1]), 0);
track->track.patternStack[2] = memory->load32(cpu, base + offsetof(struct GBAMP2kMusicPlayerTrack, patternStack[2]), 0);
} else {
memset(&track->track, 0, sizeof(track->track));
}
if (track->track.runningStatus == 0xCD) {
// XCMD isn't supported
mixer->p->externalMixing = false;
}
}
}
bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) {
if (address < BASE_WORKING_RAM) {
return false;
}
if (address != mixer->contextAddress) {
mixer->contextAddress = address;
mixer->p->externalMixing = true;
_mp2kReload(mixer);
}
return true;
}
void _mp2kStep(struct GBAAudioMixer* mixer) {
mixer->frame += mixer->p->sampleInterval;
while (mixer->frame >= VIDEO_TOTAL_LENGTH / mixer->tempo) {
int i;
for (i = 0; i < MP2K_MAX_SOUND_CHANNELS; ++i) {
struct GBAMP2kTrack* track = &mixer->activeTracks[i];
if (track->channel->status > 0) {
_stepSample(mixer, track);
} else {
track->currentOffset = 0;
track->samplePlaying = 0;
CircleBufferClear(&track->buffer);
}
}
mixer->frame -= VIDEO_TOTAL_LENGTH / mixer->tempo;
}
uint32_t interval = mixer->p->sampleInterval / OVERSAMPLE;
int i;
for (i = 0; i < OVERSAMPLE; ++i) {
struct GBAStereoSample sample = {0};
size_t track;
for (track = 0; track < MP2K_MAX_SOUND_CHANNELS; ++track) {
if (!mixer->activeTracks[track].channel->status) {
continue;
}
int16_t value;
CircleBufferRead16(&mixer->activeTracks[track].buffer, &value);
sample.left += value;
CircleBufferRead16(&mixer->activeTracks[track].buffer, &value);
sample.right += value;
}
if (mixer->p->externalMixing) {
blip_add_delta(mixer->p->psg.left, mixer->p->clock + i * interval, sample.left - mixer->last.left);
blip_add_delta(mixer->p->psg.right, mixer->p->clock + i * interval, sample.left - mixer->last.left);
}
mixer->last = sample;
}
}
void _mp2kVblank(struct GBAAudioMixer* mixer) {
if (!mixer->contextAddress) {
return;
}
mLOG(GBA_AUDIO, DEBUG, "Frame");
mixer->p->externalMixing = true;
_mp2kReload(mixer);
}

View File

@ -147,6 +147,7 @@ void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
proxyRenderer->backend->deinit(proxyRenderer->backend); proxyRenderer->backend->deinit(proxyRenderer->backend);
} else { } else {
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT); proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
mVideoLoggerRendererFlush(proxyRenderer->logger);
} }
mVideoLoggerRendererDeinit(proxyRenderer->logger); mVideoLoggerRendererDeinit(proxyRenderer->logger);
@ -307,28 +308,20 @@ void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y)
void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
}
if (!proxyRenderer->logger->block) { if (!proxyRenderer->logger->block) {
proxyRenderer->backend->finishFrame(proxyRenderer->backend); proxyRenderer->backend->finishFrame(proxyRenderer->backend);
} }
mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFinishFrame(proxyRenderer->logger);
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->unlock(proxyRenderer->logger);
}
} }
static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) {
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed // Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS); proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
mVideoLoggerRendererFlush(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger);
proxyRenderer->logger->unlock(proxyRenderer->logger);
*pixels = proxyRenderer->logger->pixelBuffer; *pixels = proxyRenderer->logger->pixelBuffer;
*stride = proxyRenderer->logger->pixelStride; *stride = proxyRenderer->logger->pixelStride;
} else { } else {
@ -340,8 +333,6 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->lock(proxyRenderer->logger);
// Insert an extra item into the queue to make sure it gets flushed
mVideoLoggerRendererFlush(proxyRenderer->logger);
} }
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {

View File

@ -241,10 +241,6 @@ void GBAReset(struct ARMCore* cpu) {
if (gba->pristineRomSize > SIZE_CART0) { if (gba->pristineRomSize > SIZE_CART0) {
GBAMatrixReset(gba); GBAMatrixReset(gba);
} }
if (!gba->romVf && gba->memory.rom) {
GBASkipBIOS(gba);
}
} }
void GBASkipBIOS(struct GBA* gba) { void GBASkipBIOS(struct GBA* gba) {
@ -788,6 +784,10 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
void GBAFrameStarted(struct GBA* gba) { void GBAFrameStarted(struct GBA* gba) {
GBATestKeypadIRQ(gba); GBATestKeypadIRQ(gba);
if (gba->audio.mixer) {
gba->audio.mixer->vblank(gba->audio.mixer);
}
size_t c; size_t c;
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);

File diff suppressed because it is too large Load Diff

View File

@ -17,19 +17,12 @@
#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \ #define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
SPRITE_YBASE_ ## DEPTH(inY); \ SPRITE_YBASE_ ## DEPTH(inY); \
unsigned tileData; \ unsigned tileData; \
if (outX % mosaicH) { \
if (!inX && xOffset > 0) { \
inX = mosaicH - (outX % mosaicH); \
outX += mosaicH - (outX % mosaicH); \
} else if (inX == width - xOffset) { \
inX = mosaicH + (outX % mosaicH); \
outX += mosaicH - (outX % mosaicH); \
} \
} \
for (; outX < condition; ++outX, inX += xOffset) { \ for (; outX < condition; ++outX, inX += xOffset) { \
int localX = inX - xOffset * (outX % mosaicH); \ int localX = inX - xOffset * (outX % mosaicH); \
if (localX < 0 || localX > width - 1) { \ if (localX < 0) { \
continue; \ localX = 0; \
} else if (localX > width - 1) {\
localX = width - 1; \
} \ } \
SPRITE_XBASE_ ## DEPTH(localX); \ SPRITE_XBASE_ ## DEPTH(localX); \
SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \ SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
@ -55,6 +48,31 @@
SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \ SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
} }
#define SPRITE_TRANSFORMED_MOSAIC_LOOP(DEPTH, TYPE) \
unsigned tileData; \
unsigned widthMask = ~(width - 1); \
unsigned heightMask = ~(height - 1); \
int localX = xAccum >> 8; \
int localY = yAccum >> 8; \
for (; outX < condition; ++outX, ++inX) { \
renderer->spriteCyclesRemaining -= 2; \
xAccum += mat.a; \
yAccum += mat.c; \
\
if (outX % mosaicH == 0) { \
localX = xAccum >> 8; \
localY = yAccum >> 8; \
} \
\
if (localX & widthMask || localY & heightMask) { \
continue; \
} \
\
SPRITE_YBASE_ ## DEPTH(localY); \
SPRITE_XBASE_ ## DEPTH(localX); \
SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
}
#define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2); #define SPRITE_XBASE_16(localX) unsigned xBase = (localX & ~0x7) * 4 + ((localX >> 1) & 2);
#define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 4; #define SPRITE_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 4;
@ -281,6 +299,13 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
if (end < condition) { if (end < condition) {
condition = end; condition = end;
} }
int mosaicH = 1;
if (GBAObjAttributesAIsMosaic(sprite->a)) {
mosaicH = GBAMosaicControlGetObjH(renderer->mosaic) + 1;
if (condition % mosaicH) {
condition += mosaicH - (condition % mosaicH);
}
}
int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1)) + (width << 7); int xAccum = mat.a * (inX - 1 - (totalWidth >> 1)) + mat.b * (inY - (totalHeight >> 1)) + (width << 7);
int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1)) + (height << 7); int yAccum = mat.c * (inX - 1 - (totalWidth >> 1)) + mat.d * (inY - (totalHeight >> 1)) + (height << 7);
@ -340,6 +365,13 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4]; palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 4];
if (flags & FLAG_OBJWIN) { if (flags & FLAG_OBJWIN) {
SPRITE_TRANSFORMED_LOOP(16, OBJWIN); SPRITE_TRANSFORMED_LOOP(16, OBJWIN);
} else if (mosaicH > 1) {
if (objwinSlowPath) {
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL_OBJWIN);
} else {
SPRITE_TRANSFORMED_MOSAIC_LOOP(16, NORMAL);
}
} else if (objwinSlowPath) { } else if (objwinSlowPath) {
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4]; objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
SPRITE_TRANSFORMED_LOOP(16, NORMAL_OBJWIN); SPRITE_TRANSFORMED_LOOP(16, NORMAL_OBJWIN);
@ -358,6 +390,12 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8]; palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8];
if (flags & FLAG_OBJWIN) { if (flags & FLAG_OBJWIN) {
SPRITE_TRANSFORMED_LOOP(256, OBJWIN); SPRITE_TRANSFORMED_LOOP(256, OBJWIN);
} else if (mosaicH > 1) {
if (objwinSlowPath) {
SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL_OBJWIN);
} else {
SPRITE_TRANSFORMED_MOSAIC_LOOP(256, NORMAL);
}
} else if (objwinSlowPath) { } else if (objwinSlowPath) {
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8]; objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN); SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);

View File

@ -942,11 +942,17 @@ int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRendere
struct GBAVideoRendererSprite* sprite = &renderer->sprites[i]; struct GBAVideoRendererSprite* sprite = &renderer->sprites[i];
int localY = y; int localY = y;
renderer->end = 0; renderer->end = 0;
if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) {
continue;
}
if (GBAObjAttributesAIsMosaic(sprite->obj.a)) { if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
localY = mosaicY; localY = mosaicY;
} if (localY < sprite->y) {
if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) { localY = sprite->y;
continue; }
if (localY >= sprite->endY) {
localY = sprite->endY - 1;
}
} }
for (w = 0; w < renderer->nWindows; ++w) { for (w = 0; w < renderer->nWindows; ++w) {
if (renderer->spriteCyclesRemaining <= 0) { if (renderer->spriteCyclesRemaining <= 0) {

View File

@ -16,6 +16,7 @@
static int cheatsSetup(void** state) { static int cheatsSetup(void** state) {
struct mCore* core = GBACoreCreate(); struct mCore* core = GBACoreCreate();
core->init(core); core->init(core);
mCoreInitConfig(core, NULL);
core->cheatDevice(core); core->cheatDevice(core);
*state = core; *state = core;
return 0; return 0;
@ -26,6 +27,7 @@ static int cheatsTeardown(void** state) {
return 0; return 0;
} }
struct mCore* core = *state; struct mCore* core = *state;
mCoreConfigDeinit(&core->config);
core->deinit(core); core->deinit(core);
return 0; return 0;
} }

View File

@ -27,7 +27,9 @@ M_TEST_DEFINE(reset) {
struct mCore* core = GBACoreCreate(); struct mCore* core = GBACoreCreate();
assert_non_null(core); assert_non_null(core);
assert_true(core->init(core)); assert_true(core->init(core));
mCoreInitConfig(core, NULL);
core->reset(core); core->reset(core);
mCoreConfigDeinit(&core->config);
core->deinit(core); core->deinit(core);
} }
@ -36,7 +38,9 @@ M_TEST_DEFINE(loadNullROM) {
assert_non_null(core); assert_non_null(core);
assert_true(core->init(core)); assert_true(core->init(core));
assert_false(core->loadROM(core, NULL)); assert_false(core->loadROM(core, NULL));
mCoreInitConfig(core, NULL);
core->reset(core); core->reset(core);
mCoreConfigDeinit(&core->config);
core->deinit(core); core->deinit(core);
} }

View File

@ -110,8 +110,7 @@ void GBAVideoReset(struct GBAVideo* video) {
memset(video->palette, 0, sizeof(video->palette)); memset(video->palette, 0, sizeof(video->palette));
memset(video->oam.raw, 0, sizeof(video->oam.raw)); memset(video->oam.raw, 0, sizeof(video->oam.raw));
video->renderer->deinit(video->renderer); video->renderer->reset(video->renderer);
video->renderer->init(video->renderer);
} }
void GBAVideoDeinit(struct GBAVideo* video) { void GBAVideoDeinit(struct GBAVideo* video) {

View File

@ -136,9 +136,11 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
glBindVertexArray(context->initialShader.vao); glBindVertexArray(context->initialShader.vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glEnableVertexAttribArray(context->initialShader.positionLocation);
glVertexAttribPointer(context->initialShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glVertexAttribPointer(context->initialShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glBindVertexArray(context->finalShader.vao); glBindVertexArray(context->finalShader.vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glEnableVertexAttribArray(context->finalShader.positionLocation);
glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL); glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glBindVertexArray(0); glBindVertexArray(0);
@ -246,7 +248,7 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
glBindTexture(GL_TEXTURE_2D, oldTex); glBindTexture(GL_TEXTURE_2D, oldTex);
} }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, shader->filter ? GL_LINEAR : GL_NEAREST);
glUseProgram(shader->program); glUseProgram(shader->program);
glUniform1i(shader->texLocation, 0); glUniform1i(shader->texLocation, 0);
@ -303,7 +305,6 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
break; break;
} }
} }
glEnableVertexAttribArray(shader->positionLocation);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glBindTexture(GL_TEXTURE_2D, shader->tex); glBindTexture(GL_TEXTURE_2D, shader->tex);
} }
@ -327,6 +328,7 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
_drawShader(context, &context->finalShader); _drawShader(context, &context->finalShader);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(0); glUseProgram(0);
glBindVertexArray(0);
} }
void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) { void mGLES2ContextPostFrame(struct VideoBackend* v, const void* frame) {
@ -463,8 +465,8 @@ void mGLES2ShaderAttach(struct mGLES2Context* context, struct mGLES2Shader* shad
glBindVertexArray(context->shaders[i].vao); glBindVertexArray(context->shaders[i].vao);
glBindBuffer(GL_ARRAY_BUFFER, context->vbo); glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(context->shaders[i].positionLocation); glEnableVertexAttribArray(context->shaders[i].positionLocation);
glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
} }
glBindVertexArray(0); glBindVertexArray(0);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);

View File

@ -67,6 +67,10 @@ void Action::trigger(bool active) {
return; return;
} }
if (m_exclusive && !m_booleanFunction) {
active = true;
}
if (m_function && active) { if (m_function && active) {
m_function(); m_function();
} }

View File

@ -291,6 +291,7 @@ if(QT_STATIC)
if(WIN32) if(WIN32)
list(APPEND QT_LIBRARIES qwindows dwmapi imm32 uxtheme Qt5EventDispatcherSupport Qt5FontDatabaseSupport Qt5ThemeSupport Qt5WindowsUIAutomationSupport) list(APPEND QT_LIBRARIES qwindows dwmapi imm32 uxtheme Qt5EventDispatcherSupport Qt5FontDatabaseSupport Qt5ThemeSupport Qt5WindowsUIAutomationSupport)
set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32") set_target_properties(Qt5::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE};version;winmm;ssl;crypto;ws2_32;iphlpapi;crypt32;userenv;netapi32;wtsapi32")
set_target_properties(Qt5::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
elseif(APPLE) elseif(APPLE)
find_package(Cups) find_package(Cups)
find_package(Qt5PrintSupport) find_package(Qt5PrintSupport)

View File

@ -86,6 +86,7 @@ CoreController::CoreController(mCore* core, QObject* parent)
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");
controller->finishFrame(); controller->finishFrame();
}; };
@ -196,9 +197,6 @@ CoreController::~CoreController() {
mCacheSetDeinit(m_cacheSet.get()); mCacheSetDeinit(m_cacheSet.get());
m_cacheSet.reset(); m_cacheSet.reset();
} }
mCoreConfigDeinit(&m_threadContext.core->config);
m_threadContext.core->deinit(m_threadContext.core);
} }
const color_t* CoreController::drawContext() { const color_t* CoreController::drawContext() {
@ -368,7 +366,6 @@ void CoreController::stop() {
#endif #endif
setPaused(false); setPaused(false);
mCoreThreadEnd(&m_threadContext); mCoreThreadEnd(&m_threadContext);
emit stopping();
} }
void CoreController::reset() { void CoreController::reset() {
@ -445,13 +442,21 @@ void CoreController::rewind(int states) {
} }
void CoreController::setFastForward(bool enable) { void CoreController::setFastForward(bool enable) {
if (m_fastForward == enable) {
return;
}
m_fastForward = enable; m_fastForward = enable;
updateFastForward(); updateFastForward();
emit fastForwardChanged(enable);
} }
void CoreController::forceFastForward(bool enable) { void CoreController::forceFastForward(bool enable) {
if (m_fastForwardForced == enable) {
return;
}
m_fastForwardForced = enable; m_fastForwardForced = enable;
updateFastForward(); updateFastForward();
emit fastForwardChanged(enable || m_fastForward);
} }
void CoreController::loadState(int slot) { void CoreController::loadState(int slot) {

View File

@ -99,6 +99,9 @@ public:
void setInputController(InputController*); void setInputController(InputController*);
void setLogger(LogController*); void setLogger(LogController*);
bool audioSync() const { return m_audioSync; }
bool videoSync() const { return m_videoSync; }
public slots: public slots:
void start(); void start();
void stop(); void stop();
@ -167,6 +170,7 @@ signals:
void crashed(const QString& errorMessage); void crashed(const QString& errorMessage);
void failed(); void failed();
void frameAvailable(); void frameAvailable();
void didReset();
void stateLoaded(); void stateLoaded();
void rewound(); void rewound();

View File

@ -20,7 +20,7 @@ Display* Display::create(QWidget* parent) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
QSurfaceFormat format; QSurfaceFormat format;
format.setSwapInterval(1); format.setSwapInterval(1);
format.setSwapBehavior(QSurfaceFormat::TripleBuffer); format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
#endif #endif
switch (s_driver) { switch (s_driver) {

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2013-2015 Jeffrey Pfau /* Copyright (c) 2013-2019 Jeffrey Pfau
* *
* This Source Code Form is subject to the terms of the Mozilla Public * 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 * License, v. 2.0. If a copy of the MPL was not distributed with this
@ -34,17 +34,32 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent)
: Display(parent) : Display(parent)
, m_gl(nullptr) , m_gl(nullptr)
{ {
setAttribute(Qt::WA_NativeWindow);
windowHandle()->create();
// This can spontaneously re-enter into this->resizeEvent before creation is done, so we // This can spontaneously re-enter into this->resizeEvent before creation is done, so we
// need to make sure it's initialized to nullptr before we assign the new object to it // need to make sure it's initialized to nullptr before we assign the new object to it
m_gl = new QOpenGLContext; m_gl = new QOpenGLContext;
m_gl->setFormat(format); m_gl->setFormat(format);
m_gl->create(); m_gl->create();
setAttribute(Qt::WA_NativeWindow);
m_gl->makeCurrent(windowHandle());
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
int majorVersion = m_gl->format().majorVersion();
QStringList extensions = QString(reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS))).split(' ');
m_gl->doneCurrent();
if (majorVersion == 2 && !extensions.contains("GL_ARB_framebuffer_object")) {
QSurfaceFormat newFormat(format);
newFormat.setVersion(1, 4);
m_gl->setFormat(newFormat);
m_gl->create();
}
m_painter = new PainterGL(&m_videoProxy, windowHandle(), m_gl); m_painter = new PainterGL(&m_videoProxy, windowHandle(), m_gl);
setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions setUpdatesEnabled(false); // Prevent paint events, which can cause race conditions
connect(&m_videoProxy, &VideoProxy::dataAvailable, &m_videoProxy, &VideoProxy::processData);
connect(&m_videoProxy, &VideoProxy::eventPosted, &m_videoProxy, &VideoProxy::handleEvent);
} }
DisplayGL::~DisplayGL() { DisplayGL::~DisplayGL() {
@ -98,6 +113,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio()); messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio());
#endif #endif
resizePainter(); resizePainter();
connect(m_context.get(), &CoreController::didReset, this, &DisplayGL::resizeContext);
} }
void DisplayGL::stopDrawing() { void DisplayGL::stopDrawing() {
@ -249,10 +265,9 @@ PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent
#endif #endif
m_backend->swap = [](VideoBackend* v) { m_backend->swap = [](VideoBackend* v) {
PainterGL* painter = static_cast<PainterGL*>(v->user); PainterGL* painter = static_cast<PainterGL*>(v->user);
if (!painter->m_gl->isValid()) { if (!painter->m_swapTimer.isActive()) {
return; QMetaObject::invokeMethod(&painter->m_swapTimer, "start");
} }
painter->m_gl->swapBuffers(painter->m_gl->surface());
}; };
m_backend->init(m_backend, 0); m_backend->init(m_backend, 0);
@ -270,6 +285,10 @@ PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
m_free.append(new uint32_t[256 * 512]); m_free.append(new uint32_t[256 * 512]);
} }
m_swapTimer.setInterval(16);
m_swapTimer.setSingleShot(true);
connect(&m_swapTimer, &QTimer::timeout, this, &PainterGL::swap);
} }
PainterGL::~PainterGL() { PainterGL::~PainterGL() {
@ -305,18 +324,8 @@ void PainterGL::resizeContext() {
return; return;
} }
if (!m_active) {
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
}
QSize size = m_context->screenDimensions(); QSize size = m_context->screenDimensions();
m_backend->setDimensions(m_backend, size.width(), size.height()); m_backend->setDimensions(m_backend, size.width(), size.height());
if (!m_active) {
m_gl->doneCurrent();
}
} }
void PainterGL::setMessagePainter(MessagePainter* messagePainter) { void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
@ -368,27 +377,22 @@ void PainterGL::draw() {
return; return;
} }
if (m_needsUnlock) {
QTimer::singleShot(0, this, &PainterGL::draw);
return;
}
if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) { if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) {
dequeue(); dequeue();
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync); forceDraw();
m_painter.begin(m_window); if (m_context->thread()->impl->sync.videoFrameWait) {
performDraw(); m_needsUnlock = true;
m_painter.end(); } else {
m_backend->swap(m_backend); mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
if (!m_delayTimer.isValid()) {
m_delayTimer.start();
} else if (m_gl->format().swapInterval()) {
while (m_delayTimer.elapsed() < 15) {
QThread::usleep(100);
}
m_delayTimer.restart();
} }
} else { } else {
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync); mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
} }
if (!m_queue.isEmpty()) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
}
} }
void PainterGL::forceDraw() { void PainterGL::forceDraw() {
@ -404,6 +408,9 @@ void PainterGL::stop() {
dequeueAll(); dequeueAll();
m_backend->clear(m_backend); m_backend->clear(m_backend);
m_backend->swap(m_backend); m_backend->swap(m_backend);
if (m_videoProxy) {
m_videoProxy->reset();
}
m_gl->doneCurrent(); m_gl->doneCurrent();
m_gl->moveToThread(m_surface->thread()); m_gl->moveToThread(m_surface->thread());
m_context.reset(); m_context.reset();
@ -428,6 +435,30 @@ void PainterGL::performDraw() {
if (m_messagePainter) { if (m_messagePainter) {
m_messagePainter->paint(&m_painter); m_messagePainter->paint(&m_painter);
} }
m_frameReady = true;
}
void PainterGL::swap() {
if (!m_gl->isValid()) {
return;
}
if (m_frameReady) {
m_gl->swapBuffers(m_surface);
m_gl->makeCurrent(m_surface);
#if defined(_WIN32) && defined(USE_EPOXY)
epoxy_handle_external_wglMakeCurrent();
#endif
m_frameReady = false;
}
if (m_needsUnlock) {
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
m_needsUnlock = false;
}
if (!m_queue.isEmpty()) {
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
} else {
m_swapTimer.start();
}
} }
void PainterGL::enqueue(const uint32_t* backing) { void PainterGL::enqueue(const uint32_t* backing) {
@ -480,12 +511,6 @@ void PainterGL::setShaders(struct VDir* dir) {
return; return;
} }
#ifdef BUILD_GLES2 #ifdef BUILD_GLES2
if (!m_active) {
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);
@ -494,9 +519,6 @@ void PainterGL::setShaders(struct VDir* dir) {
if (m_started) { if (m_started) {
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses); mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
} }
if (!m_active) {
m_gl->doneCurrent();
}
#endif #endif
} }
@ -505,19 +527,10 @@ void PainterGL::clearShaders() {
return; return;
} }
#ifdef BUILD_GLES2 #ifdef BUILD_GLES2
if (!m_active) {
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_active) {
m_gl->doneCurrent();
}
#endif #endif
} }

View File

@ -16,13 +16,13 @@
#endif #endif
#endif #endif
#include <QElapsedTimer>
#include <QOpenGLContext> #include <QOpenGLContext>
#include <QList> #include <QList>
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QQueue> #include <QQueue>
#include <QThread> #include <QThread>
#include <QTimer>
#include "VideoProxy.h" #include "VideoProxy.h"
@ -105,6 +105,9 @@ public slots:
int glTex(); int glTex();
private slots:
void swap();
private: private:
void performDraw(); void performDraw();
void dequeue(); void dequeue();
@ -125,7 +128,9 @@ private:
VideoBackend* m_backend = nullptr; VideoBackend* m_backend = nullptr;
QSize m_size; QSize m_size;
MessagePainter* m_messagePainter = nullptr; MessagePainter* m_messagePainter = nullptr;
QElapsedTimer m_delayTimer; QTimer m_swapTimer{this};
bool m_needsUnlock = false;
bool m_frameReady = false;
VideoProxy* m_videoProxy; VideoProxy* m_videoProxy;
}; };

View File

@ -127,6 +127,9 @@ void LogController::logToStdout(bool log) {
void LogController::setLogFile(const QString& file) { void LogController::setLogFile(const QString& file) {
m_logStream.reset(); m_logStream.reset();
if (file.isEmpty()) {
return;
}
m_logFile = std::make_unique<QFile>(file); m_logFile = std::make_unique<QFile>(file);
m_logFile->open(QIODevice::Append | QIODevice::Text); m_logFile->open(QIODevice::Append | QIODevice::Text);
m_logStream = std::make_unique<QTextStream>(m_logFile.get()); m_logStream = std::make_unique<QTextStream>(m_logFile.get());

View File

@ -104,7 +104,7 @@
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>4</number> <number>8</number>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -59,7 +59,7 @@
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>6</number> <number>8</number>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -57,6 +57,10 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
} }
}); });
connect(m_ui.nativeGB, &QAbstractButton::pressed, [this]() {
m_ui.fpsTarget->setValue(double(GBA_ARM7TDMI_FREQUENCY) / double(VIDEO_TOTAL_LENGTH));
});
if (m_ui.savegamePath->text().isEmpty()) { if (m_ui.savegamePath->text().isEmpty()) {
m_ui.savegameSameDir->setChecked(true); m_ui.savegameSameDir->setChecked(true);
} }
@ -364,7 +368,6 @@ void SettingsView::updateConfig() {
saveSetting("videoSync", m_ui.videoSync); saveSetting("videoSync", m_ui.videoSync);
saveSetting("audioSync", m_ui.audioSync); saveSetting("audioSync", m_ui.audioSync);
saveSetting("frameskip", m_ui.frameskip); saveSetting("frameskip", m_ui.frameskip);
saveSetting("fpsTarget", m_ui.fpsTarget);
saveSetting("autofireThreshold", m_ui.autofireThreshold); saveSetting("autofireThreshold", m_ui.autofireThreshold);
saveSetting("lockAspectRatio", m_ui.lockAspectRatio); saveSetting("lockAspectRatio", m_ui.lockAspectRatio);
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling); saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
@ -395,7 +398,7 @@ void SettingsView::updateConfig() {
saveSetting("logToStdout", m_ui.logToStdout); saveSetting("logToStdout", m_ui.logToStdout);
saveSetting("logFile", m_ui.logFile); saveSetting("logFile", m_ui.logFile);
saveSetting("useDiscordPresence", m_ui.useDiscordPresence); saveSetting("useDiscordPresence", m_ui.useDiscordPresence);
saveSetting("audioHle", m_ui.audioHle); saveSetting("gba.audioHle", m_ui.audioHle);
if (m_ui.fastForwardUnbounded->isChecked()) { if (m_ui.fastForwardUnbounded->isChecked()) {
saveSetting("fastForwardRatio", "-1"); saveSetting("fastForwardRatio", "-1");
@ -403,6 +406,13 @@ void SettingsView::updateConfig() {
saveSetting("fastForwardRatio", m_ui.fastForwardRatio); saveSetting("fastForwardRatio", m_ui.fastForwardRatio);
} }
double nativeFps = double(GBA_ARM7TDMI_FREQUENCY) / double(VIDEO_TOTAL_LENGTH);
if (nativeFps - m_ui.fpsTarget->value() < 0.0001) {
m_controller->setOption("fpsTarget", QVariant(nativeFps));
} else {
saveSetting("fpsTarget", m_ui.fpsTarget);
}
switch (m_ui.idleOptimization->currentIndex() + IDLE_LOOP_IGNORE) { switch (m_ui.idleOptimization->currentIndex() + IDLE_LOOP_IGNORE) {
case IDLE_LOOP_IGNORE: case IDLE_LOOP_IGNORE:
saveSetting("idleOptimization", "ignore"); saveSetting("idleOptimization", "ignore");
@ -549,7 +559,7 @@ void SettingsView::reloadConfig() {
loadSetting("logToStdout", m_ui.logToStdout); loadSetting("logToStdout", m_ui.logToStdout);
loadSetting("logFile", m_ui.logFile); loadSetting("logFile", m_ui.logFile);
loadSetting("useDiscordPresence", m_ui.useDiscordPresence); loadSetting("useDiscordPresence", m_ui.useDiscordPresence);
loadSetting("audioHle", m_ui.audioHle); loadSetting("gba.audioHle", m_ui.audioHle);
loadSetting("videoScale", m_ui.videoScale, 1); loadSetting("videoScale", m_ui.videoScale, 1);
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt()); m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>790</width> <width>849</width>
<height>686</height> <height>753</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -40,7 +40,7 @@
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>200</width> <width>180</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
@ -399,21 +399,21 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="9" column="0" colspan="2"> <item row="10" column="0" colspan="2">
<widget class="Line" name="line_5"> <widget class="Line" name="line_5">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0"> <item row="11" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Sync:</string> <string>Sync:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="11" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10"> <layout class="QHBoxLayout" name="horizontalLayout_10">
<item> <item>
<widget class="QCheckBox" name="videoSync"> <widget class="QCheckBox" name="videoSync">
@ -431,27 +431,34 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="11" column="1"> <item row="12" column="1">
<widget class="QCheckBox" name="lockAspectRatio"> <widget class="QCheckBox" name="lockAspectRatio">
<property name="text"> <property name="text">
<string>Lock aspect ratio</string> <string>Lock aspect ratio</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="1"> <item row="13" column="1">
<widget class="QCheckBox" name="lockIntegerScaling"> <widget class="QCheckBox" name="lockIntegerScaling">
<property name="text"> <property name="text">
<string>Force integer scaling</string> <string>Force integer scaling</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="14" column="1">
<widget class="QCheckBox" name="resampleVideo"> <widget class="QCheckBox" name="resampleVideo">
<property name="text"> <property name="text">
<string>Bilinear filtering</string> <string>Bilinear filtering</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1">
<widget class="QPushButton" name="nativeGB">
<property name="text">
<string>Native (59.7275)</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="interface_2"> <widget class="QWidget" name="interface_2">
@ -894,7 +901,7 @@
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>13</number> <number>16</number>
</property> </property>
</widget> </widget>
</item> </item>
@ -903,9 +910,6 @@
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QCheckBox" name="audioHle"> <widget class="QCheckBox" name="audioHle">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text"> <property name="text">
<string>XQ GBA audio (experimental)</string> <string>XQ GBA audio (experimental)</string>
</property> </property>

View File

@ -126,7 +126,7 @@
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>4</number> <number>8</number>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -24,6 +24,9 @@ VideoProxy::VideoProxy() {
m_logger.d.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>; m_logger.d.writeData = &callback<bool, const void*, size_t>::func<&VideoProxy::writeData>;
m_logger.d.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>; m_logger.d.readData = &callback<bool, void*, size_t, bool>::func<&VideoProxy::readData>;
m_logger.d.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>; m_logger.d.postEvent = &callback<void, enum mVideoLoggerEvent>::func<&VideoProxy::postEvent>;
connect(this, &VideoProxy::dataAvailable, this, &VideoProxy::processData);
connect(this, &VideoProxy::eventPosted, this, &VideoProxy::handleEvent);
} }
void VideoProxy::attach(CoreController* controller) { void VideoProxy::attach(CoreController* controller) {
@ -41,7 +44,10 @@ void VideoProxy::init() {
} }
void VideoProxy::reset() { void VideoProxy::reset() {
m_mutex.lock();
RingFIFOClear(&m_dirtyQueue); RingFIFOClear(&m_dirtyQueue);
m_toThreadCond.wakeAll();
m_mutex.unlock();
} }
void VideoProxy::deinit() { void VideoProxy::deinit() {
@ -92,11 +98,13 @@ void VideoProxy::unlock() {
} }
void VideoProxy::wait() { void VideoProxy::wait() {
m_mutex.lock();
while (RingFIFOSize(&m_dirtyQueue)) { while (RingFIFOSize(&m_dirtyQueue)) {
emit dataAvailable(); emit dataAvailable();
m_toThreadCond.wakeAll(); m_toThreadCond.wakeAll();
m_fromThreadCond.wait(&m_mutex, 1); m_fromThreadCond.wait(&m_mutex, 1);
} }
m_mutex.unlock();
} }
void VideoProxy::wake(int y) { void VideoProxy::wake(int y) {

View File

@ -30,11 +30,11 @@ signals:
public slots: public slots:
void processData(); void processData();
void reset();
void handleEvent(int); void handleEvent(int);
private: private:
void init(); void init();
void reset();
void deinit(); void deinit();
bool writeData(const void* data, size_t length); bool writeData(const void* data, size_t length);

View File

@ -648,8 +648,12 @@ void Window::closeEvent(QCloseEvent* event) {
m_config->setOption("width", GBA_VIDEO_HORIZONTAL_PIXELS * m_savedScale); m_config->setOption("width", GBA_VIDEO_HORIZONTAL_PIXELS * m_savedScale);
} }
saveConfig(); saveConfig();
m_display.reset(); if (m_controller) {
QMainWindow::closeEvent(event); event->ignore();
m_pendingClose = true;
} else {
m_display.reset();
}
} }
void Window::focusInEvent(QFocusEvent*) { void Window::focusInEvent(QFocusEvent*) {
@ -784,7 +788,6 @@ void Window::gameStarted() {
} }
attachWidget(m_display.get()); attachWidget(m_display.get());
setMouseTracking(true); setMouseTracking(true);
m_display->setMinimumSize(size);
setFocus(); setFocus();
#ifndef Q_OS_MAC #ifndef Q_OS_MAC
@ -792,7 +795,6 @@ void Window::gameStarted() {
menuBar()->hide(); menuBar()->hide();
} }
#endif #endif
m_display->startDrawing(m_controller);
reloadAudioDriver(); reloadAudioDriver();
multiplayerChanged(); multiplayerChanged();
@ -843,6 +845,11 @@ void Window::gameStarted() {
void Window::gameStopped() { void Window::gameStopped() {
m_controller.reset(); m_controller.reset();
m_display->stopDrawing();
if (m_pendingClose) {
m_display.reset();
close();
}
for (Action* action : m_platformActions) { for (Action* action : m_platformActions) {
action->setEnabled(true); action->setEnabled(true);
} }
@ -929,7 +936,6 @@ void Window::reloadDisplayDriver() {
m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config); m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config);
#endif #endif
connect(this, &Window::shutdown, m_display.get(), &Display::stopDrawing);
connect(m_display.get(), &Display::hideCursor, [this]() { connect(m_display.get(), &Display::hideCursor, [this]() {
if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display.get()) { if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display.get()) {
m_screenWidget->setCursor(Qt::BlankCursor); m_screenWidget->setCursor(Qt::BlankCursor);
@ -954,8 +960,6 @@ void Window::reloadDisplayDriver() {
#endif #endif
if (m_controller) { if (m_controller) {
m_display->setMinimumSize(m_controller->screenDimensions());
connect(m_controller.get(), &CoreController::stopping, m_display.get(), &Display::stopDrawing);
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext); 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::stateLoaded, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw); connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
@ -966,13 +970,12 @@ void Window::reloadDisplayDriver() {
attachWidget(m_display.get()); attachWidget(m_display.get());
m_display->startDrawing(m_controller); m_display->startDrawing(m_controller);
} else {
#ifdef M_CORE_GB
m_display->setMinimumSize(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS);
#elif defined(M_CORE_GBA)
m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
#endif
} }
#ifdef M_CORE_GB
m_display->setMinimumSize(GB_VIDEO_HORIZONTAL_PIXELS, GB_VIDEO_VERTICAL_PIXELS);
#elif defined(M_CORE_GBA)
m_display->setMinimumSize(GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS);
#endif
} }
void Window::reloadAudioDriver() { void Window::reloadAudioDriver() {
@ -991,6 +994,7 @@ void Window::reloadAudioDriver() {
m_audioProcessor->requestSampleRate(opts->sampleRate); m_audioProcessor->requestSampleRate(opts->sampleRate);
m_audioProcessor->start(); m_audioProcessor->start();
connect(m_controller.get(), &CoreController::stopping, m_audioProcessor.get(), &AudioProcessor::stop); connect(m_controller.get(), &CoreController::stopping, m_audioProcessor.get(), &AudioProcessor::stop);
connect(m_controller.get(), &CoreController::fastForwardChanged, m_audioProcessor.get(), &AudioProcessor::inputParametersChanged);
} }
void Window::tryMakePortable() { void Window::tryMakePortable() {
@ -1372,7 +1376,7 @@ void Window::setupMenu(QMenuBar* menubar) {
m_actions.addMenu(tr("Audio/&Video"), "av"); m_actions.addMenu(tr("Audio/&Video"), "av");
m_actions.addMenu(tr("Frame size"), "frame", "av"); m_actions.addMenu(tr("Frame size"), "frame", "av");
for (int i = 1; i <= 6; ++i) { for (int i = 1; i <= 8; ++i) {
Action* setSize = m_actions.addAction(tr("%1×").arg(QString::number(i)), QString("frame.%1x").arg(QString::number(i)), [this, i]() { Action* setSize = m_actions.addAction(tr("%1×").arg(QString::number(i)), QString("frame.%1x").arg(QString::number(i)), [this, i]() {
Action* setSize = m_frameSizes[i]; Action* setSize = m_frameSizes[i];
showNormal(); showNormal();
@ -1739,6 +1743,9 @@ void Window::setController(CoreController* controller, const QString& fname) {
if (!controller) { if (!controller) {
return; return;
} }
if (m_pendingClose) {
return;
}
if (m_controller) { if (m_controller) {
m_controller->stop(); m_controller->stop();
@ -1771,12 +1778,14 @@ void Window::setController(CoreController* controller, const QString& fname) {
m_inputController.recalibrateAxes(); m_inputController.recalibrateAxes();
m_controller->setInputController(&m_inputController); m_controller->setInputController(&m_inputController);
m_controller->setLogger(&m_log); m_controller->setLogger(&m_log);
m_display->startDrawing(m_controller);
connect(this, &Window::shutdown, [this]() { connect(this, &Window::shutdown, [this]() {
if (!m_controller) { if (!m_controller) {
return; return;
} }
m_controller->stop(); m_controller->stop();
disconnect(m_controller.get(), &CoreController::started, this, &Window::gameStarted);
}); });
connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted); connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted);
@ -1804,7 +1813,6 @@ void Window::setController(CoreController* controller, const QString& fname) {
emit paused(false); emit paused(false);
}); });
connect(m_controller.get(), &CoreController::stopping, m_display.get(), &Display::stopDrawing);
connect(m_controller.get(), &CoreController::stateLoaded, m_display.get(), &Display::resizeContext); 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::stateLoaded, m_display.get(), &Display::forceDraw);
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw); connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);

View File

@ -207,6 +207,7 @@ private:
QString m_pendingPatch; QString m_pendingPatch;
QString m_pendingState; QString m_pendingState;
bool m_pendingPause = false; bool m_pendingPause = false;
bool m_pendingClose = false;
bool m_hitUnimplementedBiosCall; bool m_hitUnimplementedBiosCall;

File diff suppressed because it is too large Load Diff

View File

@ -4243,21 +4243,26 @@ Game Boy Advance 是任天堂有限公司Nintendo Co., Ltd.)的注册商标
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="67"/> <location filename="../SettingsView.ui" line="67"/>
<source>Enhancements</source>
<translation></translation>
</message>
<message>
<location filename="../SettingsView.ui" line="72"/>
<source>BIOS</source> <source>BIOS</source>
<translation>BIOS</translation> <translation>BIOS</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="72"/> <location filename="../SettingsView.ui" line="77"/>
<source>Paths</source> <source>Paths</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="77"/> <location filename="../SettingsView.ui" line="82"/>
<source>Logging</source> <source>Logging</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="82"/> <location filename="../SettingsView.ui" line="87"/>
<source>Game Boy</source> <source>Game Boy</source>
<translation>Game Boy</translation> <translation>Game Boy</translation>
</message> </message>
@ -4272,491 +4277,522 @@ Game Boy Advance 是任天堂有限公司Nintendo Co., Ltd.)的注册商标
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="129"/> <location filename="../SettingsView.ui" line="134"/>
<location filename="../SettingsView.ui" line="151"/> <location filename="../SettingsView.ui" line="156"/>
<source>1536</source> <source>1536</source>
<translation>1536</translation> <translation>1536</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="136"/> <location filename="../SettingsView.ui" line="141"/>
<source>512</source> <source>512</source>
<translation>512</translation> <translation>512</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="141"/> <location filename="../SettingsView.ui" line="146"/>
<source>768</source> <source>768</source>
<translation>768</translation> <translation>768</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="146"/> <location filename="../SettingsView.ui" line="151"/>
<source>1024</source> <source>1024</source>
<translation>1024</translation> <translation>1024</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="156"/> <location filename="../SettingsView.ui" line="161"/>
<source>2048</source> <source>2048</source>
<translation>2048</translation> <translation>2048</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="161"/> <location filename="../SettingsView.ui" line="166"/>
<source>3072</source> <source>3072</source>
<translation>3072</translation> <translation>3072</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="166"/> <location filename="../SettingsView.ui" line="171"/>
<source>4096</source> <source>4096</source>
<translation>4096</translation> <translation>4096</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="174"/> <location filename="../SettingsView.ui" line="179"/>
<source>samples</source> <source>samples</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="183"/> <location filename="../SettingsView.ui" line="188"/>
<source>Sample rate:</source> <source>Sample rate:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="195"/> <location filename="../SettingsView.ui" line="200"/>
<location filename="../SettingsView.ui" line="212"/> <location filename="../SettingsView.ui" line="217"/>
<source>44100</source> <source>44100</source>
<translation>44100</translation> <translation>44100</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="202"/> <location filename="../SettingsView.ui" line="207"/>
<source>22050</source> <source>22050</source>
<translation>22050</translation> <translation>22050</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="207"/> <location filename="../SettingsView.ui" line="212"/>
<source>32000</source> <source>32000</source>
<translation>32000</translation> <translation>32000</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="217"/> <location filename="../SettingsView.ui" line="222"/>
<source>48000</source> <source>48000</source>
<translation>48000</translation> <translation>48000</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="225"/> <location filename="../SettingsView.ui" line="230"/>
<source>Hz</source> <source>Hz</source>
<translation>Hz</translation> <translation>Hz</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="234"/> <location filename="../SettingsView.ui" line="239"/>
<source>Volume:</source> <source>Volume:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="265"/> <location filename="../SettingsView.ui" line="270"/>
<location filename="../SettingsView.ui" line="305"/> <location filename="../SettingsView.ui" line="310"/>
<source>Mute</source> <source>Mute</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="274"/> <location filename="../SettingsView.ui" line="279"/>
<source>Fast forward volume:</source> <source>Fast forward volume:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="321"/> <location filename="../SettingsView.ui" line="326"/>
<source>Display driver:</source> <source>Display driver:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="338"/> <location filename="../SettingsView.ui" line="343"/>
<source>Frameskip:</source> <source>Frameskip:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="347"/> <location filename="../SettingsView.ui" line="352"/>
<source>Skip every</source> <source>Skip every</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="357"/> <location filename="../SettingsView.ui" line="362"/>
<location filename="../SettingsView.ui" line="717"/> <location filename="../SettingsView.ui" line="722"/>
<source>frames</source> <source>frames</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="366"/> <location filename="../SettingsView.ui" line="371"/>
<source>FPS target:</source> <source>FPS target:</source>
<translation> FPS:</translation> <translation> FPS:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="391"/> <location filename="../SettingsView.ui" line="396"/>
<source>frames per second</source> <source>frames per second</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="407"/> <location filename="../SettingsView.ui" line="412"/>
<source>Sync:</source> <source>Sync:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="416"/> <location filename="../SettingsView.ui" line="421"/>
<source>Video</source> <source>Video</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="423"/> <location filename="../SettingsView.ui" line="428"/>
<source>Audio</source> <source>Audio</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="432"/> <location filename="../SettingsView.ui" line="437"/>
<source>Lock aspect ratio</source> <source>Lock aspect ratio</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="439"/> <location filename="../SettingsView.ui" line="444"/>
<source>Force integer scaling</source> <source>Force integer scaling</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="446"/> <location filename="../SettingsView.ui" line="451"/>
<source>Bilinear filtering</source> <source>Bilinear filtering</source>
<translation>线</translation> <translation>线</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="457"/> <location filename="../SettingsView.ui" line="462"/>
<source>Language</source> <source>Language</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="465"/> <location filename="../SettingsView.ui" line="470"/>
<source>English</source> <source>English</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="480"/> <location filename="../SettingsView.ui" line="485"/>
<source>Library:</source> <source>Library:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="488"/> <location filename="../SettingsView.ui" line="493"/>
<source>List view</source> <source>List view</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="493"/> <location filename="../SettingsView.ui" line="498"/>
<source>Tree view</source> <source>Tree view</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="501"/> <location filename="../SettingsView.ui" line="506"/>
<source>Show when no game open</source> <source>Show when no game open</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="511"/> <location filename="../SettingsView.ui" line="516"/>
<source>Clear cache</source> <source>Clear cache</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="525"/> <location filename="../SettingsView.ui" line="530"/>
<source>Allow opposing input directions</source> <source>Allow opposing input directions</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="532"/> <location filename="../SettingsView.ui" line="537"/>
<source>Suspend screensaver</source> <source>Suspend screensaver</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="542"/> <location filename="../SettingsView.ui" line="547"/>
<source>Pause when inactive</source> <source>Pause when inactive</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="549"/> <location filename="../SettingsView.ui" line="554"/>
<source>Show FPS in title bar</source> <source>Show FPS in title bar</source>
<translation> FPS</translation> <translation> FPS</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="573"/> <location filename="../SettingsView.ui" line="578"/>
<source>Automatically save cheats</source> <source>Automatically save cheats</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="583"/> <location filename="../SettingsView.ui" line="588"/>
<source>Automatically load cheats</source> <source>Automatically load cheats</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="593"/> <location filename="../SettingsView.ui" line="598"/>
<source>Automatically save state</source> <source>Automatically save state</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="603"/> <location filename="../SettingsView.ui" line="608"/>
<source>Automatically load state</source> <source>Automatically load state</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="613"/> <location filename="../SettingsView.ui" line="618"/>
<source>Enable Discord Rich Presence</source> <source>Enable Discord Rich Presence</source>
<translation> Enable Discord Rich Presence</translation> <translation> Enable Discord Rich Presence</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="627"/> <location filename="../SettingsView.ui" line="632"/>
<source>Fast forward speed:</source> <source>Fast forward speed:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="639"/> <location filename="../SettingsView.ui" line="644"/>
<location filename="../SettingsView.ui" line="891"/>
<source>×</source> <source>×</source>
<translation>×</translation> <translation>×</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="658"/> <location filename="../SettingsView.ui" line="663"/>
<source>Unbounded</source> <source>Unbounded</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="670"/> <location filename="../SettingsView.ui" line="675"/>
<source>Autofire interval:</source> <source>Autofire interval:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="694"/> <location filename="../SettingsView.ui" line="699"/>
<source>Enable rewind</source> <source>Enable rewind</source>
<translation>退</translation> <translation>退</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="701"/> <location filename="../SettingsView.ui" line="706"/>
<source>Rewind history:</source> <source>Rewind history:</source>
<translation>退:</translation> <translation>退:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="733"/> <location filename="../SettingsView.ui" line="738"/>
<source>Idle loops:</source> <source>Idle loops:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="741"/> <location filename="../SettingsView.ui" line="746"/>
<source>Run all</source> <source>Run all</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="746"/> <location filename="../SettingsView.ui" line="751"/>
<source>Remove known</source> <source>Remove known</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="751"/> <location filename="../SettingsView.ui" line="756"/>
<source>Detect and remove</source> <source>Detect and remove</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="759"/> <location filename="../SettingsView.ui" line="764"/>
<source>Preload entire ROM into memory</source> <source>Preload entire ROM into memory</source>
<translation> ROM </translation> <translation> ROM </translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="773"/> <location filename="../SettingsView.ui" line="778"/>
<source>Savestate extra data:</source> <source>Savestate extra data:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="780"/> <location filename="../SettingsView.ui" line="785"/>
<location filename="../SettingsView.ui" line="824"/> <location filename="../SettingsView.ui" line="829"/>
<source>Screenshot</source> <source>Screenshot</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="790"/> <location filename="../SettingsView.ui" line="795"/>
<location filename="../SettingsView.ui" line="834"/> <location filename="../SettingsView.ui" line="839"/>
<source>Save data</source> <source>Save data</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="800"/> <location filename="../SettingsView.ui" line="805"/>
<location filename="../SettingsView.ui" line="841"/> <location filename="../SettingsView.ui" line="846"/>
<source>Cheat codes</source> <source>Cheat codes</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="817"/> <location filename="../SettingsView.ui" line="822"/>
<source>Load extra data:</source> <source>Load extra data:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="852"/> <location filename="../SettingsView.ui" line="857"/>
<source>GB BIOS file:</source> <source>Video renderer:</source>
<translation>GB BIOS :</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="871"/> <location filename="../SettingsView.ui" line="865"/>
<location filename="../SettingsView.ui" line="909"/> <source>Software</source>
<location filename="../SettingsView.ui" line="944"/> <translation></translation>
<location filename="../SettingsView.ui" line="972"/> </message>
<location filename="../SettingsView.ui" line="1013"/> <message>
<location filename="../SettingsView.ui" line="1061"/> <location filename="../SettingsView.ui" line="870"/>
<location filename="../SettingsView.ui" line="1109"/> <source>OpenGL</source>
<location filename="../SettingsView.ui" line="1157"/> <translation>OpenGL</translation>
<location filename="../SettingsView.ui" line="1205"/> </message>
<message>
<location filename="../SettingsView.ui" line="878"/>
<source>OpenGL enhancements</source>
<translation>OpenGL </translation>
</message>
<message>
<location filename="../SettingsView.ui" line="884"/>
<source>High-resolution scale:</source>
<translation>:</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="907"/>
<source>XQ GBA audio (experimental)</source>
<translation>XQ GBA ()</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="937"/>
<location filename="../SettingsView.ui" line="975"/>
<location filename="../SettingsView.ui" line="1010"/>
<location filename="../SettingsView.ui" line="1038"/>
<location filename="../SettingsView.ui" line="1079"/>
<location filename="../SettingsView.ui" line="1127"/>
<location filename="../SettingsView.ui" line="1175"/>
<location filename="../SettingsView.ui" line="1223"/>
<location filename="../SettingsView.ui" line="1271"/>
<source>Browse</source> <source>Browse</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="880"/> <location filename="../SettingsView.ui" line="918"/>
<source>GB BIOS file:</source>
<translation>GB BIOS :</translation>
</message>
<message>
<location filename="../SettingsView.ui" line="946"/>
<source>Use BIOS file if found</source> <source>Use BIOS file if found</source>
<translation>使 BIOS </translation> <translation>使 BIOS </translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="890"/> <location filename="../SettingsView.ui" line="956"/>
<source>Skip BIOS intro</source> <source>Skip BIOS intro</source>
<translation> BIOS </translation> <translation> BIOS </translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="918"/> <location filename="../SettingsView.ui" line="984"/>
<source>GBA BIOS file:</source> <source>GBA BIOS file:</source>
<translation>GBA BIOS :</translation> <translation>GBA BIOS :</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="925"/> <location filename="../SettingsView.ui" line="991"/>
<source>GBC BIOS file:</source> <source>GBC BIOS file:</source>
<translation>GBC BIOS :</translation> <translation>GBC BIOS :</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="953"/> <location filename="../SettingsView.ui" line="1019"/>
<source>SGB BIOS file:</source> <source>SGB BIOS file:</source>
<translation>SGB BIOS :</translation> <translation>SGB BIOS :</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="988"/> <location filename="../SettingsView.ui" line="1054"/>
<source>Save games</source> <source>Save games</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1022"/> <location filename="../SettingsView.ui" line="1088"/>
<location filename="../SettingsView.ui" line="1070"/> <location filename="../SettingsView.ui" line="1136"/>
<location filename="../SettingsView.ui" line="1118"/> <location filename="../SettingsView.ui" line="1184"/>
<location filename="../SettingsView.ui" line="1166"/> <location filename="../SettingsView.ui" line="1232"/>
<location filename="../SettingsView.ui" line="1214"/> <location filename="../SettingsView.ui" line="1280"/>
<source>Same directory as the ROM</source> <source>Same directory as the ROM</source>
<translation> ROM </translation> <translation> ROM </translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1036"/> <location filename="../SettingsView.ui" line="1036"/>
<source>Save states</source> <source>Save states</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1084"/> <location filename="../SettingsView.ui" line="1150"/>
<source>Screenshots</source> <source>Screenshots</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1132"/> <location filename="../SettingsView.ui" line="1198"/>
<source>Patches</source> <source>Patches</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1180"/> <location filename="../SettingsView.ui" line="1246"/>
<source>Cheats</source> <source>Cheats</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1237"/> <location filename="../SettingsView.ui" line="1303"/>
<source>Log to file</source> <source>Log to file</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1244"/> <location filename="../SettingsView.ui" line="1310"/>
<source>Log to console</source> <source>Log to console</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1258"/> <location filename="../SettingsView.ui" line="1324"/>
<source>Select Log File</source> <source>Select Log File</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1271"/> <location filename="../SettingsView.ui" line="1337"/>
<source>Game Boy model</source> <source>Game Boy model:</source>
<translation>Game Boy </translation> <translation>Game Boy :</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1279"/> <location filename="../SettingsView.ui" line="1345"/>
<location filename="../SettingsView.ui" line="1315"/> <location filename="../SettingsView.ui" line="1381"/>
<location filename="../SettingsView.ui" line="1351"/> <location filename="../SettingsView.ui" line="1417"/>
<source>Autodetect</source> <source>Autodetect</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1284"/> <location filename="../SettingsView.ui" line="1350"/>
<location filename="../SettingsView.ui" line="1320"/> <location filename="../SettingsView.ui" line="1386"/>
<location filename="../SettingsView.ui" line="1356"/> <location filename="../SettingsView.ui" line="1422"/>
<source>Game Boy (DMG)</source> <source>Game Boy (DMG)</source>
<translation>Game Boy (DMG)</translation> <translation>Game Boy (DMG)</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1289"/> <location filename="../SettingsView.ui" line="1355"/>
<location filename="../SettingsView.ui" line="1325"/> <location filename="../SettingsView.ui" line="1391"/>
<location filename="../SettingsView.ui" line="1361"/> <location filename="../SettingsView.ui" line="1427"/>
<source>Super Game Boy (SGB)</source> <source>Super Game Boy (SGB)</source>
<translation>Super Game Boy (SGB)</translation> <translation>Super Game Boy (SGB)</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1294"/> <location filename="../SettingsView.ui" line="1360"/>
<location filename="../SettingsView.ui" line="1330"/> <location filename="../SettingsView.ui" line="1396"/>
<location filename="../SettingsView.ui" line="1366"/> <location filename="../SettingsView.ui" line="1432"/>
<source>Game Boy Color (CGB)</source> <source>Game Boy Color (CGB)</source>
<translation>Game Boy Color (CGB)</translation> <translation>Game Boy Color (CGB)</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1299"/> <location filename="../SettingsView.ui" line="1365"/>
<location filename="../SettingsView.ui" line="1335"/> <location filename="../SettingsView.ui" line="1401"/>
<location filename="../SettingsView.ui" line="1371"/> <location filename="../SettingsView.ui" line="1437"/>
<source>Game Boy Advance (AGB)</source> <source>Game Boy Advance (AGB)</source>
<translation>Game Boy Advance (AGB)</translation> <translation>Game Boy Advance (AGB)</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1307"/> <location filename="../SettingsView.ui" line="1373"/>
<source>Super Game Boy model:</source> <source>Super Game Boy model:</source>
<translation>Super Game Boy :</translation> <translation>Super Game Boy :</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1343"/> <location filename="../SettingsView.ui" line="1409"/>
<source>Game Boy Color model:</source> <source>Game Boy Color model:</source>
<translation>Game Boy Color :</translation> <translation>Game Boy Color :</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1386"/> <location filename="../SettingsView.ui" line="1452"/>
<source>Default BG colors:</source> <source>Default BG colors:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1553"/> <location filename="../SettingsView.ui" line="1619"/>
<source>Super Game Boy borders</source> <source>Super Game Boy borders</source>
<translation>Super Game Boy </translation> <translation>Super Game Boy </translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1567"/> <location filename="../SettingsView.ui" line="1633"/>
<source>Camera driver:</source> <source>Camera driver:</source>
<translation>:</translation> <translation>:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1664"/> <location filename="../SettingsView.ui" line="1730"/>
<source>Default sprite colors 1:</source> <source>Default sprite colors 1:</source>
<translation> 1:</translation> <translation> 1:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1671"/> <location filename="../SettingsView.ui" line="1737"/>
<source>Default sprite colors 2:</source> <source>Default sprite colors 2:</source>
<translation> 2:</translation> <translation> 2:</translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1678"/> <location filename="../SettingsView.ui" line="1744"/>
<source>Use GBC colors in GB games</source> <source>Use GBC colors in GB games</source>
<translation> GB 使 GBC </translation> <translation> GB 使 GBC </translation>
</message> </message>
<message> <message>
<location filename="../SettingsView.ui" line="1685"/> <location filename="../SettingsView.ui" line="1751"/>
<source>Camera:</source> <source>Camera:</source>
<translation></translation> <translation></translation>
</message> </message>

View File

@ -51,9 +51,9 @@ void mSDLGLCommonInit(struct mSDLRenderer* renderer) {
#else #else
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->fullscreen)); SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->player.fullscreen));
#else #else
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->fullscreen)); SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_OPENGL | SDL_RESIZABLE | (SDL_FULLSCREEN * renderer->player.fullscreen));
#endif #endif
SDL_WM_SetCaption(projectName, ""); SDL_WM_SetCaption(projectName, "");
#endif #endif

View File

@ -13,7 +13,7 @@
#include <mgba/core/core.h> #include <mgba/core/core.h>
#include <mgba/core/thread.h> #include <mgba/core/thread.h>
#ifndef __APPLE__ #ifdef __linux__
#include <malloc.h> #include <malloc.h>
#endif #endif
@ -37,7 +37,7 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
size_t size = renderer->width * renderer->height * BYTES_PER_PIXEL; size_t size = renderer->width * renderer->height * BYTES_PER_PIXEL;
#ifdef _WIN32 #ifdef _WIN32
renderer->outputBuffer = _aligned_malloc(size, 16); renderer->outputBuffer = _aligned_malloc(size, 16);
#elif !defined(__APPLE__) #elif defined(__linux__)
renderer->outputBuffer = memalign(16, size); renderer->outputBuffer = memalign(16, size);
#else #else
posix_memalign((void**) &renderer->outputBuffer, 16, size); posix_memalign((void**) &renderer->outputBuffer, 16, size);

View File

@ -131,12 +131,8 @@ int main(int argc, char** argv) {
renderer.viewportWidth = renderer.core->opts.width; renderer.viewportWidth = renderer.core->opts.width;
renderer.viewportHeight = renderer.core->opts.height; renderer.viewportHeight = renderer.core->opts.height;
#if SDL_VERSION_ATLEAST(2, 0, 0)
renderer.player.fullscreen = renderer.core->opts.fullscreen; renderer.player.fullscreen = renderer.core->opts.fullscreen;
renderer.player.windowUpdated = 0; renderer.player.windowUpdated = 0;
#else
renderer.fullscreen = renderer.core->opts.fullscreen;
#endif
renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio; renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio;
renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling; renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling;

View File

@ -54,8 +54,6 @@ struct mSDLRenderer {
SDL_Texture* sdlTex; SDL_Texture* sdlTex;
SDL_Renderer* sdlRenderer; SDL_Renderer* sdlRenderer;
SDL_GLContext* glCtx; SDL_GLContext* glCtx;
#else
bool fullscreen;
#endif #endif
unsigned width; unsigned width;

View File

@ -63,10 +63,10 @@ struct mSDLPlayer {
size_t playerId; size_t playerId;
struct mInputMap* bindings; struct mInputMap* bindings;
struct SDL_JoystickCombo* joystick; struct SDL_JoystickCombo* joystick;
int fullscreen;
int windowUpdated; int windowUpdated;
#if SDL_VERSION_ATLEAST(2, 0, 0) #if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_Window* window; SDL_Window* window;
int fullscreen;
struct mSDLRumble { struct mSDLRumble {
struct mRumble d; struct mRumble d;

View File

@ -22,9 +22,9 @@ void mSDLSWCreate(struct mSDLRenderer* renderer) {
bool mSDLSWInit(struct mSDLRenderer* renderer) { bool mSDLSWInit(struct mSDLRenderer* renderer) {
#ifdef COLOR_16_BIT #ifdef COLOR_16_BIT
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->fullscreen)); SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 16, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->player.fullscreen));
#else #else
SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->fullscreen)); SDL_SetVideoMode(renderer->viewportWidth, renderer->viewportHeight, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | (SDL_FULLSCREEN * renderer->player.fullscreen));
#endif #endif
SDL_WM_SetCaption(projectName, ""); SDL_WM_SetCaption(projectName, "");