Merge branch 'master' (early part) into medusa

This commit is contained in:
Vicki Pfau 2020-02-17 16:20:41 -08:00
commit 9dbd15c144
46 changed files with 2499 additions and 1416 deletions

View File

@ -1,5 +1,9 @@
#!/bin/sh #!/bin/sh
set -e
if [ $TRAVIS_OS_NAME = "osx" ]; then if [ $TRAVIS_OS_NAME = "osx" ]; then
brew update brew update
brew install qt5 ffmpeg imagemagick sdl2 libedit libelf libpng libzip brew install qt5 ffmpeg imagemagick sdl2 libedit libelf libpng libzip
else
sudo apt-get update
sudo apt-get -y install libseccomp2
fi fi

View File

@ -24,7 +24,7 @@ matrix:
before_install: before_install:
- '[ -z "$DOCKER_TAG" ] || docker pull mgba/$DOCKER_TAG' - '[ -z "$DOCKER_TAG" ] || docker pull mgba/$DOCKER_TAG'
- '[ "$TRAVIS_OS_NAME" != "osx" ] || . ./.travis-deps.sh' - '. ./.travis-deps.sh'
- 'mkdir build && chmod 777 build' - 'mkdir build && chmod 777 build'
script: script:

11
CHANGES
View File

@ -54,6 +54,8 @@ Emulation fixes:
- GB Memory: Better emulate 0xFEA0 region on DMG, MGB and AGB - GB Memory: Better emulate 0xFEA0 region on DMG, MGB and AGB
- GB Video: Fix mode 0 window edge case (fixes mgba.io/i/1519) - GB Video: Fix mode 0 window edge case (fixes mgba.io/i/1519)
- GB Audio: Fix channel 4 volume (fixes mgba.io/i/1529) - GB Audio: Fix channel 4 volume (fixes mgba.io/i/1529)
- GB Video: Fix color scaling in AGB mode
- GB: Fix using boot ROM with MMM01 games
Other fixes: Other fixes:
- Qt: Fix some Qt display driver race conditions - Qt: Fix some Qt display driver race conditions
- Core: Improved lockstep driver reliability (Le Hoang Quyen) - Core: Improved lockstep driver reliability (Le Hoang Quyen)
@ -64,6 +66,9 @@ Other fixes:
- Qt: Only show emulator restart warning once per settings saving - Qt: Only show emulator restart warning once per settings saving
- FFmpeg: Drain recording buffers - FFmpeg: Drain recording buffers
- Shaders: Fix gba-color shader resolution (fixes mgba.io/i/1435) - Shaders: Fix gba-color shader resolution (fixes mgba.io/i/1435)
- Qt: Fix LibraryController initialization (fixes mgba.io/i/1324)
- Switch: Fix audio when video rate desyncs (fixes mgba.io/i/1532)
- GB: Fix reading ROM immediately after unmapping BIOS
Misc: Misc:
- GBA Savedata: EEPROM performance fixes - GBA Savedata: EEPROM performance fixes
- GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash - GBA Savedata: Automatically map 1Mbit Flash files as 1Mbit Flash
@ -94,6 +99,12 @@ Misc:
- OpenGL: Only resize textures when needed - OpenGL: Only resize textures when needed
- GBA BIOS: Fix clobbered registers in CpuSet (fixes mgba.io/i/1531) - GBA BIOS: Fix clobbered registers in CpuSet (fixes mgba.io/i/1531)
- Qt: Remove What's This icon from dialogs - Qt: Remove What's This icon from dialogs
- CMake: Don't use libzip on embedded platforms (fixes mgba.io/i/1527)
- Qt: Printer quality of life improvements (fixes mgba.io/i/1540)
- Qt: Add copy and QoL improvements to graphic views (closes mgba.io/i/1541)
- Qt: Show list of all sprites in sprite view
- Qt: Add option for disabling OSD messages
- Core: Add more memory search ops (closes mgba.io/i/1510)
0.7.3: (2019-09-15) 0.7.3: (2019-09-15)
Emulation fixes: Emulation fixes:

View File

@ -250,6 +250,7 @@ if(DEFINED 3DS OR DEFINED PSP2 OR DEFINED WII OR DEFINED SWITCH)
set(USE_DEBUGGERS OFF) set(USE_DEBUGGERS OFF)
set(USE_SQLITE3 OFF) set(USE_SQLITE3 OFF)
set(USE_DISCORD_RPC OFF) set(USE_DISCORD_RPC OFF)
set(USE_LIBZIP OFF CACHE BOOL "")
endif() endif()
if(DEFINED 3DS) if(DEFINED 3DS)

View File

@ -93,7 +93,7 @@ Other Unix-like platforms, such as OpenBSD, are known to work as well, but are u
### System requirements ### System requirements
Requirements are minimal[<sup>[2]</sup>](#dscaveat). Any computer that can run Windows Vista or newer should be able to handle emulation. Support for OpenGL 1.1 or newer is also required, with OpenGL 3.0 or newer for shaders and advanced features. Requirements are minimal[<sup>[2]</sup>](#dscaveat). Any computer that can run Windows Vista or newer should be able to handle emulation. Support for OpenGL 1.1 or newer is also required, with OpenGL 3.2 or newer for shaders and advanced features.
Downloads Downloads
--------- ---------

View File

@ -86,7 +86,7 @@ Andere Unix-ähnliche Plattformen wie OpenBSD sind ebenfalls dafür bekannt, mit
### Systemvoraussetzungen ### Systemvoraussetzungen
Die Systemvoraussetzungen sind minimal. Jeder Computer, der mit Windows Vista oder neuer läuft, sollte in der Lage sein, die Emulation zu bewältigen. Unterstützung für OpenGL 1.1 oder neuer ist ebenfalls voraussgesetzt. OpenGL 3.0 oder neuer wird für Shader und erweiterte Funktionen benötigt. Die Systemvoraussetzungen sind minimal. Jeder Computer, der mit Windows Vista oder neuer läuft, sollte in der Lage sein, die Emulation zu bewältigen. Unterstützung für OpenGL 1.1 oder neuer ist ebenfalls voraussgesetzt. OpenGL 3.2 oder neuer wird für Shader und erweiterte Funktionen benötigt.
Downloads Downloads
--------- ---------
@ -145,7 +145,7 @@ Damit wird mGBA gebaut und in `/usr/bin` und `/usr/lib` installiert. Installiert
Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen, dass Du den Homebrew-Paketmanager verwendest, werden folgende Schritte zum installieren der Abhängigkeiten und anschließenden bauen von mGBA empfohlen: Wenn Du macOS verwendest, sind die einzelnen Schritte etwas anders. Angenommen, dass Du den Homebrew-Paketmanager verwendest, werden folgende Schritte zum installieren der Abhängigkeiten und anschließenden bauen von mGBA empfohlen:
brew install cmake ffmpeg imagemagick libzip qt5 sdl2 libedit pkg-config brew install cmake ffmpeg libzip qt5 sdl2 libedit pkg-config
mkdir build mkdir build
cd build cd build
cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' .. cmake -DCMAKE_PREFIX_PATH='brew --prefix qt5' ..
@ -159,11 +159,11 @@ Um mGBA auf Windows zu kompilieren, wird MSYS2 empfohlen. Befolge die Installati
Für x86 (32 Bit): Für x86 (32 Bit):
pacman -Sy --needed base-devel git mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git} pacman -Sy --needed base-devel git mingw-w64-i686-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
Für x86_64 (64 Bit): Für x86_64 (64 Bit):
pacman -Sy --needed base-devel git mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,imagemagick,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git} pacman -Sy --needed base-devel git mingw-w64-x86_64-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,pkg-config,qt5,SDL2,ntldd-git}
Lade den aktuellen mGBA-Quellcode mithilfe des folgenden Kommandos herunter: Lade den aktuellen mGBA-Quellcode mithilfe des folgenden Kommandos herunter:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -22,7 +22,11 @@ enum mCoreMemorySearchOp {
mCORE_MEMORY_SEARCH_EQUAL, mCORE_MEMORY_SEARCH_EQUAL,
mCORE_MEMORY_SEARCH_GREATER, mCORE_MEMORY_SEARCH_GREATER,
mCORE_MEMORY_SEARCH_LESS, mCORE_MEMORY_SEARCH_LESS,
mCORE_MEMORY_SEARCH_ANY,
mCORE_MEMORY_SEARCH_DELTA, mCORE_MEMORY_SEARCH_DELTA,
mCORE_MEMORY_SEARCH_DELTA_POSITIVE,
mCORE_MEMORY_SEARCH_DELTA_NEGATIVE,
mCORE_MEMORY_SEARCH_DELTA_ANY,
}; };
struct mCoreMemorySearchParams { struct mCoreMemorySearchParams {

View File

@ -1,8 +1,9 @@
Jaime J. Denizard Jaime J. Denizard
Fog Benedikt Feih
Philip Horton
Oskenso Kashi Oskenso Kashi
The Libretro Team
Mored1984 Mored1984
Rohit Nirmal
Rhys Powell Rhys Powell
Johnathan Roatch
Yuri Kunde Schlesner Yuri Kunde Schlesner
Voidheim

View File

@ -19,6 +19,14 @@ static bool _op(int32_t value, int32_t match, enum mCoreMemorySearchOp op) {
case mCORE_MEMORY_SEARCH_EQUAL: case mCORE_MEMORY_SEARCH_EQUAL:
case mCORE_MEMORY_SEARCH_DELTA: case mCORE_MEMORY_SEARCH_DELTA:
return value == match; return value == match;
case mCORE_MEMORY_SEARCH_DELTA_POSITIVE:
return value > 0;
case mCORE_MEMORY_SEARCH_DELTA_NEGATIVE:
return value < 0;
case mCORE_MEMORY_SEARCH_DELTA_ANY:
return value != 0;
case mCORE_MEMORY_SEARCH_ANY:
return true;
} }
return false; return false;
} }
@ -244,20 +252,20 @@ bool _testGuess(struct mCore* core, struct mCoreMemorySearchResult* res, const s
int64_t value; int64_t value;
int32_t offset = 0; int32_t offset = 0;
char* end; char* end;
if (params->op == mCORE_MEMORY_SEARCH_DELTA) { if (params->op >= mCORE_MEMORY_SEARCH_DELTA) {
offset = res->oldValue; offset = res->oldValue;
} }
value = strtoll(params->valueStr, &end, 10); value = strtoll(params->valueStr, &end, 10);
if (end) { if (end) {
res->oldValue += value; res->oldValue += value;
if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true; return true;
} }
if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true; return true;
} }
if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true; return true;
} }
res->oldValue -= value; res->oldValue -= value;
@ -266,13 +274,13 @@ bool _testGuess(struct mCore* core, struct mCoreMemorySearchResult* res, const s
value = strtoll(params->valueStr, &end, 16); value = strtoll(params->valueStr, &end, 16);
if (end) { if (end) {
res->oldValue += value; res->oldValue += value;
if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { if (_op(core->rawRead8(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true; return true;
} }
if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { if (!(res->address & 1) && (res->width >= 2 || res->width == -1) && _op(core->rawRead16(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true; return true;
} }
if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier, value + offset, params->op)) { if (!(res->address & 3) && (res->width >= 4 || res->width == -1) && _op(core->rawRead32(core, res->address, res->segment) * res->guessDivisor / res->guessMultiplier - offset, value, params->op)) {
return true; return true;
} }
res->oldValue -= value; res->oldValue -= value;
@ -293,10 +301,7 @@ void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchP
--i; --i;
} }
} else if (params->type == mCORE_MEMORY_SEARCH_INT) { } else if (params->type == mCORE_MEMORY_SEARCH_INT) {
int32_t oldValue = params->valueInt; int32_t match = params->valueInt;
if (params->op == mCORE_MEMORY_SEARCH_DELTA) {
oldValue += res->oldValue;
}
int32_t value = 0; int32_t value = 0;
switch (params->width) { switch (params->width) {
case 1: case 1:
@ -311,7 +316,11 @@ void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchP
default: default:
break; break;
} }
if (!_op(value, oldValue, params->op)) { int32_t opValue = value;
if (params->op >= mCORE_MEMORY_SEARCH_DELTA) {
opValue -= res->oldValue;
}
if (!_op(opValue, match, params->op)) {
*res = *mCoreMemorySearchResultsGetPointer(inout, mCoreMemorySearchResultsSize(inout) - 1); *res = *mCoreMemorySearchResultsGetPointer(inout, mCoreMemorySearchResultsSize(inout) - 1);
mCoreMemorySearchResultsResize(inout, -1); mCoreMemorySearchResultsResize(inout, -1);
--i; --i;
@ -322,7 +331,7 @@ void mCoreMemorySearchRepeat(struct mCore* core, const struct mCoreMemorySearchP
break; break;
case mCORE_MEMORY_SEARCH_STRING: case mCORE_MEMORY_SEARCH_STRING:
case mCORE_MEMORY_SEARCH_GUESS: case mCORE_MEMORY_SEARCH_GUESS:
// TOOD // TODO
break; break;
} }
} }

View File

@ -415,23 +415,6 @@ void GBReset(struct LR35902Core* cpu) {
gb->memory.romBase = gb->memory.rom; gb->memory.romBase = gb->memory.rom;
GBDetectModel(gb); GBDetectModel(gb);
if (gb->biosVf) {
if (!GBIsBIOS(gb->biosVf)) {
gb->biosVf->close(gb->biosVf);
gb->biosVf = NULL;
} else {
GBMapBIOS(gb);
cpu->a = 0;
cpu->f.packed = 0;
cpu->c = 0;
cpu->e = 0;
cpu->h = 0;
cpu->l = 0;
cpu->sp = 0;
cpu->pc = 0;
}
}
cpu->b = 0; cpu->b = 0;
cpu->d = 0; cpu->d = 0;
@ -457,6 +440,24 @@ void GBReset(struct LR35902Core* cpu) {
mTimingClear(&gb->timing); mTimingClear(&gb->timing);
GBMemoryReset(gb); GBMemoryReset(gb);
if (gb->biosVf) {
if (!GBIsBIOS(gb->biosVf)) {
gb->biosVf->close(gb->biosVf);
gb->biosVf = NULL;
} else {
GBMapBIOS(gb);
cpu->a = 0;
cpu->f.packed = 0;
cpu->c = 0;
cpu->e = 0;
cpu->h = 0;
cpu->l = 0;
cpu->sp = 0;
cpu->pc = 0;
}
}
GBVideoReset(&gb->video); GBVideoReset(&gb->video);
GBTimerReset(&gb->timer); GBTimerReset(&gb->timer);
if (!gb->biosVf) { if (!gb->biosVf) {
@ -561,18 +562,23 @@ void GBSkipBIOS(struct GB* gb) {
void GBMapBIOS(struct GB* gb) { void GBMapBIOS(struct GB* gb) {
gb->biosVf->seek(gb->biosVf, 0, SEEK_SET); gb->biosVf->seek(gb->biosVf, 0, SEEK_SET);
uint8_t* oldRomBase = gb->memory.romBase;
gb->memory.romBase = malloc(GB_SIZE_CART_BANK0); gb->memory.romBase = malloc(GB_SIZE_CART_BANK0);
ssize_t size = gb->biosVf->read(gb->biosVf, gb->memory.romBase, GB_SIZE_CART_BANK0); ssize_t size = gb->biosVf->read(gb->biosVf, gb->memory.romBase, GB_SIZE_CART_BANK0);
memcpy(&gb->memory.romBase[size], &gb->memory.rom[size], GB_SIZE_CART_BANK0 - size); memcpy(&gb->memory.romBase[size], &oldRomBase[size], GB_SIZE_CART_BANK0 - size);
if (size > 0x100) { if (size > 0x100) {
memcpy(&gb->memory.romBase[0x100], &gb->memory.rom[0x100], sizeof(struct GBCartridge)); memcpy(&gb->memory.romBase[0x100], &oldRomBase[0x100], sizeof(struct GBCartridge));
} }
} }
void GBUnmapBIOS(struct GB* gb) { void GBUnmapBIOS(struct GB* gb) {
if (gb->memory.romBase < gb->memory.rom || gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) { if (gb->memory.romBase < gb->memory.rom || gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) {
free(gb->memory.romBase); free(gb->memory.romBase);
gb->memory.romBase = gb->memory.rom; if (gb->memory.mbcType == GB_MMM01) {
GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2);
} else {
GBMBCSwitchBank0(gb, 0);
}
} }
// XXX: Force AGB registers for AGB-mode // XXX: Force AGB registers for AGB-mode
if (gb->model == GB_MODEL_AGB && gb->cpu->pc == 0x100) { if (gb->model == GB_MODEL_AGB && gb->cpu->pc == 0x100) {

View File

@ -461,8 +461,11 @@ static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer
color = mColorFrom555(r | (g << 5) | (b << 10)); color = mColorFrom555(r | (g << 5) | (b << 10));
#else #else
r >>= 2; r >>= 2;
r += r >> 4;
g >>= 2; g >>= 2;
g += g >> 4;
b >>= 2; b >>= 2;
b += b >> 4;
color = r | (g << 8) | (b << 16); color = r | (g << 8) | (b << 16);
#endif #endif
} }

View File

@ -64,7 +64,7 @@ static const GLchar* const _gles3Header =
"precision highp isampler2D;\n"; "precision highp isampler2D;\n";
static const GLchar* const _gl3Header = static const GLchar* const _gl3Header =
"#version 130\n" "#version 150 core\n"
"#define OUT(n)\n" "#define OUT(n)\n"
PALETTE_ENTRY PALETTE_ENTRY
"precision highp float;\n"; "precision highp float;\n";

View File

@ -141,6 +141,8 @@ static void GBAVideoSoftwareRendererReset(struct GBAVideoRenderer* renderer) {
memset(softwareRenderer->cache, 0, sizeof(softwareRenderer->cache)); memset(softwareRenderer->cache, 0, sizeof(softwareRenderer->cache));
memset(softwareRenderer->nextIo, 0, sizeof(softwareRenderer->nextIo)); memset(softwareRenderer->nextIo, 0, sizeof(softwareRenderer->nextIo));
softwareRenderer->lastHighlightAmount = 0;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i]; struct GBAVideoSoftwareBackground* bg = &softwareRenderer->bg[i];
bg->index = i; bg->index = i;

View File

@ -21,8 +21,17 @@ static const GLchar* const _gles2Header =
"#version 100\n" "#version 100\n"
"precision mediump float;\n"; "precision mediump float;\n";
static const GLchar* const _gl3Header = static const GLchar* const _gl32VHeader =
"#version 120\n"; "#version 150 core\n"
"#define attribute in\n"
"#define varying out\n";
static const GLchar* const _gl32FHeader =
"#version 150 core\n"
"#define varying in\n"
"#define texture2D texture\n"
"out vec4 compat_FragColor;\n"
"#define gl_FragColor compat_FragColor\n";
static const char* const _vertexShader = static const char* const _vertexShader =
"attribute vec4 position;\n" "attribute vec4 position;\n"
@ -449,7 +458,7 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
const GLchar* shaderBuffer[2]; const GLchar* shaderBuffer[2];
const GLubyte* version = glGetString(GL_VERSION); const GLubyte* version = glGetString(GL_VERSION);
if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) { if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) {
shaderBuffer[0] = _gl3Header; shaderBuffer[0] = _gl32VHeader;
} else { } else {
shaderBuffer[0] = _gles2Header; shaderBuffer[0] = _gles2Header;
} }
@ -460,6 +469,9 @@ void mGLES2ShaderInit(struct mGLES2Shader* shader, const char* vs, const char* f
} }
glShaderSource(shader->vertexShader, 2, shaderBuffer, 0); glShaderSource(shader->vertexShader, 2, shaderBuffer, 0);
if (strncmp((const char*) version, "OpenGL ES ", strlen("OpenGL ES "))) {
shaderBuffer[0] = _gl32FHeader;
}
if (fs) { if (fs) {
shaderBuffer[1] = fs; shaderBuffer[1] = fs;
} else { } else {

View File

@ -116,6 +116,9 @@ void AssetTile::selectIndex(int index) {
m_ui.preview->setColor(i ^ flip, data[i]); m_ui.preview->setColor(i ^ flip, data[i]);
} }
m_ui.preview->update(); m_ui.preview->update();
QImage tile(reinterpret_cast<const uchar*>(data), 8, 8, QImage::Format_ARGB32);
m_activeTile = tile.rgbSwapped();
} }
void AssetTile::setFlip(bool h, bool v) { void AssetTile::setFlip(bool h, bool v) {

View File

@ -21,6 +21,7 @@ Q_OBJECT
public: public:
AssetTile(QWidget* parent = nullptr); AssetTile(QWidget* parent = nullptr);
void setController(std::shared_ptr<CoreController>); void setController(std::shared_ptr<CoreController>);
QImage activeTile() const { return m_activeTile; }
public slots: public slots:
void setPalette(int); void setPalette(int);
@ -48,6 +49,7 @@ private:
bool m_flipV = false; bool m_flipV = false;
QMap<QString, QLabel*> m_customProperties; QMap<QString, QLabel*> m_customProperties;
QImage m_activeTile;
}; };
} }

View File

@ -26,7 +26,8 @@ Display* Display::create(QWidget* parent) {
switch (s_driver) { switch (s_driver) {
#if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY) #if defined(BUILD_GL) || defined(BUILD_GLES2) || defined(USE_EPOXY)
case Driver::OPENGL: case Driver::OPENGL:
format.setVersion(3, 0); format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
return new DisplayGL(format, parent); return new DisplayGL(format, parent);
#endif #endif
#ifdef BUILD_GL #ifdef BUILD_GL
@ -91,6 +92,10 @@ void Display::interframeBlending(bool lock) {
m_interframeBlending = lock; m_interframeBlending = lock;
} }
void Display::showOSDMessages(bool enable) {
m_showOSD = enable;
}
void Display::filter(bool filter) { void Display::filter(bool filter) {
m_filter = filter; m_filter = filter;
} }

View File

@ -44,6 +44,7 @@ public:
bool isIntegerScalingLocked() const { return m_lockIntegerScaling; } bool isIntegerScalingLocked() const { return m_lockIntegerScaling; }
bool hasInterframeBlending() const { return m_interframeBlending; } bool hasInterframeBlending() const { return m_interframeBlending; }
bool isFiltered() const { return m_filter; } bool isFiltered() const { return m_filter; }
bool isShowOSD() const { return m_showOSD; }
virtual void startDrawing(std::shared_ptr<CoreController>) = 0; virtual void startDrawing(std::shared_ptr<CoreController>) = 0;
virtual bool isDrawing() const = 0; virtual bool isDrawing() const = 0;
@ -68,6 +69,7 @@ public slots:
virtual void lockAspectRatio(bool lock); virtual void lockAspectRatio(bool lock);
virtual void lockIntegerScaling(bool lock); virtual void lockIntegerScaling(bool lock);
virtual void interframeBlending(bool enable); virtual void interframeBlending(bool enable);
virtual void showOSDMessages(bool enable);
virtual void filter(bool filter); virtual void filter(bool filter);
virtual void framePosted() = 0; virtual void framePosted() = 0;
virtual void setShaders(struct VDir*) = 0; virtual void setShaders(struct VDir*) = 0;
@ -89,6 +91,7 @@ private:
static const int MOUSE_DISAPPEAR_TIMER = 1000; static const int MOUSE_DISAPPEAR_TIMER = 1000;
MessagePainter m_messagePainter; MessagePainter m_messagePainter;
bool m_showOSD = true;
bool m_lockAspectRatio = false; bool m_lockAspectRatio = false;
bool m_lockIntegerScaling = false; bool m_lockIntegerScaling = false;
bool m_interframeBlending = false; bool m_interframeBlending = false;

View File

@ -114,6 +114,7 @@ void DisplayGL::startDrawing(std::shared_ptr<CoreController> controller) {
lockAspectRatio(isAspectRatioLocked()); lockAspectRatio(isAspectRatioLocked());
lockIntegerScaling(isIntegerScalingLocked()); lockIntegerScaling(isIntegerScalingLocked());
interframeBlending(hasInterframeBlending()); interframeBlending(hasInterframeBlending());
showOSDMessages(isShowOSD());
filter(isFiltered()); filter(isFiltered());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF()); messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF());
@ -187,6 +188,13 @@ void DisplayGL::interframeBlending(bool enable) {
} }
} }
void DisplayGL::showOSDMessages(bool enable) {
Display::showOSDMessages(enable);
if (m_drawThread) {
QMetaObject::invokeMethod(m_painter, "showOSD", Q_ARG(bool, enable));
}
}
void DisplayGL::filter(bool filter) { void DisplayGL::filter(bool filter) {
Display::filter(filter); Display::filter(filter);
if (m_drawThread) { if (m_drawThread) {
@ -353,6 +361,7 @@ void PainterGL::setMessagePainter(MessagePainter* messagePainter) {
void PainterGL::resize(const QSize& size) { void PainterGL::resize(const QSize& size) {
m_size = size; m_size = size;
m_window->setSize(m_size);
if (m_started && !m_active) { if (m_started && !m_active) {
forceDraw(); forceDraw();
} }
@ -372,6 +381,10 @@ void PainterGL::interframeBlending(bool enable) {
m_backend->interframeBlending = enable; m_backend->interframeBlending = enable;
} }
void PainterGL::showOSD(bool enable) {
m_showOSD = enable;
}
void PainterGL::filter(bool filter) { void PainterGL::filter(bool filter) {
m_backend->filter = filter; m_backend->filter = filter;
if (m_started && !m_active) { if (m_started && !m_active) {
@ -460,7 +473,7 @@ void PainterGL::performDraw() {
m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r); m_backend->resized(m_backend, m_size.width() * r, m_size.height() * r);
m_backend->drawFrame(m_backend); m_backend->drawFrame(m_backend);
m_painter.endNativePainting(); m_painter.endNativePainting();
if (m_messagePainter) { if (m_showOSD && m_messagePainter) {
m_messagePainter->paint(&m_painter); m_messagePainter->paint(&m_painter);
} }
} }

View File

@ -29,6 +29,8 @@
#include "platform/video-backend.h" #include "platform/video-backend.h"
class QOpenGLPaintDevice;
namespace QGBA { namespace QGBA {
class PainterGL; class PainterGL;
@ -54,6 +56,7 @@ public slots:
void lockAspectRatio(bool lock) override; void lockAspectRatio(bool lock) override;
void lockIntegerScaling(bool lock) override; void lockIntegerScaling(bool lock) override;
void interframeBlending(bool enable) override; void interframeBlending(bool enable) override;
void showOSDMessages(bool enable) override;
void filter(bool filter) override; void filter(bool filter) override;
void framePosted() override; void framePosted() override;
void setShaders(struct VDir*) override; void setShaders(struct VDir*) override;
@ -100,6 +103,7 @@ public slots:
void lockAspectRatio(bool lock); void lockAspectRatio(bool lock);
void lockIntegerScaling(bool lock); void lockIntegerScaling(bool lock);
void interframeBlending(bool enable); void interframeBlending(bool enable);
void showOSD(bool enable);
void filter(bool filter); void filter(bool filter);
void resizeContext(); void resizeContext();
@ -119,12 +123,13 @@ private:
QPainter m_painter; QPainter m_painter;
QMutex m_mutex; QMutex m_mutex;
QWindow* m_surface; QWindow* m_surface;
QPaintDevice* m_window; QOpenGLPaintDevice* m_window;
QOpenGLContext* m_gl; QOpenGLContext* m_gl;
bool m_active = false; bool m_active = false;
bool m_started = false; bool m_started = false;
std::shared_ptr<CoreController> m_context = nullptr; std::shared_ptr<CoreController> m_context = nullptr;
bool m_supportsShaders; bool m_supportsShaders;
bool m_showOSD;
VideoShader m_shader{}; VideoShader m_shader{};
VideoBackend* m_backend = nullptr; VideoBackend* m_backend = nullptr;
QSize m_size; QSize m_size;

View File

@ -123,5 +123,7 @@ void DisplayQt::paintEvent(QPaintEvent*) {
} }
painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height)); painter.drawImage(full, m_backing, QRect(0, 0, m_width, m_height));
painter.setOpacity(1); painter.setOpacity(1);
if (isShowOSD()) {
messagePainter()->paint(&painter); messagePainter()->paint(&painter);
} }
}

View File

@ -22,7 +22,9 @@
#include <mgba/internal/gb/memory.h> #include <mgba/internal/gb/memory.h>
#endif #endif
#include <QAction>
#include <QButtonGroup> #include <QButtonGroup>
#include <QClipboard>
#include <QFontDatabase> #include <QFontDatabase>
#include <QMouseEvent> #include <QMouseEvent>
#include <QRadioButton> #include <QRadioButton>
@ -88,11 +90,19 @@ MapView::MapView(std::shared_ptr<CoreController> controller, QWidget* parent)
}); });
group->addButton(button); group->addButton(button);
} }
#ifdef USE_PNG
connect(m_ui.exportButton, &QAbstractButton::clicked, this, &MapView::exportMap); connect(m_ui.exportButton, &QAbstractButton::clicked, this, &MapView::exportMap);
#else connect(m_ui.copyButton, &QAbstractButton::clicked, this, &MapView::copyMap);
m_ui.exportButton->setVisible(false);
#endif QAction* exportAction = new QAction(this);
exportAction->setShortcut(QKeySequence::Save);
connect(exportAction, &QAction::triggered, this, &MapView::exportMap);
addAction(exportAction);
QAction* copyAction = new QAction(this);
copyAction->setShortcut(QKeySequence::Copy);
connect(copyAction, &QAction::triggered, this, &MapView::copyMap);
addAction(copyAction);
m_ui.map->installEventFilter(this); m_ui.map->installEventFilter(this);
m_ui.tile->addCustomProperty("mapAddr", tr("Map Addr.")); m_ui.tile->addCustomProperty("mapAddr", tr("Map Addr."));
m_ui.tile->addCustomProperty("flip", tr("Mirror")); m_ui.tile->addCustomProperty("flip", tr("Mirror"));
@ -211,7 +221,7 @@ void MapView::updateTilesGBA(bool force) {
mBitmapCacheCleanRow(bitmapCache, m_bitmapStatus, j); mBitmapCacheCleanRow(bitmapCache, m_bitmapStatus, j);
memcpy(static_cast<void*>(&bgBits[width * j * 4]), mBitmapCacheGetRow(bitmapCache, j), width * 4); memcpy(static_cast<void*>(&bgBits[width * j * 4]), mBitmapCacheGetRow(bitmapCache, j), width * 4);
} }
m_rawMap = m_rawMap.rgbSwapped(); m_rawMap = m_rawMap.convertToFormat(QImage::Format_RGB32).rgbSwapped();
} else { } else {
mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map); mMapCache* mapCache = mMapCacheSetGetPointer(&m_cacheSet->maps, m_map);
int tilesW = 1 << mMapCacheSystemInfoGetTilesWide(mapCache->sysConfig); int tilesW = 1 << mMapCacheSystemInfoGetTilesWide(mapCache->sysConfig);
@ -242,23 +252,18 @@ void MapView::updateTilesGB(bool force) {
} }
#endif #endif
#ifdef USE_PNG
void MapView::exportMap() { void MapView::exportMap() {
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export map"), QString filename = GBAApp::app()->getSaveFileName(this, tr("Export map"),
tr("Portable Network Graphics (*.png)")); tr("Portable Network Graphics (*.png)"));
VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); if (filename.isNull()) {
if (!vf) {
LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename);
return; return;
} }
CoreController::Interrupter interrupter(m_controller); CoreController::Interrupter interrupter(m_controller);
png_structp png = PNGWriteOpen(vf); m_rawMap.save(filename, "PNG");
png_infop info = PNGWriteHeaderA(png, m_rawMap.width(), m_rawMap.height()); }
QImage map = m_rawMap.rgbSwapped(); void MapView::copyMap() {
PNGWritePixelsA(png, map.width(), map.height(), map.bytesPerLine() / 4, static_cast<const void*>(map.constBits())); CoreController::Interrupter interrupter(m_controller);
PNGWriteClose(png, info); GBAApp::app()->clipboard()->setImage(m_rawMap);
vf->close(vf);
} }
#endif

View File

@ -21,10 +21,9 @@ Q_OBJECT
public: public:
MapView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr); MapView(std::shared_ptr<CoreController> controller, QWidget* parent = nullptr);
#ifdef USE_PNG
public slots: public slots:
void exportMap(); void exportMap();
#endif void copyMap();
private slots: private slots:
void selectMap(int); void selectMap(int);

View File

@ -13,38 +13,7 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Maps</string> <string>Maps</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0"> <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0">
<item row="3" column="0">
<widget class="QGBA::AssetTile" name="tile"/>
</item>
<item row="2" column="0">
<widget class="QGBA::AssetInfo" name="bgInfo">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="exportButton">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0"> <item row="1" column="0">
<layout class="QVBoxLayout" name="bgLayout"> <layout class="QVBoxLayout" name="bgLayout">
<item> <item>
@ -79,7 +48,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="1" rowspan="5" colspan="2"> <item row="1" column="1" rowspan="6" colspan="2">
<widget class="QScrollArea" name="scrollArea"> <widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable"> <property name="widgetResizable">
<bool>true</bool> <bool>true</bool>
@ -133,21 +102,59 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QGBA::AssetTile" name="tile"/>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0">
<widget class="QPushButton" name="exportButton">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QGBA::AssetInfo" name="bgInfo">
<property name="title">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2"/>
</widget>
</item>
<item row="5" column="0">
<widget class="QPushButton" name="copyButton">
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>QGBA::AssetInfo</class>
<extends>QGroupBox</extends>
<header>AssetInfo.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>QGBA::AssetTile</class> <class>QGBA::AssetTile</class>
<extends>QGroupBox</extends> <extends>QGroupBox</extends>
<header>AssetTile.h</header> <header>AssetTile.h</header>
<container>1</container> <container>1</container>
</customwidget> </customwidget>
<customwidget>
<class>QGBA::AssetInfo</class>
<extends>QGroupBox</extends>
<header>AssetInfo.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -35,19 +35,28 @@ MemorySearch::~MemorySearch() {
} }
bool MemorySearch::createParams(mCoreMemorySearchParams* params) { bool MemorySearch::createParams(mCoreMemorySearchParams* params) {
params->memoryFlags = mCORE_MEMORY_RW; params->memoryFlags = mCORE_MEMORY_WRITE;
if (m_ui.searchROM->isChecked()) {
params->memoryFlags |= mCORE_MEMORY_READ;
}
mCore* core = m_controller->thread()->core; mCore* core = m_controller->thread()->core;
QByteArray string; QByteArray string;
bool ok = false; bool ok = false;
if (m_ui.typeNum->isChecked()) { if (m_ui.typeNum->isChecked()) {
params->type = mCORE_MEMORY_SEARCH_INT; params->type = mCORE_MEMORY_SEARCH_INT;
if (m_ui.opDelta->isChecked()) { if (m_ui.opDelta->isChecked() || m_ui.opDelta0->isChecked()) {
params->op = mCORE_MEMORY_SEARCH_DELTA; params->op = mCORE_MEMORY_SEARCH_DELTA;
} else if (m_ui.opGreater->isChecked()) { } else if (m_ui.opGreater->isChecked()) {
params->op = mCORE_MEMORY_SEARCH_GREATER; params->op = mCORE_MEMORY_SEARCH_GREATER;
} else if (m_ui.opLess->isChecked()) { } else if (m_ui.opLess->isChecked()) {
params->op = mCORE_MEMORY_SEARCH_LESS; params->op = mCORE_MEMORY_SEARCH_LESS;
} else if (m_ui.opUnknown->isChecked()) {
params->op = mCORE_MEMORY_SEARCH_ANY;
} else if (m_ui.opDeltaPositive->isChecked()) {
params->op = mCORE_MEMORY_SEARCH_DELTA_POSITIVE;
} else if (m_ui.opDeltaNegative->isChecked()) {
params->op = mCORE_MEMORY_SEARCH_DELTA_NEGATIVE;
} else { } else {
params->op = mCORE_MEMORY_SEARCH_EQUAL; params->op = mCORE_MEMORY_SEARCH_EQUAL;
} }
@ -103,9 +112,15 @@ bool MemorySearch::createParams(mCoreMemorySearchParams* params) {
} }
if (m_ui.numGuess->isChecked()) { if (m_ui.numGuess->isChecked()) {
params->type = mCORE_MEMORY_SEARCH_GUESS; params->type = mCORE_MEMORY_SEARCH_GUESS;
if (m_ui.opDelta0->isChecked()) {
m_string = QString("0").toLocal8Bit();
} else {
m_string = m_ui.value->text().toLocal8Bit(); m_string = m_ui.value->text().toLocal8Bit();
}
params->valueStr = m_string.constData(); params->valueStr = m_string.constData();
ok = true; ok = true;
} else if (m_ui.opDelta0->isChecked()) {
params->valueInt = 0;
} }
} }
if (m_ui.typeStr->isChecked()) { if (m_ui.typeStr->isChecked()) {
@ -140,6 +155,9 @@ void MemorySearch::searchWithin() {
mCore* core = m_controller->thread()->core; mCore* core = m_controller->thread()->core;
if (createParams(&params)) { if (createParams(&params)) {
if (m_ui.opUnknown->isChecked()) {
params.op = mCORE_MEMORY_SEARCH_DELTA_ANY;
}
mCoreMemorySearchRepeat(core, &params, &m_results); mCoreMemorySearchRepeat(core, &params, &m_results);
} }
@ -153,6 +171,9 @@ void MemorySearch::refresh() {
m_ui.results->clearContents(); m_ui.results->clearContents();
m_ui.results->setRowCount(mCoreMemorySearchResultsSize(&m_results)); m_ui.results->setRowCount(mCoreMemorySearchResultsSize(&m_results));
m_ui.opDelta->setEnabled(false); m_ui.opDelta->setEnabled(false);
m_ui.opDelta0->setEnabled(false);
m_ui.opDeltaPositive->setEnabled(false);
m_ui.opDeltaNegative->setEnabled(false);
for (size_t i = 0; i < mCoreMemorySearchResultsSize(&m_results); ++i) { for (size_t i = 0; i < mCoreMemorySearchResultsSize(&m_results); ++i) {
mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i); mCoreMemorySearchResult* result = mCoreMemorySearchResultsGetPointer(&m_results, i);
QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0'))); QTableWidgetItem* item = new QTableWidgetItem(QString("%1").arg(result->address, 8, 16, QChar('0')));
@ -214,9 +235,18 @@ void MemorySearch::refresh() {
m_ui.results->setItem(i, 1, item); m_ui.results->setItem(i, 1, item);
m_ui.results->setItem(i, 2, type); m_ui.results->setItem(i, 2, type);
m_ui.opDelta->setEnabled(true); m_ui.opDelta->setEnabled(true);
m_ui.opDelta0->setEnabled(true);
m_ui.opDeltaPositive->setEnabled(true);
m_ui.opDeltaNegative->setEnabled(true);
} }
if (m_ui.opDelta->isChecked() && !m_ui.opDelta->isEnabled()) { if (m_ui.opDelta->isChecked() && !m_ui.opDelta->isEnabled()) {
m_ui.opEqual->setChecked(true); m_ui.opEqual->setChecked(true);
} else if (m_ui.opDelta0->isChecked() && !m_ui.opDelta0->isEnabled()) {
m_ui.opEqual->setChecked(true);
} else if (m_ui.opDeltaPositive->isChecked() && !m_ui.opDeltaPositive->isEnabled()) {
m_ui.opEqual->setChecked(true);
} else if (m_ui.opDeltaNegative->isChecked() && !m_ui.opDeltaNegative->isEnabled()) {
m_ui.opEqual->setChecked(true);
} }
m_ui.results->sortItems(0); m_ui.results->sortItems(0);
} }

View File

@ -6,14 +6,20 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>540</width> <width>725</width>
<height>491</height> <height>813</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>540</width> <width>540</width>
<height>241</height> <height>400</height>
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -99,21 +105,21 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="2"> <item row="4" column="0" colspan="2">
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="5" column="0">
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Width</string> <string>Width</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1"> <item row="5" column="1">
<widget class="QRadioButton" name="bitsGuess"> <widget class="QRadioButton" name="bitsGuess">
<property name="text"> <property name="text">
<string>Guess</string> <string>Guess</string>
@ -126,7 +132,7 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QRadioButton" name="bits8"> <widget class="QRadioButton" name="bits8">
<property name="text"> <property name="text">
<string>1 Byte (8-bit)</string> <string>1 Byte (8-bit)</string>
@ -136,7 +142,7 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="QRadioButton" name="bits16"> <widget class="QRadioButton" name="bits16">
<property name="text"> <property name="text">
<string>2 Bytes (16-bit)</string> <string>2 Bytes (16-bit)</string>
@ -146,7 +152,7 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QRadioButton" name="bits32"> <widget class="QRadioButton" name="bits32">
<property name="text"> <property name="text">
<string>4 Bytes (32-bit)</string> <string>4 Bytes (32-bit)</string>
@ -159,21 +165,21 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="8" column="0" colspan="2"> <item row="9" column="0" colspan="2">
<widget class="Line" name="line_2"> <widget class="Line" name="line_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Number type</string> <string>Number type</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QRadioButton" name="numGuess"> <widget class="QRadioButton" name="numGuess">
<property name="text"> <property name="text">
<string>Guess</string> <string>Guess</string>
@ -183,38 +189,38 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="11" column="1">
<widget class="QRadioButton" name="numDec"> <widget class="QRadioButton" name="numDec">
<property name="text"> <property name="text">
<string>Decimal</string> <string>Decimal</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="12" column="1">
<widget class="QRadioButton" name="numHex"> <widget class="QRadioButton" name="numHex">
<property name="text"> <property name="text">
<string>Hexadecimal</string> <string>Hexadecimal</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0" colspan="2"> <item row="13" column="0" colspan="2">
<widget class="Line" name="line_3"> <widget class="Line" name="line_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="0"> <item row="14" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>Compare</string> <string>Search type</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="1"> <item row="14" column="1">
<widget class="QRadioButton" name="opEqual"> <widget class="QRadioButton" name="opEqual">
<property name="text"> <property name="text">
<string>Equal</string> <string>Equal to value</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
@ -224,20 +230,10 @@
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="15" column="1">
<widget class="QRadioButton" name="opGreater"> <widget class="QRadioButton" name="opGreater">
<property name="text"> <property name="text">
<string>Greater</string> <string>Greater than value</string>
</property>
<attribute name="buttonGroup">
<string notr="true">op</string>
</attribute>
</widget>
</item>
<item row="15" column="1">
<widget class="QRadioButton" name="opLess">
<property name="text">
<string>Less</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">op</string> <string notr="true">op</string>
@ -245,18 +241,84 @@
</widget> </widget>
</item> </item>
<item row="16" column="1"> <item row="16" column="1">
<widget class="QRadioButton" name="opLess">
<property name="text">
<string>Less than value</string>
</property>
<attribute name="buttonGroup">
<string notr="true">op</string>
</attribute>
</widget>
</item>
<item row="17" column="1">
<widget class="QRadioButton" name="opUnknown">
<property name="text">
<string>Unknown/changed</string>
</property>
<attribute name="buttonGroup">
<string notr="true">op</string>
</attribute>
</widget>
</item>
<item row="18" column="1">
<widget class="QRadioButton" name="opDelta"> <widget class="QRadioButton" name="opDelta">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Delta</string> <string>Changed by value</string>
</property> </property>
<attribute name="buttonGroup"> <attribute name="buttonGroup">
<string notr="true">op</string> <string notr="true">op</string>
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="21" column="1">
<widget class="QRadioButton" name="opDelta0">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Unchanged</string>
</property>
<attribute name="buttonGroup">
<string notr="true">op</string>
</attribute>
</widget>
</item>
<item row="19" column="1">
<widget class="QRadioButton" name="opDeltaPositive">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Increased</string>
</property>
<attribute name="buttonGroup">
<string notr="true">op</string>
</attribute>
</widget>
</item>
<item row="20" column="1">
<widget class="QRadioButton" name="opDeltaNegative">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Decreased</string>
</property>
<attribute name="buttonGroup">
<string notr="true">op</string>
</attribute>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="searchROM">
<property name="text">
<string>Search ROM</string>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item row="2" column="0" colspan="2"> <item row="2" column="0" colspan="2">
@ -271,7 +333,7 @@
<item> <item>
<widget class="QPushButton" name="search"> <widget class="QPushButton" name="search">
<property name="text"> <property name="text">
<string>Search</string> <string>New Search</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -318,10 +380,26 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>opDelta0</sender>
<signal>toggled(bool)</signal>
<receiver>value</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>231</x>
<y>768</y>
</hint>
<hint type="destinationlabel">
<x>272</x>
<y>26</y>
</hint>
</hints>
</connection>
</connections> </connections>
<buttongroups> <buttongroups>
<buttongroup name="width"/>
<buttongroup name="type"/> <buttongroup name="type"/>
<buttongroup name="op"/> <buttongroup name="op"/>
<buttongroup name="width"/>
</buttongroups> </buttongroups>
</ui> </ui>

View File

@ -8,7 +8,10 @@
#include "CoreController.h" #include "CoreController.h"
#include "GBAApp.h" #include "GBAApp.h"
#include <QAction>
#include <QClipboard>
#include <QFontDatabase> #include <QFontDatabase>
#include <QListWidgetItem>
#include <QTimer> #include <QTimer>
#include "LogController.h" #include "LogController.h"
@ -52,10 +55,36 @@ ObjView::ObjView(std::shared_ptr<CoreController> controller, QWidget* parent)
updateTiles(true); updateTiles(true);
}); });
connect(m_ui.exportButton, &QAbstractButton::clicked, this, &ObjView::exportObj); connect(m_ui.exportButton, &QAbstractButton::clicked, this, &ObjView::exportObj);
connect(m_ui.copyButton, &QAbstractButton::clicked, this, &ObjView::copyObj);
connect(m_ui.objList, &QListWidget::currentItemChanged, [this]() {
QListWidgetItem* item = m_ui.objList->currentItem();
if (item) {
selectObj(item->data(Qt::UserRole).toInt());
}
});
QAction* exportAction = new QAction(this);
exportAction->setShortcut(QKeySequence::Save);
connect(exportAction, &QAction::triggered, this, &ObjView::exportObj);
addAction(exportAction);
QAction* copyAction = new QAction(this);
copyAction->setShortcut(QKeySequence::Copy);
connect(copyAction, &QAction::triggered, this, &ObjView::copyObj);
addAction(copyAction);
} }
void ObjView::selectObj(int obj) { void ObjView::selectObj(int obj) {
m_objId = obj; m_objId = obj;
bool blocked = m_ui.objId->blockSignals(true);
m_ui.objId->setValue(m_objId);
m_ui.objId->blockSignals(blocked);
if (m_objs.size() > obj) {
blocked = m_ui.objList->blockSignals(true);
m_ui.objList->setCurrentItem(m_objs[obj]);
m_ui.objList->blockSignals(blocked);
}
updateTiles(true); updateTiles(true);
} }
@ -71,6 +100,8 @@ void ObjView::updateTilesGBA(bool force) {
const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board); const GBA* gba = static_cast<const GBA*>(m_controller->thread()->core->board);
const GBAObj* obj = &gba->video.oam.obj[m_objId]; const GBAObj* obj = &gba->video.oam.obj[m_objId];
updateObjList(128);
ObjInfo newInfo; ObjInfo newInfo;
lookupObj(m_objId, &newInfo); lookupObj(m_objId, &newInfo);
@ -153,6 +184,8 @@ void ObjView::updateTilesGB(bool force) {
const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board); const GB* gb = static_cast<const GB*>(m_controller->thread()->core->board);
const GBObj* obj = &gb->video.oam.obj[m_objId]; const GBObj* obj = &gb->video.oam.obj[m_objId];
updateObjList(40);
ObjInfo newInfo; ObjInfo newInfo;
lookupObj(m_objId, &newInfo); lookupObj(m_objId, &newInfo);
@ -200,10 +233,38 @@ void ObjView::updateTilesGB(bool force) {
} }
#endif #endif
void ObjView::updateObjList(int maxObj) {
for (int i = 0; i < maxObj; ++i) {
if (m_objs.size() <= i) {
QListWidgetItem* item = new QListWidgetItem;
item->setText(QString::number(i));
item->setData(Qt::UserRole, i);
item->setSizeHint(QSize(64, 96));
if (m_objId == i) {
item->setSelected(true);
}
m_objs.append(item);
m_ui.objList->addItem(item);
}
QListWidgetItem* item = m_objs[i];
ObjInfo info;
lookupObj(i, &info);
item->setIcon(QPixmap::fromImage(std::move(compositeObj(info))));
}
}
void ObjView::exportObj() { void ObjView::exportObj() {
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"), QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"),
tr("Portable Network Graphics (*.png)")); tr("Portable Network Graphics (*.png)"));
if (filename.isNull()) {
return;
}
CoreController::Interrupter interrupter(m_controller); CoreController::Interrupter interrupter(m_controller);
QImage obj = compositeObj(m_objInfo); QImage obj = compositeObj(m_objInfo);
obj.save(filename, "PNG"); obj.save(filename, "PNG");
} }
void ObjView::copyObj() {
CoreController::Interrupter interrupter(m_controller);
GBAApp::app()->clipboard()->setImage(compositeObj(m_objInfo));
}

View File

@ -9,8 +9,12 @@
#include "ui_ObjView.h" #include "ui_ObjView.h"
#include <QList>
#include <mgba/core/tile-cache.h> #include <mgba/core/tile-cache.h>
class QListWidgetItem;
namespace QGBA { namespace QGBA {
class CoreController; class CoreController;
@ -23,6 +27,7 @@ public:
public slots: public slots:
void exportObj(); void exportObj();
void copyObj();
private slots: private slots:
void selectObj(int); void selectObj(int);
@ -36,6 +41,8 @@ private:
void updateTilesGB(bool force) override; void updateTilesGB(bool force) override;
#endif #endif
void updateObjList(int maxObj);
Ui::ObjView m_ui; Ui::ObjView m_ui;
std::shared_ptr<CoreController> m_controller; std::shared_ptr<CoreController> m_controller;
@ -43,6 +50,8 @@ private:
int m_objId = 0; int m_objId = 0;
ObjInfo m_objInfo = {}; ObjInfo m_objInfo = {};
QList<QListWidgetItem*> m_objs;
int m_tileOffset; int m_tileOffset;
int m_boundary; int m_boundary;
}; };

View File

@ -6,21 +6,178 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>454</width> <width>800</width>
<height>385</height> <height>730</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Sprites</string> <string>Sprites</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,0,1"> <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,1" columnstretch="0,0,1,1">
<item row="0" column="2" rowspan="4"> <item row="4" column="3">
<widget class="QPushButton" name="copyButton">
<property name="text">
<string>Copy</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Geometry</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Position</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="x">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tileId_3">
<property name="text">
<string>, </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="y">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Dimensions</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="w">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>8</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tileId_5">
<property name="text">
<string>×</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="h">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>8</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="2" rowspan="4" colspan="2">
<widget class="QFrame" name="frame"> <widget class="QFrame" name="frame">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::StyledPanel</enum>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QGridLayout" name="gridLayout_2">
<property name="margin"> <property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item row="0" column="0" alignment="Qt::AlignHCenter|Qt::AlignVCenter"> <item row="0" column="0" alignment="Qt::AlignHCenter|Qt::AlignVCenter">
@ -42,44 +199,21 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item row="0" column="1" rowspan="5">
<layout class="QHBoxLayout" name="horizontalLayout_4"> <widget class="QGBA::AssetTile" name="tile">
<item> <property name="title">
<widget class="QSpinBox" name="magnification"> <string>Tile</string>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="4" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="exportButton"> <widget class="QPushButton" name="exportButton">
<property name="text"> <property name="text">
<string>Export</string> <string>Export</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="3" column="0" rowspan="2">
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="title"> <property name="title">
<string>Attributes</string> <string>Attributes</string>
@ -383,154 +517,6 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="0" column="1" rowspan="4">
<widget class="QGBA::AssetTile" name="tile">
<property name="title">
<string>Tile</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Geometry</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Position</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="x">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tileId_3">
<property name="text">
<string>, </string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="y">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>0</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Dimensions</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="w">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>8</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tileId_5">
<property name="text">
<string>×</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="h">
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>8</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
@ -568,6 +554,64 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSpinBox" name="magnification">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="5" column="0" colspan="4">
<widget class="QListWidget" name="objList">
<property name="iconSize">
<size>
<width>64</width>
<height>64</height>
</size>
</property>
<property name="flow">
<enum>QListView::LeftToRight</enum>
</property>
<property name="resizeMode">
<enum>QListView::Adjust</enum>
</property>
<property name="gridSize">
<size>
<width>64</width>
<height>96</height>
</size>
</property>
<property name="viewMode">
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -42,9 +42,12 @@ OverrideView::OverrideView(ConfigController* config, QWidget* parent)
s_mbcList.append(GB_MBC3_RTC); s_mbcList.append(GB_MBC3_RTC);
s_mbcList.append(GB_MBC5); s_mbcList.append(GB_MBC5);
s_mbcList.append(GB_MBC5_RUMBLE); s_mbcList.append(GB_MBC5_RUMBLE);
s_mbcList.append(GB_MBC6);
s_mbcList.append(GB_MBC7); s_mbcList.append(GB_MBC7);
s_mbcList.append(GB_MMM01);
s_mbcList.append(GB_POCKETCAM); s_mbcList.append(GB_POCKETCAM);
s_mbcList.append(GB_TAMA5); s_mbcList.append(GB_TAMA5);
s_mbcList.append(GB_HuC1);
s_mbcList.append(GB_HuC3); s_mbcList.append(GB_HuC3);
} }
if (s_gbModelList.isEmpty()) { if (s_gbModelList.isEmpty()) {

View File

@ -319,11 +319,21 @@
<string>MBC5 + Rumble</string> <string>MBC5 + Rumble</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>MBC6</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>MBC7</string> <string>MBC7</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>MMM01</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>Pocket Cam</string> <string>Pocket Cam</string>
@ -334,6 +344,11 @@
<string>TAMA5</string> <string>TAMA5</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>HuC-1</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>HuC-3</string> <string>HuC-3</string>

View File

@ -8,6 +8,7 @@
#include "CoreController.h" #include "CoreController.h"
#include "GBAApp.h" #include "GBAApp.h"
#include <QAction>
#include <QPainter> #include <QPainter>
using namespace QGBA; using namespace QGBA;
@ -24,6 +25,22 @@ PrinterView::PrinterView(std::shared_ptr<CoreController> controller, QWidget* pa
connect(m_ui.tear, &QAbstractButton::clicked, this, &PrinterView::clear); connect(m_ui.tear, &QAbstractButton::clicked, this, &PrinterView::clear);
connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &PrinterView::save); connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &PrinterView::save);
m_timer.setInterval(80); m_timer.setInterval(80);
connect(m_ui.magnification, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this](int mag) {
if (m_image.isNull()) {
return;
}
int oldMag = m_ui.image->size().width() / m_image.size().width();
m_ui.image->setPixmap(m_image.scaled(m_image.size() * mag));
m_ui.image->setFixedWidth(m_image.size().width() * mag);
m_ui.image->setFixedHeight(m_ui.image->size().height() / oldMag * mag);
});
QAction* save = new QAction(this);
save->setShortcut(QKeySequence::Save);
connect(save, &QAction::triggered, this, &PrinterView::save);
addAction(save);
clear(); clear();
} }
@ -52,22 +69,22 @@ void PrinterView::printImage(const QImage& image) {
painter.drawPixmap(0, 0, m_image); painter.drawPixmap(0, 0, m_image);
painter.drawImage(0, m_image.height(), image); painter.drawImage(0, m_image.height(), image);
m_image = pixmap; m_image = pixmap;
m_ui.image->setPixmap(m_image); m_ui.image->setPixmap(m_image.scaled(m_image.size() * m_ui.magnification->value()));
m_timer.start(); m_timer.start();
m_ui.hurry->setEnabled(true); m_ui.hurry->setEnabled(true);
} }
void PrinterView::printLine() { void PrinterView::printLine() {
m_ui.image->setFixedHeight(m_ui.image->height() + 1); m_ui.image->setFixedHeight(m_ui.image->height() + m_ui.magnification->value());
m_ui.scrollArea->ensureVisible(0, m_ui.image->height(), 0, 0); m_ui.scrollArea->ensureVisible(0, m_ui.image->height(), 0, 0);
if (m_ui.image->height() >= m_image.height()) { if (m_ui.image->height() >= m_image.height() * m_ui.magnification->value()) {
printAll(); printAll();
} }
} }
void PrinterView::printAll() { void PrinterView::printAll() {
m_timer.stop(); m_timer.stop();
m_ui.image->setFixedHeight(m_image.height()); m_ui.image->setFixedHeight(m_image.height() * m_ui.magnification->value());
m_controller->endPrint(); m_controller->endPrint();
m_ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true); m_ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(true);
m_ui.hurry->setEnabled(false); m_ui.hurry->setEnabled(false);

View File

@ -6,15 +6,15 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>241</width> <width>246</width>
<height>311</height> <height>425</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Game Boy Printer</string> <string>Game Boy Printer</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item alignment="Qt::AlignHCenter"> <item>
<widget class="QScrollArea" name="scrollArea"> <widget class="QScrollArea" name="scrollArea">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
@ -23,10 +23,15 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<widget class="QWidget" name="scrollAreaWidgetContents"> <widget class="QWidget" name="scrollAreaWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout_2"> <property name="geometry">
<property name="spacing"> <rect>
<number>0</number> <x>0</x>
<y>0</y>
<width>234</width>
<height>249</height>
</rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout" rowstretch="1,0,1" columnminimumwidth="0,160,0">
<property name="leftMargin"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
@ -39,14 +44,36 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item> <property name="spacing">
<number>0</number>
</property>
<item row="0" column="1">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer> </spacer>
</item> </item>
<item> <item row="0" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLabel" name="image"> <widget class="QLabel" name="image">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -60,18 +87,12 @@
<height>1</height> <height>1</height>
</size> </size>
</property> </property>
<property name="maximumSize">
<size>
<width>160</width>
<height>16777215</height>
</size>
</property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set> <set>Qt::AlignHCenter|Qt::AlignTop</set>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="2" column="1">
<widget class="Line" name="line"> <widget class="Line" name="line">
<property name="frameShadow"> <property name="frameShadow">
<enum>QFrame::Plain</enum> <enum>QFrame::Plain</enum>
@ -81,6 +102,19 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
@ -106,6 +140,62 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QSpinBox" name="magnification">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string>×</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>8</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Magnification</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons"> <property name="standardButtons">

View File

@ -371,6 +371,7 @@ void SettingsView::updateConfig() {
saveSetting("lockAspectRatio", m_ui.lockAspectRatio); saveSetting("lockAspectRatio", m_ui.lockAspectRatio);
saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling); saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
saveSetting("interframeBlending", m_ui.interframeBlending); saveSetting("interframeBlending", m_ui.interframeBlending);
saveSetting("showOSD", m_ui.showOSD);
saveSetting("volume", m_ui.volume); saveSetting("volume", m_ui.volume);
saveSetting("mute", m_ui.mute); saveSetting("mute", m_ui.mute);
saveSetting("fastForwardVolume", m_ui.volumeFf); saveSetting("fastForwardVolume", m_ui.volumeFf);
@ -546,6 +547,7 @@ void SettingsView::reloadConfig() {
loadSetting("lockAspectRatio", m_ui.lockAspectRatio); loadSetting("lockAspectRatio", m_ui.lockAspectRatio);
loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling); loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling);
loadSetting("interframeBlending", m_ui.interframeBlending); loadSetting("interframeBlending", m_ui.interframeBlending);
loadSetting("showOSD", m_ui.showOSD, true);
loadSetting("volume", m_ui.volume, 0x100); loadSetting("volume", m_ui.volume, 0x100);
loadSetting("mute", m_ui.mute, false); loadSetting("mute", m_ui.mute, false);
loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value()); loadSetting("fastForwardVolume", m_ui.volumeFf, m_ui.volume->value());

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>849</width> <width>885</width>
<height>797</height> <height>797</height>
</rect> </rect>
</property> </property>
@ -562,7 +562,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="9" column="1">
<widget class="QCheckBox" name="pauseOnMinimize">
<property name="text">
<string>Pause when minimized</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="2">
<widget class="Line" name="line_17">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="showFps"> <widget class="QCheckBox" name="showFps">
<property name="text"> <property name="text">
<string>Show FPS in title bar</string> <string>Show FPS in title bar</string>
@ -572,41 +586,21 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0" colspan="2"> <item row="13" column="1">
<widget class="QCheckBox" name="useDiscordPresence">
<property name="text">
<string>Enable Discord Rich Presence</string>
</property>
</widget>
</item>
<item row="14" column="0" colspan="2">
<widget class="Line" name="line_13"> <widget class="Line" name="line_13">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="15" column="0" colspan="2"> <item row="15" column="1">
<widget class="Line" name="line_16">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="16" column="1">
<widget class="QCheckBox" name="cheatAutosave">
<property name="text">
<string>Automatically save cheats</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QCheckBox" name="cheatAutoload">
<property name="text">
<string>Automatically load cheats</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QCheckBox" name="autosave"> <widget class="QCheckBox" name="autosave">
<property name="text"> <property name="text">
<string>Automatically save state</string> <string>Automatically save state</string>
@ -616,7 +610,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="14" column="1"> <item row="16" column="1">
<widget class="QCheckBox" name="autoload"> <widget class="QCheckBox" name="autoload">
<property name="text"> <property name="text">
<string>Automatically load state</string> <string>Automatically load state</string>
@ -626,17 +620,40 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="1"> <item row="17" column="0" colspan="2">
<widget class="QCheckBox" name="useDiscordPresence"> <widget class="Line" name="line_16">
<property name="text"> <property name="orientation">
<string>Enable Discord Rich Presence</string> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="18" column="1">
<widget class="QCheckBox" name="pauseOnMinimize"> <widget class="QCheckBox" name="cheatAutosave">
<property name="text"> <property name="text">
<string>Pause when minimized</string> <string>Automatically save cheats</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="19" column="1">
<widget class="QCheckBox" name="cheatAutoload">
<property name="text">
<string>Automatically load cheats</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QCheckBox" name="showOSD">
<property name="text">
<string>Show OSD messages</string>
</property>
<property name="checked">
<bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
@ -2106,6 +2123,16 @@
<signal>toggled(bool)</signal> <signal>toggled(bool)</signal>
<receiver>fastForwardHeldRatio</receiver> <receiver>fastForwardHeldRatio</receiver>
<slot>setDisabled(bool)</slot> <slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>20</x>
<y>20</y>
</hint>
<hint type="destinationlabel">
<x>20</x>
<y>20</y>
</hint>
</hints>
</connection> </connection>
</connections> </connections>
</ui> </ui>

View File

@ -8,6 +8,8 @@
#include "CoreController.h" #include "CoreController.h"
#include "GBAApp.h" #include "GBAApp.h"
#include <QAction>
#include <QClipboard>
#include <QFontDatabase> #include <QFontDatabase>
#include <QTimer> #include <QTimer>
@ -87,7 +89,20 @@ TileView::TileView(std::shared_ptr<CoreController> controller, QWidget* parent)
updateTiles(true); updateTiles(true);
}); });
connect(m_ui.exportButton, &QAbstractButton::clicked, this, &TileView::exportTiles); connect(m_ui.exportAll, &QAbstractButton::clicked, this, &TileView::exportTiles);
connect(m_ui.exportOne, &QAbstractButton::clicked, this, &TileView::exportTile);
connect(m_ui.copyAll, &QAbstractButton::clicked, this, &TileView::copyTiles);
connect(m_ui.copyOne, &QAbstractButton::clicked, this, &TileView::copyTile);
QAction* exportAll = new QAction(this);
exportAll->setShortcut(QKeySequence::Save);
connect(exportAll, &QAction::triggered, this, &TileView::exportTiles);
addAction(exportAll);
QAction* copyOne = new QAction(this);
copyOne->setShortcut(QKeySequence::Copy);
connect(copyOne, &QAction::triggered, this, &TileView::copyTile);
addAction(copyOne);
} }
#ifdef M_CORE_GBA #ifdef M_CORE_GBA
@ -162,8 +177,36 @@ void TileView::updatePalette(int palette) {
void TileView::exportTiles() { void TileView::exportTiles() {
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export tiles"), QString filename = GBAApp::app()->getSaveFileName(this, tr("Export tiles"),
tr("Portable Network Graphics (*.png)")); tr("Portable Network Graphics (*.png)"));
if (filename.isNull()) {
return;
}
CoreController::Interrupter interrupter(m_controller); CoreController::Interrupter interrupter(m_controller);
updateTiles(false); updateTiles(false);
QPixmap pixmap(m_ui.tiles->backing()); QPixmap pixmap(m_ui.tiles->backing());
pixmap.save(filename, "PNG"); pixmap.save(filename, "PNG");
} }
void TileView::exportTile() {
QString filename = GBAApp::app()->getSaveFileName(this, tr("Export tile"),
tr("Portable Network Graphics (*.png)"));
if (filename.isNull()) {
return;
}
CoreController::Interrupter interrupter(m_controller);
updateTiles(false);
QImage image(m_ui.tile->activeTile());
image.save(filename, "PNG");
}
void TileView::copyTiles() {
CoreController::Interrupter interrupter(m_controller);
updateTiles(false);
QPixmap pixmap();
GBAApp::app()->clipboard()->setPixmap(m_ui.tiles->backing());
}
void TileView::copyTile() {
CoreController::Interrupter interrupter(m_controller);
updateTiles(false);
GBAApp::app()->clipboard()->setImage(m_ui.tile->activeTile());
}

View File

@ -24,6 +24,9 @@ public:
public slots: public slots:
void updatePalette(int); void updatePalette(int);
void exportTiles(); void exportTiles();
void exportTile();
void copyTiles();
void copyTile();
private: private:
#ifdef M_CORE_GBA #ifdef M_CORE_GBA

View File

@ -6,14 +6,28 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>693</width> <width>748</width>
<height>467</height> <height>823</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Tiles</string> <string>Tiles</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1"> <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,1,0,0,0,0" columnstretch="0,1">
<item row="5" column="0">
<widget class="QPushButton" name="exportOne">
<property name="text">
<string>Export Selected</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QPushButton" name="exportAll">
<property name="text">
<string>Export All</string>
</property>
</widget>
</item>
<item row="0" column="0"> <item row="0" column="0">
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="0" column="0"> <item row="0" column="0">
@ -104,17 +118,7 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="1" column="0"> <item row="0" column="1" rowspan="8">
<widget class="QGBA::AssetTile" name="tile"/>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="exportButton">
<property name="text">
<string>Export</string>
</property>
</widget>
</item>
<item row="0" column="1" rowspan="5">
<widget class="QScrollArea" name="scrollArea"> <widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding"> <sizepolicy hsizetype="Minimum" vsizetype="Expanding">
@ -133,7 +137,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>405</width> <width>480</width>
<height>768</height> <height>768</height>
</rect> </rect>
</property> </property>
@ -179,6 +183,23 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item row="1" column="0">
<widget class="QGBA::AssetTile" name="tile"/>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="copyOne">
<property name="text">
<string>Copy Selected</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QPushButton" name="copyAll">
<property name="text">
<string>Copy All</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -802,6 +802,7 @@ void Window::gameStarted() {
m_config->updateOption("lockIntegerScaling"); m_config->updateOption("lockIntegerScaling");
m_config->updateOption("lockAspectRatio"); m_config->updateOption("lockAspectRatio");
m_config->updateOption("interframeBlending"); m_config->updateOption("interframeBlending");
m_config->updateOption("showOSD");
if (m_savedScale > 0) { if (m_savedScale > 0) {
resizeFrame(size * m_savedScale); resizeFrame(size * m_savedScale);
} }
@ -971,8 +972,10 @@ void Window::reloadDisplayDriver() {
const mCoreOptions* opts = m_config->options(); const mCoreOptions* opts = m_config->options();
m_display->lockAspectRatio(opts->lockAspectRatio); m_display->lockAspectRatio(opts->lockAspectRatio);
m_display->lockIntegerScaling(opts->lockIntegerScaling);
m_display->interframeBlending(opts->interframeBlending); m_display->interframeBlending(opts->interframeBlending);
m_display->filter(opts->resampleVideo); m_display->filter(opts->resampleVideo);
m_config->updateOption("showOSD");
#if defined(BUILD_GL) || defined(BUILD_GLES2) #if defined(BUILD_GL) || defined(BUILD_GLES2)
if (opts->shader) { if (opts->shader) {
struct VDir* shader = VDirOpen(opts->shader); struct VDir* shader = VDirOpen(opts->shader);
@ -1694,6 +1697,13 @@ void Window::setupMenu(QMenuBar* menubar) {
} }
}, this); }, this);
ConfigOption* showOSD = m_config->addOption("showOSD");
showOSD->connect([this](const QVariant& value) {
if (m_display) {
m_display->showOSDMessages(value.toBool());
}
}, this);
m_actions.addHiddenAction(tr("Exit fullscreen"), "exitFullScreen", this, &Window::exitFullScreen, "frame", QKeySequence("Esc")); m_actions.addHiddenAction(tr("Exit fullscreen"), "exitFullScreen", this, &Window::exitFullScreen, "frame", QKeySequence("Esc"));
m_actions.addHeldAction(tr("GameShark Button (held)"), "holdGSButton", [this](bool held) { m_actions.addHeldAction(tr("GameShark Button (held)"), "holdGSButton", [this](bool held) {

View File

@ -51,6 +51,7 @@ LibraryController::LibraryController(QWidget* parent, const QString& path, Confi
m_libraryGrid = std::make_unique<LibraryGrid>(this); m_libraryGrid = std::make_unique<LibraryGrid>(this);
addWidget(m_libraryGrid->widget()); addWidget(m_libraryGrid->widget());
m_currentStyle = LibraryStyle::STYLE_TREE; // Make sure setViewStyle does something
setViewStyle(LibraryStyle::STYLE_LIST); setViewStyle(LibraryStyle::STYLE_LIST);
refresh(); refresh();
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -20,7 +20,7 @@
#include <GLES3/gl31.h> #include <GLES3/gl31.h>
#define AUTO_INPUT 0x4E585031 #define AUTO_INPUT 0x4E585031
#define SAMPLES 0x400 #define SAMPLES 0x200
#define BUFFER_SIZE 0x1000 #define BUFFER_SIZE 0x1000
#define N_BUFFERS 4 #define N_BUFFERS 4
#define ANALOG_DEADZONE 0x4000 #define ANALOG_DEADZONE 0x4000
@ -281,6 +281,8 @@ static void _setup(struct mGUIRunner* runner) {
if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) { if (mCoreConfigGetUIntValue(&runner->config, "screenMode", &mode) && mode < SM_MAX) {
screenMode = mode; screenMode = mode;
} }
runner->core->setAudioBufferSize(runner->core, SAMPLES);
} }
static void _gameLoaded(struct mGUIRunner* runner) { static void _gameLoaded(struct mGUIRunner* runner) {
@ -539,6 +541,11 @@ static void _postAudioBuffer(struct mAVStream* stream, blip_t* left, blip_t* rig
if (enqueuedBuffers >= N_BUFFERS - 1 && R_SUCCEEDED(audoutWaitPlayFinish(&releasedBuffers, &audoutNReleasedBuffers, 10000000))) { if (enqueuedBuffers >= N_BUFFERS - 1 && R_SUCCEEDED(audoutWaitPlayFinish(&releasedBuffers, &audoutNReleasedBuffers, 10000000))) {
enqueuedBuffers -= audoutNReleasedBuffers; enqueuedBuffers -= audoutNReleasedBuffers;
} }
if (enqueuedBuffers >= N_BUFFERS) {
blip_clear(left);
blip_clear(right);
return;
}
struct GBAStereoSample* samples = audioBuffer[audioBufferActive]; struct GBAStereoSample* samples = audioBuffer[audioBufferActive];
blip_read_samples(left, &samples[0].left, SAMPLES, true); blip_read_samples(left, &samples[0].left, SAMPLES, true);
@ -754,7 +761,7 @@ int main(int argc, char* argv[]) {
audoutBuffer[i].next = NULL; audoutBuffer[i].next = NULL;
audoutBuffer[i].buffer = audioBuffer[i]; audoutBuffer[i].buffer = audioBuffer[i];
audoutBuffer[i].buffer_size = BUFFER_SIZE; audoutBuffer[i].buffer_size = BUFFER_SIZE;
audoutBuffer[i].data_size = BUFFER_SIZE; audoutBuffer[i].data_size = SAMPLES * 4;
audoutBuffer[i].data_offset = 0; audoutBuffer[i].data_offset = 0;
} }