Merge branch 'master' (early part) into medusa
12
CHANGES
|
@ -28,6 +28,8 @@ Features:
|
|||
- Support Discord Rich Presence
|
||||
- Debugger: Add tracing to file
|
||||
- Map viewer supports bitmapped GBA modes
|
||||
- OpenGL renderer with high-resolution upscaling support
|
||||
- Experimental high level "XQ" audio for most GBA games
|
||||
Emulation fixes:
|
||||
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
|
||||
- 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: 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 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:
|
||||
- Qt: More app metadata fixes
|
||||
- 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)
|
||||
- Qt: Fix some Qt display driver race conditions
|
||||
- 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:
|
||||
- GBA Savedata: EEPROM performance fixes
|
||||
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
|
||||
|
@ -79,6 +88,9 @@ Misc:
|
|||
- Qt: Open a message box for Qt frontend errors
|
||||
- GBA Video: Clean up dead code in sprite rendering loop
|
||||
- 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)
|
||||
Bugfixes:
|
||||
|
|
|
@ -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 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_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 CORE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/*.c)
|
||||
file(GLOB CORE_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/core/test/*.c)
|
||||
|
@ -261,15 +262,10 @@ if(WIN32)
|
|||
endif()
|
||||
elseif(UNIX)
|
||||
set(USE_PTHREADS ON)
|
||||
add_definitions(-DUSE_PTHREADS)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_definitions(-D_GNU_SOURCE)
|
||||
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)
|
||||
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)
|
||||
endif()
|
||||
|
||||
include(CheckCCompilerFlag)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckIncludeFiles)
|
||||
check_function_exists(strdup HAVE_STRDUP)
|
||||
|
@ -406,6 +403,27 @@ endif()
|
|||
check_function_exists(chmod HAVE_CHMOD)
|
||||
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)
|
||||
|
||||
if(HAVE_STRDUP)
|
||||
|
@ -447,6 +465,18 @@ if(HAVE_UMASK)
|
|||
list(APPEND FUNCTION_DEFINES HAVE_UMASK)
|
||||
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
|
||||
set(FEATURE_DEFINES)
|
||||
set(FEATURE_FLAGS)
|
||||
|
|
|
@ -233,7 +233,6 @@ Footnotes
|
|||
<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))
|
||||
- Mosaic for transformed OBJs ([Bug #9](http://mgba.io/b/9))
|
||||
|
||||
Missing features on DS are
|
||||
|
||||
|
|
|
@ -215,7 +215,6 @@ Fußnoten
|
|||
<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))
|
||||
- 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.
|
||||
|
||||
|
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.3 KiB |
|
@ -12,7 +12,7 @@ CXX_GUARD_START
|
|||
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#ifdef HAVE_PTHREAD_NP_H
|
||||
#include <pthread_np.h>
|
||||
#elif defined(__HAIKU__)
|
||||
#include <OS.h>
|
||||
|
@ -85,20 +85,15 @@ static inline int ThreadJoin(Thread thread) {
|
|||
}
|
||||
|
||||
static inline int ThreadSetName(const char* name) {
|
||||
#ifdef __APPLE__
|
||||
#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1060
|
||||
#if defined(__APPLE__) && defined(HAVE_PTHREAD_SETNAME_NP)
|
||||
return pthread_setname_np(name);
|
||||
#else
|
||||
UNUSED(name);
|
||||
return 0;
|
||||
#endif
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#elif defined(HAVE_PTHREAD_SET_NAME_NP)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
return 0;
|
||||
#elif defined(__HAIKU__)
|
||||
rename_thread(find_thread(NULL), name);
|
||||
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);
|
||||
#else
|
||||
UNUSED(name);
|
||||
|
|
|
@ -13,6 +13,10 @@ CXX_GUARD_START
|
|||
enum mCPUComponentType {
|
||||
CPU_COMPONENT_DEBUGGER,
|
||||
CPU_COMPONENT_CHEAT_DEVICE,
|
||||
CPU_COMPONENT_MISC_1,
|
||||
CPU_COMPONENT_MISC_2,
|
||||
CPU_COMPONENT_MISC_3,
|
||||
CPU_COMPONENT_MISC_4,
|
||||
CPU_COMPONENT_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -10,10 +10,14 @@
|
|||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/internal/gb/audio.h>
|
||||
#include <mgba-util/circle-buffer.h>
|
||||
|
||||
#define MP2K_MAGIC 0x68736D53
|
||||
#define MP2K_MAX_SOUND_CHANNELS 12
|
||||
|
||||
mLOG_DECLARE_CATEGORY(GBA_AUDIO);
|
||||
|
||||
struct GBADMA;
|
||||
|
@ -44,6 +48,7 @@ DECL_BITFIELD(GBARegisterSOUNDBIAS, uint16_t);
|
|||
DECL_BITS(GBARegisterSOUNDBIAS, Bias, 0, 10);
|
||||
DECL_BITS(GBARegisterSOUNDBIAS, Resolution, 14, 2);
|
||||
|
||||
struct GBAAudioMixer;
|
||||
struct GBAAudio {
|
||||
struct GBA* p;
|
||||
|
||||
|
@ -71,6 +76,8 @@ struct GBAAudio {
|
|||
|
||||
GBARegisterSOUNDBIAS soundbias;
|
||||
|
||||
struct GBAAudioMixer* mixer;
|
||||
bool externalMixing;
|
||||
int32_t sampleInterval;
|
||||
|
||||
bool forceDisableChA;
|
||||
|
@ -85,6 +92,188 @@ struct GBAStereoSample {
|
|||
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 GBAAudioReset(struct GBAAudio* audio);
|
||||
void GBAAudioDeinit(struct GBAAudio* audio);
|
||||
|
|
|
@ -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
|
|
@ -62,20 +62,25 @@ struct GBAVideoGLBackground {
|
|||
int32_t refx;
|
||||
int32_t refy;
|
||||
|
||||
struct GBAVideoGLAffine affine[4];
|
||||
struct GBAVideoGLAffine affine;
|
||||
};
|
||||
|
||||
enum {
|
||||
GBA_GL_FBO_OBJ = 0,
|
||||
GBA_GL_FBO_WINDOW = 1,
|
||||
GBA_GL_FBO_OUTPUT = 2,
|
||||
GBA_GL_FBO_BACKDROP,
|
||||
GBA_GL_FBO_WINDOW,
|
||||
GBA_GL_FBO_OUTPUT,
|
||||
GBA_GL_FBO_MAX
|
||||
};
|
||||
|
||||
enum {
|
||||
GBA_GL_TEX_OBJ_COLOR = 0,
|
||||
GBA_GL_TEX_OBJ_FLAGS = 1,
|
||||
GBA_GL_TEX_WINDOW = 6,
|
||||
GBA_GL_TEX_OBJ_FLAGS,
|
||||
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
|
||||
};
|
||||
|
||||
|
@ -91,6 +96,8 @@ enum {
|
|||
GBA_GL_BG_OFFSET,
|
||||
GBA_GL_BG_INFLAGS,
|
||||
GBA_GL_BG_TRANSFORM,
|
||||
GBA_GL_BG_RANGE,
|
||||
GBA_GL_BG_MOSAIC,
|
||||
|
||||
GBA_GL_OBJ_VRAM = 2,
|
||||
GBA_GL_OBJ_PALETTE,
|
||||
|
@ -101,6 +108,7 @@ enum {
|
|||
GBA_GL_OBJ_TRANSFORM,
|
||||
GBA_GL_OBJ_DIMS,
|
||||
GBA_GL_OBJ_OBJWIN,
|
||||
GBA_GL_OBJ_MOSAIC,
|
||||
|
||||
GBA_GL_FINALIZE_SCALE = 2,
|
||||
GBA_GL_FINALIZE_LAYERS,
|
||||
|
@ -124,6 +132,7 @@ struct GBAVideoGLRenderer {
|
|||
uint32_t* temporaryBuffer;
|
||||
|
||||
struct GBAVideoGLBackground bg[4];
|
||||
struct GBAVideoGLAffine affine[2][GBA_VIDEO_VERTICAL_PIXELS];
|
||||
|
||||
int oamMax;
|
||||
bool oamDirty;
|
||||
|
@ -144,6 +153,9 @@ struct GBAVideoGLRenderer {
|
|||
GLuint vramTex;
|
||||
unsigned vramDirty;
|
||||
|
||||
uint16_t shadowRegs[0x30];
|
||||
uint64_t regsDirty;
|
||||
|
||||
struct GBAVideoGLShader bgShader[6];
|
||||
struct GBAVideoGLShader objShader[2];
|
||||
struct GBAVideoGLShader finalizeShader;
|
||||
|
@ -162,7 +174,7 @@ struct GBAVideoGLRenderer {
|
|||
GBAMosaicControl mosaic;
|
||||
|
||||
struct GBAVideoGLWindowN {
|
||||
struct GBAVideoWindowRegion h;
|
||||
struct GBAVideoWindowRegion h[2];
|
||||
struct GBAVideoWindowRegion v;
|
||||
GBAWindowControl control;
|
||||
} winN[2];
|
||||
|
@ -171,6 +183,7 @@ struct GBAVideoGLRenderer {
|
|||
GBAWindowControl objwin;
|
||||
|
||||
int firstAffine;
|
||||
int firstY;
|
||||
|
||||
int scale;
|
||||
};
|
||||
|
|
|
@ -153,10 +153,12 @@ static void _wait(struct mVideoLogger* logger) {
|
|||
_proxyThreadRecover(proxyRenderer);
|
||||
return;
|
||||
}
|
||||
MutexLock(&proxyRenderer->mutex);
|
||||
while (RingFIFOSize(&proxyRenderer->dirtyQueue)) {
|
||||
ConditionWake(&proxyRenderer->toThreadCond);
|
||||
ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex);
|
||||
}
|
||||
MutexUnlock(&proxyRenderer->mutex);
|
||||
}
|
||||
|
||||
static void _unlock(struct mVideoLogger* logger) {
|
||||
|
|
|
@ -250,31 +250,21 @@ void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y)
|
|||
|
||||
void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
}
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
}
|
||||
mVideoLoggerRendererFinishFrame(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) {
|
||||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
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
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
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) {
|
||||
|
@ -297,9 +287,6 @@ static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size
|
|||
struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer;
|
||||
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
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->wait(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
|
|
|
@ -723,7 +723,7 @@ static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cycle
|
|||
|
||||
void GBHalt(struct LR35902Core* cpu) {
|
||||
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->halted = true;
|
||||
} else if (gb->model < GB_MODEL_CGB) {
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include <mgba/internal/gba/serialize.h>
|
||||
#include <mgba/internal/gba/video.h>
|
||||
|
||||
#define MP2K_LOCK_MAX 8
|
||||
|
||||
#ifdef _3DS
|
||||
#define blip_add_delta blip_add_delta_fast
|
||||
#endif
|
||||
|
@ -24,7 +26,7 @@ const unsigned GBA_AUDIO_SAMPLES = 2048;
|
|||
const unsigned GBA_AUDIO_FIFO_SIZE = 8 * sizeof(int32_t);
|
||||
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 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->chB.fifo, GBA_AUDIO_FIFO_SIZE);
|
||||
|
||||
audio->externalMixing = false;
|
||||
audio->forceDisableChA = false;
|
||||
audio->forceDisableChB = false;
|
||||
audio->masterVolume = GBA_AUDIO_VOLUME_MAX;
|
||||
audio->mixer = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
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 = GBADMARegisterSetWidth(info->reg, 1);
|
||||
}
|
||||
|
@ -265,6 +283,10 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
|||
sampleLeft >>= psgShift;
|
||||
sampleRight >>= psgShift;
|
||||
|
||||
if (audio->mixer) {
|
||||
audio->mixer->step(audio->mixer);
|
||||
}
|
||||
if (!audio->externalMixing) {
|
||||
if (!audio->forceDisableChA) {
|
||||
if (audio->chALeft) {
|
||||
sampleLeft += (audio->chA.sample << 2) >> !audio->volumeChA;
|
||||
|
@ -284,6 +306,7 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
|||
sampleRight += (audio->chB.sample << 2) >> !audio->volumeChB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sampleLeft = _applyBias(audio, sampleLeft);
|
||||
sampleRight = _applyBias(audio, sampleRight);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <mgba/internal/gba/cheats.h>
|
||||
#include <mgba/internal/gba/gba.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/overrides.h>
|
||||
#ifndef DISABLE_THREADING
|
||||
|
@ -127,6 +128,9 @@ static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = {
|
|||
};
|
||||
|
||||
struct mVideoLogContext;
|
||||
|
||||
#define CPU_COMPONENT_AUDIO_MIXER CPU_COMPONENT_MISC_1
|
||||
|
||||
struct GBACore {
|
||||
struct mCore d;
|
||||
struct GBAVideoSoftwareRenderer renderer;
|
||||
|
@ -144,6 +148,7 @@ struct GBACore {
|
|||
const struct Configuration* overrides;
|
||||
struct mDebuggerPlatform* debuggerPlatform;
|
||||
struct mCheatDevice* cheatDevice;
|
||||
struct GBAAudioMixer* audioMixer;
|
||||
};
|
||||
|
||||
static bool _GBACoreInit(struct mCore* core) {
|
||||
|
@ -166,6 +171,7 @@ static bool _GBACoreInit(struct mCore* core) {
|
|||
gbacore->debuggerPlatform = NULL;
|
||||
gbacore->cheatDevice = NULL;
|
||||
gbacore->logContext = NULL;
|
||||
gbacore->audioMixer = NULL;
|
||||
|
||||
GBACreate(gba);
|
||||
// TODO: Restore cheats
|
||||
|
@ -217,6 +223,7 @@ static void _GBACoreDeinit(struct mCore* core) {
|
|||
mCheatDeviceDestroy(gbacore->cheatDevice);
|
||||
}
|
||||
free(gbacore->cheatDevice);
|
||||
free(gbacore->audioMixer);
|
||||
mCoreConfigFreeOpts(&core->opts);
|
||||
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, "gba.bios");
|
||||
mCoreConfigCopyValue(&core->config, config, "gba.audioHle");
|
||||
|
||||
#ifndef DISABLE_THREADING
|
||||
mCoreConfigCopyValue(&core->config, config, "threadedVideo");
|
||||
|
@ -475,6 +483,16 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
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);
|
||||
|
||||
#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2
|
||||
|
@ -521,7 +539,7 @@ static void _GBACoreReset(struct mCore* core) {
|
|||
#endif
|
||||
|
||||
ARMReset(core->cpu);
|
||||
if (core->opts.skipBios && gba->isPristine) {
|
||||
if (core->opts.skipBios && (gba->romVf || gba->memory.rom)) {
|
||||
GBASkipBIOS(core->board);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -147,6 +147,7 @@ void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) {
|
|||
proxyRenderer->backend->deinit(proxyRenderer->backend);
|
||||
} else {
|
||||
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_DEINIT);
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
}
|
||||
|
||||
mVideoLoggerRendererDeinit(proxyRenderer->logger);
|
||||
|
@ -307,28 +308,20 @@ void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y)
|
|||
|
||||
void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
proxyRenderer->logger->lock(proxyRenderer->logger);
|
||||
}
|
||||
if (!proxyRenderer->logger->block) {
|
||||
proxyRenderer->backend->finishFrame(proxyRenderer->backend);
|
||||
}
|
||||
mVideoLoggerRendererFinishFrame(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) {
|
||||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
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
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->postEvent(proxyRenderer->logger, LOGGER_EVENT_GET_PIXELS);
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
proxyRenderer->logger->unlock(proxyRenderer->logger);
|
||||
*pixels = proxyRenderer->logger->pixelBuffer;
|
||||
*stride = proxyRenderer->logger->pixelStride;
|
||||
} else {
|
||||
|
@ -340,8 +333,6 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si
|
|||
struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer;
|
||||
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
|
||||
mVideoLoggerRendererFlush(proxyRenderer->logger);
|
||||
}
|
||||
proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels);
|
||||
if (proxyRenderer->logger->block && proxyRenderer->logger->wait) {
|
||||
|
|
|
@ -241,10 +241,6 @@ void GBAReset(struct ARMCore* cpu) {
|
|||
if (gba->pristineRomSize > SIZE_CART0) {
|
||||
GBAMatrixReset(gba);
|
||||
}
|
||||
|
||||
if (!gba->romVf && gba->memory.rom) {
|
||||
GBASkipBIOS(gba);
|
||||
}
|
||||
}
|
||||
|
||||
void GBASkipBIOS(struct GBA* gba) {
|
||||
|
@ -788,6 +784,10 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) {
|
|||
void GBAFrameStarted(struct GBA* gba) {
|
||||
GBATestKeypadIRQ(gba);
|
||||
|
||||
if (gba->audio.mixer) {
|
||||
gba->audio.mixer->vblank(gba->audio.mixer);
|
||||
}
|
||||
|
||||
size_t c;
|
||||
for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) {
|
||||
struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c);
|
||||
|
|
|
@ -17,19 +17,12 @@
|
|||
#define SPRITE_MOSAIC_LOOP(DEPTH, TYPE) \
|
||||
SPRITE_YBASE_ ## DEPTH(inY); \
|
||||
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) { \
|
||||
int localX = inX - xOffset * (outX % mosaicH); \
|
||||
if (localX < 0 || localX > width - 1) { \
|
||||
continue; \
|
||||
if (localX < 0) { \
|
||||
localX = 0; \
|
||||
} else if (localX > width - 1) {\
|
||||
localX = width - 1; \
|
||||
} \
|
||||
SPRITE_XBASE_ ## DEPTH(localX); \
|
||||
SPRITE_DRAW_PIXEL_ ## DEPTH ## _ ## TYPE(localX); \
|
||||
|
@ -55,6 +48,31 @@
|
|||
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_YBASE_16(localY) unsigned yBase = (localY & ~0x7) * stride + (localY & 0x7) * 4;
|
||||
|
||||
|
@ -281,6 +299,13 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
|
|||
if (end < condition) {
|
||||
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 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];
|
||||
if (flags & FLAG_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) {
|
||||
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 4];
|
||||
SPRITE_TRANSFORMED_LOOP(16, NORMAL_OBJWIN);
|
||||
|
@ -358,6 +390,12 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re
|
|||
palette = &palette[GBAObjAttributesCGetPalette(sprite->c) << 8];
|
||||
if (flags & FLAG_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) {
|
||||
objwinPalette = &objwinPalette[GBAObjAttributesCGetPalette(sprite->c) << 8];
|
||||
SPRITE_TRANSFORMED_LOOP(256, NORMAL_OBJWIN);
|
||||
|
|
|
@ -942,11 +942,17 @@ int GBAVideoSoftwareRendererPreprocessSpriteLayer(struct GBAVideoSoftwareRendere
|
|||
struct GBAVideoRendererSprite* sprite = &renderer->sprites[i];
|
||||
int localY = y;
|
||||
renderer->end = 0;
|
||||
if ((y < sprite->y && (sprite->endY - 256 < 0 || y >= sprite->endY - 256)) || y >= sprite->endY) {
|
||||
continue;
|
||||
}
|
||||
if (GBAObjAttributesAIsMosaic(sprite->obj.a)) {
|
||||
localY = mosaicY;
|
||||
if (localY < sprite->y) {
|
||||
localY = sprite->y;
|
||||
}
|
||||
if (localY >= sprite->endY) {
|
||||
localY = sprite->endY - 1;
|
||||
}
|
||||
if ((localY < sprite->y && (sprite->endY - 256 < 0 || localY >= sprite->endY - 256)) || localY >= sprite->endY) {
|
||||
continue;
|
||||
}
|
||||
for (w = 0; w < renderer->nWindows; ++w) {
|
||||
if (renderer->spriteCyclesRemaining <= 0) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
static int cheatsSetup(void** state) {
|
||||
struct mCore* core = GBACoreCreate();
|
||||
core->init(core);
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->cheatDevice(core);
|
||||
*state = core;
|
||||
return 0;
|
||||
|
@ -26,6 +27,7 @@ static int cheatsTeardown(void** state) {
|
|||
return 0;
|
||||
}
|
||||
struct mCore* core = *state;
|
||||
mCoreConfigDeinit(&core->config);
|
||||
core->deinit(core);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,9 @@ M_TEST_DEFINE(reset) {
|
|||
struct mCore* core = GBACoreCreate();
|
||||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->reset(core);
|
||||
mCoreConfigDeinit(&core->config);
|
||||
core->deinit(core);
|
||||
}
|
||||
|
||||
|
@ -36,7 +38,9 @@ M_TEST_DEFINE(loadNullROM) {
|
|||
assert_non_null(core);
|
||||
assert_true(core->init(core));
|
||||
assert_false(core->loadROM(core, NULL));
|
||||
mCoreInitConfig(core, NULL);
|
||||
core->reset(core);
|
||||
mCoreConfigDeinit(&core->config);
|
||||
core->deinit(core);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,8 +110,7 @@ void GBAVideoReset(struct GBAVideo* video) {
|
|||
memset(video->palette, 0, sizeof(video->palette));
|
||||
memset(video->oam.raw, 0, sizeof(video->oam.raw));
|
||||
|
||||
video->renderer->deinit(video->renderer);
|
||||
video->renderer->init(video->renderer);
|
||||
video->renderer->reset(video->renderer);
|
||||
}
|
||||
|
||||
void GBAVideoDeinit(struct GBAVideo* video) {
|
||||
|
|
|
@ -136,9 +136,11 @@ static void mGLES2ContextInit(struct VideoBackend* v, WHandle handle) {
|
|||
|
||||
glBindVertexArray(context->initialShader.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||
glEnableVertexAttribArray(context->initialShader.positionLocation);
|
||||
glVertexAttribPointer(context->initialShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
glBindVertexArray(context->finalShader.vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||
glEnableVertexAttribArray(context->finalShader.positionLocation);
|
||||
glVertexAttribPointer(context->finalShader.positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
glBindVertexArray(0);
|
||||
|
||||
|
@ -246,7 +248,7 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
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);
|
||||
glUseProgram(shader->program);
|
||||
glUniform1i(shader->texLocation, 0);
|
||||
|
@ -303,7 +305,6 @@ void _drawShader(struct mGLES2Context* context, struct mGLES2Shader* shader) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
glEnableVertexAttribArray(shader->positionLocation);
|
||||
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
||||
glBindTexture(GL_TEXTURE_2D, shader->tex);
|
||||
}
|
||||
|
@ -327,6 +328,7 @@ void mGLES2ContextDrawFrame(struct VideoBackend* v) {
|
|||
_drawShader(context, &context->finalShader);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glUseProgram(0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
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);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, context->vbo);
|
||||
glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
glEnableVertexAttribArray(context->shaders[i].positionLocation);
|
||||
glVertexAttribPointer(context->shaders[i].positionLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
|
|
@ -67,6 +67,10 @@ void Action::trigger(bool active) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_exclusive && !m_booleanFunction) {
|
||||
active = true;
|
||||
}
|
||||
|
||||
if (m_function && active) {
|
||||
m_function();
|
||||
}
|
||||
|
|
|
@ -291,6 +291,7 @@ if(QT_STATIC)
|
|||
if(WIN32)
|
||||
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::Gui PROPERTIES INTERFACE_LINK_LIBRARIES ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY})
|
||||
elseif(APPLE)
|
||||
find_package(Cups)
|
||||
find_package(Qt5PrintSupport)
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(controller, "didReset");
|
||||
controller->finishFrame();
|
||||
};
|
||||
|
||||
|
@ -196,9 +197,6 @@ CoreController::~CoreController() {
|
|||
mCacheSetDeinit(m_cacheSet.get());
|
||||
m_cacheSet.reset();
|
||||
}
|
||||
|
||||
mCoreConfigDeinit(&m_threadContext.core->config);
|
||||
m_threadContext.core->deinit(m_threadContext.core);
|
||||
}
|
||||
|
||||
const color_t* CoreController::drawContext() {
|
||||
|
@ -368,7 +366,6 @@ void CoreController::stop() {
|
|||
#endif
|
||||
setPaused(false);
|
||||
mCoreThreadEnd(&m_threadContext);
|
||||
emit stopping();
|
||||
}
|
||||
|
||||
void CoreController::reset() {
|
||||
|
@ -445,13 +442,21 @@ void CoreController::rewind(int states) {
|
|||
}
|
||||
|
||||
void CoreController::setFastForward(bool enable) {
|
||||
if (m_fastForward == enable) {
|
||||
return;
|
||||
}
|
||||
m_fastForward = enable;
|
||||
updateFastForward();
|
||||
emit fastForwardChanged(enable);
|
||||
}
|
||||
|
||||
void CoreController::forceFastForward(bool enable) {
|
||||
if (m_fastForwardForced == enable) {
|
||||
return;
|
||||
}
|
||||
m_fastForwardForced = enable;
|
||||
updateFastForward();
|
||||
emit fastForwardChanged(enable || m_fastForward);
|
||||
}
|
||||
|
||||
void CoreController::loadState(int slot) {
|
||||
|
|
|
@ -99,6 +99,9 @@ public:
|
|||
void setInputController(InputController*);
|
||||
void setLogger(LogController*);
|
||||
|
||||
bool audioSync() const { return m_audioSync; }
|
||||
bool videoSync() const { return m_videoSync; }
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
void stop();
|
||||
|
@ -167,6 +170,7 @@ signals:
|
|||
void crashed(const QString& errorMessage);
|
||||
void failed();
|
||||
void frameAvailable();
|
||||
void didReset();
|
||||
void stateLoaded();
|
||||
void rewound();
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ Display* Display::create(QWidget* parent) {
|
|||
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
|
||||
QSurfaceFormat format;
|
||||
format.setSwapInterval(1);
|
||||
format.setSwapBehavior(QSurfaceFormat::TripleBuffer);
|
||||
format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
|
||||
#endif
|
||||
|
||||
switch (s_driver) {
|
||||
|
|
|
@ -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
|
||||
* 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)
|
||||
, m_gl(nullptr)
|
||||
{
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
windowHandle()->create();
|
||||
|
||||
// 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
|
||||
m_gl = new QOpenGLContext;
|
||||
m_gl->setFormat(format);
|
||||
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);
|
||||
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() {
|
||||
|
@ -98,6 +113,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
|
|||
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatio());
|
||||
#endif
|
||||
resizePainter();
|
||||
connect(m_context.get(), &CoreController::didReset, this, &DisplayGL::resizeContext);
|
||||
}
|
||||
|
||||
void DisplayGL::stopDrawing() {
|
||||
|
@ -249,10 +265,9 @@ PainterGL::PainterGL(VideoProxy* proxy, QWindow* surface, QOpenGLContext* parent
|
|||
#endif
|
||||
m_backend->swap = [](VideoBackend* v) {
|
||||
PainterGL* painter = static_cast<PainterGL*>(v->user);
|
||||
if (!painter->m_gl->isValid()) {
|
||||
return;
|
||||
if (!painter->m_swapTimer.isActive()) {
|
||||
QMetaObject::invokeMethod(&painter->m_swapTimer, "start");
|
||||
}
|
||||
painter->m_gl->swapBuffers(painter->m_gl->surface());
|
||||
};
|
||||
|
||||
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) {
|
||||
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() {
|
||||
|
@ -305,18 +324,8 @@ void PainterGL::resizeContext() {
|
|||
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();
|
||||
m_backend->setDimensions(m_backend, size.width(), size.height());
|
||||
if (!m_active) {
|
||||
m_gl->doneCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
|
||||
|
@ -368,26 +377,21 @@ void PainterGL::draw() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_needsUnlock) {
|
||||
QTimer::singleShot(0, this, &PainterGL::draw);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCoreSyncWaitFrameStart(&m_context->thread()->impl->sync) || !m_queue.isEmpty()) {
|
||||
dequeue();
|
||||
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
|
||||
m_painter.begin(m_window);
|
||||
performDraw();
|
||||
m_painter.end();
|
||||
m_backend->swap(m_backend);
|
||||
if (!m_delayTimer.isValid()) {
|
||||
m_delayTimer.start();
|
||||
} else if (m_gl->format().swapInterval()) {
|
||||
while (m_delayTimer.elapsed() < 15) {
|
||||
QThread::usleep(100);
|
||||
}
|
||||
m_delayTimer.restart();
|
||||
}
|
||||
forceDraw();
|
||||
if (m_context->thread()->impl->sync.videoFrameWait) {
|
||||
m_needsUnlock = true;
|
||||
} else {
|
||||
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
|
||||
}
|
||||
if (!m_queue.isEmpty()) {
|
||||
QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection);
|
||||
} else {
|
||||
mCoreSyncWaitFrameEnd(&m_context->thread()->impl->sync);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -404,6 +408,9 @@ void PainterGL::stop() {
|
|||
dequeueAll();
|
||||
m_backend->clear(m_backend);
|
||||
m_backend->swap(m_backend);
|
||||
if (m_videoProxy) {
|
||||
m_videoProxy->reset();
|
||||
}
|
||||
m_gl->doneCurrent();
|
||||
m_gl->moveToThread(m_surface->thread());
|
||||
m_context.reset();
|
||||
|
@ -428,6 +435,30 @@ void PainterGL::performDraw() {
|
|||
if (m_messagePainter) {
|
||||
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) {
|
||||
|
@ -480,12 +511,6 @@ void PainterGL::setShaders(struct VDir* dir) {
|
|||
return;
|
||||
}
|
||||
#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) {
|
||||
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
|
||||
mGLES2ShaderFree(&m_shader);
|
||||
|
@ -494,9 +519,6 @@ void PainterGL::setShaders(struct VDir* dir) {
|
|||
if (m_started) {
|
||||
mGLES2ShaderAttach(reinterpret_cast<mGLES2Context*>(m_backend), static_cast<mGLES2Shader*>(m_shader.passes), m_shader.nPasses);
|
||||
}
|
||||
if (!m_active) {
|
||||
m_gl->doneCurrent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -505,19 +527,10 @@ void PainterGL::clearShaders() {
|
|||
return;
|
||||
}
|
||||
#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) {
|
||||
mGLES2ShaderDetach(reinterpret_cast<mGLES2Context*>(m_backend));
|
||||
mGLES2ShaderFree(&m_shader);
|
||||
}
|
||||
if (!m_active) {
|
||||
m_gl->doneCurrent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QOpenGLContext>
|
||||
#include <QList>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QQueue>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include "VideoProxy.h"
|
||||
|
||||
|
@ -105,6 +105,9 @@ public slots:
|
|||
|
||||
int glTex();
|
||||
|
||||
private slots:
|
||||
void swap();
|
||||
|
||||
private:
|
||||
void performDraw();
|
||||
void dequeue();
|
||||
|
@ -125,7 +128,9 @@ private:
|
|||
VideoBackend* m_backend = nullptr;
|
||||
QSize m_size;
|
||||
MessagePainter* m_messagePainter = nullptr;
|
||||
QElapsedTimer m_delayTimer;
|
||||
QTimer m_swapTimer{this};
|
||||
bool m_needsUnlock = false;
|
||||
bool m_frameReady = false;
|
||||
VideoProxy* m_videoProxy;
|
||||
};
|
||||
|
||||
|
|
|
@ -127,6 +127,9 @@ void LogController::logToStdout(bool log) {
|
|||
|
||||
void LogController::setLogFile(const QString& file) {
|
||||
m_logStream.reset();
|
||||
if (file.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_logFile = std::make_unique<QFile>(file);
|
||||
m_logFile->open(QIODevice::Append | QIODevice::Text);
|
||||
m_logStream = std::make_unique<QTextStream>(m_logFile.get());
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>4</number>
|
||||
<number>8</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>6</number>
|
||||
<number>8</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -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()) {
|
||||
m_ui.savegameSameDir->setChecked(true);
|
||||
}
|
||||
|
@ -364,7 +368,6 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("videoSync", m_ui.videoSync);
|
||||
saveSetting("audioSync", m_ui.audioSync);
|
||||
saveSetting("frameskip", m_ui.frameskip);
|
||||
saveSetting("fpsTarget", m_ui.fpsTarget);
|
||||
saveSetting("autofireThreshold", m_ui.autofireThreshold);
|
||||
saveSetting("lockAspectRatio", m_ui.lockAspectRatio);
|
||||
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
|
||||
|
@ -395,7 +398,7 @@ void SettingsView::updateConfig() {
|
|||
saveSetting("logToStdout", m_ui.logToStdout);
|
||||
saveSetting("logFile", m_ui.logFile);
|
||||
saveSetting("useDiscordPresence", m_ui.useDiscordPresence);
|
||||
saveSetting("audioHle", m_ui.audioHle);
|
||||
saveSetting("gba.audioHle", m_ui.audioHle);
|
||||
|
||||
if (m_ui.fastForwardUnbounded->isChecked()) {
|
||||
saveSetting("fastForwardRatio", "-1");
|
||||
|
@ -403,6 +406,13 @@ void SettingsView::updateConfig() {
|
|||
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) {
|
||||
case IDLE_LOOP_IGNORE:
|
||||
saveSetting("idleOptimization", "ignore");
|
||||
|
@ -549,7 +559,7 @@ void SettingsView::reloadConfig() {
|
|||
loadSetting("logToStdout", m_ui.logToStdout);
|
||||
loadSetting("logFile", m_ui.logFile);
|
||||
loadSetting("useDiscordPresence", m_ui.useDiscordPresence);
|
||||
loadSetting("audioHle", m_ui.audioHle);
|
||||
loadSetting("gba.audioHle", m_ui.audioHle);
|
||||
loadSetting("videoScale", m_ui.videoScale, 1);
|
||||
|
||||
m_ui.libraryStyle->setCurrentIndex(loadSetting("libraryStyle").toInt());
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>790</width>
|
||||
<height>686</height>
|
||||
<width>849</width>
|
||||
<height>753</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
@ -40,7 +40,7 @@
|
|||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<width>180</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
|
@ -399,21 +399,21 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Sync:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1">
|
||||
<item row="11" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="videoSync">
|
||||
|
@ -431,27 +431,34 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QCheckBox" name="lockAspectRatio">
|
||||
<property name="text">
|
||||
<string>Lock aspect ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="QCheckBox" name="lockIntegerScaling">
|
||||
<property name="text">
|
||||
<string>Force integer scaling</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<item row="14" column="1">
|
||||
<widget class="QCheckBox" name="resampleVideo">
|
||||
<property name="text">
|
||||
<string>Bilinear filtering</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QPushButton" name="nativeGB">
|
||||
<property name="text">
|
||||
<string>Native (59.7275)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="interface_2">
|
||||
|
@ -894,7 +901,7 @@
|
|||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>13</number>
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -903,9 +910,6 @@
|
|||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="audioHle">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>XQ GBA audio (experimental)</string>
|
||||
</property>
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>4</number>
|
||||
<number>8</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -24,6 +24,9 @@ VideoProxy::VideoProxy() {
|
|||
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.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) {
|
||||
|
@ -41,7 +44,10 @@ void VideoProxy::init() {
|
|||
}
|
||||
|
||||
void VideoProxy::reset() {
|
||||
m_mutex.lock();
|
||||
RingFIFOClear(&m_dirtyQueue);
|
||||
m_toThreadCond.wakeAll();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void VideoProxy::deinit() {
|
||||
|
@ -92,11 +98,13 @@ void VideoProxy::unlock() {
|
|||
}
|
||||
|
||||
void VideoProxy::wait() {
|
||||
m_mutex.lock();
|
||||
while (RingFIFOSize(&m_dirtyQueue)) {
|
||||
emit dataAvailable();
|
||||
m_toThreadCond.wakeAll();
|
||||
m_fromThreadCond.wait(&m_mutex, 1);
|
||||
}
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void VideoProxy::wake(int y) {
|
||||
|
|
|
@ -30,11 +30,11 @@ signals:
|
|||
|
||||
public slots:
|
||||
void processData();
|
||||
void reset();
|
||||
void handleEvent(int);
|
||||
|
||||
private:
|
||||
void init();
|
||||
void reset();
|
||||
void deinit();
|
||||
|
||||
bool writeData(const void* data, size_t length);
|
||||
|
|
|
@ -648,8 +648,12 @@ void Window::closeEvent(QCloseEvent* event) {
|
|||
m_config->setOption("width", GBA_VIDEO_HORIZONTAL_PIXELS * m_savedScale);
|
||||
}
|
||||
saveConfig();
|
||||
if (m_controller) {
|
||||
event->ignore();
|
||||
m_pendingClose = true;
|
||||
} else {
|
||||
m_display.reset();
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::focusInEvent(QFocusEvent*) {
|
||||
|
@ -784,7 +788,6 @@ void Window::gameStarted() {
|
|||
}
|
||||
attachWidget(m_display.get());
|
||||
setMouseTracking(true);
|
||||
m_display->setMinimumSize(size);
|
||||
setFocus();
|
||||
|
||||
#ifndef Q_OS_MAC
|
||||
|
@ -792,7 +795,6 @@ void Window::gameStarted() {
|
|||
menuBar()->hide();
|
||||
}
|
||||
#endif
|
||||
m_display->startDrawing(m_controller);
|
||||
|
||||
reloadAudioDriver();
|
||||
multiplayerChanged();
|
||||
|
@ -843,6 +845,11 @@ void Window::gameStarted() {
|
|||
|
||||
void Window::gameStopped() {
|
||||
m_controller.reset();
|
||||
m_display->stopDrawing();
|
||||
if (m_pendingClose) {
|
||||
m_display.reset();
|
||||
close();
|
||||
}
|
||||
for (Action* action : m_platformActions) {
|
||||
action->setEnabled(true);
|
||||
}
|
||||
|
@ -929,7 +936,6 @@ void Window::reloadDisplayDriver() {
|
|||
m_shaderView = std::make_unique<ShaderSelector>(m_display.get(), m_config);
|
||||
#endif
|
||||
|
||||
connect(this, &Window::shutdown, m_display.get(), &Display::stopDrawing);
|
||||
connect(m_display.get(), &Display::hideCursor, [this]() {
|
||||
if (static_cast<QStackedLayout*>(m_screenWidget->layout())->currentWidget() == m_display.get()) {
|
||||
m_screenWidget->setCursor(Qt::BlankCursor);
|
||||
|
@ -954,8 +960,6 @@ void Window::reloadDisplayDriver() {
|
|||
#endif
|
||||
|
||||
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::forceDraw);
|
||||
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
|
||||
|
@ -966,13 +970,12 @@ void Window::reloadDisplayDriver() {
|
|||
|
||||
attachWidget(m_display.get());
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
void Window::reloadAudioDriver() {
|
||||
|
@ -991,6 +994,7 @@ void Window::reloadAudioDriver() {
|
|||
m_audioProcessor->requestSampleRate(opts->sampleRate);
|
||||
m_audioProcessor->start();
|
||||
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() {
|
||||
|
@ -1372,7 +1376,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
|||
|
||||
m_actions.addMenu(tr("Audio/&Video"), "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_frameSizes[i];
|
||||
showNormal();
|
||||
|
@ -1739,6 +1743,9 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
|||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
if (m_pendingClose) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_controller) {
|
||||
m_controller->stop();
|
||||
|
@ -1771,12 +1778,14 @@ void Window::setController(CoreController* controller, const QString& fname) {
|
|||
m_inputController.recalibrateAxes();
|
||||
m_controller->setInputController(&m_inputController);
|
||||
m_controller->setLogger(&m_log);
|
||||
m_display->startDrawing(m_controller);
|
||||
|
||||
connect(this, &Window::shutdown, [this]() {
|
||||
if (!m_controller) {
|
||||
return;
|
||||
}
|
||||
m_controller->stop();
|
||||
disconnect(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);
|
||||
});
|
||||
|
||||
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::forceDraw);
|
||||
connect(m_controller.get(), &CoreController::rewound, m_display.get(), &Display::forceDraw);
|
||||
|
|
|
@ -207,6 +207,7 @@ private:
|
|||
QString m_pendingPatch;
|
||||
QString m_pendingState;
|
||||
bool m_pendingPause = false;
|
||||
bool m_pendingClose = false;
|
||||
|
||||
bool m_hitUnimplementedBiosCall;
|
||||
|
||||
|
|
|
@ -4243,21 +4243,26 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标
|
|||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="67"/>
|
||||
<source>Enhancements</source>
|
||||
<translation>增强</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="72"/>
|
||||
<source>BIOS</source>
|
||||
<translation>BIOS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="72"/>
|
||||
<location filename="../SettingsView.ui" line="77"/>
|
||||
<source>Paths</source>
|
||||
<translation>路径</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="77"/>
|
||||
<location filename="../SettingsView.ui" line="82"/>
|
||||
<source>Logging</source>
|
||||
<translation>日志记录</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="82"/>
|
||||
<location filename="../SettingsView.ui" line="87"/>
|
||||
<source>Game Boy</source>
|
||||
<translation>Game Boy</translation>
|
||||
</message>
|
||||
|
@ -4272,491 +4277,522 @@ Game Boy Advance 是任天堂有限公司(Nintendo Co., Ltd.)的注册商标
|
|||
<translation>音频缓冲:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="129"/>
|
||||
<location filename="../SettingsView.ui" line="151"/>
|
||||
<location filename="../SettingsView.ui" line="134"/>
|
||||
<location filename="../SettingsView.ui" line="156"/>
|
||||
<source>1536</source>
|
||||
<translation>1536</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="136"/>
|
||||
<location filename="../SettingsView.ui" line="141"/>
|
||||
<source>512</source>
|
||||
<translation>512</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="141"/>
|
||||
<location filename="../SettingsView.ui" line="146"/>
|
||||
<source>768</source>
|
||||
<translation>768</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="146"/>
|
||||
<location filename="../SettingsView.ui" line="151"/>
|
||||
<source>1024</source>
|
||||
<translation>1024</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="156"/>
|
||||
<location filename="../SettingsView.ui" line="161"/>
|
||||
<source>2048</source>
|
||||
<translation>2048</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="161"/>
|
||||
<location filename="../SettingsView.ui" line="166"/>
|
||||
<source>3072</source>
|
||||
<translation>3072</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="166"/>
|
||||
<location filename="../SettingsView.ui" line="171"/>
|
||||
<source>4096</source>
|
||||
<translation>4096</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="174"/>
|
||||
<location filename="../SettingsView.ui" line="179"/>
|
||||
<source>samples</source>
|
||||
<translation>采样</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="183"/>
|
||||
<location filename="../SettingsView.ui" line="188"/>
|
||||
<source>Sample rate:</source>
|
||||
<translation>采样率:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="195"/>
|
||||
<location filename="../SettingsView.ui" line="212"/>
|
||||
<location filename="../SettingsView.ui" line="200"/>
|
||||
<location filename="../SettingsView.ui" line="217"/>
|
||||
<source>44100</source>
|
||||
<translation>44100</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="202"/>
|
||||
<location filename="../SettingsView.ui" line="207"/>
|
||||
<source>22050</source>
|
||||
<translation>22050</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="207"/>
|
||||
<location filename="../SettingsView.ui" line="212"/>
|
||||
<source>32000</source>
|
||||
<translation>32000</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="217"/>
|
||||
<location filename="../SettingsView.ui" line="222"/>
|
||||
<source>48000</source>
|
||||
<translation>48000</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="225"/>
|
||||
<location filename="../SettingsView.ui" line="230"/>
|
||||
<source>Hz</source>
|
||||
<translation>Hz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="234"/>
|
||||
<location filename="../SettingsView.ui" line="239"/>
|
||||
<source>Volume:</source>
|
||||
<translation>音量:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="265"/>
|
||||
<location filename="../SettingsView.ui" line="305"/>
|
||||
<location filename="../SettingsView.ui" line="270"/>
|
||||
<location filename="../SettingsView.ui" line="310"/>
|
||||
<source>Mute</source>
|
||||
<translation>静音</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="274"/>
|
||||
<location filename="../SettingsView.ui" line="279"/>
|
||||
<source>Fast forward volume:</source>
|
||||
<translation>快进音量:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="321"/>
|
||||
<location filename="../SettingsView.ui" line="326"/>
|
||||
<source>Display driver:</source>
|
||||
<translation>显示驱动:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="338"/>
|
||||
<location filename="../SettingsView.ui" line="343"/>
|
||||
<source>Frameskip:</source>
|
||||
<translation>跳帧:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="347"/>
|
||||
<location filename="../SettingsView.ui" line="352"/>
|
||||
<source>Skip every</source>
|
||||
<translation>每间隔</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="357"/>
|
||||
<location filename="../SettingsView.ui" line="717"/>
|
||||
<location filename="../SettingsView.ui" line="362"/>
|
||||
<location filename="../SettingsView.ui" line="722"/>
|
||||
<source>frames</source>
|
||||
<translation>帧</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="366"/>
|
||||
<location filename="../SettingsView.ui" line="371"/>
|
||||
<source>FPS target:</source>
|
||||
<translation>目标 FPS:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="391"/>
|
||||
<location filename="../SettingsView.ui" line="396"/>
|
||||
<source>frames per second</source>
|
||||
<translation>帧每秒</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="407"/>
|
||||
<location filename="../SettingsView.ui" line="412"/>
|
||||
<source>Sync:</source>
|
||||
<translation>同步:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="416"/>
|
||||
<location filename="../SettingsView.ui" line="421"/>
|
||||
<source>Video</source>
|
||||
<translation>视频</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="423"/>
|
||||
<location filename="../SettingsView.ui" line="428"/>
|
||||
<source>Audio</source>
|
||||
<translation>音频</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="432"/>
|
||||
<location filename="../SettingsView.ui" line="437"/>
|
||||
<source>Lock aspect ratio</source>
|
||||
<translation>锁定纵横比</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="439"/>
|
||||
<location filename="../SettingsView.ui" line="444"/>
|
||||
<source>Force integer scaling</source>
|
||||
<translation>强制整数缩放</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="446"/>
|
||||
<location filename="../SettingsView.ui" line="451"/>
|
||||
<source>Bilinear filtering</source>
|
||||
<translation>双线性过滤</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="457"/>
|
||||
<location filename="../SettingsView.ui" line="462"/>
|
||||
<source>Language</source>
|
||||
<translation>语言</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="465"/>
|
||||
<location filename="../SettingsView.ui" line="470"/>
|
||||
<source>English</source>
|
||||
<translation>英语</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="480"/>
|
||||
<location filename="../SettingsView.ui" line="485"/>
|
||||
<source>Library:</source>
|
||||
<translation>库:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="488"/>
|
||||
<location filename="../SettingsView.ui" line="493"/>
|
||||
<source>List view</source>
|
||||
<translation>列表查看</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="493"/>
|
||||
<location filename="../SettingsView.ui" line="498"/>
|
||||
<source>Tree view</source>
|
||||
<translation>树状查看</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="501"/>
|
||||
<location filename="../SettingsView.ui" line="506"/>
|
||||
<source>Show when no game open</source>
|
||||
<translation>未打开游戏时显示</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="511"/>
|
||||
<location filename="../SettingsView.ui" line="516"/>
|
||||
<source>Clear cache</source>
|
||||
<translation>清除缓存</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="525"/>
|
||||
<location filename="../SettingsView.ui" line="530"/>
|
||||
<source>Allow opposing input directions</source>
|
||||
<translation>允许逆向输入</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="532"/>
|
||||
<location filename="../SettingsView.ui" line="537"/>
|
||||
<source>Suspend screensaver</source>
|
||||
<translation>停用屏幕保护程序</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="542"/>
|
||||
<location filename="../SettingsView.ui" line="547"/>
|
||||
<source>Pause when inactive</source>
|
||||
<translation>非活动时暂停</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="549"/>
|
||||
<location filename="../SettingsView.ui" line="554"/>
|
||||
<source>Show FPS in title bar</source>
|
||||
<translation>在标题栏显示 FPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="573"/>
|
||||
<location filename="../SettingsView.ui" line="578"/>
|
||||
<source>Automatically save cheats</source>
|
||||
<translation>自动保存作弊码</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="583"/>
|
||||
<location filename="../SettingsView.ui" line="588"/>
|
||||
<source>Automatically load cheats</source>
|
||||
<translation>自动载入作弊码</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="593"/>
|
||||
<location filename="../SettingsView.ui" line="598"/>
|
||||
<source>Automatically save state</source>
|
||||
<translation>自动存档</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="603"/>
|
||||
<location filename="../SettingsView.ui" line="608"/>
|
||||
<source>Automatically load state</source>
|
||||
<translation>自动读档</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="613"/>
|
||||
<location filename="../SettingsView.ui" line="618"/>
|
||||
<source>Enable Discord Rich Presence</source>
|
||||
<translation>启用 Enable Discord Rich Presence</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="627"/>
|
||||
<location filename="../SettingsView.ui" line="632"/>
|
||||
<source>Fast forward speed:</source>
|
||||
<translation>快进速度:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="639"/>
|
||||
<location filename="../SettingsView.ui" line="644"/>
|
||||
<location filename="../SettingsView.ui" line="891"/>
|
||||
<source>×</source>
|
||||
<translation>×</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="658"/>
|
||||
<location filename="../SettingsView.ui" line="663"/>
|
||||
<source>Unbounded</source>
|
||||
<translation>不限制</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="670"/>
|
||||
<location filename="../SettingsView.ui" line="675"/>
|
||||
<source>Autofire interval:</source>
|
||||
<translation>连发间隔:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="694"/>
|
||||
<location filename="../SettingsView.ui" line="699"/>
|
||||
<source>Enable rewind</source>
|
||||
<translation>启用回退</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="701"/>
|
||||
<location filename="../SettingsView.ui" line="706"/>
|
||||
<source>Rewind history:</source>
|
||||
<translation>回退历史:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="733"/>
|
||||
<location filename="../SettingsView.ui" line="738"/>
|
||||
<source>Idle loops:</source>
|
||||
<translation>空循环:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="741"/>
|
||||
<location filename="../SettingsView.ui" line="746"/>
|
||||
<source>Run all</source>
|
||||
<translation>运行所有</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="746"/>
|
||||
<location filename="../SettingsView.ui" line="751"/>
|
||||
<source>Remove known</source>
|
||||
<translation>移除选定</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="751"/>
|
||||
<location filename="../SettingsView.ui" line="756"/>
|
||||
<source>Detect and remove</source>
|
||||
<translation>检测并移除</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="759"/>
|
||||
<location filename="../SettingsView.ui" line="764"/>
|
||||
<source>Preload entire ROM into memory</source>
|
||||
<translation>将整个 ROM 预加载到内存中</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="773"/>
|
||||
<location filename="../SettingsView.ui" line="778"/>
|
||||
<source>Savestate extra data:</source>
|
||||
<translation>即时存档额外数据:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="780"/>
|
||||
<location filename="../SettingsView.ui" line="824"/>
|
||||
<location filename="../SettingsView.ui" line="785"/>
|
||||
<location filename="../SettingsView.ui" line="829"/>
|
||||
<source>Screenshot</source>
|
||||
<translation>截图</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="790"/>
|
||||
<location filename="../SettingsView.ui" line="834"/>
|
||||
<location filename="../SettingsView.ui" line="795"/>
|
||||
<location filename="../SettingsView.ui" line="839"/>
|
||||
<source>Save data</source>
|
||||
<translation>保存数据</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="800"/>
|
||||
<location filename="../SettingsView.ui" line="841"/>
|
||||
<location filename="../SettingsView.ui" line="805"/>
|
||||
<location filename="../SettingsView.ui" line="846"/>
|
||||
<source>Cheat codes</source>
|
||||
<translation>作弊码</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="817"/>
|
||||
<location filename="../SettingsView.ui" line="822"/>
|
||||
<source>Load extra data:</source>
|
||||
<translation>读档时载入额外数据:</translation>
|
||||
<translation>载入额外数据:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="852"/>
|
||||
<source>GB BIOS file:</source>
|
||||
<translation>GB BIOS 文件:</translation>
|
||||
<location filename="../SettingsView.ui" line="857"/>
|
||||
<source>Video renderer:</source>
|
||||
<translation>视频渲染器:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="871"/>
|
||||
<location filename="../SettingsView.ui" line="909"/>
|
||||
<location filename="../SettingsView.ui" line="944"/>
|
||||
<location filename="../SettingsView.ui" line="972"/>
|
||||
<location filename="../SettingsView.ui" line="1013"/>
|
||||
<location filename="../SettingsView.ui" line="1061"/>
|
||||
<location filename="../SettingsView.ui" line="1109"/>
|
||||
<location filename="../SettingsView.ui" line="1157"/>
|
||||
<location filename="../SettingsView.ui" line="1205"/>
|
||||
<location filename="../SettingsView.ui" line="865"/>
|
||||
<source>Software</source>
|
||||
<translation>软件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="870"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</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>
|
||||
<translation>浏览</translation>
|
||||
</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>
|
||||
<translation>当可用时使用 BIOS 文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="890"/>
|
||||
<location filename="../SettingsView.ui" line="956"/>
|
||||
<source>Skip BIOS intro</source>
|
||||
<translation>跳过 BIOS 启动画面</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="918"/>
|
||||
<location filename="../SettingsView.ui" line="984"/>
|
||||
<source>GBA BIOS file:</source>
|
||||
<translation>GBA BIOS 文件:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="925"/>
|
||||
<location filename="../SettingsView.ui" line="991"/>
|
||||
<source>GBC BIOS file:</source>
|
||||
<translation>GBC BIOS 文件:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="953"/>
|
||||
<location filename="../SettingsView.ui" line="1019"/>
|
||||
<source>SGB BIOS file:</source>
|
||||
<translation>SGB BIOS 文件:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="988"/>
|
||||
<location filename="../SettingsView.ui" line="1054"/>
|
||||
<source>Save games</source>
|
||||
<translation>已保存的游戏</translation>
|
||||
<translation>游戏存档</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1022"/>
|
||||
<location filename="../SettingsView.ui" line="1070"/>
|
||||
<location filename="../SettingsView.ui" line="1118"/>
|
||||
<location filename="../SettingsView.ui" line="1166"/>
|
||||
<location filename="../SettingsView.ui" line="1214"/>
|
||||
<location filename="../SettingsView.ui" line="1088"/>
|
||||
<location filename="../SettingsView.ui" line="1136"/>
|
||||
<location filename="../SettingsView.ui" line="1184"/>
|
||||
<location filename="../SettingsView.ui" line="1232"/>
|
||||
<location filename="../SettingsView.ui" line="1280"/>
|
||||
<source>Same directory as the ROM</source>
|
||||
<translation>保存在 ROM 所在目录</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1036"/>
|
||||
<source>Save states</source>
|
||||
<translation>保存即时存档</translation>
|
||||
<translation>即时存档</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1084"/>
|
||||
<location filename="../SettingsView.ui" line="1150"/>
|
||||
<source>Screenshots</source>
|
||||
<translation>截图</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1132"/>
|
||||
<location filename="../SettingsView.ui" line="1198"/>
|
||||
<source>Patches</source>
|
||||
<translation>补丁</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1180"/>
|
||||
<location filename="../SettingsView.ui" line="1246"/>
|
||||
<source>Cheats</source>
|
||||
<translation>作弊码</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1237"/>
|
||||
<location filename="../SettingsView.ui" line="1303"/>
|
||||
<source>Log to file</source>
|
||||
<translation>记录日志到文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1244"/>
|
||||
<location filename="../SettingsView.ui" line="1310"/>
|
||||
<source>Log to console</source>
|
||||
<translation>记录日志到控制台</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1258"/>
|
||||
<location filename="../SettingsView.ui" line="1324"/>
|
||||
<source>Select Log File</source>
|
||||
<translation>选择日志文件</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1271"/>
|
||||
<source>Game Boy model</source>
|
||||
<translation>Game Boy 模型</translation>
|
||||
<location filename="../SettingsView.ui" line="1337"/>
|
||||
<source>Game Boy model:</source>
|
||||
<translation>Game Boy 模型:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1279"/>
|
||||
<location filename="../SettingsView.ui" line="1315"/>
|
||||
<location filename="../SettingsView.ui" line="1351"/>
|
||||
<location filename="../SettingsView.ui" line="1345"/>
|
||||
<location filename="../SettingsView.ui" line="1381"/>
|
||||
<location filename="../SettingsView.ui" line="1417"/>
|
||||
<source>Autodetect</source>
|
||||
<translation>自动检测</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1284"/>
|
||||
<location filename="../SettingsView.ui" line="1320"/>
|
||||
<location filename="../SettingsView.ui" line="1356"/>
|
||||
<location filename="../SettingsView.ui" line="1350"/>
|
||||
<location filename="../SettingsView.ui" line="1386"/>
|
||||
<location filename="../SettingsView.ui" line="1422"/>
|
||||
<source>Game Boy (DMG)</source>
|
||||
<translation>Game Boy (DMG)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1289"/>
|
||||
<location filename="../SettingsView.ui" line="1325"/>
|
||||
<location filename="../SettingsView.ui" line="1361"/>
|
||||
<location filename="../SettingsView.ui" line="1355"/>
|
||||
<location filename="../SettingsView.ui" line="1391"/>
|
||||
<location filename="../SettingsView.ui" line="1427"/>
|
||||
<source>Super Game Boy (SGB)</source>
|
||||
<translation>Super Game Boy (SGB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1294"/>
|
||||
<location filename="../SettingsView.ui" line="1330"/>
|
||||
<location filename="../SettingsView.ui" line="1366"/>
|
||||
<location filename="../SettingsView.ui" line="1360"/>
|
||||
<location filename="../SettingsView.ui" line="1396"/>
|
||||
<location filename="../SettingsView.ui" line="1432"/>
|
||||
<source>Game Boy Color (CGB)</source>
|
||||
<translation>Game Boy Color (CGB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1299"/>
|
||||
<location filename="../SettingsView.ui" line="1335"/>
|
||||
<location filename="../SettingsView.ui" line="1371"/>
|
||||
<location filename="../SettingsView.ui" line="1365"/>
|
||||
<location filename="../SettingsView.ui" line="1401"/>
|
||||
<location filename="../SettingsView.ui" line="1437"/>
|
||||
<source>Game Boy Advance (AGB)</source>
|
||||
<translation>Game Boy Advance (AGB)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1307"/>
|
||||
<location filename="../SettingsView.ui" line="1373"/>
|
||||
<source>Super Game Boy model:</source>
|
||||
<translation>Super Game Boy 模型:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1343"/>
|
||||
<location filename="../SettingsView.ui" line="1409"/>
|
||||
<source>Game Boy Color model:</source>
|
||||
<translation>Game Boy Color 模型:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1386"/>
|
||||
<location filename="../SettingsView.ui" line="1452"/>
|
||||
<source>Default BG colors:</source>
|
||||
<translation>默认背景颜色:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1553"/>
|
||||
<location filename="../SettingsView.ui" line="1619"/>
|
||||
<source>Super Game Boy borders</source>
|
||||
<translation>Super Game Boy 边框</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1567"/>
|
||||
<location filename="../SettingsView.ui" line="1633"/>
|
||||
<source>Camera driver:</source>
|
||||
<translation>相机驱动:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1664"/>
|
||||
<location filename="../SettingsView.ui" line="1730"/>
|
||||
<source>Default sprite colors 1:</source>
|
||||
<translation>默认精灵图颜色 1:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1671"/>
|
||||
<location filename="../SettingsView.ui" line="1737"/>
|
||||
<source>Default sprite colors 2:</source>
|
||||
<translation>默认精灵图颜色 2:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1678"/>
|
||||
<location filename="../SettingsView.ui" line="1744"/>
|
||||
<source>Use GBC colors in GB games</source>
|
||||
<translation>在 GB 游戏中使用 GBC 颜色</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SettingsView.ui" line="1685"/>
|
||||
<location filename="../SettingsView.ui" line="1751"/>
|
||||
<source>Camera:</source>
|
||||
<translation>相机</translation>
|
||||
</message>
|
||||
|
|
|
@ -51,9 +51,9 @@ void mSDLGLCommonInit(struct mSDLRenderer* renderer) {
|
|||
#else
|
||||
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
|
||||
#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
|
||||
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
|
||||
SDL_WM_SetCaption(projectName, "");
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include <mgba/core/core.h>
|
||||
#include <mgba/core/thread.h>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#ifdef __linux__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
|
@ -37,7 +37,7 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) {
|
|||
size_t size = renderer->width * renderer->height * BYTES_PER_PIXEL;
|
||||
#ifdef _WIN32
|
||||
renderer->outputBuffer = _aligned_malloc(size, 16);
|
||||
#elif !defined(__APPLE__)
|
||||
#elif defined(__linux__)
|
||||
renderer->outputBuffer = memalign(16, size);
|
||||
#else
|
||||
posix_memalign((void**) &renderer->outputBuffer, 16, size);
|
||||
|
|
|
@ -131,12 +131,8 @@ int main(int argc, char** argv) {
|
|||
|
||||
renderer.viewportWidth = renderer.core->opts.width;
|
||||
renderer.viewportHeight = renderer.core->opts.height;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
renderer.player.fullscreen = renderer.core->opts.fullscreen;
|
||||
renderer.player.windowUpdated = 0;
|
||||
#else
|
||||
renderer.fullscreen = renderer.core->opts.fullscreen;
|
||||
#endif
|
||||
|
||||
renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio;
|
||||
renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling;
|
||||
|
|
|
@ -54,8 +54,6 @@ struct mSDLRenderer {
|
|||
SDL_Texture* sdlTex;
|
||||
SDL_Renderer* sdlRenderer;
|
||||
SDL_GLContext* glCtx;
|
||||
#else
|
||||
bool fullscreen;
|
||||
#endif
|
||||
|
||||
unsigned width;
|
||||
|
|
|
@ -63,10 +63,10 @@ struct mSDLPlayer {
|
|||
size_t playerId;
|
||||
struct mInputMap* bindings;
|
||||
struct SDL_JoystickCombo* joystick;
|
||||
int fullscreen;
|
||||
int windowUpdated;
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 0)
|
||||
SDL_Window* window;
|
||||
int fullscreen;
|
||||
|
||||
struct mSDLRumble {
|
||||
struct mRumble d;
|
||||
|
|
|
@ -22,9 +22,9 @@ void mSDLSWCreate(struct mSDLRenderer* renderer) {
|
|||
|
||||
bool mSDLSWInit(struct mSDLRenderer* renderer) {
|
||||
#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
|
||||
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
|
||||
SDL_WM_SetCaption(projectName, "");
|
||||
|
||||
|
|