From e838c4fb0e1a585ec925d8d04d13ec33d09e55b3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 26 Mar 2017 12:24:45 -0700 Subject: [PATCH 001/159] Qt: Add HEVC and NVENC to video formats --- src/platform/qt/VideoView.cpp | 4 ++++ src/platform/qt/VideoView.ui | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index 6a6523b6e..681371217 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -63,7 +63,9 @@ VideoView::VideoView(QWidget* parent) if (s_vcodecMap.empty()) { s_vcodecMap["dirac"] = "libschroedinger"; s_vcodecMap["h264"] = "libx264"; + s_vcodecMap["h264 nvenc"] = "h264_nvenc"; s_vcodecMap["hevc"] = "libx265"; + s_vcodecMap["hevc nvenc"] = "hevc_nvenc"; s_vcodecMap["theora"] = "libtheora"; s_vcodecMap["vp8"] = "libvpx"; s_vcodecMap["vp9"] = "libvpx-vp9"; @@ -458,6 +460,8 @@ void VideoView::uncheckIncompatible() { QString VideoView::sanitizeCodec(const QString& codec, const QMap& mapping) { QString sanitized = codec.toLower(); sanitized = sanitized.remove(QChar('.')); + sanitized = sanitized.remove(QChar('(')); + sanitized = sanitized.remove(QChar(')')); if (mapping.contains(sanitized)) { sanitized = mapping[sanitized]; } diff --git a/src/platform/qt/VideoView.ui b/src/platform/qt/VideoView.ui index ff9dcaed1..4bdcefe75 100644 --- a/src/platform/qt/VideoView.ui +++ b/src/platform/qt/VideoView.ui @@ -266,12 +266,17 @@ - VP8 + h.264 (NVENC) - Xvid + HEVC + + + + + VP8 From fc67f3fbe4f805035b8d872fe0eb2fb4d7712f68 Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Tue, 28 Mar 2017 20:16:57 -0400 Subject: [PATCH 002/159] version.cmake: Correctly set the working directory. Fixes out-of-tree builds. --- version.cmake | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/version.cmake b/version.cmake index 12e0d7c01..2c132db64 100644 --- a/version.cmake +++ b/version.cmake @@ -10,11 +10,11 @@ set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator") find_program(GIT git) if(GIT AND NOT SKIP_GIT) - execute_process(COMMAND ${GIT} describe --always --abbrev=40 --dirty OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} describe --always --dirty OUTPUT_VARIABLE GIT_COMMIT_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} symbolic-ref --short HEAD OUTPUT_VARIABLE GIT_BRANCH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} rev-list HEAD --count OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process(COMMAND ${GIT} describe --tag --exact-match OUTPUT_VARIABLE GIT_TAG ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} describe --always --abbrev=40 --dirty WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} describe --always --dirty WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_COMMIT_SHORT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} symbolic-ref --short HEAD WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_BRANCH ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} rev-list HEAD --count WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_REV ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + execute_process(COMMAND ${GIT} describe --tag --exact-match WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" OUTPUT_VARIABLE GIT_TAG ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) endif() if(NOT GIT_REV) From 93dbae1e5fc50411fc229c799ce49d3b4ea97272 Mon Sep 17 00:00:00 2001 From: Jeremy Herbert Date: Mon, 27 Mar 2017 22:07:19 +1000 Subject: [PATCH 003/159] Fix GDB stub reporting of CPSR --- src/debugger/gdb-stub.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index b497c24d7..7c4111c86 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -321,13 +321,31 @@ static void _readGPRs(struct GDBStub* stub, const char* message) { UNUSED(message); int r; int i = 0; + + // General purpose registers for (r = 0; r < ARM_PC; ++r) { _int2hex32(cpu->gprs[r], &stub->outgoing[i]); i += 8; } + + // Program counter _int2hex32(cpu->gprs[ARM_PC] - (cpu->cpsr.t ? WORD_SIZE_THUMB : WORD_SIZE_ARM), &stub->outgoing[i]); i += 8; + // Floating point registers, unused on the GBA (8 of them, 24 bits each) + for (r = 0; r < 8 * 3; ++r) { + _int2hex32(0, &stub->outgoing[i]); + i += 8; + } + + // Floating point status, unused on the GBA (32 bits) + _int2hex32(0, &stub->outgoing[i]); + i += 8; + + // CPU status + _int2hex32(cpu->cpsr.packed, &stub->outgoing[i]); + i += 8; + stub->outgoing[i] = 0; _sendMessage(stub); } From 874ad93f4dba11722ccfb02aa7a3533777d8e03e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 29 Mar 2017 18:46:36 -0700 Subject: [PATCH 004/159] GBA Memory: Fix copy-on-write for ROM when executing in ROM --- src/gba/memory.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gba/memory.c b/src/gba/memory.c index 69cdc8b28..a6280254e 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -1552,6 +1552,9 @@ void _pristineCow(struct GBA* gba) { void* newRom = anonymousMemoryMap(SIZE_CART0); memcpy(newRom, gba->memory.rom, gba->memory.romSize); memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); + if (gba->cpu->memory.activeRegion == gba->memory.rom) { + gba->cpu->memory.activeRegion = newRom; + } if (gba->romVf) { #ifndef _3DS gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->memory.romSize); From 40f87518be2d84bcfa650d55568ee39c2ac6294d Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Wed, 29 Mar 2017 23:46:20 -0400 Subject: [PATCH 005/159] Qt: Don't link against SDLMAIN, Qt already handles that. (#551) --- src/platform/qt/CMakeLists.txt | 2 +- src/platform/qt/main.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index efaed3f05..11605a181 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -22,7 +22,7 @@ if(BUILD_SDL) if(SDL2_FOUND) link_directories(${SDL2_LIBDIR}) endif() - list(APPEND PLATFORM_LIBRARY ${SDL_LIBRARY} ${SDLMAIN_LIBRARY}) + list(APPEND PLATFORM_LIBRARY ${SDL_LIBRARY}) list(APPEND PLATFORM_SRC ${PLATFORM_SRC} ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-events.c ${CMAKE_SOURCE_DIR}/src/platform/sdl/sdl-audio.c) include_directories(${SDL_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/src/platform/sdl) endif() diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 29df3ea8d..631040d82 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -3,6 +3,10 @@ * 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/. */ + +// This must be defined before anything else is included. +#define SDL_MAIN_HANDLED + #include "GBAApp.h" #include "Window.h" @@ -22,6 +26,9 @@ Q_IMPORT_PLUGIN(QWindowsAudioPlugin); #endif int main(int argc, char* argv[]) { +#ifdef BUILD_SDL + SDL_SetMainReady(); +#endif QGBA::GBAApp application(argc, argv); QLocale locale = QLocale::system(); From 4449361f5c61f6c13829bb1f37a706f652592f03 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 1 Apr 2017 14:08:19 -0700 Subject: [PATCH 006/159] Qt: Fix setting audio and video sync without reloading the game --- src/platform/qt/GameController.cpp | 13 +++++++++++++ src/platform/qt/GameController.h | 2 ++ src/platform/qt/Window.cpp | 4 ++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 66d8eb81e..e26ea13fe 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -345,6 +345,8 @@ void GameController::setConfig(const mCoreConfig* config) { if (isLoaded()) { Interrupter interrupter(this); mCoreLoadForeignConfig(m_threadContext.core, config); + m_audioSync = m_threadContext.sync.audioWait; + m_videoSync = m_threadContext.sync.videoFrameWait; m_audioProcessor->setInput(&m_threadContext); } } @@ -1098,6 +1100,17 @@ void GameController::setSync(bool enable) { } m_sync = enable; } + +void GameController::setAudioSync(bool enable) { + m_audioSync = enable; + m_threadContext.sync.audioWait = enable; +} + +void GameController::setVideoSync(bool enable) { + m_videoSync = enable; + m_threadContext.sync.videoFrameWait = enable; +} + void GameController::setAVStream(mAVStream* stream) { Interrupter interrupter(this); m_stream = stream; diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 70f397c62..75b9a1137 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -147,6 +147,8 @@ public slots: void setTurbo(bool, bool forced = true); void setTurboSpeed(float ratio); void setSync(bool); + void setAudioSync(bool); + void setVideoSync(bool); void setAVStream(mAVStream*); void clearAVStream(); void reloadAudioDriver(); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 6e86d9ed3..ba5e2dd58 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1157,14 +1157,14 @@ void Window::setupMenu(QMenuBar* menubar) { ConfigOption* videoSync = m_config->addOption("videoSync"); videoSync->addBoolean(tr("Sync to &video"), emulationMenu); videoSync->connect([this](const QVariant& value) { - reloadConfig(); + m_controller->setVideoSync(value.toBool()); }, this); m_config->updateOption("videoSync"); ConfigOption* audioSync = m_config->addOption("audioSync"); audioSync->addBoolean(tr("Sync to &audio"), emulationMenu); audioSync->connect([this](const QVariant& value) { - reloadConfig(); + m_controller->setAudioSync(value.toBool()); }, this); m_config->updateOption("audioSync"); From 5d13a00cf751daa51aae95a0d064a3070ca0ee72 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 2 Apr 2017 00:37:33 -0700 Subject: [PATCH 007/159] GB Video: Fix frame end callbacks not getting called while screen is off --- src/gb/video.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/gb/video.c b/src/gb/video.c index 465380fa9..038729b20 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -128,14 +128,6 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); - - size_t c; - for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { - struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); - if (callbacks->videoFrameEnded) { - callbacks->videoFrameEnded(callbacks->context); - } - } } if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); @@ -231,6 +223,14 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat return; } + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); + if (callbacks->videoFrameEnded) { + callbacks->videoFrameEnded(callbacks->context); + } + } + GBFrameEnded(video->p); --video->frameskipCounter; if (video->frameskipCounter < 0) { @@ -247,7 +247,6 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat video->p->stream->postVideoFrame(video->p->stream, pixels, stride); } - size_t c; for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); if (callbacks->videoFrameStarted) { From 10fe4a743c1b01c496987b5cac2dfc479e89c8dc Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Thu, 30 Mar 2017 11:11:29 -0400 Subject: [PATCH 008/159] Qt: Show native directory separators in the GUI. The core still gets '/'s always (it chokes on '\'s), but the Qt interface always uses the native separators. In the process of doing this, also removed the custom FileDialog subclass and made everything use GBAApp::get*FileDialog instead. Also fixes #552, because I had to change that code anyway. --- src/platform/qt/GBAApp.cpp | 36 +++--------------------------- src/platform/qt/GBAApp.h | 13 ----------- src/platform/qt/GameController.cpp | 4 ++-- src/platform/qt/ObjView.cpp | 8 ++----- src/platform/qt/PaletteView.cpp | 13 ++++------- src/platform/qt/Window.cpp | 2 +- 6 files changed, 12 insertions(+), 64 deletions(-) diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index b54ea49ee..088d490cc 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -163,7 +163,7 @@ QString GBAApp::getOpenFileName(QWidget* owner, const QString& title, const QStr QString filename = QFileDialog::getOpenFileName(owner, title, m_configController.getOption("lastDirectory"), filter); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); + m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; } @@ -174,7 +174,7 @@ QString GBAApp::getSaveFileName(QWidget* owner, const QString& title, const QStr QString filename = QFileDialog::getSaveFileName(owner, title, m_configController.getOption("lastDirectory"), filter); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); + m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; } @@ -185,23 +185,11 @@ QString GBAApp::getOpenDirectoryName(QWidget* owner, const QString& title) { QString filename = QFileDialog::getExistingDirectory(owner, title, m_configController.getOption("lastDirectory")); continueAll(paused); if (!filename.isEmpty()) { - m_configController.setOption("lastDirectory", QFileInfo(filename).dir().path()); + m_configController.setOption("lastDirectory", QFileInfo(filename).dir().canonicalPath()); } return filename; } -QFileDialog* GBAApp::getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter) { - FileDialog* dialog = new FileDialog(this, owner, title, filter); - dialog->setAcceptMode(QFileDialog::AcceptOpen); - return dialog; -} - -QFileDialog* GBAApp::getSaveFileDialog(QWidget* owner, const QString& title, const QString& filter) { - FileDialog* dialog = new FileDialog(this, owner, title, filter); - dialog->setAcceptMode(QFileDialog::AcceptSave); - return dialog; -} - QString GBAApp::dataDir() { #ifdef DATADIR QString path = QString::fromUtf8(DATADIR); @@ -241,24 +229,6 @@ bool GBAApp::reloadGameDB() { } #endif -GBAApp::FileDialog::FileDialog(GBAApp* app, QWidget* parent, const QString& caption, const QString& filter) - : QFileDialog(parent, caption, app->m_configController.getOption("lastDirectory"), filter) - , m_app(app) -{ -} - -int GBAApp::FileDialog::exec() { - QList paused; - m_app->pauseAll(&paused); - bool didAccept = QFileDialog::exec() == QDialog::Accepted; - QStringList filenames = selectedFiles(); - if (!filenames.isEmpty()) { - m_app->m_configController.setOption("lastDirectory", QFileInfo(filenames[0]).dir().path()); - } - m_app->continueAll(paused); - return didAccept; -} - #ifdef USE_SQLITE3 GameDBParser::GameDBParser(NoIntroDB* db, QObject* parent) : QObject(parent) diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 8ade195e1..be0e620d4 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -55,9 +55,6 @@ public: QString getSaveFileName(QWidget* owner, const QString& title, const QString& filter = QString()); QString getOpenDirectoryName(QWidget* owner, const QString& title); - QFileDialog* getOpenFileDialog(QWidget* owner, const QString& title, const QString& filter = QString()); - QFileDialog* getSaveFileDialog(QWidget* owner, const QString& title, const QString& filter = QString()); - const NoIntroDB* gameDB() const { return m_db; } bool reloadGameDB(); @@ -65,16 +62,6 @@ protected: bool event(QEvent*); private: - class FileDialog : public QFileDialog { - public: - FileDialog(GBAApp* app, QWidget* parent = nullptr, const QString& caption = QString(), - const QString& filter = QString()); - virtual int exec() override; - - private: - GBAApp* m_app; - }; - Window* newWindowInternal(); void pauseAll(QList* paused); diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index e26ea13fe..bf81defd0 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -409,10 +409,10 @@ void GameController::loadGame(VFile* vf, const QString& path, const QString& bas closeGame(); QFileInfo info(base); if (info.isDir()) { - m_fname = base + QDir::separator() + path; + m_fname = QFileInfo(base + '/' + path).canonicalFilePath(); m_fsub = QString(); } else { - m_fname = base; + m_fname = info.canonicalFilePath(); m_fsub = path; } m_vf = vf; diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index 2749d7a50..dc74bebb6 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -244,12 +244,8 @@ void ObjView::updateTilesGB(bool force) { void ObjView::exportObj() { GameController::Interrupter interrupter(m_controller); - QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export sprite"), - tr("Portable Network Graphics (*.png)")); - if (!dialog->exec()) { - return; - } - QString filename = dialog->selectedFiles()[0]; + QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"), + tr("Portable Network Graphics (*.png)")); VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { LOG(QT, ERROR) << tr("Failed to open output PNG file: %1").arg(filename); diff --git a/src/platform/qt/PaletteView.cpp b/src/platform/qt/PaletteView.cpp index af55bdd4b..53a8aaa6e 100644 --- a/src/platform/qt/PaletteView.cpp +++ b/src/platform/qt/PaletteView.cpp @@ -134,21 +134,16 @@ void PaletteView::exportPalette(int start, int length) { } GameController::Interrupter interrupter(m_controller); - QFileDialog* dialog = GBAApp::app()->getSaveFileDialog(this, tr("Export palette"), - tr("Windows PAL (*.pal);;Adobe Color Table (*.act)")); - if (!dialog->exec()) { - return; - } - QString filename = dialog->selectedFiles()[0]; + QString filename = GBAApp::app()->getSaveFileName(this, tr("Export palette"), + tr("Windows PAL (*.pal);;Adobe Color Table (*.act)")); VFile* vf = VFileDevice::open(filename, O_WRONLY | O_CREAT | O_TRUNC); if (!vf) { LOG(QT, ERROR) << tr("Failed to open output palette file: %1").arg(filename); return; } - QString filter = dialog->selectedNameFilter(); - if (filter.contains("*.pal")) { + if (filename.endsWith(".pal", Qt::CaseInsensitive)) { exportPaletteRIFF(vf, length, &static_cast(m_controller->thread()->core->board)->video.palette[start]); - } else if (filter.contains("*.act")) { + } else if (filename.endsWith(".act", Qt::CaseInsensitive)) { exportPaletteACT(vf, length, &static_cast(m_controller->thread()->core->board)->video.palette[start]); } vf->close(vf); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index ba5e2dd58..76756e628 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1580,7 +1580,7 @@ void Window::updateMRU() { m_mruMenu->clear(); int i = 0; for (const QString& file : m_mruFiles) { - QAction* item = new QAction(file, m_mruMenu); + QAction* item = new QAction(QDir::toNativeSeparators(file).replace("&", "&&"), m_mruMenu); item->setShortcut(QString("Ctrl+%1").arg(i)); connect(item, &QAction::triggered, [this, file]() { m_controller->loadGame(file); }); m_mruMenu->addAction(item); From 35b5626053dea8a4c53c63b6da23831fea6bb649 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 2 Apr 2017 16:47:46 -0700 Subject: [PATCH 009/159] SDL: Fix game crash check --- CHANGES | 1 + src/platform/sdl/main.c | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 656e7b80f..190d6a6d7 100644 --- a/CHANGES +++ b/CHANGES @@ -31,6 +31,7 @@ Bugfixes: - Qt: Fix linking after some windows have been closed - GBA Video: Fix wrong palette on 256-color sprites in OBJWIN - Windows: Fix VDir.rewind + - SDL: Fix game crash check Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index a5d960b23..cf740f54f 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -194,6 +194,10 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { if (mCoreThreadStart(&thread)) { renderer->runloop(renderer, &thread); mSDLPauseAudio(&renderer->audio); + if (mCoreThreadHasCrashed(&thread)) { + didFail = true; + printf("The game crashed!\n"); + } mCoreThreadJoin(&thread); } else { didFail = true; @@ -204,11 +208,6 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { mSDLResumeScreensaver(&renderer->events); mSDLSetScreensaverSuspendable(&renderer->events, false); #endif - - if (mCoreThreadHasCrashed(&thread)) { - didFail = true; - printf("The game crashed!\n"); - } } renderer->core->unloadROM(renderer->core); return didFail; From 4a38f9b97998fb977f04d2b980830d3b697a6bd4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 3 Apr 2017 10:54:49 -0700 Subject: [PATCH 010/159] Qt: Simplify high-framerate fix (fixes #545) --- src/platform/qt/DisplayGL.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index da5781a3c..49d9ce5b1 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -318,15 +318,6 @@ void PainterGL::draw() { if (m_queue.isEmpty() || !mCoreThreadIsActive(m_context)) { return; } - if (!m_delayTimer.isValid()) { - m_delayTimer.start(); - } else if (m_delayTimer.elapsed() < 16) { - QMetaObject::invokeMethod(this, "draw", Qt::QueuedConnection); - QThread::usleep(500); - return; - } else { - m_delayTimer.restart(); - } if (mCoreSyncWaitFrameStart(&m_context->sync) || !m_queue.isEmpty()) { dequeue(); @@ -335,6 +326,14 @@ void PainterGL::draw() { performDraw(); m_painter.end(); m_backend->swap(m_backend); + if (!m_delayTimer.isValid()) { + m_delayTimer.start(); + } else { + while (m_delayTimer.elapsed() < 15) { + QThread::usleep(100); + } + m_delayTimer.restart(); + } } else { mCoreSyncWaitFrameEnd(&m_context->sync); } From 232e67f5291f2857dd92f9385e892adf14dab9af Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 3 Apr 2017 12:20:28 -0700 Subject: [PATCH 011/159] SDL: Fix race condition with audio thread when starting --- CHANGES | 1 + src/platform/sdl/main.c | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 190d6a6d7..a873c957d 100644 --- a/CHANGES +++ b/CHANGES @@ -32,6 +32,7 @@ Bugfixes: - GBA Video: Fix wrong palette on 256-color sprites in OBJWIN - Windows: Fix VDir.rewind - SDL: Fix game crash check + - SDL: Fix race condition with audio thread when starting Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index cf740f54f..24e0353c2 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -185,29 +185,31 @@ int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args) { renderer->audio.samples = renderer->core->opts.audioBuffers; renderer->audio.sampleRate = 44100; - bool didFail = !mSDLInitAudio(&renderer->audio, &thread); + bool didFail = !mCoreThreadStart(&thread); if (!didFail) { #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLSetScreensaverSuspendable(&renderer->events, renderer->core->opts.suspendScreensaver); mSDLSuspendScreensaver(&renderer->events); #endif - if (mCoreThreadStart(&thread)) { + if (mSDLInitAudio(&renderer->audio, &thread)) { renderer->runloop(renderer, &thread); mSDLPauseAudio(&renderer->audio); if (mCoreThreadHasCrashed(&thread)) { didFail = true; printf("The game crashed!\n"); } - mCoreThreadJoin(&thread); } else { didFail = true; - printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); + printf("Could not initialize audio.\n"); } - #if SDL_VERSION_ATLEAST(2, 0, 0) mSDLResumeScreensaver(&renderer->events); mSDLSetScreensaverSuspendable(&renderer->events, false); #endif + + mCoreThreadJoin(&thread); + } else { + printf("Could not run game. Are you sure the file exists and is a compatible game?\n"); } renderer->core->unloadROM(renderer->core); return didFail; From f73fd7f3da3e36b6a11fe5e15cafaca8c0fd733f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 3 Apr 2017 14:31:40 -0700 Subject: [PATCH 012/159] GB: Fix flickering when screen is strobed quickly --- CHANGES | 1 + include/mgba/internal/gb/video.h | 1 + src/gb/renderers/software.c | 25 +++++++--------------- src/gb/video.c | 36 +++++++++++++++++++------------- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/CHANGES b/CHANGES index a873c957d..cdb0889eb 100644 --- a/CHANGES +++ b/CHANGES @@ -33,6 +33,7 @@ Bugfixes: - Windows: Fix VDir.rewind - SDL: Fix game crash check - SDL: Fix race condition with audio thread when starting + - GB: Fix flickering when screen is strobed quickly Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index 67bf96405..a7b143077 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -119,6 +119,7 @@ struct GBVideo { int ocpIndex; bool ocpIncrement; + uint16_t dmgPalette[4]; uint16_t palette[64]; int32_t frameCounter; diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 61daa9f98..6c4740d76 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -24,26 +24,15 @@ static void GBVideoSoftwareRendererDrawBackground(struct GBVideoSoftwareRenderer static void GBVideoSoftwareRendererDrawObj(struct GBVideoSoftwareRenderer* renderer, struct GBObj* obj, int startX, int endX, int y); static void _clearScreen(struct GBVideoSoftwareRenderer* renderer) { - // TODO: Dynamic from dmgPalette -#ifdef COLOR_16_BIT -#ifdef COLOR_5_6_5 - color_t palette0 = 0xFFDF; -#else - color_t palette0 = 0x7FFF; -#endif -#else - color_t palette0 = 0xFFFFFF; -#endif - int y; for (y = 0; y < GB_VIDEO_VERTICAL_PIXELS; ++y) { color_t* row = &renderer->outputBuffer[renderer->outputBufferStride * y]; int x; for (x = 0; x < GB_VIDEO_HORIZONTAL_PIXELS; x += 4) { - row[x + 0] = palette0; - row[x + 1] = palette0; - row[x + 2] = palette0; - row[x + 3] = palette0; + row[x + 0] = renderer->palette[0]; + row[x + 1] = renderer->palette[0]; + row[x + 2] = renderer->palette[0]; + row[x + 3] = renderer->palette[0]; } } } @@ -85,9 +74,6 @@ static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; switch (address) { case REG_LCDC: - if (GBRegisterLCDCIsEnable(softwareRenderer->lcdc) && !GBRegisterLCDCIsEnable(value)) { - _clearScreen(softwareRenderer); - } softwareRenderer->lcdc = value; break; case REG_SCY: @@ -197,6 +183,9 @@ static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer) mappedMemoryFree(softwareRenderer->temporaryBuffer, GB_VIDEO_HORIZONTAL_PIXELS * GB_VIDEO_VERTICAL_PIXELS * 4); softwareRenderer->temporaryBuffer = 0; } + if (!GBRegisterLCDCIsEnable(softwareRenderer->lcdc)) { + _clearScreen(softwareRenderer); + } softwareRenderer->currentWy = 0; } diff --git a/src/gb/video.c b/src/gb/video.c index 038729b20..e1650acd9 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -61,6 +61,11 @@ void GBVideoInit(struct GBVideo* video) { video->frameEvent.name = "GB Video Frame"; video->frameEvent.callback = _updateFrameCount; video->frameEvent.priority = 9; + + video->dmgPalette[0] = 0x7FFF; + video->dmgPalette[1] = 0x56B5; + video->dmgPalette[2] = 0x294A; + video->dmgPalette[3] = 0x0000; } void GBVideoReset(struct GBVideo* video) { @@ -157,7 +162,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } - video->renderer->finishFrame(video->renderer); if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { video->p->memory.rotation->sample(video->p->memory.rotation); } @@ -234,6 +238,7 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat GBFrameEnded(video->p); --video->frameskipCounter; if (video->frameskipCounter < 0) { + video->renderer->finishFrame(video->renderer); mCoreSyncPostFrame(video->p->sync); video->frameskipCounter = video->frameskip; } @@ -318,6 +323,8 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { GBUpdateIRQs(video->p); } video->p->memory.io[REG_STAT] = video->stat; + video->renderer->writePalette(video->renderer, 0, video->palette[0]); + mTimingDeschedule(&video->p->timing, &video->frameEvent); } if (GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC]) && !GBRegisterLCDCIsEnable(value)) { @@ -326,6 +333,8 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { video->p->memory.io[REG_STAT] = video->stat; video->ly = 0; video->p->memory.io[REG_LY] = 0; + video->renderer->writePalette(video->renderer, 0, video->dmgPalette[0]); + mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); } @@ -351,34 +360,33 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) { } void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value) { - static const uint16_t dmgPalette[4] = { 0x7FFF, 0x56B5, 0x294A, 0x0000}; if (video->p->model < GB_MODEL_CGB) { switch (address) { case REG_BGP: - video->palette[0] = dmgPalette[value & 3]; - video->palette[1] = dmgPalette[(value >> 2) & 3]; - video->palette[2] = dmgPalette[(value >> 4) & 3]; - video->palette[3] = dmgPalette[(value >> 6) & 3]; + video->palette[0] = video->dmgPalette[value & 3]; + video->palette[1] = video->dmgPalette[(value >> 2) & 3]; + video->palette[2] = video->dmgPalette[(value >> 4) & 3]; + video->palette[3] = video->dmgPalette[(value >> 6) & 3]; video->renderer->writePalette(video->renderer, 0, video->palette[0]); video->renderer->writePalette(video->renderer, 1, video->palette[1]); video->renderer->writePalette(video->renderer, 2, video->palette[2]); video->renderer->writePalette(video->renderer, 3, video->palette[3]); break; case REG_OBP0: - video->palette[8 * 4 + 0] = dmgPalette[value & 3]; - video->palette[8 * 4 + 1] = dmgPalette[(value >> 2) & 3]; - video->palette[8 * 4 + 2] = dmgPalette[(value >> 4) & 3]; - video->palette[8 * 4 + 3] = dmgPalette[(value >> 6) & 3]; + video->palette[8 * 4 + 0] = video->dmgPalette[value & 3]; + video->palette[8 * 4 + 1] = video->dmgPalette[(value >> 2) & 3]; + video->palette[8 * 4 + 2] = video->dmgPalette[(value >> 4) & 3]; + video->palette[8 * 4 + 3] = video->dmgPalette[(value >> 6) & 3]; video->renderer->writePalette(video->renderer, 8 * 4 + 0, video->palette[8 * 4 + 0]); video->renderer->writePalette(video->renderer, 8 * 4 + 1, video->palette[8 * 4 + 1]); video->renderer->writePalette(video->renderer, 8 * 4 + 2, video->palette[8 * 4 + 2]); video->renderer->writePalette(video->renderer, 8 * 4 + 3, video->palette[8 * 4 + 3]); break; case REG_OBP1: - video->palette[9 * 4 + 0] = dmgPalette[value & 3]; - video->palette[9 * 4 + 1] = dmgPalette[(value >> 2) & 3]; - video->palette[9 * 4 + 2] = dmgPalette[(value >> 4) & 3]; - video->palette[9 * 4 + 3] = dmgPalette[(value >> 6) & 3]; + video->palette[9 * 4 + 0] = video->dmgPalette[value & 3]; + video->palette[9 * 4 + 1] = video->dmgPalette[(value >> 2) & 3]; + video->palette[9 * 4 + 2] = video->dmgPalette[(value >> 4) & 3]; + video->palette[9 * 4 + 3] = video->dmgPalette[(value >> 6) & 3]; video->renderer->writePalette(video->renderer, 9 * 4 + 0, video->palette[9 * 4 + 0]); video->renderer->writePalette(video->renderer, 9 * 4 + 1, video->palette[9 * 4 + 1]); video->renderer->writePalette(video->renderer, 9 * 4 + 2, video->palette[9 * 4 + 2]); From ba65740b159a2ce3bceecf1088f859f326d69b34 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 3 Apr 2017 14:32:21 -0700 Subject: [PATCH 013/159] GB: Allow setting DMG palette --- include/mgba/internal/gb/video.h | 2 ++ src/gb/core.c | 15 +++++++++++++++ src/gb/video.c | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index a7b143077..80899c723 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -139,6 +139,8 @@ void GBVideoWriteLYC(struct GBVideo* video, uint8_t value); void GBVideoWritePalette(struct GBVideo* video, uint16_t address, uint8_t value); void GBVideoSwitchBank(struct GBVideo* video, uint8_t value); +void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color); + struct GBSerializedState; void GBVideoSerialize(const struct GBVideo* video, struct GBSerializedState* state); void GBVideoDeserialize(struct GBVideo* video, const struct GBSerializedState* state); diff --git a/src/gb/core.c b/src/gb/core.c index f7f552914..8263696f1 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -105,6 +105,21 @@ static void _GBCoreLoadConfig(struct mCore* core, const struct mCoreConfig* conf gb->audio.masterVolume = core->opts.volume; } gb->video.frameskip = core->opts.frameskip; + + int color; + if (mCoreConfigGetIntValue(&core->config, "gb.pal[0]", &color)) { + GBVideoSetPalette(&gb->video, 0, color); + } + if (mCoreConfigGetIntValue(&core->config, "gb.pal[1]", &color)) { + GBVideoSetPalette(&gb->video, 1, color); + } + if (mCoreConfigGetIntValue(&core->config, "gb.pal[2]", &color)) { + GBVideoSetPalette(&gb->video, 2, color); + } + if (mCoreConfigGetIntValue(&core->config, "gb.pal[3]", &color)) { + GBVideoSetPalette(&gb->video, 3, color); + } + mCoreConfigCopyValue(&core->config, config, "gb.bios"); mCoreConfigCopyValue(&core->config, config, "gbc.bios"); diff --git a/src/gb/video.c b/src/gb/video.c index e1650acd9..cefe73cf7 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -439,6 +439,13 @@ void GBVideoSwitchBank(struct GBVideo* video, uint8_t value) { video->vramCurrentBank = value; } +void GBVideoSetPalette(struct GBVideo* video, unsigned index, uint16_t color) { + if (index >= 4) { + return; + } + video->dmgPalette[index] = color; +} + static void GBVideoDummyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { UNUSED(renderer); UNUSED(model); From 133424bd4f6b262dccb7da55b3f38a69c9da826b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 4 Apr 2017 02:00:10 -0700 Subject: [PATCH 014/159] FFmpeg: Fix overflow and general issues with audio encoding --- CHANGES | 1 + src/feature/ffmpeg/ffmpeg-encoder.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index cdb0889eb..f41c98a8c 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,7 @@ Bugfixes: - SDL: Fix game crash check - SDL: Fix race condition with audio thread when starting - GB: Fix flickering when screen is strobed quickly + - FFmpeg: Fix overflow and general issues with audio encoding Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index a902d6abf..4f0afef53 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -389,27 +389,27 @@ void _ffmpegPostAudioFrame(struct mAVStream* stream, int16_t left, int16_t right encoder->audioBuffer[encoder->currentAudioSample * 2] = left; encoder->audioBuffer[encoder->currentAudioSample * 2 + 1] = right; - ++encoder->currentAudioFrame; ++encoder->currentAudioSample; - if ((encoder->currentAudioSample * 4) < encoder->audioBufferSize) { + if (encoder->currentAudioSample * 4 < encoder->audioBufferSize) { return; } int channelSize = 2 * av_get_bytes_per_sample(encoder->audio->sample_fmt); - avresample_convert(encoder->resampleContext, - 0, 0, 0, - (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); + avresample_convert(encoder->resampleContext, 0, 0, 0, + (uint8_t**) &encoder->audioBuffer, 0, encoder->audioBufferSize / 4); + + encoder->currentAudioSample = 0; if (avresample_available(encoder->resampleContext) < encoder->audioFrame->nb_samples) { return; } #if LIBAVCODEC_VERSION_MAJOR >= 55 av_frame_make_writable(encoder->audioFrame); #endif - avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); + int samples = avresample_read(encoder->resampleContext, encoder->audioFrame->data, encoder->postaudioBufferSize / channelSize); - encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame - encoder->currentAudioSample, encoder->audio->time_base, encoder->audioStream->time_base); - encoder->currentAudioSample = 0; + encoder->audioFrame->pts = av_rescale_q(encoder->currentAudioFrame, encoder->audio->time_base, encoder->audioStream->time_base); + encoder->currentAudioFrame += samples; AVPacket packet; av_init_packet(&packet); From d9b645b2098cc4ccb5662d0705d3f3a3d37b7486 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 4 Apr 2017 02:01:06 -0700 Subject: [PATCH 015/159] FFmpeg: Return false if a file fails to open --- CHANGES | 1 + src/feature/ffmpeg/ffmpeg-encoder.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index f41c98a8c..ffdeb47c1 100644 --- a/CHANGES +++ b/CHANGES @@ -81,6 +81,7 @@ Misc: - Qt: Remove audio thread - Qt: Remove audio buffer sizing in AudioProcessorQt - Qt: Re-enable QtMultimedia on Windows + - FFmpeg: Return false if a file fails to open 0.5.2: (2016-12-31) Bugfixes: diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index 4f0afef53..41e5d0877 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -320,7 +320,9 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { avcodec_parameters_from_context(encoder->videoStream->codecpar, encoder->video); #endif - avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE); + if (avio_open(&encoder->context->pb, outfile, AVIO_FLAG_WRITE) < 0) { + return false; + } return avformat_write_header(encoder->context, 0) >= 0; } From 6a77d2aaeafd999f2b7258db70f377d44305ef08 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 4 Apr 2017 02:02:29 -0700 Subject: [PATCH 016/159] FFmpeg: Force MP4 files to YUV420P --- CHANGES | 1 + src/feature/ffmpeg/ffmpeg-encoder.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index ffdeb47c1..c7080c30d 100644 --- a/CHANGES +++ b/CHANGES @@ -82,6 +82,7 @@ Misc: - Qt: Remove audio buffer sizing in AudioProcessorQt - Qt: Re-enable QtMultimedia on Windows - FFmpeg: Return false if a file fails to open + - FFmpeg: Force MP4 files to YUV420P 0.5.2: (2016-12-31) Bugfixes: diff --git a/src/feature/ffmpeg/ffmpeg-encoder.c b/src/feature/ffmpeg/ffmpeg-encoder.c index 41e5d0877..a7a206788 100644 --- a/src/feature/ffmpeg/ffmpeg-encoder.c +++ b/src/feature/ffmpeg/ffmpeg-encoder.c @@ -304,6 +304,14 @@ bool FFmpegEncoderOpen(struct FFmpegEncoder* encoder, const char* outfile) { } av_opt_set(encoder->video->priv_data, "tune", "zerolatency", 0); } + + if (encoder->video->codec->id == AV_CODEC_ID_H264 && + (strcasecmp(encoder->containerFormat, "mp4") || + strcasecmp(encoder->containerFormat, "m4v") || + strcasecmp(encoder->containerFormat, "mov"))) { + // QuickTime and a few other things require YUV420 + encoder->video->pix_fmt = AV_PIX_FMT_YUV420P; + } avcodec_open2(encoder->video, vcodec, 0); #if LIBAVCODEC_VERSION_MAJOR >= 55 encoder->videoFrame = av_frame_alloc(); From 81f38fd76f8a0d1db377243e0e69f568118dde1f Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Sun, 2 Apr 2017 17:17:52 -0400 Subject: [PATCH 017/159] SDL: Fix build on MSVC. --- src/platform/sdl/CMakeLists.txt | 1 - src/platform/sdl/main.c | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/platform/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index f4b4c37b9..9a653fcc3 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -6,7 +6,6 @@ if (SDL_VERSION EQUAL "2") if (SDL2_FOUND) set(SDL_INCLUDE_DIR ${SDL2_INCLUDE_DIRS}) set(SDL_LIBRARY ${SDL2_LIBRARIES}) - set(SDLMAIN_LIBRARY "") link_directories(${SDL2_LIBDIR}) set(SDL_VERSION_DEBIAN "2-2.0-0") endif() diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 24e0353c2..33aaf0b01 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -27,7 +27,6 @@ #include #include -#include #define PORT "sdl" @@ -37,7 +36,7 @@ static void mSDLDeinit(struct mSDLRenderer* renderer); static int mSDLRun(struct mSDLRenderer* renderer, struct mArguments* args); int main(int argc, char** argv) { - struct mSDLRenderer renderer = {}; + struct mSDLRenderer renderer = {0}; struct mCoreOptions opts = { .useBios = true, From 2fbd31c51a1ef7c0976b3205e524d51d604d78d1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 5 Apr 2017 13:17:11 -0700 Subject: [PATCH 018/159] Qt: Make "Mute" able to be bound to a key (fixes #559) --- CHANGES | 1 + src/platform/qt/Window.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index c7080c30d..94459b0c1 100644 --- a/CHANGES +++ b/CHANGES @@ -83,6 +83,7 @@ Misc: - Qt: Re-enable QtMultimedia on Windows - FFmpeg: Return false if a file fails to open - FFmpeg: Force MP4 files to YUV420P + - Qt: Make "Mute" able to be bound to a key 0.5.2: (2016-12-31) Bugfixes: diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 76756e628..b3b15f909 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1266,11 +1266,12 @@ void Window::setupMenu(QMenuBar* menubar) { avMenu->addSeparator(); ConfigOption* mute = m_config->addOption("mute"); - mute->addBoolean(tr("Mute"), avMenu); + QAction* muteAction = mute->addBoolean(tr("Mute"), avMenu); mute->connect([this](const QVariant& value) { reloadConfig(); }, this); m_config->updateOption("mute"); + addControlledAction(avMenu, muteAction, "mute"); QMenu* target = avMenu->addMenu(tr("FPS target")); ConfigOption* fpsTargetOption = m_config->addOption("fpsTarget"); From ee74e4affa976010517ecee674a4f1cac2c9678e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 6 Apr 2017 09:27:33 -0700 Subject: [PATCH 019/159] Res: Update patron list --- res/patrons.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/res/patrons.txt b/res/patrons.txt index e10e13221..0cf2e6e28 100644 --- a/res/patrons.txt +++ b/res/patrons.txt @@ -1,7 +1,9 @@ -Trey Boyer -Christopher Cole +Jaime J. Denizard +Fog +Reilly Grant Philip Horton Jordan Jorgensen -Joshua Minor Rohit Nirmal +Rhys Powell +rootfather Yuri Kunde Schlesner From 19d0ca3b8b63dd25c17539357d09a47b44d2e6db Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 6 Apr 2017 11:54:13 -0700 Subject: [PATCH 020/159] Qt: Fix crash when changing audio settings after a game is closed --- CHANGES | 1 + src/platform/qt/GameController.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 94459b0c1..4607a27b8 100644 --- a/CHANGES +++ b/CHANGES @@ -35,6 +35,7 @@ Bugfixes: - SDL: Fix race condition with audio thread when starting - GB: Fix flickering when screen is strobed quickly - FFmpeg: Fix overflow and general issues with audio encoding + - Qt: Fix crash when changing audio settings after a game is closed Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index bf81defd0..6c73d06e8 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -654,6 +654,7 @@ void GameController::cleanGame() { delete[] m_frontBuffer; m_threadContext.core->deinit(m_threadContext.core); + m_threadContext.core = nullptr; m_gameOpen = false; } @@ -1222,7 +1223,7 @@ void GameController::updateKeys() { } void GameController::redoSamples(int samples) { - if (m_threadContext.core) { + if (m_gameOpen && m_threadContext.core) { m_threadContext.core->setAudioBufferSize(m_threadContext.core, samples); } m_audioProcessor->inputParametersChanged(); From 7784ecb2c5dd24939fcac34743f3de34dab66718 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 7 Apr 2017 22:21:41 -0700 Subject: [PATCH 021/159] Qt: Fix threading issues shutting down game audio --- src/platform/qt/GameController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 6c73d06e8..e64243682 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -174,7 +174,7 @@ GameController::GameController(QObject* parent) controller->m_patch = QString(); controller->clearOverride(); - controller->m_audioProcessor->pause(); + QMetaObject::invokeMethod(controller->m_audioProcessor, "pause"); QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(mCoreThread*, context)); QMetaObject::invokeMethod(controller, "cleanGame"); From f0ea421feada3a4960ed37e32cb7096103e275ab Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 8 Apr 2017 23:45:07 -0700 Subject: [PATCH 022/159] Qt: Fix Qt5Network removal --- src/platform/qt/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 11605a181..464b12652 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -141,7 +141,7 @@ set(GB_SRC qt5_wrap_ui(UI_SRC ${UI_FILES}) set(QT_LIBRARIES) -set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5,libqt5network5") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5") set(AUDIO_SRC) if(BUILD_SDL) @@ -242,7 +242,7 @@ endif() add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES}) set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}") -list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::OpenGL Qt5::Network) +list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::OpenGL) target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE) From 484618ca4c5f59f62a73018051ca6397472ea35e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 9 Apr 2017 19:13:13 -0700 Subject: [PATCH 023/159] Core: Restore sleep callback --- CHANGES | 1 + include/mgba/core/interface.h | 5 +---- include/mgba/core/thread.h | 1 + include/mgba/internal/gba/gba.h | 1 - src/core/thread.c | 11 +++++++++++ src/gba/gba.c | 11 ++++++----- src/platform/qt/GameController.cpp | 10 ++++------ 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 4607a27b8..25720a77c 100644 --- a/CHANGES +++ b/CHANGES @@ -85,6 +85,7 @@ Misc: - FFmpeg: Return false if a file fails to open - FFmpeg: Force MP4 files to YUV420P - Qt: Make "Mute" able to be bound to a key + - Core: Restore sleep callback 0.5.2: (2016-12-31) Bugfixes: diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 933e8d2d5..3a702c1f1 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -38,6 +38,7 @@ struct mCoreCallbacks { void (*videoFrameStarted)(void* context); void (*videoFrameEnded)(void* context); void (*coreCrashed)(void* context); + void (*sleep)(void* context); }; DECLARE_VECTOR(mCoreCallbacksList, struct mCoreCallbacks); @@ -53,10 +54,6 @@ struct mKeyCallback { uint16_t (*readKeys)(struct mKeyCallback*); }; -struct mStopCallback { - void (*stop)(struct mStopCallback*); -}; - struct mRotationSource { void (*sample)(struct mRotationSource*); diff --git a/include/mgba/core/thread.h b/include/mgba/core/thread.h index f586bfd3b..3c7a168e9 100644 --- a/include/mgba/core/thread.h +++ b/include/mgba/core/thread.h @@ -62,6 +62,7 @@ struct mCoreThread { ThreadCallback resetCallback; ThreadCallback cleanCallback; ThreadCallback frameCallback; + ThreadCallback sleepCallback; void* userData; void (*run)(struct mCoreThread*); diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index e5b1f8899..732a4aba6 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -99,7 +99,6 @@ struct GBA { struct mAVStream* stream; struct mKeyCallback* keyCallback; - struct mStopCallback* stopCallback; struct mCoreCallbacksList coreCallbacks; enum GBAIdleLoopOptimization idleOptimization; diff --git a/src/core/thread.c b/src/core/thread.c index 686330615..54f83f39f 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -120,6 +120,16 @@ void _crashed(void* context) { _changeState(thread, THREAD_CRASHED, true); } +void _sleep(void* context) { + struct mCoreThread* thread = context; + if (!thread) { + return; + } + if (thread->sleepCallback) { + thread->sleepCallback(thread); + } +} + static THREAD_ENTRY _mCoreThreadRun(void* context) { struct mCoreThread* threadContext = context; #ifdef USE_PTHREADS @@ -143,6 +153,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { .videoFrameStarted = _frameStarted, .videoFrameEnded = _frameEnded, .coreCrashed = _crashed, + .sleep = _sleep, .context = threadContext }; core->addCoreCallbacks(core, &callbacks); diff --git a/src/gba/gba.c b/src/gba/gba.c index c7059df12..25ff7670a 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -89,8 +89,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->stream = NULL; gba->keyCallback = NULL; - gba->stopCallback = NULL; - gba->stopCallback = NULL; mCoreCallbacksListInit(&gba->coreCallbacks, 0); gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); @@ -450,11 +448,14 @@ void GBAHalt(struct GBA* gba) { } void GBAStop(struct GBA* gba) { - if (!gba->stopCallback) { - return; + size_t c; + for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gba->coreCallbacks, c); + if (callbacks->sleep) { + callbacks->sleep(callbacks->context); + } } gba->cpu->nextEvent = gba->cpu->cycles; - gba->stopCallback->stop(gba->stopCallback); } void GBADebug(struct GBA* gba, uint16_t flags) { diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index e64243682..72f7b75b1 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -217,18 +217,16 @@ GameController::GameController(QObject* parent) } }; - // TODO: Put back - /*m_threadContext.stopCallback = [](mCoreThread* context) { + m_threadContext.sleepCallback = [](mCoreThread* context) { if (!context) { - return false; + return; } GameController* controller = static_cast(context->userData); if (!mCoreSaveState(context->core, 0, controller->m_saveStateFlags)) { - return false; + return; } QMetaObject::invokeMethod(controller, "closeGame"); - return true; - };*/ + }; m_threadContext.logger.d.log = [](mLogger* logger, int category, enum mLogLevel level, const char* format, va_list args) { mThreadLogger* logContext = reinterpret_cast(logger); From 5646ba7d606a5da1155be568946f80602a1c2e72 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 9 Apr 2017 19:18:29 -0700 Subject: [PATCH 024/159] Core: Fix Windows build --- src/core/thread.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/thread.c b/src/core/thread.c index 54f83f39f..652561b8c 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -120,7 +120,7 @@ void _crashed(void* context) { _changeState(thread, THREAD_CRASHED, true); } -void _sleep(void* context) { +void _coreSleep(void* context) { struct mCoreThread* thread = context; if (!thread) { return; @@ -153,7 +153,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { .videoFrameStarted = _frameStarted, .videoFrameEnded = _frameEnded, .coreCrashed = _crashed, - .sleep = _sleep, + .sleep = _coreSleep, .context = threadContext }; core->addCoreCallbacks(core, &callbacks); From e063e056623dcd1ca1ed516e154a68e306b93595 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 9 Apr 2017 19:37:43 -0700 Subject: [PATCH 025/159] Core: Unify peripheral attachment --- include/mgba/core/core.h | 3 +-- include/mgba/core/interface.h | 6 ++++++ src/gb/core.c | 21 ++++++++++++--------- src/gba/core.c | 21 ++++++++++++--------- src/platform/3ds/main.c | 2 +- src/platform/libretro/libretro.c | 2 +- src/platform/psp2/psp2-context.c | 4 ++-- src/platform/qt/GameController.cpp | 4 ++-- src/platform/sdl/main.c | 2 +- src/platform/wii/main.c | 4 ++-- 10 files changed, 40 insertions(+), 29 deletions(-) diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 82ee3d6d6..e382a7beb 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -110,8 +110,7 @@ struct mCore { void (*getGameTitle)(const struct mCore*, char* title); void (*getGameCode)(const struct mCore*, char* title); - void (*setRotation)(struct mCore*, struct mRotationSource*); - void (*setRumble)(struct mCore*, struct mRumble*); + void (*setPeripheral)(struct mCore*, int type, void*); uint32_t (*busRead8)(struct mCore*, uint32_t address); uint32_t (*busRead16)(struct mCore*, uint32_t address); diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index 3a702c1f1..acaa930cd 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -54,6 +54,12 @@ struct mKeyCallback { uint16_t (*readKeys)(struct mKeyCallback*); }; +enum mPeripheral { + mPERIPH_ROTATION = 1, + mPERIPH_RUMBLE, + mPERIPH_CUSTOM = 0x1000 +}; + struct mRotationSource { void (*sample)(struct mRotationSource*); diff --git a/src/gb/core.c b/src/gb/core.c index 8263696f1..c3eb1b22a 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -408,14 +408,18 @@ static void _GBCoreGetGameCode(const struct mCore* core, char* title) { GBGetGameCode(core->board, title); } -static void _GBCoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { +static void _GBCoreSetPeripheral(struct mCore* core, int type, void* periph) { struct GB* gb = core->board; - gb->memory.rotation = rotation; -} - -static void _GBCoreSetRumble(struct mCore* core, struct mRumble* rumble) { - struct GB* gb = core->board; - gb->memory.rumble = rumble; + switch (type) { + case mPERIPH_ROTATION: + gb->memory.rotation = periph; + break; + case mPERIPH_RUMBLE: + gb->memory.rumble = periph; + break; + default: + return; + } } static uint32_t _GBCoreBusRead8(struct mCore* core, uint32_t address) { @@ -620,8 +624,7 @@ struct mCore* GBCoreCreate(void) { core->frequency = _GBCoreFrequency; core->getGameTitle = _GBCoreGetGameTitle; core->getGameCode = _GBCoreGetGameCode; - core->setRotation = _GBCoreSetRotation; - core->setRumble = _GBCoreSetRumble; + core->setPeripheral = _GBCoreSetPeripheral; core->busRead8 = _GBCoreBusRead8; core->busRead16 = _GBCoreBusRead16; core->busRead32 = _GBCoreBusRead32; diff --git a/src/gba/core.c b/src/gba/core.c index 2ed68e435..2d653deca 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -405,14 +405,18 @@ static void _GBACoreGetGameCode(const struct mCore* core, char* title) { GBAGetGameCode(core->board, title); } -static void _GBACoreSetRotation(struct mCore* core, struct mRotationSource* rotation) { +static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) { struct GBA* gba = core->board; - gba->rotationSource = rotation; -} - -static void _GBACoreSetRumble(struct mCore* core, struct mRumble* rumble) { - struct GBA* gba = core->board; - gba->rumble = rumble; + switch (type) { + case mPERIPH_ROTATION: + gba->rotationSource = periph; + break; + case mPERIPH_RUMBLE: + gba->rumble = periph; + break; + default: + return; + } } static uint32_t _GBACoreBusRead8(struct mCore* core, uint32_t address) { @@ -619,8 +623,7 @@ struct mCore* GBACoreCreate(void) { core->frequency = _GBACoreFrequency; core->getGameTitle = _GBACoreGetGameTitle; core->getGameCode = _GBACoreGetGameCode; - core->setRotation = _GBACoreSetRotation; - core->setRumble = _GBACoreSetRumble; + core->setPeripheral = _GBACoreSetPeripheral; core->busRead8 = _GBACoreBusRead8; core->busRead16 = _GBACoreBusRead16; core->busRead32 = _GBACoreBusRead32; diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 0d17ba9fa..da0954a21 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -242,7 +242,7 @@ static void _setup(struct mGUIRunner* runner) { mCoreLoadForeignConfig(runner->core, &runner->config); } - runner->core->setRotation(runner->core, &rotation.d); + runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d); if (hasSound != NO_SOUND) { runner->core->setAVStream(runner->core, &stream); } diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 95f3f8e24..e640a91f0 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -398,7 +398,7 @@ bool retro_load_game(const struct retro_game_info* game) { blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 32768); blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 32768); - core->setRumble(core, &rumble); + core->setPeripheral(core, mPERIPH_RUMBLE, &rumble); savedata = anonymousMemoryMap(SIZE_CART_FLASH1M); struct VFile* save = VFileFromMemory(savedata, SIZE_CART_FLASH1M); diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index f58ee6a1d..b3aba3593 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -203,11 +203,11 @@ void mPSP2Setup(struct mGUIRunner* runner) { rotation.d.readTiltX = _readTiltX; rotation.d.readTiltY = _readTiltY; rotation.d.readGyroZ = _readGyroZ; - runner->core->setRotation(runner->core, &rotation.d); + runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation.d); rumble.d.setRumble = _setRumble; CircleBufferInit(&rumble.history, RUMBLE_PWM); - runner->core->setRumble(runner->core, &rumble.d); + runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble.d); frameLimiter = true; backdrop = vita2d_load_PNG_buffer(_binary_backdrop_png_start); diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 72f7b75b1..7d8484b84 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -87,8 +87,8 @@ GameController::GameController(QObject* parent) m_threadContext.startCallback = [](mCoreThread* context) { GameController* controller = static_cast(context->userData); - context->core->setRotation(context->core, controller->m_inputController->rotationSource()); - context->core->setRumble(context->core, controller->m_inputController->rumble()); + context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource()); + context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble()); #ifdef M_CORE_GBA GBA* gba = static_cast(context->core->board); diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 33aaf0b01..f5ec5fe53 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -130,7 +130,7 @@ int main(int argc, char** argv) { mSDLPlayerLoadConfig(&renderer.player, mCoreConfigGetInput(&renderer.core->config)); #if SDL_VERSION_ATLEAST(2, 0, 0) - renderer.core->setRumble(renderer.core, &renderer.player.rumble.d); + renderer.core->setPeripheral(renderer.core, mPERIPH_RUMBLE, &renderer.player.rumble.d); #endif int ret; diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 29096042e..f7561f018 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -641,8 +641,8 @@ void _guiPrepare(void) { } void _setup(struct mGUIRunner* runner) { - runner->core->setRotation(runner->core, &rotation); - runner->core->setRumble(runner->core, &rumble); + runner->core->setPeripheral(runner->core, mPERIPH_ROTATION, &rotation); + runner->core->setPeripheral(runner->core, mPERIPH_RUMBLE, &rumble); _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_A, GBA_KEY_A); _mapKey(&runner->core->inputMap, GCN1_INPUT, PAD_BUTTON_B, GBA_KEY_B); From 473ae29d821bd7b24c78d08e74ea4673eeb64a1b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 9 Apr 2017 19:47:19 -0700 Subject: [PATCH 026/159] GBA: Add GBA luminance peripheral --- include/mgba/gba/interface.h | 4 ++++ src/feature/gui/gui-runner.c | 2 +- src/gba/core.c | 3 +++ src/platform/libretro/libretro.c | 3 +-- src/platform/qt/GameController.cpp | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/mgba/gba/interface.h b/include/mgba/gba/interface.h index 3a5412645..90f5e0697 100644 --- a/include/mgba/gba/interface.h +++ b/include/mgba/gba/interface.h @@ -28,6 +28,10 @@ struct GBAVideoRenderer; extern const int GBA_LUX_LEVELS[10]; +enum { + mPERIPH_GBA_LUMINANCE = 0x1000 +}; + struct GBALuminanceSource { void (*sample)(struct GBALuminanceSource*); diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index c26cbe662..ad59ae893 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -298,7 +298,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { return; } if (runner->core->platform(runner->core) == PLATFORM_GBA) { - ((struct GBA*) runner->core->board)->luminanceSource = &runner->luminanceSource.d; + runner->core->setPeripheral(runner->core, mPERIPH_GBA_LUMINANCE, &runner->luminanceSource.d); } mLOG(GUI_RUNNER, DEBUG, "Loading config..."); mCoreLoadForeignConfig(runner->core, &runner->config); diff --git a/src/gba/core.c b/src/gba/core.c index 2d653deca..4f7963cea 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -414,6 +414,9 @@ static void _GBACoreSetPeripheral(struct mCore* core, int type, void* periph) { case mPERIPH_RUMBLE: gba->rumble = periph; break; + case mPERIPH_GBA_LUMINANCE: + gba->luminanceSource = periph; + break; default: return; } diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index e640a91f0..d19c5006f 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -409,8 +409,7 @@ bool retro_load_game(const struct retro_game_info* game) { #ifdef M_CORE_GBA if (core->platform(core) == PLATFORM_GBA) { - struct GBA* gba = core->board; - gba->luminanceSource = &lux; + core->setPeripheral(core, mPERIPH_GBA_LUMINANCE, &lux); const char* sysDir = 0; if (core->opts.useBios && environCallback(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &sysDir)) { diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 7d8484b84..486c22d6e 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -99,7 +99,7 @@ GameController::GameController(QObject* parent) switch (context->core->platform(context->core)) { #ifdef M_CORE_GBA case PLATFORM_GBA: - gba->luminanceSource = &controller->m_lux; + context->core->setPeripheral(context->core, mPERIPH_GBA_LUMINANCE, &controller->m_lux); gba->audio.psg.forceDisableCh[0] = !controller->m_audioChannels[0]; gba->audio.psg.forceDisableCh[1] = !controller->m_audioChannels[1]; gba->audio.psg.forceDisableCh[2] = !controller->m_audioChannels[2]; From 7bc6c579ce332efe0097c14ba4e40ccb26872f86 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 9 Apr 2017 23:34:23 -0700 Subject: [PATCH 027/159] Qt: Ensure audio processor is paused before closing thread --- src/platform/qt/GameController.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 486c22d6e..28efaa8ea 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -641,6 +641,8 @@ void GameController::cleanGame() { if (!m_gameOpen || mCoreThreadIsActive(&m_threadContext)) { return; } + + m_audioProcessor->pause(); mCoreThreadJoin(&m_threadContext); if (m_tileCache) { From f8eef19bd6b5357629e392955a149b7359a84c37 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Apr 2017 11:05:53 -0700 Subject: [PATCH 028/159] Qt: Remove debug messages --- src/platform/qt/ts.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platform/qt/ts.cmake b/src/platform/qt/ts.cmake index 3bb8c2e43..fdf58779a 100644 --- a/src/platform/qt/ts.cmake +++ b/src/platform/qt/ts.cmake @@ -1,8 +1,6 @@ file(GLOB TRANSLATION_FILES "${QM_BASE}/*.qm") file(WRITE ${TRANSLATION_QRC} "\n\t\n") -message(STATUS ${TRANSLATION_FILES}) foreach(TS ${TRANSLATION_FILES}) - message(STATUS ${TS}) get_filename_component(TS_BASE "${TS}" NAME) file(APPEND ${TRANSLATION_QRC} "\t\t${TS_BASE}\n") endforeach() From 06d6586e6fde8c9346558bfd2dc4c915211bb6da Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Apr 2017 13:30:42 -0700 Subject: [PATCH 029/159] Qt: Add .gb/.gbc files to the extension list in Info.plist --- CHANGES | 1 + res/info.plist.in | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGES b/CHANGES index 25720a77c..da5eea6cc 100644 --- a/CHANGES +++ b/CHANGES @@ -86,6 +86,7 @@ Misc: - FFmpeg: Force MP4 files to YUV420P - Qt: Make "Mute" able to be bound to a key - Core: Restore sleep callback + - Qt: Add .gb/.gbc files to the extension list in Info.plist 0.5.2: (2016-12-31) Bugfixes: diff --git a/res/info.plist.in b/res/info.plist.in index 3a32700f9..f37904dee 100644 --- a/res/info.plist.in +++ b/res/info.plist.in @@ -44,6 +44,17 @@ CFBundleTypeRole Viewer + + CFBundleTypeExtensions + + gb + gbc + + CFBundleTypeName + Game Boy ROM Image + CFBundleTypeRole + Viewer + From 7dc9eb4d5641d5980a2cc0eb8f1e459b4bfdfb4a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Apr 2017 21:29:24 -0700 Subject: [PATCH 030/159] Feature: Make -l option explicit --- CHANGES | 1 + src/feature/commandline.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index da5eea6cc..8739f213a 100644 --- a/CHANGES +++ b/CHANGES @@ -87,6 +87,7 @@ Misc: - Qt: Make "Mute" able to be bound to a key - Core: Restore sleep callback - Qt: Add .gb/.gbc files to the extension list in Info.plist + - Feature: Make -l option explicit 0.5.2: (2016-12-31) Bugfixes: diff --git a/src/feature/commandline.c b/src/feature/commandline.c index ae9f72c18..9e9158208 100644 --- a/src/feature/commandline.c +++ b/src/feature/commandline.c @@ -38,6 +38,7 @@ static const struct option _options[] = { { "gdb", no_argument, 0, 'g' }, #endif { "help", no_argument, 0, 'h' }, + { "log-level", required_argument, 0, 'l' }, { "movie", required_argument, 0, 'v' }, { "patch", required_argument, 0, 'p' }, { "version", no_argument, 0, '\0' }, @@ -217,6 +218,7 @@ void usage(const char* arg0, const char* extraOptions) { #ifdef USE_GDB_STUB puts(" -g, --gdb Start GDB session (default port 2345)"); #endif + puts(" -l, --log-level N Log level mask"); puts(" -v, --movie FILE Play back a movie of recorded input"); puts(" -p, --patch FILE Apply a specified patch file when running"); puts(" -s, --frameskip N Skip every N frames"); From bf716b947a113056d315ee75802e40c6a5df3d65 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Apr 2017 21:41:53 -0700 Subject: [PATCH 031/159] Util: Move StringList to string.c --- include/mgba-util/vector.h | 2 ++ include/mgba/core/cheats.h | 1 - src/core/cheats.c | 1 - src/util/string.c | 4 ++++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index 664221b7d..ae542d41b 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -92,6 +92,8 @@ CXX_GUARD_START dest->size = src->size; \ } \ +DECLARE_VECTOR(StringList, char*); + CXX_GUARD_END #endif diff --git a/include/mgba/core/cheats.h b/include/mgba/core/cheats.h index 7960ae58b..25ad3bbe1 100644 --- a/include/mgba/core/cheats.h +++ b/include/mgba/core/cheats.h @@ -48,7 +48,6 @@ struct mCheat { mLOG_DECLARE_CATEGORY(CHEATS); DECLARE_VECTOR(mCheatList, struct mCheat); -DECLARE_VECTOR(StringList, char*); struct mCheatDevice; struct mCheatSet { diff --git a/src/core/cheats.c b/src/core/cheats.c index 83bdde0b8..5085a3d67 100644 --- a/src/core/cheats.c +++ b/src/core/cheats.c @@ -17,7 +17,6 @@ mLOG_DEFINE_CATEGORY(CHEATS, "Cheats", "core.cheats"); DEFINE_VECTOR(mCheatList, struct mCheat); DEFINE_VECTOR(mCheatSets, struct mCheatSet*); -DEFINE_VECTOR(StringList, char*); static int32_t _readMem(struct mCore* core, uint32_t address, int width) { switch (width) { diff --git a/src/util/string.c b/src/util/string.c index a776bc16d..cef28e5cd 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -5,8 +5,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include + #include +DEFINE_VECTOR(StringList, char*); + #ifndef HAVE_STRNDUP char* strndup(const char* start, size_t len) { // This is suboptimal, but anything recent should have strndup From d16df6979010a1864a9f4ef518edfd4387d2e6d2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Apr 2017 22:20:37 -0700 Subject: [PATCH 032/159] Feature: Command line ability to override configuration values --- CHANGES | 1 + src/feature/commandline.c | 45 ++++++++++++++++++++++++++++++--------- src/feature/commandline.h | 4 ++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 8739f213a..e48baefc3 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Features: - Add option for whether rewinding restores save games - Qt: German translation (by Lothar Serra Mari) - Savestates now contain any RTC override data + - Command line ability to override configuration values Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/src/feature/commandline.c b/src/feature/commandline.c index 9e9158208..07da69760 100644 --- a/src/feature/commandline.c +++ b/src/feature/commandline.c @@ -48,10 +48,27 @@ static const struct option _options[] = { static bool _parseGraphicsArg(struct mSubParser* parser, int option, const char* arg); static void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config); +static void _tableInsert(struct Table* table, const char* pair) { + char* eq = strchr(pair, '='); + if (eq) { + char option[128] = ""; + strncpy(option, pair, eq - pair); + option[sizeof(option) - 1] = '\0'; + HashTableInsert(table, option, strdup(&eq[1])); + } else { + HashTableInsert(table, pair, strdup("1")); + } +} + +static void _tableApply(const char* key, void* value, void* user) { + struct mCoreConfig* config = user; + mCoreConfigSetOverrideValue(config, key, value); +} + bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct mSubParser* subparser) { int ch; char options[64] = - "b:c:hl:p:s:v:" + "b:c:C:hl:p:s:v:" #ifdef USE_EDITLINE "d" #endif @@ -62,6 +79,7 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct memset(args, 0, sizeof(*args)); args->frameskip = -1; args->logLevel = INT_MIN; + HashTableInit(&args->configOverrides, 0, free); if (subparser && subparser->extraOptions) { // TODO: modularize options to subparsers strncat(options, subparser->extraOptions, sizeof(options) - strlen(options) - 1); @@ -83,6 +101,9 @@ bool parseArguments(struct mArguments* args, int argc, char* const* argv, struct case 'c': args->cheatsFile = strdup(optarg); break; + case 'C': + _tableInsert(&args->configOverrides, optarg); + break; #ifdef USE_EDITLINE case 'd': if (args->debuggerType != DEBUGGER_NONE) { @@ -145,6 +166,7 @@ void applyArguments(const struct mArguments* args, struct mSubParser* subparser, if (args->bios) { mCoreConfigSetOverrideValue(config, "bios", args->bios); } + HashTableEnumerate(&args->configOverrides, _tableApply, config); if (subparser) { subparser->apply(subparser, config); } @@ -165,6 +187,8 @@ void freeArguments(struct mArguments* args) { free(args->bios); args->bios = 0; + + HashTableDeinit(&args->configOverrides); } void initParserForGraphics(struct mSubParser* parser, struct mGraphicsOpts* opts) { @@ -210,19 +234,20 @@ void _applyGraphicsArgs(struct mSubParser* parser, struct mCoreConfig* config) { void usage(const char* arg0, const char* extraOptions) { printf("usage: %s [option ...] file\n", arg0); puts("\nGeneric options:"); - puts(" -b, --bios FILE GBA BIOS file to use"); - puts(" -c, --cheats FILE Apply cheat codes from a file"); + puts(" -b, --bios FILE GBA BIOS file to use"); + puts(" -c, --cheats FILE Apply cheat codes from a file"); + puts(" -C, --config OPTION=VALUE Override config value"); #ifdef USE_EDITLINE - puts(" -d, --debug Use command-line debugger"); + puts(" -d, --debug Use command-line debugger"); #endif #ifdef USE_GDB_STUB - puts(" -g, --gdb Start GDB session (default port 2345)"); + puts(" -g, --gdb Start GDB session (default port 2345)"); #endif - puts(" -l, --log-level N Log level mask"); - puts(" -v, --movie FILE Play back a movie of recorded input"); - puts(" -p, --patch FILE Apply a specified patch file when running"); - puts(" -s, --frameskip N Skip every N frames"); - puts(" --version Print version and exit"); + puts(" -l, --log-level N Log level mask"); + puts(" -v, --movie FILE Play back a movie of recorded input"); + puts(" -p, --patch FILE Apply a specified patch file when running"); + puts(" -s, --frameskip N Skip every N frames"); + puts(" --version Print version and exit"); if (extraOptions) { puts(extraOptions); } diff --git a/src/feature/commandline.h b/src/feature/commandline.h index 322c8e87b..b47442c21 100644 --- a/src/feature/commandline.h +++ b/src/feature/commandline.h @@ -10,6 +10,8 @@ CXX_GUARD_START +#include + #include struct mArguments { @@ -21,6 +23,8 @@ struct mArguments { int logLevel; int frameskip; + struct Table configOverrides; + enum mDebuggerType debuggerType; bool debugAtStart; bool showHelp; From 9ced6724c15fa79a44a33a9aa6f50009f569157f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 12 Apr 2017 13:23:10 -0700 Subject: [PATCH 033/159] Qt: Attempt to fix #622 --- src/platform/qt/ConfigController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 520b6d9f2..862cb3d06 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -92,7 +92,7 @@ QString ConfigController::s_configDir; ConfigController::ConfigController(QObject* parent) : QObject(parent) - , m_opts() + , m_opts{} { QString fileName = configDir(); fileName.append(QDir::separator()); From 58c9bcf67aa4ab64bc28bc96741508570a47caa4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 12 Apr 2017 13:28:21 -0700 Subject: [PATCH 034/159] Core: Ability to enumerate and modify video and audio channels --- CHANGES | 1 + include/mgba/core/core.h | 5 ++ include/mgba/core/interface.h | 7 ++ src/gb/core.c | 48 ++++++++++++++ src/gba/core.c | 69 +++++++++++++++++++ src/platform/qt/GameController.cpp | 103 ++++++----------------------- src/platform/qt/GameController.h | 4 +- 7 files changed, 153 insertions(+), 84 deletions(-) diff --git a/CHANGES b/CHANGES index e48baefc3..55d3c0e9b 100644 --- a/CHANGES +++ b/CHANGES @@ -89,6 +89,7 @@ Misc: - Core: Restore sleep callback - Qt: Add .gb/.gbc files to the extension list in Info.plist - Feature: Make -l option explicit + - Core: Ability to enumerate and modify video and audio channels 0.5.2: (2016-12-31) Bugfixes: diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index e382a7beb..b83c4aca0 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -140,6 +140,11 @@ struct mCore { size_t (*savedataClone)(struct mCore*, void** sram); bool (*savedataRestore)(struct mCore*, const void* sram, size_t size, bool writeback); + + size_t (*listVideoLayers)(const struct mCore*, const struct mCoreChannelInfo**); + size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**); + void (*enableVideoLayer)(struct mCore*, size_t id, bool enable); + void (*enableAudioChannel)(struct mCore*, size_t id, bool enable); }; #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 diff --git a/include/mgba/core/interface.h b/include/mgba/core/interface.h index acaa930cd..7f0515a60 100644 --- a/include/mgba/core/interface.h +++ b/include/mgba/core/interface.h @@ -105,6 +105,13 @@ struct mRumble { void (*setRumble)(struct mRumble*, int enable); }; +struct mCoreChannelInfo { + size_t id; + const char* internalName; + const char* visibleName; + const char* visibleType; +}; + CXX_GUARD_END #endif diff --git a/src/gb/core.c b/src/gb/core.c index c3eb1b22a..48d178719 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -20,6 +20,19 @@ #include #include +const static struct mCoreChannelInfo _GBVideoLayers[] = { + { 0, "bg", "Background", NULL }, + { 1, "obj", "Objects", NULL }, + { 2, "win", "Window", NULL }, +}; + +const static struct mCoreChannelInfo _GBAudioChannels[] = { + { 0, "ch0", "Channel 0", "Square/Sweep" }, + { 1, "ch1", "Channel 1", "Square" }, + { 2, "ch2", "Channel 2", "PCM" }, + { 3, "ch3", "Channel 3", "Noise" }, +}; + struct GBCore { struct mCore d; struct GBVideoSoftwareRenderer renderer; @@ -579,6 +592,37 @@ static bool _GBCoreSavedataRestore(struct mCore* core, const void* sram, size_t return true; } +static size_t _GBCoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) { + UNUSED(core); + *info = _GBVideoLayers; + return sizeof(_GBVideoLayers) / sizeof(*_GBVideoLayers); +} + +static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) { + UNUSED(core); + *info = _GBAudioChannels; + return sizeof(_GBAudioChannels) / sizeof(*_GBAudioChannels); +} + +static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) { + struct GB* gb = core->board; + // TODO +} + +static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) { + struct GB* gb = core->board; + switch (id) { + case 0: + case 1: + case 2: + case 3: + gb->audio.forceDisableCh[id] = !enable; + break; + default: + break; + } +} + struct mCore* GBCoreCreate(void) { struct GBCore* gbcore = malloc(sizeof(*gbcore)); struct mCore* core = &gbcore->d; @@ -647,5 +691,9 @@ struct mCore* GBCoreCreate(void) { core->cheatDevice = _GBCoreCheatDevice; core->savedataClone = _GBCoreSavedataClone; core->savedataRestore = _GBCoreSavedataRestore; + core->listVideoLayers = _GBCoreListVideoLayers; + core->listAudioChannels = _GBCoreListAudioChannels; + core->enableVideoLayer = _GBCoreEnableVideoLayer; + core->enableAudioChannel = _GBCoreEnableAudioChannel; return core; } diff --git a/src/gba/core.c b/src/gba/core.c index 4f7963cea..8636489f2 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -22,6 +22,23 @@ #include #include +const static struct mCoreChannelInfo _GBAVideoLayers[] = { + { 0, "bg0", "Background 0", NULL }, + { 1, "bg1", "Background 1", NULL }, + { 2, "bg2", "Background 2", NULL }, + { 3, "bg3", "Background 3", NULL }, + { 4, "obj", "Objects", NULL }, +}; + +const static struct mCoreChannelInfo _GBAAudioChannels[] = { + { 0, "ch0", "PSG Channel 0", "Square/Sweep" }, + { 1, "ch1", "PSG Channel 1", "Square" }, + { 2, "ch2", "PSG Channel 2", "PCM" }, + { 3, "ch3", "PSG Channel 3", "Noise" }, + { 4, "chA", "FIFO Channel A", NULL }, + { 5, "chB", "FIFO Channel B", NULL }, +}; + struct GBACore { struct mCore d; struct GBAVideoSoftwareRenderer renderer; @@ -581,6 +598,54 @@ static bool _GBACoreSavedataRestore(struct mCore* core, const void* sram, size_t return success; } +static size_t _GBACoreListVideoLayers(const struct mCore* core, const struct mCoreChannelInfo** info) { + UNUSED(core); + *info = _GBAVideoLayers; + return sizeof(_GBAVideoLayers) / sizeof(*_GBAVideoLayers); +} + +static size_t _GBACoreListAudioChannels(const struct mCore* core, const struct mCoreChannelInfo** info) { + UNUSED(core); + *info = _GBAAudioChannels; + return sizeof(_GBAAudioChannels) / sizeof(*_GBAAudioChannels); +} + +static void _GBACoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) { + struct GBA* gba = core->board; + switch (id) { + case 0: + case 1: + case 2: + case 3: + gba->video.renderer->disableBG[id] = !enable; + break; + case 4: + gba->video.renderer->disableOBJ = !enable; + break; + default: + break; + } +} + +static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) { + struct GBA* gba = core->board; + switch (id) { + case 0: + case 1: + case 2: + case 3: + gba->audio.psg.forceDisableCh[id] = !enable; + break; + case 4: + gba->audio.forceDisableChA = !enable; + case 5: + gba->audio.forceDisableChB = !enable; + break; + default: + break; + } +} + struct mCore* GBACoreCreate(void) { struct GBACore* gbacore = malloc(sizeof(*gbacore)); struct mCore* core = &gbacore->d; @@ -649,5 +714,9 @@ struct mCore* GBACoreCreate(void) { core->cheatDevice = _GBACoreCheatDevice; core->savedataClone = _GBACoreSavedataClone; core->savedataRestore = _GBACoreSavedataRestore; + core->listVideoLayers = _GBACoreListVideoLayers; + core->listAudioChannels = _GBACoreListAudioChannels; + core->enableVideoLayer = _GBACoreEnableVideoLayer; + core->enableAudioChannel = _GBACoreEnableAudioChannel; return core; } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 28efaa8ea..0eb922b0e 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -57,8 +57,8 @@ GameController::GameController(QObject* parent) , m_turboForced(false) , m_turboSpeed(-1) , m_wasPaused(false) - , m_audioChannels{ true, true, true, true, true, true } - , m_videoLayers{ true, true, true, true, true } + , m_audioChannels() + , m_videoLayers() , m_autofire{} , m_autofireStatus{} , m_inputController(nullptr) @@ -90,35 +90,17 @@ GameController::GameController(QObject* parent) context->core->setPeripheral(context->core, mPERIPH_ROTATION, controller->m_inputController->rotationSource()); context->core->setPeripheral(context->core, mPERIPH_RUMBLE, controller->m_inputController->rumble()); -#ifdef M_CORE_GBA - GBA* gba = static_cast(context->core->board); -#endif -#ifdef M_CORE_GB - GB* gb = static_cast(context->core->board); -#endif + for (size_t i = 0; i < controller->m_audioChannels.size(); ++i) { + context->core->enableAudioChannel(context->core, i, controller->m_audioChannels[i]); + } + for (size_t i = 0; i < controller->m_videoLayers.size(); ++i) { + context->core->enableVideoLayer(context->core, i, controller->m_videoLayers[i]); + } + switch (context->core->platform(context->core)) { #ifdef M_CORE_GBA case PLATFORM_GBA: context->core->setPeripheral(context->core, mPERIPH_GBA_LUMINANCE, &controller->m_lux); - gba->audio.psg.forceDisableCh[0] = !controller->m_audioChannels[0]; - gba->audio.psg.forceDisableCh[1] = !controller->m_audioChannels[1]; - gba->audio.psg.forceDisableCh[2] = !controller->m_audioChannels[2]; - gba->audio.psg.forceDisableCh[3] = !controller->m_audioChannels[3]; - gba->audio.forceDisableChA = !controller->m_audioChannels[4]; - gba->audio.forceDisableChB = !controller->m_audioChannels[5]; - gba->video.renderer->disableBG[0] = !controller->m_videoLayers[0]; - gba->video.renderer->disableBG[1] = !controller->m_videoLayers[1]; - gba->video.renderer->disableBG[2] = !controller->m_videoLayers[2]; - gba->video.renderer->disableBG[3] = !controller->m_videoLayers[3]; - gba->video.renderer->disableOBJ = !controller->m_videoLayers[4]; - break; -#endif -#ifdef M_CORE_GB - case PLATFORM_GB: - gb->audio.forceDisableCh[0] = !controller->m_audioChannels[0]; - gb->audio.forceDisableCh[1] = !controller->m_audioChannels[1]; - gb->audio.forceDisableCh[2] = !controller->m_audioChannels[2]; - gb->audio.forceDisableCh[3] = !controller->m_audioChannels[3]; break; #endif default: @@ -866,47 +848,13 @@ void GameController::setAudioChannelEnabled(int channel, bool enable) { if (channel > 5 || channel < 0) { return; } -#ifdef M_CORE_GBA - GBA* gba = static_cast(m_threadContext.core->board); -#endif -#ifdef M_CORE_GB - GB* gb = static_cast(m_threadContext.core->board); -#endif + m_audioChannels.reserve(channel + 1); + while (m_audioChannels.size() <= channel) { + m_audioChannels.append(true); + } m_audioChannels[channel] = enable; if (isLoaded()) { - switch (channel) { - case 0: - case 1: - case 2: - case 3: - switch (m_threadContext.core->platform(m_threadContext.core)) { -#ifdef M_CORE_GBA - case PLATFORM_GBA: - gba->audio.psg.forceDisableCh[channel] = !enable; - break; -#endif -#ifdef M_CORE_GB - case PLATFORM_GB: - gb->audio.forceDisableCh[channel] = !enable; - break; -#endif - default: - break; - } - break; -#ifdef M_CORE_GBA - case 4: - if (m_threadContext.core->platform(m_threadContext.core) == PLATFORM_GBA) { - gba->audio.forceDisableChA = !enable; - } - break; - case 5: - if (m_threadContext.core->platform(m_threadContext.core) == PLATFORM_GBA) { - gba->audio.forceDisableChB = !enable; - } - break; -#endif - } + m_threadContext.core->enableAudioChannel(m_threadContext.core, channel, enable); } } @@ -925,23 +873,14 @@ void GameController::setVideoLayerEnabled(int layer, bool enable) { if (layer > 4 || layer < 0) { return; } - m_videoLayers[layer] = enable; -#ifdef M_CORE_GBA - if (isLoaded() && m_threadContext.core->platform(m_threadContext.core) == PLATFORM_GBA) { - GBA* gba = static_cast(m_threadContext.core->board); - switch (layer) { - case 0: - case 1: - case 2: - case 3: - gba->video.renderer->disableBG[layer] = !enable; - break; - case 4: - gba->video.renderer->disableOBJ = !enable; - break; - } + m_videoLayers.reserve(layer + 1); + while (m_videoLayers.size() <= layer) { + m_videoLayers.append(true); + } + m_videoLayers[layer] = enable; + if (isLoaded()) { + m_threadContext.core->enableVideoLayer(m_threadContext.core, layer, enable); } -#endif } void GameController::setFPSTarget(float fps) { diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 75b9a1137..d8137908d 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -222,8 +222,8 @@ private: std::shared_ptr m_tileCache; - bool m_audioChannels[6]; - bool m_videoLayers[5]; + QList m_audioChannels; + QList m_videoLayers; bool m_autofire[GBA_KEY_MAX]; int m_autofireStatus[GBA_KEY_MAX]; From 959f66a1a0faff37607a53c4da1ea7b508d6a43a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 13 Apr 2017 00:28:28 -0700 Subject: [PATCH 035/159] Core: Add option to allow preloading the entire ROM before running --- CHANGES | 1 + include/mgba/core/core.h | 3 +++ src/core/core.c | 29 +++++++++++++++++++++++++++++ src/platform/qt/GameController.cpp | 22 ++++++++++++++++++---- src/platform/qt/GameController.h | 3 +++ src/platform/qt/SettingsView.cpp | 2 ++ src/platform/qt/SettingsView.ui | 27 +++++++++++++++++---------- src/platform/qt/Window.cpp | 6 ++++++ 8 files changed, 79 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 55d3c0e9b..4b31d0426 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Features: - Qt: German translation (by Lothar Serra Mari) - Savestates now contain any RTC override data - Command line ability to override configuration values + - Add option to allow preloading the entire ROM before running Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index b83c4aca0..2c226c577 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -151,6 +151,9 @@ struct mCore { struct mCore* mCoreFind(const char* path); bool mCoreLoadFile(struct mCore* core, const char* path); +bool mCorePreloadVF(struct mCore* core, struct VFile* vf); +bool mCorePreloadFile(struct mCore* core, const char* path); + bool mCoreAutoloadSave(struct mCore* core); bool mCoreAutoloadPatch(struct mCore* core); diff --git a/src/core/core.c b/src/core/core.c index 7eacdcfc8..439f89c3e 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -115,6 +115,35 @@ bool mCoreLoadFile(struct mCore* core, const char* path) { return ret; } +bool mCorePreloadVF(struct mCore* core, struct VFile* vf) { + struct VFile* vfm = VFileMemChunk(NULL, vf->size(vf)); + uint8_t buffer[2048]; + ssize_t read; + vf->seek(vf, 0, SEEK_SET); + while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) { + vfm->write(vfm, buffer, read); + } + vf->close(vf); + bool ret = core->loadROM(core, vfm); + if (!ret) { + vfm->close(vfm); + } + return ret; +} + +bool mCorePreloadFile(struct mCore* core, const char* path) { + struct VFile* rom = mDirectorySetOpenPath(&core->dirs, path, core->isROM); + if (!rom) { + return false; + } + + bool ret = mCorePreloadVF(core, rom); + if (!ret) { + rom->close(rom); + } + return ret; +} + bool mCoreAutoloadSave(struct mCore* core) { return core->loadSave(core, mDirectorySetOpenSuffix(&core->dirs, core->dirs.save, ".sav", O_CREAT | O_RDWR)); } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 0eb922b0e..53ecf3ed9 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -69,6 +69,7 @@ GameController::GameController(QObject* parent) , m_backupSaveState(nullptr) , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) + , m_preload(false) , m_override(nullptr) { #ifdef M_CORE_GBA @@ -458,11 +459,20 @@ void GameController::openGame(bool biosOnly) { QByteArray bytes; if (!biosOnly) { bytes = m_fname.toUtf8(); - if (m_vf) { - m_threadContext.core->loadROM(m_threadContext.core, m_vf); + if (m_preload) { + if (m_vf) { + mCorePreloadVF(m_threadContext.core, m_vf); + } else { + mCorePreloadFile(m_threadContext.core, bytes.constData()); + mDirectorySetDetachBase(&m_threadContext.core->dirs); + } } else { - mCoreLoadFile(m_threadContext.core, bytes.constData()); - mDirectorySetDetachBase(&m_threadContext.core->dirs); + if (m_vf) { + m_threadContext.core->loadROM(m_threadContext.core, m_vf); + } else { + mCoreLoadFile(m_threadContext.core, bytes.constData()); + mDirectorySetDetachBase(&m_threadContext.core->dirs); + } } } else { bytes = m_bios.toUtf8(); @@ -1107,6 +1117,10 @@ void GameController::setLoadStateExtdata(int flags) { m_loadStateFlags = flags; } +void GameController::setPreload(bool preload) { + m_preload = preload; +} + void GameController::setLuminanceValue(uint8_t value) { m_luxValue = value; value = std::max(value - 0x16, 0); diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index d8137908d..acd09f258 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -154,6 +154,7 @@ public slots: void reloadAudioDriver(); void setSaveStateExtdata(int flags); void setLoadStateExtdata(int flags); + void setPreload(bool); #ifdef USE_PNG void screenshot(); @@ -234,6 +235,8 @@ private: int m_saveStateFlags; int m_loadStateFlags; + bool m_preload; + InputController* m_inputController; MultiplayerController* m_multiplayer; diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 15d13b4a1..510885501 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -204,6 +204,7 @@ void SettingsView::updateConfig() { saveSetting("screenshotPath", m_ui.screenshotPath); saveSetting("patchPath", m_ui.patchPath); saveSetting("showLibrary", m_ui.showLibrary); + saveSetting("preload", m_ui.preload); if (m_ui.fastForwardUnbounded->isChecked()) { saveSetting("fastForwardRatio", "-1"); @@ -283,6 +284,7 @@ void SettingsView::reloadConfig() { loadSetting("screenshotPath", m_ui.screenshotPath); loadSetting("patchPath", m_ui.patchPath); loadSetting("showLibrary", m_ui.showLibrary); + loadSetting("preload", m_ui.preload); double fastForwardRatio = loadSetting("fastForwardRatio").toDouble(); if (fastForwardRatio <= 0) { diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index ad59bc5bd..967c17cb1 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -568,21 +568,21 @@ - + Qt::Horizontal - + Savestate extra data: - + Screenshot @@ -592,7 +592,7 @@ - + Save data @@ -602,7 +602,7 @@ - + Cheat codes @@ -612,14 +612,14 @@ - + Load extra data: - + Screenshot @@ -629,21 +629,21 @@ - + Save data - + Cheat codes - + Qt::Horizontal @@ -660,6 +660,13 @@ + + + + Preload entire ROM into memory + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index b3b15f909..7be87c484 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1476,6 +1476,12 @@ void Window::setupMenu(QMenuBar* menubar) { m_controller->setLoadStateExtdata(value.toInt()); }, this); + ConfigOption* preload = m_config->addOption("preload"); + preload->connect([this](const QVariant& value) { + m_controller->setPreload(value.toBool()); + }, this); + m_config->updateOption("preload"); + QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu); connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen())); exitFullScreen->setShortcut(QKeySequence("Esc")); From 0c917138ea39b94d17c092b92a43986ac9c7f790 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 13 Apr 2017 00:28:41 -0700 Subject: [PATCH 036/159] Qt: Code cleanup --- src/platform/qt/Window.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 7be87c484..a679054cf 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -302,19 +302,6 @@ void Window::reloadConfig() { m_log.setLevels(opts->logLevel); - QString saveStateExtdata = m_config->getOption("saveStateExtdata"); - bool ok; - int flags = saveStateExtdata.toInt(&ok); - if (ok) { - m_controller->setSaveStateExtdata(flags); - } - - QString loadStateExtdata = m_config->getOption("loadStateExtdata"); - flags = loadStateExtdata.toInt(&ok); - if (ok) { - m_controller->setLoadStateExtdata(flags); - } - m_controller->setConfig(m_config->config()); m_display->lockAspectRatio(opts->lockAspectRatio); m_display->filter(opts->resampleVideo); @@ -1470,11 +1457,13 @@ void Window::setupMenu(QMenuBar* menubar) { saveStateExtdata->connect([this](const QVariant& value) { m_controller->setSaveStateExtdata(value.toInt()); }, this); + m_config->updateOption("saveStateExtdata"); ConfigOption* loadStateExtdata = m_config->addOption("loadStateExtdata"); loadStateExtdata->connect([this](const QVariant& value) { m_controller->setLoadStateExtdata(value.toInt()); }, this); + m_config->updateOption("loadStateExtdata"); ConfigOption* preload = m_config->addOption("preload"); preload->connect([this](const QVariant& value) { From b486223532adbc5bb55d9306cf31709d226e4c79 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Apr 2017 21:25:07 -0700 Subject: [PATCH 037/159] GB, GBA: Make ensure clock end frame is always the same --- src/gb/audio.c | 4 ++-- src/gba/audio.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gb/audio.c b/src/gb/audio.c index 7bed5d621..8fbca6d9b 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -640,8 +640,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { audio->lastRight = sampleRight; audio->clock += audio->sampleInterval; if (audio->clock >= CLOCKS_PER_BLIP_FRAME) { - blip_end_frame(audio->left, audio->clock); - blip_end_frame(audio->right, audio->clock); + blip_end_frame(audio->left, CLOCKS_PER_BLIP_FRAME); + blip_end_frame(audio->right, CLOCKS_PER_BLIP_FRAME); audio->clock -= CLOCKS_PER_BLIP_FRAME; } } diff --git a/src/gba/audio.c b/src/gba/audio.c index 2ceba8a5a..be736e78b 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -297,8 +297,8 @@ static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) { audio->lastRight = sampleRight; audio->clock += audio->sampleInterval; if (audio->clock >= CLOCKS_PER_FRAME) { - blip_end_frame(audio->psg.left, audio->clock); - blip_end_frame(audio->psg.right, audio->clock); + blip_end_frame(audio->psg.left, CLOCKS_PER_FRAME); + blip_end_frame(audio->psg.right, CLOCKS_PER_FRAME); audio->clock -= CLOCKS_PER_FRAME; } } From b65992a1b8dbc6b8309cce8030fe734c4755d71a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Apr 2017 01:12:23 -0700 Subject: [PATCH 038/159] PSP2: Update for new toolchain --- include/mgba-util/common.h | 5 +++++ src/platform/psp2/main.c | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/mgba-util/common.h b/include/mgba-util/common.h index 4f36c0a17..63e3dd30d 100644 --- a/include/mgba-util/common.h +++ b/include/mgba-util/common.h @@ -56,6 +56,11 @@ typedef intptr_t ssize_t; #include #endif +#ifdef PSP2 +// For PATH_MAX on modern toolchains +#include +#endif + #ifndef SSIZE_MAX #define SSIZE_MAX ((ssize_t) (SIZE_MAX >> 1)) #endif diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index 95dabc13c..4cd041cfe 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include From f116713f2eb6303f8efef8370b033efd3c97e23a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 17:26:30 -0700 Subject: [PATCH 039/159] Python: Fix build in some cases --- src/platform/python/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/platform/python/CMakeLists.txt b/src/platform/python/CMakeLists.txt index adda679bd..7b7605e7d 100644 --- a/src/platform/python/CMakeLists.txt +++ b/src/platform/python/CMakeLists.txt @@ -2,8 +2,13 @@ find_program(PYTHON python) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in ${CMAKE_CURRENT_BINARY_DIR}/setup.py) +get_property(INCLUDE_DIRECTORIES DIRECTORY PROPERTY INCLUDE_DIRECTORIES) +set(INCLUDE_FLAGS) +foreach(DIR IN LISTS INCLUDE_DIRECTORIES) + list(APPEND INCLUDE_FLAGS "-I${DIR}") +endforeach() add_custom_command(OUTPUT build/lib/${BINARY_NAME}/__init__.py - COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR} + COMMAND BINDIR=${CMAKE_CURRENT_BINARY_DIR}/.. CPPFLAGS="${INCLUDE_FLAGS}" ${PYTHON} ${CMAKE_CURRENT_BINARY_DIR}/setup.py build --build-base ${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${BINARY_NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/setup.py From 997ba3572b0fd15ec5f0f5575720dec77c1904c8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 20 Apr 2017 01:41:43 -0700 Subject: [PATCH 040/159] PSP2: Minor touchups --- src/platform/psp2/main.c | 1 + src/platform/psp2/psp2-context.c | 16 +++++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index 4cd041cfe..d41ae49c6 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -163,6 +163,7 @@ int main() { mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_RIGHT, GUI_INPUT_RIGHT); mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE); + scePowerSetArmClockFrequency(444); mGUIRunloop(&runner); vita2d_fini(); diff --git a/src/platform/psp2/psp2-context.c b/src/platform/psp2/psp2-context.c index b3aba3593..b2bef2bae 100644 --- a/src/platform/psp2/psp2-context.c +++ b/src/platform/psp2/psp2-context.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include @@ -63,8 +63,8 @@ bool frameLimiter = true; extern const uint8_t _binary_backdrop_png_start[]; static vita2d_texture* backdrop = 0; -#define PSP2_SAMPLES 128 -#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 40) +#define PSP2_SAMPLES 256 +#define PSP2_AUDIO_BUFFER_SIZE (PSP2_SAMPLES * 20) static struct mPSP2AudioContext { struct RingFIFO buffer; @@ -176,7 +176,6 @@ void mPSP2Setup(struct mGUIRunner* runner) { mCoreConfigSetDefaultIntValue(&runner->config, "threadedVideo", 1); mCoreLoadForeignConfig(runner->core, &runner->config); - scePowerSetArmClockFrequency(333); mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CROSS, GBA_KEY_A); mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_CIRCLE, GBA_KEY_B); mPSP2MapKey(&runner->core->inputMap, SCE_CTRL_START, GBA_KEY_START); @@ -193,8 +192,10 @@ void mPSP2Setup(struct mGUIRunner* runner) { desc = (struct mInputAxis) { GBA_KEY_RIGHT, GBA_KEY_LEFT, 192, 64 }; mInputBindAxis(&runner->core->inputMap, PSP2_INPUT, 1, &desc); - tex = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); - screenshot = vita2d_create_empty_texture_format(256, 256, SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + unsigned width, height; + runner->core->desiredVideoDimensions(runner->core, &width, &height); + tex = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); + screenshot = vita2d_create_empty_texture_format(256, toPow2(height), SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1BGR); outputBuffer = vita2d_texture_get_datap(tex); runner->core->setVideoBuffer(runner->core, outputBuffer, 256); @@ -219,7 +220,6 @@ void mPSP2Setup(struct mGUIRunner* runner) { } void mPSP2LoadROM(struct mGUIRunner* runner) { - scePowerSetArmClockFrequency(444); float rate = 60.0f / 1.001f; sceDisplayGetRefreshRate(&rate); double ratio = GBAAudioCalculateRatio(1, rate, 1); @@ -262,7 +262,6 @@ void mPSP2PrepareForFrame(struct mGUIRunner* runner) { blip_clear(runner->core->getAudioChannel(runner->core, 1)); break; } - sceKernelDelayThread(400); } blip_read_samples(runner->core->getAudioChannel(runner->core, 0), &samples[0].left, PSP2_SAMPLES, true); blip_read_samples(runner->core->getAudioChannel(runner->core, 1), &samples[0].right, PSP2_SAMPLES, true); @@ -297,7 +296,6 @@ void mPSP2UnloadROM(struct mGUIRunner* runner) { default: break; } - scePowerSetArmClockFrequency(333); } void mPSP2Paused(struct mGUIRunner* runner) { From c9d411a762ec6ef13c3108d0479408f8326df442 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 20 Apr 2017 17:53:01 -0700 Subject: [PATCH 041/159] GBA BIOS: Fix ArcTan sign in HLE BIOS --- CHANGES | 1 + src/gba/bios.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 4b31d0426..e86548eeb 100644 --- a/CHANGES +++ b/CHANGES @@ -38,6 +38,7 @@ Bugfixes: - GB: Fix flickering when screen is strobed quickly - FFmpeg: Fix overflow and general issues with audio encoding - Qt: Fix crash when changing audio settings after a game is closed + - GBA BIOS: Fix ArcTan sign in HLE BIOS Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gba/bios.c b/src/gba/bios.c index 9d8b6ccdc..2bc853a3c 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -271,7 +271,7 @@ static void _Div(struct GBA* gba, int32_t num, int32_t denom) { } } -static int16_t _ArcTan(int16_t i) { +static int16_t _ArcTan(int32_t i) { int32_t a = -((i * i) >> 14); int32_t b = ((0xA9 * a) >> 14) + 0x390; b = ((b * a) >> 14) + 0x91C; @@ -356,7 +356,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { cpu->gprs[0] = sqrt((uint32_t) cpu->gprs[0]); break; case 0x9: - cpu->gprs[0] = (uint16_t) _ArcTan(cpu->gprs[0]); + cpu->gprs[0] = _ArcTan(cpu->gprs[0]); break; case 0xA: cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]); From f4abdd6c4c475c5249754d184abc9c42d63c24b0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 20 Apr 2017 17:59:02 -0700 Subject: [PATCH 042/159] GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes #689) --- CHANGES | 1 + src/gba/bios.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index e86548eeb..eb9428338 100644 --- a/CHANGES +++ b/CHANGES @@ -39,6 +39,7 @@ Bugfixes: - FFmpeg: Fix overflow and general issues with audio encoding - Qt: Fix crash when changing audio settings after a game is closed - GBA BIOS: Fix ArcTan sign in HLE BIOS + - GBA BIOS: Fix ArcTan2 sign in HLE BIOS Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gba/bios.c b/src/gba/bios.c index 2bc853a3c..b1e9574e3 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -283,7 +283,7 @@ static int16_t _ArcTan(int32_t i) { return (i * b) >> 16; } -static int16_t _ArcTan2(int16_t x, int16_t y) { +static int16_t _ArcTan2(int32_t x, int32_t y) { if (!y) { if (x >= 0) { return 0; @@ -299,7 +299,7 @@ static int16_t _ArcTan2(int16_t x, int16_t y) { if (y >= 0) { if (x >= 0) { if (x >= y) { - return _ArcTan((y << 14)/ x); + return _ArcTan((y << 14) / x); } } else if (-x >= y) { return _ArcTan((y << 14) / x) + 0x8000; @@ -359,7 +359,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { cpu->gprs[0] = _ArcTan(cpu->gprs[0]); break; case 0xA: - cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]); + cpu->gprs[0] = _ArcTan2(cpu->gprs[0], cpu->gprs[1]); break; case 0xB: case 0xC: From b154457857d3367a4c0196a4abadeeb6c850ffdf Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 20 Apr 2017 23:18:51 -0700 Subject: [PATCH 043/159] GBA BIOS: More tiny fixes to ArcTan2 --- CHANGES | 2 +- src/gba/bios.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index eb9428338..66c37278a 100644 --- a/CHANGES +++ b/CHANGES @@ -39,7 +39,7 @@ Bugfixes: - FFmpeg: Fix overflow and general issues with audio encoding - Qt: Fix crash when changing audio settings after a game is closed - GBA BIOS: Fix ArcTan sign in HLE BIOS - - GBA BIOS: Fix ArcTan2 sign in HLE BIOS + - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gba/bios.c b/src/gba/bios.c index b1e9574e3..65504098f 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -359,7 +359,8 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { cpu->gprs[0] = _ArcTan(cpu->gprs[0]); break; case 0xA: - cpu->gprs[0] = _ArcTan2(cpu->gprs[0], cpu->gprs[1]); + cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]); + cpu->gprs[3] = 0x170; break; case 0xB: case 0xC: From 14dc01409c9e971ea0697f5017b45d0db6a7faf5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 00:01:35 -0700 Subject: [PATCH 044/159] GBA BIOS: ArcTan/ArcTan2 residual register state --- src/gba/bios.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/gba/bios.c b/src/gba/bios.c index 65504098f..199ffab8f 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -271,7 +271,7 @@ static void _Div(struct GBA* gba, int32_t num, int32_t denom) { } } -static int16_t _ArcTan(int32_t i) { +static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3) { int32_t a = -((i * i) >> 14); int32_t b = ((0xA9 * a) >> 14) + 0x390; b = ((b * a) >> 14) + 0x91C; @@ -280,10 +280,16 @@ static int16_t _ArcTan(int32_t i) { b = ((b * a) >> 14) + 0x2081; b = ((b * a) >> 14) + 0x3651; b = ((b * a) >> 14) + 0xA2F9; + if (r1) { + *r1 = a; + } + if (r3) { + *r3 = b; + } return (i * b) >> 16; } -static int16_t _ArcTan2(int32_t x, int32_t y) { +static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1) { if (!y) { if (x >= 0) { return 0; @@ -299,21 +305,21 @@ static int16_t _ArcTan2(int32_t x, int32_t y) { if (y >= 0) { if (x >= 0) { if (x >= y) { - return _ArcTan((y << 14) / x); + return _ArcTan((y << 14) / x, r1, NULL); } } else if (-x >= y) { - return _ArcTan((y << 14) / x) + 0x8000; + return _ArcTan((y << 14) / x, r1, NULL) + 0x8000; } - return 0x4000 - _ArcTan((x << 14) / y); + return 0x4000 - _ArcTan((x << 14) / y, r1, NULL); } else { if (x <= 0) { if (-x > -y) { - return _ArcTan((y << 14) / x) + 0x8000; + return _ArcTan((y << 14) / x, r1, NULL) + 0x8000; } } else if (x >= -y) { - return _ArcTan((y << 14) / x) + 0x10000; + return _ArcTan((y << 14) / x, r1, NULL) + 0x10000; } - return 0xC000 - _ArcTan((x << 14) / y); + return 0xC000 - _ArcTan((x << 14) / y, r1, NULL); } } @@ -356,10 +362,10 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { cpu->gprs[0] = sqrt((uint32_t) cpu->gprs[0]); break; case 0x9: - cpu->gprs[0] = _ArcTan(cpu->gprs[0]); + cpu->gprs[0] = _ArcTan(cpu->gprs[0], &cpu->gprs[1], &cpu->gprs[3]); break; case 0xA: - cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1]); + cpu->gprs[0] = (uint16_t) _ArcTan2(cpu->gprs[0], cpu->gprs[1], &cpu->gprs[1]); cpu->gprs[3] = 0x170; break; case 0xB: From a159e377932c8def76c2d77495b94f70083defcd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 03:15:39 -0700 Subject: [PATCH 045/159] GBA Video: Don't update background scanline params in mode 0 (fixes #377) --- CHANGES | 1 + src/gba/renderers/video-software.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 66c37278a..a8a1106c3 100644 --- a/CHANGES +++ b/CHANGES @@ -40,6 +40,7 @@ Bugfixes: - Qt: Fix crash when changing audio settings after a game is closed - GBA BIOS: Fix ArcTan sign in HLE BIOS - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) + - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gba/renderers/video-software.c b/src/gba/renderers/video-software.c index 2c20a475a..70dac8811 100644 --- a/src/gba/renderers/video-software.c +++ b/src/gba/renderers/video-software.c @@ -807,10 +807,12 @@ static void _drawScanline(struct GBAVideoSoftwareRenderer* renderer, int y) { } } } - renderer->bg[2].sx += renderer->bg[2].dmx; - renderer->bg[2].sy += renderer->bg[2].dmy; - renderer->bg[3].sx += renderer->bg[3].dmx; - renderer->bg[3].sy += renderer->bg[3].dmy; + if (GBARegisterDISPCNTGetMode(renderer->dispcnt) != 0) { + renderer->bg[2].sx += renderer->bg[2].dmx; + renderer->bg[2].sy += renderer->bg[2].dmy; + renderer->bg[3].sx += renderer->bg[3].dmx; + renderer->bg[3].sy += renderer->bg[3].dmy; + } } static void _updatePalettes(struct GBAVideoSoftwareRenderer* renderer) { From 8b805b39099eb7c1f41be9401d34ae6c6aa6f420 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 16:20:52 -0700 Subject: [PATCH 046/159] Debugger: Make attaching a backend idempotent --- CHANGES | 1 + src/debugger/cli-debugger.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index a8a1106c3..e770b9473 100644 --- a/CHANGES +++ b/CHANGES @@ -94,6 +94,7 @@ Misc: - Qt: Add .gb/.gbc files to the extension list in Info.plist - Feature: Make -l option explicit - Core: Ability to enumerate and modify video and audio channels + - Debugger: Make attaching a backend idempotent 0.5.2: (2016-12-31) Bugfixes: diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index d5b6dc535..96fc1e1a2 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -751,6 +751,9 @@ void CLIDebuggerAttachSystem(struct CLIDebugger* debugger, struct CLIDebuggerSys } void CLIDebuggerAttachBackend(struct CLIDebugger* debugger, struct CLIDebuggerBackend* backend) { + if (debugger->backend == backend) { + return; + } if (debugger->backend && debugger->backend->deinit) { debugger->backend->deinit(debugger->backend); } From 01832ec513b83ba554f47f45997d2a8b24606c74 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 16:21:50 -0700 Subject: [PATCH 047/159] Qt: Ensure CLI backend is attached when submitting commands (fixes #662) --- CHANGES | 1 + src/platform/qt/DebuggerConsoleController.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index e770b9473..8bb66f6ed 100644 --- a/CHANGES +++ b/CHANGES @@ -41,6 +41,7 @@ Bugfixes: - GBA BIOS: Fix ArcTan sign in HLE BIOS - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) + - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index 0752eb89e..4670d7470 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -40,6 +40,7 @@ void DebuggerConsoleController::enterLine(const QString& line) { void DebuggerConsoleController::attachInternal() { mCore* core = m_gameController->thread()->core; + CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d); CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core)); } From 84dc182a5d5e3bb7113dda93893bbb65faf68f04 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 16:22:48 -0700 Subject: [PATCH 048/159] Core: Fix crash with rewind if savestates shrink --- CHANGES | 1 + src/core/rewind.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 8bb66f6ed..409129e69 100644 --- a/CHANGES +++ b/CHANGES @@ -42,6 +42,7 @@ Bugfixes: - GBA BIOS: Fix ArcTan2 sign in HLE BIOS (fixes mgba.io/i/689) - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) + - Core: Fix crash with rewind if savestates shrink Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/core/rewind.c b/src/core/rewind.c index 9919acf2e..53ee67188 100644 --- a/src/core/rewind.c +++ b/src/core/rewind.c @@ -50,6 +50,8 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) { if (size2 > size) { context->currentState->truncate(context->currentState, size2); size = size2; + } else if (size > size2) { + nextState->truncate(nextState, size); } void* current = context->currentState->map(context->currentState, size, MAP_READ); void* next = nextState->map(nextState, size, MAP_READ); From 2a7f642d4cba0e962520135a1dff47095dd78c75 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 16:38:26 -0700 Subject: [PATCH 049/159] Qt: Fix regression with windows not closing --- src/platform/qt/Window.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index a679054cf..6de1e2355 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -444,6 +444,7 @@ void Window::selectPatch() { void Window::openView(QWidget* widget) { connect(this, SIGNAL(shutdown()), widget, SLOT(close())); + connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), widget, SLOT(close())); widget->setAttribute(Qt::WA_DeleteOnClose); widget->show(); } From 3a8c5c4bf16e87f115aa7d960c53ba2f20cb1155 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 17:30:30 -0700 Subject: [PATCH 050/159] GB: Video/audio channel enabling/disabling --- CHANGES | 1 + include/mgba/internal/gb/video.h | 4 ++++ src/gb/core.c | 14 +++++++++++++- src/gb/renderers/software.c | 19 ++++++++++++++----- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 409129e69..9d2f4c649 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,7 @@ Features: - Savestates now contain any RTC override data - Command line ability to override configuration values - Add option to allow preloading the entire ROM before running + - GB: Video/audio channel enabling/disabling Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index 80899c723..fc751d43a 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -71,6 +71,10 @@ struct GBVideoRenderer { uint8_t* vram; union GBOAM* oam; struct mTileCache* cache; + + bool disableBG; + bool disableOBJ; + bool disableWIN; }; DECL_BITFIELD(GBRegisterLCDC, uint8_t); diff --git a/src/gb/core.c b/src/gb/core.c index 48d178719..369bcf7b5 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -606,7 +606,19 @@ static size_t _GBCoreListAudioChannels(const struct mCore* core, const struct mC static void _GBCoreEnableVideoLayer(struct mCore* core, size_t id, bool enable) { struct GB* gb = core->board; - // TODO + switch (id) { + case 0: + gb->video.renderer->disableBG = !enable; + break; + case 1: + gb->video.renderer->disableOBJ = !enable; + break; + case 2: + gb->video.renderer->disableWIN = !enable; + break; + default: + break; + } } static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable) { diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index 6c4740d76..c8922e0b8 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -49,6 +49,10 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->d.getPixels = GBVideoSoftwareRendererGetPixels; renderer->d.putPixels = GBVideoSoftwareRendererPutPixels; + renderer->d.disableBG = false; + renderer->d.disableOBJ = false; + renderer->d.disableWIN = false; + renderer->temporaryBuffer = 0; } @@ -128,9 +132,12 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i if (GBRegisterLCDCIsTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; } + if (softwareRenderer->d.disableBG) { + memset(&softwareRenderer->row[startX], 0, endX - startX); + } if (GBRegisterLCDCIsBgEnable(softwareRenderer->lcdc) || softwareRenderer->model >= GB_MODEL_CGB) { if (GBRegisterLCDCIsWindow(softwareRenderer->lcdc) && softwareRenderer->wy <= y && endX >= softwareRenderer->wx - 7) { - if (softwareRenderer->wx - 7 > 0) { + if (softwareRenderer->wx - 7 > 0 && !softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, softwareRenderer->wx - 7, softwareRenderer->scx, softwareRenderer->scy + y); } @@ -138,15 +145,17 @@ static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, i if (GBRegisterLCDCIsWindowTileMap(softwareRenderer->lcdc)) { maps += GB_SIZE_MAP; } - GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy); - } else { + if (!softwareRenderer->d.disableWIN) { + GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, softwareRenderer->wx - 7, endX, 7 - softwareRenderer->wx, softwareRenderer->currentWy); + } + } else if (!softwareRenderer->d.disableBG) { GBVideoSoftwareRendererDrawBackground(softwareRenderer, maps, startX, endX, softwareRenderer->scx, softwareRenderer->scy + y); } - } else { + } else if (!softwareRenderer->d.disableBG) { memset(&softwareRenderer->row[startX], 0, endX - startX); } - if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc)) { + if (GBRegisterLCDCIsObjEnable(softwareRenderer->lcdc) && !softwareRenderer->d.disableOBJ) { size_t i; for (i = 0; i < oamMax; ++i) { GBVideoSoftwareRendererDrawObj(softwareRenderer, &obj[i], startX, endX, y); From 189169e8ec2ca526f6e07308464ff079dd1f56b2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 17:31:04 -0700 Subject: [PATCH 051/159] Qt: Auto-generate audio/video layer menus --- src/platform/qt/Window.cpp | 80 ++++++++++++++++++++------------------ src/platform/qt/Window.h | 2 + 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 6de1e2355..548ef4926 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -746,6 +746,39 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { m_hitUnimplementedBiosCall = false; m_fpsTimer.start(); m_focusCheck.start(); + + m_controller->threadInterrupt(); + if (m_controller->isLoaded()) { + mCore* core = m_controller->thread()->core; + const mCoreChannelInfo* videoLayers; + const mCoreChannelInfo* audioChannels; + size_t nVideo = core->listVideoLayers(core, &videoLayers); + size_t nAudio = core->listAudioChannels(core, &audioChannels); + + if (nVideo) { + for (size_t i = 0; i < nVideo; ++i) { + QAction* action = new QAction(videoLayers[i].visibleName, m_videoLayers); + action->setCheckable(true); + action->setChecked(true); + connect(action, &QAction::triggered, [this, videoLayers, i](bool enable) { + m_controller->setVideoLayerEnabled(videoLayers[i].id, enable); + }); + m_videoLayers->addAction(action); + } + } + if (nAudio) { + for (size_t i = 0; i < nAudio; ++i) { + QAction* action = new QAction(audioChannels[i].visibleName, m_audioChannels); + action->setCheckable(true); + action->setChecked(true); + connect(action, &QAction::triggered, [this, audioChannels, i](bool enable) { + m_controller->setAudioChannelEnabled(audioChannels[i].id, enable); + }); + m_audioChannels->addAction(action); + } + } + } + m_controller->threadContinue(); } void Window::gameStopped() { @@ -770,6 +803,9 @@ void Window::gameStopped() { #endif m_screenWidget->setMinimumSize(m_display->minimumSize()); + m_videoLayers->clear(); + m_audioChannels->clear(); + m_fpsTimer.stop(); m_focusCheck.stop(); } @@ -1302,45 +1338,13 @@ void Window::setupMenu(QMenuBar* menubar) { #endif avMenu->addSeparator(); - QMenu* videoLayers = avMenu->addMenu(tr("Video layers")); - m_shortcutController->addMenu(videoLayers, avMenu); + m_videoLayers = avMenu->addMenu(tr("Video layers")); + m_shortcutController->addMenu(m_videoLayers, avMenu); + m_gameWidgets.append(m_videoLayers); - for (int i = 0; i < 4; ++i) { - QAction* enableBg = new QAction(tr("Background %0").arg(i), videoLayers); - enableBg->setCheckable(true); - enableBg->setChecked(true); - connect(enableBg, &QAction::triggered, [this, i](bool enable) { m_controller->setVideoLayerEnabled(i, enable); }); - addControlledAction(videoLayers, enableBg, QString("enableBG%0").arg(i)); - } - - QAction* enableObj = new QAction(tr("OBJ (sprites)"), videoLayers); - enableObj->setCheckable(true); - enableObj->setChecked(true); - connect(enableObj, &QAction::triggered, [this](bool enable) { m_controller->setVideoLayerEnabled(4, enable); }); - addControlledAction(videoLayers, enableObj, "enableOBJ"); - - QMenu* audioChannels = avMenu->addMenu(tr("Audio channels")); - m_shortcutController->addMenu(audioChannels, avMenu); - - for (int i = 0; i < 4; ++i) { - QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels); - enableCh->setCheckable(true); - enableCh->setChecked(true); - connect(enableCh, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(i, enable); }); - addControlledAction(audioChannels, enableCh, QString("enableCh%0").arg(i + 1)); - } - - QAction* enableChA = new QAction(tr("Channel A"), audioChannels); - enableChA->setCheckable(true); - enableChA->setChecked(true); - connect(enableChA, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(4, enable); }); - addControlledAction(audioChannels, enableChA, QString("enableChA")); - - QAction* enableChB = new QAction(tr("Channel B"), audioChannels); - enableChB->setCheckable(true); - enableChB->setChecked(true); - connect(enableChB, &QAction::triggered, [this, i](bool enable) { m_controller->setAudioChannelEnabled(5, enable); }); - addControlledAction(audioChannels, enableChB, QString("enableChB")); + m_audioChannels = avMenu->addMenu(tr("Audio channels")); + m_shortcutController->addMenu(m_audioChannels, avMenu); + m_gameWidgets.append(m_audioChannels); QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_shortcutController->addMenu(toolsMenu); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 37ef814df..e6bc01dbe 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -173,6 +173,8 @@ private: QTimer m_fpsTimer; QList m_mruFiles; QMenu* m_mruMenu; + QMenu* m_videoLayers; + QMenu* m_audioChannels; ShortcutController* m_shortcutController; ShaderSelector* m_shaderView; bool m_fullscreenOnStart; From 69a30f95016a44047d3928b8e5bc1ac4b9a69033 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 17:50:22 -0700 Subject: [PATCH 052/159] Qt: Fix build --- src/platform/qt/Window.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 548ef4926..35f90e7b3 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1340,11 +1340,9 @@ void Window::setupMenu(QMenuBar* menubar) { avMenu->addSeparator(); m_videoLayers = avMenu->addMenu(tr("Video layers")); m_shortcutController->addMenu(m_videoLayers, avMenu); - m_gameWidgets.append(m_videoLayers); m_audioChannels = avMenu->addMenu(tr("Audio channels")); m_shortcutController->addMenu(m_audioChannels, avMenu); - m_gameWidgets.append(m_audioChannels); QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_shortcutController->addMenu(toolsMenu); From 2d930cbc610b28d82746bbcd121efcd47be47592 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 21 Apr 2017 18:13:16 -0700 Subject: [PATCH 053/159] All: Add option to lock video to integer scaling --- CHANGES | 1 + include/mgba/core/config.h | 1 + src/core/config.c | 4 ++++ src/platform/opengl/gl.c | 4 ++++ src/platform/opengl/gles2.c | 4 ++++ src/platform/qt/Display.cpp | 5 +++++ src/platform/qt/Display.h | 3 +++ src/platform/qt/DisplayGL.cpp | 15 +++++++++++++++ src/platform/qt/DisplayGL.h | 2 ++ src/platform/qt/DisplayQt.cpp | 9 +++++++++ src/platform/qt/DisplayQt.h | 1 + src/platform/qt/SettingsView.cpp | 2 ++ src/platform/qt/SettingsView.ui | 15 +++++++++++---- src/platform/qt/Window.cpp | 16 ++++++++++++++++ src/platform/qt/Window.h | 2 ++ src/platform/video-backend.h | 1 + 16 files changed, 81 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 9d2f4c649..e50660b19 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,7 @@ Features: - Command line ability to override configuration values - Add option to allow preloading the entire ROM before running - GB: Video/audio channel enabling/disabling + - Add option to lock video to integer scaling Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/core/config.h b/include/mgba/core/config.h index 6dbc73dc4..0c6426e7a 100644 --- a/include/mgba/core/config.h +++ b/include/mgba/core/config.h @@ -42,6 +42,7 @@ struct mCoreOptions { int width; int height; bool lockAspectRatio; + bool lockIntegerScaling; bool resampleVideo; bool suspendScreensaver; char* shader; diff --git a/src/core/config.c b/src/core/config.c index b4d654c54..cbb6f32ee 100644 --- a/src/core/config.c +++ b/src/core/config.c @@ -349,6 +349,9 @@ void mCoreConfigMap(const struct mCoreConfig* config, struct mCoreOptions* opts) if (_lookupIntValue(config, "lockAspectRatio", &fakeBool)) { opts->lockAspectRatio = fakeBool; } + if (_lookupIntValue(config, "lockIntegerScaling", &fakeBool)) { + opts->lockIntegerScaling = fakeBool; + } if (_lookupIntValue(config, "resampleVideo", &fakeBool)) { opts->resampleVideo = fakeBool; } @@ -396,6 +399,7 @@ void mCoreConfigLoadDefaults(struct mCoreConfig* config, const struct mCoreOptio ConfigurationSetIntValue(&config->defaultsTable, 0, "volume", opts->volume); ConfigurationSetIntValue(&config->defaultsTable, 0, "mute", opts->mute); ConfigurationSetIntValue(&config->defaultsTable, 0, "lockAspectRatio", opts->lockAspectRatio); + ConfigurationSetIntValue(&config->defaultsTable, 0, "lockIntegerScaling", opts->lockIntegerScaling); ConfigurationSetIntValue(&config->defaultsTable, 0, "resampleVideo", opts->resampleVideo); ConfigurationSetIntValue(&config->defaultsTable, 0, "suspendScreensaver", opts->suspendScreensaver); } diff --git a/src/platform/opengl/gl.c b/src/platform/opengl/gl.c index 31bb101aa..87b679c6b 100644 --- a/src/platform/opengl/gl.c +++ b/src/platform/opengl/gl.c @@ -65,6 +65,10 @@ static void mGLContextResized(struct VideoBackend* v, unsigned w, unsigned h) { drawH = w * v->height / v->width; } } + if (v->lockIntegerScaling) { + drawW -= drawW % v->width; + drawH -= drawH % v->height; + } glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0, 0, 0, 0); diff --git a/src/platform/opengl/gles2.c b/src/platform/opengl/gles2.c index 2dff30f3d..47842273e 100644 --- a/src/platform/opengl/gles2.c +++ b/src/platform/opengl/gles2.c @@ -171,6 +171,10 @@ static void mGLES2ContextResized(struct VideoBackend* v, unsigned w, unsigned h) drawH = w * v->height / v->width; } } + if (v->lockIntegerScaling) { + drawW -= drawW % v->width; + drawH -= drawH % v->height; + } glViewport(0, 0, v->width, v->height); glClearColor(0.f, 0.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 506afe76b..a6719a845 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -54,6 +54,7 @@ Display* Display::create(QWidget* parent) { Display::Display(QWidget* parent) : QWidget(parent) , m_lockAspectRatio(false) + , m_lockIntegerScaling(false) , m_filter(false) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -77,6 +78,10 @@ void Display::lockAspectRatio(bool lock) { m_messagePainter.resize(size(), m_lockAspectRatio, devicePixelRatio()); } +void Display::lockIntegerScaling(bool lock) { + m_lockIntegerScaling = lock; +} + void Display::filter(bool filter) { m_filter = filter; } diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index a78810fe8..1e3dc4e22 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -38,6 +38,7 @@ public: static void setDriver(Driver driver) { s_driver = driver; } bool isAspectRatioLocked() const { return m_lockAspectRatio; } + bool isIntegerScalingLocked() const { return m_lockIntegerScaling; } bool isFiltered() const { return m_filter; } virtual bool isDrawing() const = 0; @@ -55,6 +56,7 @@ public slots: virtual void unpauseDrawing() = 0; virtual void forceDraw() = 0; virtual void lockAspectRatio(bool lock); + virtual void lockIntegerScaling(bool lock); virtual void filter(bool filter); virtual void framePosted(const uint32_t*) = 0; virtual void setShaders(struct VDir*) = 0; @@ -74,6 +76,7 @@ private: MessagePainter m_messagePainter; bool m_lockAspectRatio; + bool m_lockIntegerScaling; bool m_filter; QTimer m_mouseTimer; }; diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 49d9ce5b1..27d06df93 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -73,6 +73,7 @@ void DisplayGL::startDrawing(mCoreThread* thread) { mCoreSyncSetVideoSync(&m_context->sync, false); lockAspectRatio(isAspectRatioLocked()); + lockIntegerScaling(isIntegerScalingLocked()); filter(isFiltered()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) messagePainter()->resize(size(), isAspectRatioLocked(), devicePixelRatioF()); @@ -136,6 +137,13 @@ void DisplayGL::lockAspectRatio(bool lock) { } } +void DisplayGL::lockIntegerScaling(bool lock) { + Display::lockIntegerScaling(lock); + if (m_drawThread) { + QMetaObject::invokeMethod(m_painter, "lockIntegerScaling", Q_ARG(bool, lock)); + } +} + void DisplayGL::filter(bool filter) { Display::filter(filter); if (m_drawThread) { @@ -290,6 +298,13 @@ void PainterGL::lockAspectRatio(bool lock) { } } +void PainterGL::lockIntegerScaling(bool lock) { + m_backend->lockIntegerScaling = lock; + if (m_started && !m_active) { + forceDraw(); + } +} + void PainterGL::filter(bool filter) { m_backend->filter = filter; if (m_started && !m_active) { diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index b4b4bd056..0a7227c01 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -55,6 +55,7 @@ public slots: void unpauseDrawing() override; void forceDraw() override; void lockAspectRatio(bool lock) override; + void lockIntegerScaling(bool lock) override; void filter(bool filter) override; void framePosted(const uint32_t*) override; void setShaders(struct VDir*) override; @@ -96,6 +97,7 @@ public slots: void unpause(); void resize(const QSize& size); void lockAspectRatio(bool lock); + void lockIntegerScaling(bool lock); void filter(bool filter); void setShaders(struct VDir*); diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index 284347d58..db17469d0 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -30,6 +30,11 @@ void DisplayQt::lockAspectRatio(bool lock) { update(); } +void DisplayQt::lockIntegerScaling(bool lock) { + Display::lockIntegerScaling(lock); + update(); +} + void DisplayQt::filter(bool filter) { Display::filter(filter); update(); @@ -66,6 +71,10 @@ void DisplayQt::paintEvent(QPaintEvent*) { ds.setHeight(s.width() * m_height / m_width); } } + if (isIntegerScalingLocked()) { + ds.setWidth(ds.width() - ds.width() % m_width); + ds.setHeight(ds.height() - ds.height() % m_height); + } QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2); QRect full(origin, ds); diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index d40b1053c..eb7ec5723 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -30,6 +30,7 @@ public slots: void unpauseDrawing() override { m_isDrawing = true; } void forceDraw() override { update(); } void lockAspectRatio(bool lock) override; + void lockIntegerScaling(bool lock) override; void filter(bool filter) override; void framePosted(const uint32_t*) override; void setShaders(struct VDir*) override {} diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 510885501..9f5b7527c 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -190,6 +190,7 @@ void SettingsView::updateConfig() { saveSetting("frameskip", m_ui.frameskip); saveSetting("fpsTarget", m_ui.fpsTarget); saveSetting("lockAspectRatio", m_ui.lockAspectRatio); + saveSetting("lockIntegerScaling", m_ui.lockIntegerScaling); saveSetting("volume", m_ui.volume); saveSetting("mute", m_ui.mute); saveSetting("rewindEnable", m_ui.rewind); @@ -270,6 +271,7 @@ void SettingsView::reloadConfig() { loadSetting("frameskip", m_ui.frameskip); loadSetting("fpsTarget", m_ui.fpsTarget); loadSetting("lockAspectRatio", m_ui.lockAspectRatio); + loadSetting("lockIntegerScaling", m_ui.lockIntegerScaling); loadSetting("volume", m_ui.volume); loadSetting("mute", m_ui.mute); loadSetting("rewindEnable", m_ui.rewind); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index 967c17cb1..caabf1c4c 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -7,7 +7,7 @@ 0 0 650 - 450 + 454 @@ -115,7 +115,7 @@ true - + 1536 @@ -181,7 +181,7 @@ true - + 44100 @@ -380,13 +380,20 @@ - + Bilinear filtering + + + + Force integer scaling + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 35f90e7b3..1588df0eb 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1263,6 +1263,14 @@ void Window::setupMenu(QMenuBar* menubar) { }, this); m_config->updateOption("lockAspectRatio"); + ConfigOption* lockIntegerScaling = m_config->addOption("lockIntegerScaling"); + lockIntegerScaling->addBoolean(tr("Force integer scaling"), avMenu); + lockIntegerScaling->connect([this](const QVariant& value) { + m_display->lockIntegerScaling(value.toBool()); + m_screenWidget->setLockIntegerScaling(value.toBool()); + }, this); + m_config->updateOption("lockIntegerScaling"); + ConfigOption* resampleVideo = m_config->addOption("resampleVideo"); resampleVideo->addBoolean(tr("Bilinear filtering"), avMenu); resampleVideo->connect([this](const QVariant& value) { @@ -1637,6 +1645,10 @@ void WindowBackground::setLockAspectRatio(int width, int height) { m_aspectHeight = height; } +void WindowBackground::setLockIntegerScaling(bool lock) { + m_lockIntegerScaling = lock; +} + void WindowBackground::paintEvent(QPaintEvent*) { const QPixmap* logo = pixmap(); if (!logo) { @@ -1652,6 +1664,10 @@ void WindowBackground::paintEvent(QPaintEvent*) { } else if (ds.width() * m_aspectHeight < ds.height() * m_aspectWidth) { ds.setHeight(ds.width() * m_aspectHeight / m_aspectWidth); } + if (m_lockIntegerScaling) { + ds.setWidth(ds.width() - ds.width() % m_aspectWidth); + ds.setHeight(ds.height() - ds.height() % m_aspectHeight); + } QPoint origin = QPoint((s.width() - ds.width()) / 2, (s.height() - ds.height()) / 2); QRect full(origin, ds); painter.drawPixmap(full, *logo); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index e6bc01dbe..f224a28bf 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -210,6 +210,7 @@ public: void setSizeHint(const QSize& size); virtual QSize sizeHint() const override; void setLockAspectRatio(int width, int height); + void setLockIntegerScaling(bool lock); protected: virtual void paintEvent(QPaintEvent*) override; @@ -218,6 +219,7 @@ private: QSize m_sizeHint; int m_aspectWidth; int m_aspectHeight; + bool m_lockIntegerScaling; }; } diff --git a/src/platform/video-backend.h b/src/platform/video-backend.h index 79264c4e2..f45a56dc5 100644 --- a/src/platform/video-backend.h +++ b/src/platform/video-backend.h @@ -35,6 +35,7 @@ struct VideoBackend { bool filter; bool lockAspectRatio; + bool lockIntegerScaling; }; struct VideoShader { From 8c095ec885e7131993ee50ae7f1bc9b1ea570128 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Apr 2017 00:41:58 -0700 Subject: [PATCH 054/159] Qt: Fix crash when first CLI command is empty (fixes #693) --- src/platform/qt/DebuggerConsoleController.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index 4670d7470..50021eb2c 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -88,6 +88,9 @@ const char* DebuggerConsoleController::historyLast(struct CLIDebuggerBackend* be DebuggerConsoleController* self = consoleBe->self; GameController::Interrupter interrupter(self->m_gameController, true); QMutexLocker lock(&self->m_mutex); + if (self->m_history.isEmpty()) { + return nullptr; + } self->m_last = self->m_history.last().toUtf8(); return self->m_last.constData(); } From 3b4ccb84d26604b72746d56f3cddfea5b6e5ba2c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Apr 2017 01:39:21 -0700 Subject: [PATCH 055/159] Qt: Fix several debugger hangs and odd cases (fixes #691) --- src/gba/gba.c | 10 +++++++--- src/platform/qt/DebuggerConsoleController.cpp | 14 +++++++++++++- src/platform/qt/DebuggerConsoleController.h | 1 + src/platform/qt/GameController.cpp | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index 25ff7670a..2d04c4290 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -268,6 +268,7 @@ static void GBAProcessEvents(struct ARMCore* cpu) { } } +#ifdef USE_DEBUGGERS void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) { gba->debugger = (struct ARMDebugger*) debugger->platform; gba->debugger->setSoftwareBreakpoint = _setSoftwareBreakpoint; @@ -277,10 +278,13 @@ void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger) { } void GBADetachDebugger(struct GBA* gba) { - gba->debugger = 0; - ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER); - gba->cpu->components[CPU_COMPONENT_DEBUGGER] = 0; + if (gba->debugger) { + ARMHotplugDetach(gba->cpu, CPU_COMPONENT_DEBUGGER); + } + gba->cpu->components[CPU_COMPONENT_DEBUGGER] = NULL; + gba->debugger = NULL; } +#endif bool GBALoadMB(struct GBA* gba, struct VFile* vf) { GBAUnloadROM(gba); diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index 50021eb2c..ff8577414 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -38,7 +38,14 @@ void DebuggerConsoleController::enterLine(const QString& line) { m_cond.wakeOne(); } +void DebuggerConsoleController::detach() { + m_lines.append(QString()); + m_cond.wakeOne(); + DebuggerController::detach(); +} + void DebuggerConsoleController::attachInternal() { + m_history.clear(); mCore* core = m_gameController->thread()->core; CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d); CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core)); @@ -61,6 +68,8 @@ void DebuggerConsoleController::init(struct CLIDebuggerBackend* be) { void DebuggerConsoleController::deinit(struct CLIDebuggerBackend* be) { Backend* consoleBe = reinterpret_cast(be); DebuggerConsoleController* self = consoleBe->self; + self->m_lines.append(QString()); + self->m_cond.wakeOne(); } const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, size_t* len) { @@ -72,6 +81,9 @@ const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, s self->m_cond.wait(&self->m_mutex); } self->m_last = self->m_lines.takeFirst().toUtf8(); + if (self->m_last.isEmpty()) { + self->m_last = "\n"; + } *len = self->m_last.size(); return self->m_last.constData(); @@ -89,7 +101,7 @@ const char* DebuggerConsoleController::historyLast(struct CLIDebuggerBackend* be GameController::Interrupter interrupter(self->m_gameController, true); QMutexLocker lock(&self->m_mutex); if (self->m_history.isEmpty()) { - return nullptr; + return "\n"; } self->m_last = self->m_history.last().toUtf8(); return self->m_last.constData(); diff --git a/src/platform/qt/DebuggerConsoleController.h b/src/platform/qt/DebuggerConsoleController.h index 36e7903cb..543dabeae 100644 --- a/src/platform/qt/DebuggerConsoleController.h +++ b/src/platform/qt/DebuggerConsoleController.h @@ -30,6 +30,7 @@ signals: public slots: void enterLine(const QString&); + virtual void detach() override; protected: virtual void attachInternal() override; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 53ecf3ed9..bf59d7ee5 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -623,6 +623,7 @@ void GameController::closeGame() { return; } + setDebugger(nullptr); if (mCoreThreadIsPaused(&m_threadContext)) { mCoreThreadUnpause(&m_threadContext); } From 36c66e7db434bc3034ec172323cc7f0cd7e5c1ec Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Apr 2017 18:29:10 -0700 Subject: [PATCH 056/159] Qt: Fix logo missing with integer scaling (fixes #696) --- src/platform/qt/Window.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 1588df0eb..112ee95bf 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -135,6 +135,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) #endif m_screenWidget->setPixmap(m_logo); m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); + m_screenWidget->setLockIntegerScaling(false); setCentralWidget(m_screenWidget); connect(m_controller, SIGNAL(gameStarted(mCoreThread*, const QString&)), this, SLOT(gameStarted(mCoreThread*, const QString&))); @@ -732,6 +733,7 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { context->core->desiredVideoDimensions(context->core, &width, &height); m_display->setMinimumSize(width, height); m_screenWidget->setMinimumSize(m_display->minimumSize()); + m_config->updateOption("lockIntegerScaling"); if (m_savedScale > 0) { resizeFrame(QSize(width, height) * m_savedScale); } @@ -794,6 +796,7 @@ void Window::gameStopped() { updateTitle(); detachWidget(m_display); m_screenWidget->setLockAspectRatio(m_logo.width(), m_logo.height()); + m_screenWidget->setLockIntegerScaling(false); m_screenWidget->setPixmap(m_logo); m_screenWidget->unsetCursor(); #ifdef M_CORE_GB @@ -1267,7 +1270,9 @@ void Window::setupMenu(QMenuBar* menubar) { lockIntegerScaling->addBoolean(tr("Force integer scaling"), avMenu); lockIntegerScaling->connect([this](const QVariant& value) { m_display->lockIntegerScaling(value.toBool()); - m_screenWidget->setLockIntegerScaling(value.toBool()); + if (m_controller->isLoaded()) { + m_screenWidget->setLockIntegerScaling(value.toBool()); + } }, this); m_config->updateOption("lockIntegerScaling"); From 6fb7c0780a4cd81aa2d125808dece19066aebc2a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Apr 2017 18:40:55 -0700 Subject: [PATCH 057/159] Qt: Handle interrupting CLI debugger better (fixes #697) --- src/platform/qt/DebuggerConsoleController.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index ff8577414..e7c35144a 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -80,10 +80,11 @@ const char* DebuggerConsoleController::readLine(struct CLIDebuggerBackend* be, s while (self->m_lines.isEmpty()) { self->m_cond.wait(&self->m_mutex); } - self->m_last = self->m_lines.takeFirst().toUtf8(); - if (self->m_last.isEmpty()) { - self->m_last = "\n"; + QString last = self->m_lines.takeFirst(); + if (last.isNull()) { + return nullptr; } + self->m_last = last.toUtf8(); *len = self->m_last.size(); return self->m_last.constData(); @@ -101,7 +102,7 @@ const char* DebuggerConsoleController::historyLast(struct CLIDebuggerBackend* be GameController::Interrupter interrupter(self->m_gameController, true); QMutexLocker lock(&self->m_mutex); if (self->m_history.isEmpty()) { - return "\n"; + return "i"; } self->m_last = self->m_history.last().toUtf8(); return self->m_last.constData(); From 93122df1f42a46c9475f871f67a66ada85eed0db Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 03:36:48 -0700 Subject: [PATCH 058/159] Test: Fix crash when loading invalid file --- CHANGES | 1 + src/platform/test/fuzz-main.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index e50660b19..50b437e74 100644 --- a/CHANGES +++ b/CHANGES @@ -45,6 +45,7 @@ Bugfixes: - GBA Video: Don't update background scanline params in mode 0 (fixes mgba.io/i/377) - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) - Core: Fix crash with rewind if savestates shrink + - Test: Fix crash when loading invalid file Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index d40a15937..611d3079a 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -68,6 +68,9 @@ int main(int argc, char** argv) { return 0; } struct mCore* core = mCoreFind(args.fname); + if (!core) { + return 1; + } core->init(core); mCoreInitConfig(core, "fuzz"); applyArguments(&args, NULL, &core->config); From 422c3a25b887ef357e992d725a7e9d06fbfd5713 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 13:34:57 -0700 Subject: [PATCH 059/159] GBA Hardware: Fix crash if a savestate lies about game hardware --- CHANGES | 1 + src/gba/gba.c | 2 ++ src/gba/hardware.c | 3 +++ 3 files changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 50b437e74..3484ca3f5 100644 --- a/CHANGES +++ b/CHANGES @@ -46,6 +46,7 @@ Bugfixes: - Qt: Ensure CLI backend is attached when submitting commands (fixes mgba.io/i/662) - Core: Fix crash with rewind if savestates shrink - Test: Fix crash when loading invalid file + - GBA Hardware: Fix crash if a savestate lies about game hardware Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gba/gba.c b/src/gba/gba.c index 2d04c4290..c795a4a51 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -76,6 +76,8 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->sio.p = gba; GBASIOInit(&gba->sio); + GBAHardwareInit(&gba->memory.hw, NULL); + gba->springIRQ = 0; gba->keySource = 0; gba->rotationSource = 0; diff --git a/src/gba/hardware.c b/src/gba/hardware.c index ff7562840..f252afd96 100644 --- a/src/gba/hardware.c +++ b/src/gba/hardware.c @@ -77,6 +77,9 @@ void GBAHardwareClear(struct GBACartridgeHardware* hw) { } void GBAHardwareGPIOWrite(struct GBACartridgeHardware* hw, uint32_t address, uint16_t value) { + if (!hw->gpioBase) { + return; + } switch (address) { case GPIO_REG_DATA: hw->pinState &= ~hw->direction; From 870c375cf61ddf416c88141d9d7bc9a63acf4d99 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 13:35:28 -0700 Subject: [PATCH 060/159] Test: Fix crash when fuzzing fails to load a file --- CHANGES | 1 + src/platform/test/fuzz-main.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 3484ca3f5..a13ba5c91 100644 --- a/CHANGES +++ b/CHANGES @@ -47,6 +47,7 @@ Bugfixes: - Core: Fix crash with rewind if savestates shrink - Test: Fix crash when loading invalid file - GBA Hardware: Fix crash if a savestate lies about game hardware + - Test: Fix crash when fuzzing fails to load a file Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index 611d3079a..7e86ec914 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -94,10 +94,15 @@ int main(int argc, char** argv) { #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif + + bool cleanExit = true; + if (!mCoreLoadFile(core, args.fname)) { + cleanExit = false; + goto loadError; + } if (args.patch) { core->loadPatch(core, VFileOpen(args.patch, O_RDONLY)); } - mCoreLoadFile(core, args.fname); struct VFile* savestate = 0; struct VFile* savestateOverlay = 0; @@ -158,13 +163,14 @@ int main(int argc, char** argv) { savestateOverlay->close(savestateOverlay); } +loadError: freeArguments(&args); if (outputBuffer) { free(outputBuffer); } core->deinit(core); - return 0; + return !cleanExit; } static void _fuzzRunloop(struct mCore* core, int frames) { From bcf0e8ec59be807073ba95a1561b74b5fea9dbcd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 25 Apr 2017 11:58:05 -0700 Subject: [PATCH 061/159] GBA: Fix multiboot loading resulting in too small WRAM --- CHANGES | 1 + src/gba/gba.c | 11 +++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index a13ba5c91..e24c84992 100644 --- a/CHANGES +++ b/CHANGES @@ -48,6 +48,7 @@ Bugfixes: - Test: Fix crash when loading invalid file - GBA Hardware: Fix crash if a savestate lies about game hardware - Test: Fix crash when fuzzing fails to load a file + - GBA: Fix multiboot loading resulting in too small WRAM Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gba/gba.c b/src/gba/gba.c index c795a4a51..4a145f5d8 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -297,14 +297,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) { gba->pristineRomSize = SIZE_WORKING_RAM; } gba->isPristine = true; -#ifdef _3DS - if (gba->pristineRomSize <= romBufferSize) { - gba->memory.wram = romBuffer; - vf->read(vf, romBuffer, gba->pristineRomSize); - } -#else - gba->memory.wram = vf->map(vf, gba->pristineRomSize, MAP_READ); -#endif + gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM); + memset(gba->memory.wram, 0, SIZE_WORKING_RAM); + vf->read(vf, gba->memory.wram, gba->pristineRomSize); if (!gba->memory.wram) { mLOG(GBA, WARN, "Couldn't map ROM"); return false; From c4e06cc904318a23fc6a4a2d103df6e191fb29dc Mon Sep 17 00:00:00 2001 From: Lothar Serra Mari Date: Tue, 25 Apr 2017 11:06:02 +0200 Subject: [PATCH 062/159] Qt: Update German GUI translation Some new strings added and a few strings polished. --- src/platform/qt/ts/mgba-de.ts | 489 +++++++++++++++++----------------- 1 file changed, 243 insertions(+), 246 deletions(-) diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index 6b748558c..e8318c194 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -1045,28 +1045,28 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::GameController - - + + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen - + Failed to start audio processor Fehler beim Starten des Audio-Prozessors @@ -2676,7 +2676,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Portable Network Graphics (*.png) - + Failed to open output PNG file: %1 Fehler beim Öffnen der Ausgabe-PNG-Datei: %1 @@ -2716,7 +2716,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Windows PAL (*.pal);;Adobe Color Table (*.act) - + Failed to open output palette file: %1 Fehler beim Öffnen der Ausgabe-Palettendatei: %1 @@ -2851,17 +2851,17 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::VideoView - + Failed to open output video file: %1 Fehler beim Öffnen der Ausgabe-Videodatei: %1 - + Native (%0x%1) Nativ (%0x%1) - + Select output file Ausgabedatei auswählen @@ -2869,67 +2869,67 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance-ROMs (%1) - + Game Boy ROMs (%1) Game Boy-ROMs (%1) - + All ROMs (%1) Alle ROMs (%1) - + Archives (%1) Archive (%1) - - - + + + Select ROM ROM auswählen - + Game Boy Advance save files (%1) Game Boy Advance-Speicherdateien (%1) - - - + + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Patches (*.ips *.ups *.bps) - - + + GameShark saves (*.sps *.xps) GameShark-Speicherdaten (*.sps *.xps) - + Crash Absturz - + The game has crashed with the following error: %1 @@ -2938,633 +2938,613 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - + Couldn't Load Konnte nicht geladen werden - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Sind Sie sicher, dass es im korrekten Format vorliegt? - + Unimplemented BIOS call Nichtimplementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 - + &File &Datei - + Load &ROM... &ROM laden... - + Load ROM in archive... ROM aus Archiv laden... - + Load temporary save... Temporäre Speicherdatei laden... - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate &laden - + F10 F10 - + &Save state Savestate &speichern - + Shift+F10 Umschalt+F10 - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Stand - + Undo load state Laden des Savestate rückgängig machen - + F11 F11 - + Undo save state Speichern des Savestate rückgängig machen - + Shift+F11 Umschalt+F11 - - + + State &%1 Savestate &%1 - + F%1 F%1 - + Shift+F%1 Umschalt+F%1 - + Import GameShark Save Importiere GameShark-Speicherstand - + Export GameShark Save Exportiere GameShark-Speicherstand - + New multiplayer window Neues Multiplayer-Fenster - + About Über - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Ctrl+R Strg+R - + Sh&utdown B&eenden - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + Ctrl+P Strg+P - + &Next frame &Nächstes Bild - + Ctrl+N Strg+N - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Shift+Tab Umschalt+Tab - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + ~ ~ - + Step backwards Schrittweiser Rücklauf - + Ctrl+B Strg+B - + Sync to &video Mit &Video synchronisieren - + Sync to &audio Mit &Audio synchronisieren - + Solar sensor Solar-Sensor - + Increase solar level Sonnen-Level erhöhen - + Decrease solar level Sonnen-Level verringern - + Brightest solar level Hellster Sonnen-Level - + Darkest solar level Dunkelster Sonnen-Level - + Brightness %1 Helligkeit %1 - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + %1x %1x - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio - Seitenverhältnis sperren + Seitenverhältnis korrigieren - + + Force integer scaling + Pixelgenaue Skalierung (Integer scaling) + + + Frame&skip Frame&skip - + Shader options... Shader-Optionen... - + Mute Stummschalten - + FPS target Bildwiederholrate - + 15 15 - + 30 30 - + 45 45 - + Native (59.7) Nativ (59.7) - + 60 60 - + 90 90 - + 120 120 - + 240 240 - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Record output... Ausgabe aufzeichen... - + Record GIF... GIF aufzeichen... - + Video layers Video-Ebenen - - Background %0 - Hintergrund %0 - - - - OBJ (sprites) - OBJ (Sprites) - - - + Audio channels Audio-Kanäle - - Channel %0 - Kanal %0 - - - - Channel A - Kanal A - - - - Channel B - Kanal B - - - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + Game &Pak sensors... Game &Pak-Sensoren... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole äffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... - + Select folder Ordner auswählen - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Bilinear filtering Bilineare Filterung - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View memory... Speicher betrachten... - + View &I/O registers... &I/O-Register betrachten... - + Exit fullscreen Vollbildmodus beenden - + Autofire Autofeuer - + Autofire A Autofeuer A - + Autofire B Autofeuer B - + Autofire L Autofeuer L - + Autofire R Autofeuer R - + Autofire Start Autofeuer Start - + Autofire Select Autofeuer Select - + Autofire Up Autofeuer nach oben - + Autofire Right Autofeuer rechts - + Autofire Down Autofeuer nach unten - + Autofire Left Autofeuer links @@ -3838,7 +3818,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - + frames Bilder @@ -3870,67 +3850,79 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. Lock aspect ratio - Seitenverhältnis sperren + Seitenverhältnis korrigieren - + + Force integer scaling + Erzwinge pixelgenaue Skalierung +(Integer scaling) + + + Library: Bibliothek: - + Show when no game open Anzeigen, wenn kein Spiel geöffnet ist - + Clear cache Cache leeren - + Fast forward speed: Vorlauf-Geschwindigkeit: - + Rewind affects save data Rücklauf beeinflusst Speicherdaten - - - - - - - + + Preload entire ROM into memory + ROM-Datei vollständig +in Arbeitsspeicher vorladen + + + + + + + + + Browse Durchsuchen - + Use BIOS file if found BIOS-Datei verwenden, wenn vorhanden - + Skip BIOS intro BIOS-Intro überspringen - + × × - + Unbounded unbegrenzt - + Suspend screensaver Bildschirmschoner deaktivieren @@ -3940,50 +3932,50 @@ Speicherdaten BIOS - + Pause when inactive Pause, wenn inaktiv - + Run all Alle ausführen - + Remove known Bekannte entfernen - + Detect and remove Erkennen und entfernen - + Allow opposing input directions Gegensätzliche Eingaberichtungen erlauben - - + + Screenshot Screenshot - - + + Save data Speicherdaten - - + + Cheat codes Cheat-Codes - + Enable rewind Rücklauf aktivieren @@ -3993,65 +3985,65 @@ Speicherdaten Bilineare Filterung - + Rewind history: Rücklauf-Verlauf: - + Idle loops: Leerlaufprozesse: - + Savestate extra data: Zusätzliche Savestate-Daten: - + Load extra data: Lade zusätzliche Daten: - + GB BIOS file: - BIOS-Datei für GB: + GB-BIOS: - + GBA BIOS file: - BIOS-Datei für GBA: + GBA-BIOS: - + GBC BIOS file: - BIOS-Datei für GBC: + GBC-BIOS: - + Save games Spielstände - - - - + + + + Same directory as the ROM - Gleiches Verzeichnis wie ROM + Verzeichnis der ROM-Datei - + Save states Savestates - + Screenshots Screenshots - + Patches Patches @@ -4240,86 +4232,91 @@ Speicherdaten + h.264 (NVENC) + h.264 (NVENC) + + + + HEVC + HEVC + + + VP8 VP8 - - Xvid - Xvid - - - + FFV1 FFV1 - + FLAC FLAC - + Opus Opus - + Vorbis Vorbis - + MP3 MP3 - + AAC AAC - + Uncompressed Unkomprimiert - + Bitrate (kbps) Bitrate (kbps) - + VBR VBR - + ABR ABR - + Dimensions Abmessungen - + : : - + × × - + Lock aspect ratio Seitenverhältnis sperren - + Show advanced Erweiterte Optionen anzeigen From c30352fde1fce666d09db36dd3ddc7c4100e5078 Mon Sep 17 00:00:00 2001 From: hunterk Date: Thu, 13 Apr 2017 07:39:35 -0500 Subject: [PATCH 063/159] add gba-color shader --- res/shaders/gba-color.shader/gba-color.fs | 42 ++++++++++ res/shaders/gba-color.shader/manifest.ini | 99 +++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 res/shaders/gba-color.shader/gba-color.fs create mode 100644 res/shaders/gba-color.shader/manifest.ini diff --git a/res/shaders/gba-color.shader/gba-color.fs b/res/shaders/gba-color.shader/gba-color.fs new file mode 100644 index 000000000..6f996bfde --- /dev/null +++ b/res/shaders/gba-color.shader/gba-color.fs @@ -0,0 +1,42 @@ +varying vec2 texCoord; +uniform sampler2D tex; +uniform vec2 texSize; + +uniform float darken_screen; +uniform float target_gamma; +uniform float display_gamma; +uniform float sat; +uniform float lum; +uniform float contrast; +uniform float blr; +uniform float blg; +uniform float blb; +uniform float r; +uniform float g; +uniform float b; +uniform float rg; +uniform float rb; +uniform float gr; +uniform float gb; +uniform float br; +uniform float bg; + +void main() { + vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; + vec4 avglum = vec4(0.5); + screen = mix(screen, avglum, (1.0 - contrast)); + +mat4 color = mat4(r, rg, rb, 0.0, + gr, g, gb, 0.0, + br, bg, b, 0.0, + blr, blg, blb, 1.0); + +mat4 adjust = mat4((1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0, +(1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0, +(1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0, +0.0, 0.0, 0.0, 1.0); + color *= adjust; + screen = clamp(screen * lum, 0.0, 1.0); + screen = color * screen; + gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen / 8.))); +} diff --git a/res/shaders/gba-color.shader/manifest.ini b/res/shaders/gba-color.shader/manifest.ini new file mode 100644 index 000000000..af6c57cf5 --- /dev/null +++ b/res/shaders/gba-color.shader/manifest.ini @@ -0,0 +1,99 @@ +[shader] +name=GBA Color +author=Pokefan531 and hunterk +description=Modifies the color output to simulate the GBA LCD characteristics. +passes=1 + +[pass.0] +fragmentShader=gba-color.fs +blend=1 + +[pass.0.uniform.darken_screen] +type=float +default=0.5 +readableName=Darken Screen + +[pass.0.uniform.target_gamma] +type=float +default=2.2 +readableName=Target Gamma + +[pass.0.uniform.display_gamma] +type=float +default=2.5 +readableName=Display Gamma + +[pass.0.uniform.sat] +type=float +default=1.0 +readableName=Saturation + +[pass.0.uniform.lum] +type=float +default=0.99 +readableName=Luminance + +[pass.0.uniform.contrast] +type=float +default=1.0 +readableName=Constrast + +[pass.0.uniform.blr] +type=float +default=0.0 +readableName=Black's Red Tint + +[pass.0.uniform.blg] +type=float +default=0.0 +readableName=Black's Green Tint + +[pass.0.uniform.blb] +type=float +default=0.0 +readableName=Black's Blue Tint + +[pass.0.uniform.r] +type=float +default=0.84 +readableName=Red's Intensity + +[pass.0.uniform.g] +type=float +default=0.67 +readableName=Green's Intensity + +[pass.0.uniform.b] +type=float +default=0.73 +readableName=Blue's Intensity + +[pass.0.uniform.rg] +type=float +default=0.09 +readableName=Red's Green Tint + +[pass.0.uniform.rb] +type=float +default=0.15 +readableName=Red's Blue Tint + +[pass.0.uniform.gr] +type=float +default=0.18 +readableName=Green's Red Tint + +[pass.0.uniform.gb] +type=float +default=0.10 +readableName=Green's Blue Tint + +[pass.0.uniform.br] +type=float +default=0.0 +readableName=Blue's Red Tint + +[pass.0.uniform.bg] +type=float +default=0.26 +readableName=Blue's Green Tint \ No newline at end of file From 30a308d04fbb6f26be74b440aa3fe034701db752 Mon Sep 17 00:00:00 2001 From: hunterk Date: Thu, 13 Apr 2017 15:29:00 -0500 Subject: [PATCH 064/159] gba-color cleanups --- res/shaders/gba-color.shader/gba-color.fs | 48 ++++++------- res/shaders/gba-color.shader/manifest.ini | 85 ----------------------- 2 files changed, 20 insertions(+), 113 deletions(-) diff --git a/res/shaders/gba-color.shader/gba-color.fs b/res/shaders/gba-color.shader/gba-color.fs index 6f996bfde..3730b1881 100644 --- a/res/shaders/gba-color.shader/gba-color.fs +++ b/res/shaders/gba-color.shader/gba-color.fs @@ -3,38 +3,30 @@ uniform sampler2D tex; uniform vec2 texSize; uniform float darken_screen; -uniform float target_gamma; -uniform float display_gamma; -uniform float sat; -uniform float lum; -uniform float contrast; -uniform float blr; -uniform float blg; -uniform float blb; -uniform float r; -uniform float g; -uniform float b; -uniform float rg; -uniform float rb; -uniform float gr; -uniform float gb; -uniform float br; -uniform float bg; +const float target_gamma = 2.2; +const float display_gamma = 2.5; +const float sat = 1.0; +const float lum = 0.99; +const float contrast = 1.0; +const vec3 bl = vec3(0.0, 0.0, 0.0); +const vec3 r = vec3(0.84, 0.09, 0.15); +const vec3 g = vec3(0.18, 0.67, 0.10); +const vec3 b = vec3(0.0, 0.26, 0.73); void main() { - vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; - vec4 avglum = vec4(0.5); - screen = mix(screen, avglum, (1.0 - contrast)); + vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; + vec4 avglum = vec4(0.5); + screen = mix(screen, avglum, (1.0 - contrast)); -mat4 color = mat4(r, rg, rb, 0.0, - gr, g, gb, 0.0, - br, bg, b, 0.0, - blr, blg, blb, 1.0); + mat4 color = mat4( r.r, r.g, r.b, 0.0, + g.r, g.g, g.b, 0.0, + b.r, b.g, b.b, 0.0, + bl.r, bl.g, bl.b, 1.0); -mat4 adjust = mat4((1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0, -(1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0, -(1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0, -0.0, 0.0, 0.0, 1.0); + mat4 adjust = mat4( (1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0, + (1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0, + (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0, + 0.0, 0.0, 0.0, 1.0); color *= adjust; screen = clamp(screen * lum, 0.0, 1.0); screen = color * screen; diff --git a/res/shaders/gba-color.shader/manifest.ini b/res/shaders/gba-color.shader/manifest.ini index af6c57cf5..4ab3403ec 100644 --- a/res/shaders/gba-color.shader/manifest.ini +++ b/res/shaders/gba-color.shader/manifest.ini @@ -12,88 +12,3 @@ blend=1 type=float default=0.5 readableName=Darken Screen - -[pass.0.uniform.target_gamma] -type=float -default=2.2 -readableName=Target Gamma - -[pass.0.uniform.display_gamma] -type=float -default=2.5 -readableName=Display Gamma - -[pass.0.uniform.sat] -type=float -default=1.0 -readableName=Saturation - -[pass.0.uniform.lum] -type=float -default=0.99 -readableName=Luminance - -[pass.0.uniform.contrast] -type=float -default=1.0 -readableName=Constrast - -[pass.0.uniform.blr] -type=float -default=0.0 -readableName=Black's Red Tint - -[pass.0.uniform.blg] -type=float -default=0.0 -readableName=Black's Green Tint - -[pass.0.uniform.blb] -type=float -default=0.0 -readableName=Black's Blue Tint - -[pass.0.uniform.r] -type=float -default=0.84 -readableName=Red's Intensity - -[pass.0.uniform.g] -type=float -default=0.67 -readableName=Green's Intensity - -[pass.0.uniform.b] -type=float -default=0.73 -readableName=Blue's Intensity - -[pass.0.uniform.rg] -type=float -default=0.09 -readableName=Red's Green Tint - -[pass.0.uniform.rb] -type=float -default=0.15 -readableName=Red's Blue Tint - -[pass.0.uniform.gr] -type=float -default=0.18 -readableName=Green's Red Tint - -[pass.0.uniform.gb] -type=float -default=0.10 -readableName=Green's Blue Tint - -[pass.0.uniform.br] -type=float -default=0.0 -readableName=Blue's Red Tint - -[pass.0.uniform.bg] -type=float -default=0.26 -readableName=Blue's Green Tint \ No newline at end of file From 9a78f564676de81899f47f23486488d01ea8ed6b Mon Sep 17 00:00:00 2001 From: hunterk Date: Thu, 13 Apr 2017 15:34:34 -0500 Subject: [PATCH 065/159] fix some indents --- res/shaders/gba-color.shader/gba-color.fs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/res/shaders/gba-color.shader/gba-color.fs b/res/shaders/gba-color.shader/gba-color.fs index 3730b1881..79a4c85d8 100644 --- a/res/shaders/gba-color.shader/gba-color.fs +++ b/res/shaders/gba-color.shader/gba-color.fs @@ -8,10 +8,10 @@ const float display_gamma = 2.5; const float sat = 1.0; const float lum = 0.99; const float contrast = 1.0; -const vec3 bl = vec3(0.0, 0.0, 0.0); -const vec3 r = vec3(0.84, 0.09, 0.15); -const vec3 g = vec3(0.18, 0.67, 0.10); -const vec3 b = vec3(0.0, 0.26, 0.73); +const vec3 bl = vec3(0.0, 0.0, 0.0); +const vec3 r = vec3(0.84, 0.09, 0.15); +const vec3 g = vec3(0.18, 0.67, 0.10); +const vec3 b = vec3(0.0, 0.26, 0.73); void main() { vec4 screen = pow(texture2D(tex, texCoord), vec4(target_gamma + darken_screen)).rgba; @@ -19,14 +19,14 @@ void main() { screen = mix(screen, avglum, (1.0 - contrast)); mat4 color = mat4( r.r, r.g, r.b, 0.0, - g.r, g.g, g.b, 0.0, - b.r, b.g, b.b, 0.0, - bl.r, bl.g, bl.b, 1.0); + g.r, g.g, g.b, 0.0, + b.r, b.g, b.b, 0.0, + bl.r, bl.g, bl.b, 1.0); mat4 adjust = mat4( (1.0 - sat) * 0.3086 + sat, (1.0 - sat) * 0.3086, (1.0 - sat) * 0.3086, 1.0, - (1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0, - (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0, - 0.0, 0.0, 0.0, 1.0); + (1.0 - sat) * 0.6094, (1.0 - sat) * 0.6094 + sat, (1.0 - sat) * 0.6094, 1.0, + (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820, (1.0 - sat) * 0.0820 + sat, 1.0, + 0.0, 0.0, 0.0, 1.0); color *= adjust; screen = clamp(screen * lum, 0.0, 1.0); screen = color * screen; From 86db6fa7e2329203344dee59d984a92baa8f85cf Mon Sep 17 00:00:00 2001 From: hunterk Date: Tue, 25 Apr 2017 22:37:48 -0500 Subject: [PATCH 066/159] change division to multiplication in gba-color shader --- res/shaders/gba-color.shader/gba-color.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/shaders/gba-color.shader/gba-color.fs b/res/shaders/gba-color.shader/gba-color.fs index 79a4c85d8..09177ddfe 100644 --- a/res/shaders/gba-color.shader/gba-color.fs +++ b/res/shaders/gba-color.shader/gba-color.fs @@ -30,5 +30,5 @@ void main() { color *= adjust; screen = clamp(screen * lum, 0.0, 1.0); screen = color * screen; - gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen / 8.))); + gl_FragColor = pow(screen, vec4(1.0 / display_gamma + (darken_screen * 0.125))); } From 953335846b9a6dca420875cfd5edaea055eae681 Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Mon, 24 Apr 2017 00:16:11 -0400 Subject: [PATCH 067/159] All: Fix build on Haiku. --- include/mgba-util/platform/posix/threading.h | 5 +++++ src/util/vfs/vfs-dirent.c | 2 +- src/util/vfs/vfs-fd.c | 4 ++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/mgba-util/platform/posix/threading.h b/include/mgba-util/platform/posix/threading.h index b880c548d..f79d97b10 100644 --- a/include/mgba-util/platform/posix/threading.h +++ b/include/mgba-util/platform/posix/threading.h @@ -14,6 +14,8 @@ CXX_GUARD_START #include #if defined(__FreeBSD__) || defined(__OpenBSD__) #include +#elif defined(__HAIKU__) +#include #endif #define THREAD_ENTRY void* @@ -88,6 +90,9 @@ static inline int ThreadSetName(const char* name) { #elif defined(__FreeBSD__) || defined(__OpenBSD__) 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 return pthread_setname_np(pthread_self(), name); #else diff --git a/src/util/vfs/vfs-dirent.c b/src/util/vfs/vfs-dirent.c index d124af355..c41c5b728 100644 --- a/src/util/vfs/vfs-dirent.c +++ b/src/util/vfs/vfs-dirent.c @@ -147,7 +147,7 @@ const char* _vdeName(struct VDirEntry* vde) { static enum VFSType _vdeType(struct VDirEntry* vde) { struct VDirEntryDE* vdede = (struct VDirEntryDE*) vde; -#ifndef WIN32 +#if !defined(WIN32) && !defined(__HAIKU__) if (vdede->ent->d_type == DT_DIR) { return VFS_DIRECTORY; } diff --git a/src/util/vfs/vfs-fd.c b/src/util/vfs/vfs-fd.c index 2b301703d..4a20eaf79 100644 --- a/src/util/vfs/vfs-fd.c +++ b/src/util/vfs/vfs-fd.c @@ -166,7 +166,11 @@ static bool _vfdSync(struct VFile* vf, const void* buffer, size_t size) { UNUSED(size); struct VFileFD* vfd = (struct VFileFD*) vf; #ifndef _WIN32 +#ifdef __HAIKU__ + futimens(vfd->fd, NULL); +#else futimes(vfd->fd, NULL); +#endif if (buffer && size) { return msync(buffer, size, MS_SYNC) == 0; } From d68a4f97c51a6277ea7ba95245679f0cc62cd973 Mon Sep 17 00:00:00 2001 From: Anty-Lemon Date: Thu, 27 Apr 2017 13:56:44 -0400 Subject: [PATCH 068/159] SDL: Fix software renderer build --- src/platform/sdl/sw-sdl.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/platform/sdl/sw-sdl.c b/src/platform/sdl/sw-sdl.c index 3074dec0d..498c18783 100644 --- a/src/platform/sdl/sw-sdl.c +++ b/src/platform/sdl/sw-sdl.c @@ -5,9 +5,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "main.h" -#include "core/thread.h" -#include "core/version.h" -#include "util/arm-algo.h" +#include +#include +#include +#include static bool mSDLSWInit(struct mSDLRenderer* renderer); static void mSDLSWRunloop(struct mSDLRenderer* renderer, void* user); From d84cee4700a11825259d6396d0b519b0b997f641 Mon Sep 17 00:00:00 2001 From: Anty-Lemon Date: Thu, 27 Apr 2017 13:57:05 -0400 Subject: [PATCH 069/159] SDL: Fix GLES2 build --- src/platform/sdl/gles2-sdl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/sdl/gles2-sdl.c b/src/platform/sdl/gles2-sdl.c index 7d55362c9..041315b8e 100644 --- a/src/platform/sdl/gles2-sdl.c +++ b/src/platform/sdl/gles2-sdl.c @@ -7,7 +7,8 @@ #include "gl-common.h" -#include "core/thread.h" +#include +#include #ifndef __APPLE__ #include From a5f029c2faa14f9e65d6c8413498057348100ede Mon Sep 17 00:00:00 2001 From: Anty-Lemon Date: Thu, 27 Apr 2017 13:35:26 -0400 Subject: [PATCH 070/159] GBA: Fix typo of GBAIsBIOS --- src/gba/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gba/core.c b/src/gba/core.c index 8636489f2..9a758afd2 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -335,7 +335,7 @@ static void _GBACoreReset(struct mCore* core) { mCoreConfigDirectory(path, PATH_MAX); strncat(path, PATH_SEP "gba_bios.bin", PATH_MAX - strlen(path)); bios = VFileOpen(path, O_RDONLY); - if (bios && GBIsBIOS(bios)) { + if (bios && GBAIsBIOS(bios)) { found = true; } else if (bios) { bios->close(bios); From a861af6a97cf3bc0fe891ad571a6eccb96638164 Mon Sep 17 00:00:00 2001 From: Anty-Lemon Date: Fri, 28 Apr 2017 12:04:27 -0400 Subject: [PATCH 071/159] Qt: Fix builds without GDB stub and libpng --- src/platform/qt/GameController.cpp | 5 +++-- src/platform/qt/GameController.h | 2 +- src/platform/qt/ObjView.cpp | 6 ++++++ src/platform/qt/ObjView.h | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index bf59d7ee5..8cf59c8f1 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -332,7 +332,7 @@ void GameController::setConfig(const mCoreConfig* config) { } } -#ifdef USE_GDB_STUB +#ifdef USE_DEBUGGERS mDebugger* GameController::debugger() { if (!isLoaded()) { return nullptr; @@ -622,8 +622,9 @@ void GameController::closeGame() { if (!m_gameOpen) { return; } - +#ifdef USE_DEBUGGERS setDebugger(nullptr); +#endif if (mCoreThreadIsPaused(&m_threadContext)) { mCoreThreadUnpause(&m_threadContext); } diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index acd09f258..3fe79d087 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -86,7 +86,7 @@ public: int stateSlot() const { return m_stateSlot; } -#ifdef USE_GDB_STUB +#ifdef USE_DEBUGGERS mDebugger* debugger(); void setDebugger(mDebugger*); #endif diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index dc74bebb6..ae76ecf76 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -51,7 +51,11 @@ ObjView::ObjView(GameController* controller, QWidget* parent) connect(m_ui.magnification, static_cast(&QSpinBox::valueChanged), [this]() { updateTiles(true); }); +#ifdef USE_PNG connect(m_ui.exportButton, SIGNAL(clicked()), this, SLOT(exportObj())); +#else + m_ui.exportButton->setVisible(false); +#endif } void ObjView::selectObj(int obj) { @@ -242,6 +246,7 @@ void ObjView::updateTilesGB(bool force) { } #endif +#ifdef USE_PNG void ObjView::exportObj() { GameController::Interrupter interrupter(m_controller); QString filename = GBAApp::app()->getSaveFileName(this, tr("Export sprite"), @@ -282,6 +287,7 @@ void ObjView::exportObj() { PNGWriteClose(png, info); delete[] buffer; } +#endif bool ObjView::ObjInfo::operator!=(const ObjInfo& other) { return other.tile != tile || diff --git a/src/platform/qt/ObjView.h b/src/platform/qt/ObjView.h index 16a72961b..487a54264 100644 --- a/src/platform/qt/ObjView.h +++ b/src/platform/qt/ObjView.h @@ -21,8 +21,10 @@ Q_OBJECT public: ObjView(GameController* controller, QWidget* parent = nullptr); +#ifdef USE_PNG public slots: void exportObj(); +#endif private slots: void selectObj(int); From 11edac0aa453e3735e4405b8165a2975e7d555f1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 13 Apr 2017 17:15:16 -0700 Subject: [PATCH 072/159] Core: Start working on video proxy --- include/mgba/core/video-proxy.h | 61 ++++++++ .../internal/gba/renderers/thread-proxy.h | 9 +- src/core/video-proxy.c | 116 ++++++++++++++ src/gba/renderers/thread-proxy.c | 145 +++++------------- 4 files changed, 220 insertions(+), 111 deletions(-) create mode 100644 include/mgba/core/video-proxy.h create mode 100644 src/core/video-proxy.c diff --git a/include/mgba/core/video-proxy.h b/include/mgba/core/video-proxy.h new file mode 100644 index 000000000..0d6d94def --- /dev/null +++ b/include/mgba/core/video-proxy.h @@ -0,0 +1,61 @@ +/* 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 VIDEO_PROXY_H +#define VIDEO_PROXY_H + +#include + +CXX_GUARD_START + +enum mVideoProxyDirtyType { + DIRTY_DUMMY = 0, + DIRTY_FLUSH, + DIRTY_SCANLINE, + DIRTY_REGISTER, + DIRTY_OAM, + DIRTY_PALETTE, + DIRTY_VRAM +}; + +struct mVideoProxyDirtyInfo { + enum mVideoProxyDirtyType type; + uint32_t address; + uint16_t value; + uint32_t padding; +}; + +struct mVideoProxy { + bool (*writeData)(struct mVideoProxy* proxy, void* data, size_t length); + uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address); + void* context; + + size_t vramSize; + size_t oamSize; + size_t paletteSize; + + uint32_t* vramDirtyBitmap; + uint32_t* oamDirtyBitmap; + + uint16_t* vram; + uint16_t* oam; + uint16_t* palette; +}; + +void mVideoProxyRendererInit(struct mVideoProxy* proxy); +void mVideoProxyRendererDeinit(struct mVideoProxy* proxy); +void mVideoProxyRendererReset(struct mVideoProxy* proxy); + +void mVideoProxyRendererWriteVideoRegister(struct mVideoProxy* proxy, uint32_t address, uint16_t value); +void mVideoProxyRendererWriteVRAM(struct mVideoProxy* proxy, uint32_t address); +void mVideoProxyRendererWritePalette(struct mVideoProxy* proxy, uint32_t address, uint16_t value); +void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, uint16_t value); + +void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y); +void mVideoProxyRendererFlush(struct mVideoProxy* proxy); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/internal/gba/renderers/thread-proxy.h b/include/mgba/internal/gba/renderers/thread-proxy.h index 6fa07215a..07bd93bc1 100644 --- a/include/mgba/internal/gba/renderers/thread-proxy.h +++ b/include/mgba/internal/gba/renderers/thread-proxy.h @@ -10,6 +10,7 @@ CXX_GUARD_START +#include #include #include #include @@ -23,6 +24,7 @@ enum GBAVideoThreadProxyState { struct GBAVideoThreadProxyRenderer { struct GBAVideoRenderer d; struct GBAVideoRenderer* backend; + struct mVideoProxy proxy; Thread thread; Condition fromThreadCond; @@ -31,13 +33,6 @@ struct GBAVideoThreadProxyRenderer { enum GBAVideoThreadProxyState threadState; struct RingFIFO dirtyQueue; - - uint32_t vramDirtyBitmap; - uint32_t oamDirtyBitmap[16]; - - uint16_t* vramProxy; - union GBAOAM oamProxy; - uint16_t paletteProxy[512]; }; void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend); diff --git a/src/core/video-proxy.c b/src/core/video-proxy.c new file mode 100644 index 000000000..5e736c720 --- /dev/null +++ b/src/core/video-proxy.c @@ -0,0 +1,116 @@ +/* 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 + +#include + +static inline size_t _roundUp(size_t value, int shift) { + value += (1 << shift) - 1; + return value >> shift; +} + +void mVideoProxyRendererInit(struct mVideoProxy* proxy) { + proxy->palette = anonymousMemoryMap(proxy->paletteSize); + proxy->vram = anonymousMemoryMap(proxy->vramSize); + proxy->oam = anonymousMemoryMap(proxy->oamSize); + + proxy->vramDirtyBitmap = calloc(_roundUp(proxy->vramSize, 17), sizeof(uint32_t)); + proxy->oamDirtyBitmap = calloc(_roundUp(proxy->oamSize, 6), sizeof(uint32_t)); +} + +void mVideoProxyRendererDeinit(struct mVideoProxy* proxy) { + mappedMemoryFree(proxy->palette, proxy->paletteSize); + mappedMemoryFree(proxy->vram, proxy->vramSize); + mappedMemoryFree(proxy->oam, proxy->oamSize); + + free(proxy->vramDirtyBitmap); + free(proxy->oamDirtyBitmap); +} + +void mVideoProxyRendererReset(struct mVideoProxy* proxy) { + memset(proxy->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(proxy->vramSize, 17)); + memset(proxy->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(proxy->oamSize, 6)); +} + +void mVideoProxyRendererWriteVideoRegister(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_REGISTER, + address, + value, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + +void mVideoProxyRendererWriteVRAM(struct mVideoProxy* proxy, uint32_t address) { + int bit = 1 << (address >> 12); + if (proxy->vramDirtyBitmap[address >> 17] & bit) { + return; + } + proxy->vramDirtyBitmap[address >> 17] |= bit; +} + +void mVideoProxyRendererWritePalette(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_PALETTE, + address, + value, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + +void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_OAM, + address, + value, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + +void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y) { + size_t i; + for (i = 0; i < _roundUp(proxy->vramSize, 17); ++i) { + if (proxy->vramDirtyBitmap[i]) { + uint32_t bitmap = proxy->vramDirtyBitmap[i]; + proxy->vramDirtyBitmap[i] = 0; + int j; + for (j = 0; j < 32; ++j) { + if (!(bitmap & (1 << j))) { + continue; + } + struct mVideoProxyDirtyInfo dirty = { + DIRTY_VRAM, + j * 0x1000, + 0xABCD, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); + proxy->writeData(proxy, proxy->vramBlock(proxy, j * 0x1000), 0x1000); + } + } + } + struct mVideoProxyDirtyInfo dirty = { + DIRTY_SCANLINE, + y, + 0, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} + + +void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { + struct mVideoProxyDirtyInfo dirty = { + DIRTY_FLUSH, + 0, + 0, + 0xDEADBEEF, + }; + proxy->writeData(proxy, &dirty, sizeof(dirty)); +} diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index 15c8bba0e..dc9002ed7 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -13,23 +13,6 @@ #ifndef DISABLE_THREADING -enum GBAVideoDirtyType { - DIRTY_DUMMY = 0, - DIRTY_REGISTER, - DIRTY_OAM, - DIRTY_PALETTE, - DIRTY_VRAM, - DIRTY_SCANLINE, - DIRTY_FLUSH -}; - -struct GBAVideoDirtyInfo { - enum GBAVideoDirtyType type; - uint32_t address; - uint16_t value; - uint32_t padding; -}; - static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer); static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer); static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer); @@ -44,6 +27,9 @@ static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* render static THREAD_ENTRY _proxyThread(void* renderer); +static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length); +static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address); + void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { renderer->d.init = GBAVideoThreadProxyRendererInit; renderer->d.reset = GBAVideoThreadProxyRendererReset; @@ -63,6 +49,13 @@ void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* rende renderer->d.disableBG[3] = false; renderer->d.disableOBJ = false; + renderer->proxy.context = renderer; + renderer->proxy.writeData = _writeData; + renderer->proxy.vramBlock = _vramBlock; + renderer->proxy.paletteSize = SIZE_PALETTE_RAM; + renderer->proxy.vramSize = SIZE_VRAM; + renderer->proxy.oamSize = SIZE_OAM; + renderer->backend = backend; } @@ -73,15 +66,15 @@ void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) { MutexInit(&proxyRenderer->mutex); RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000); - proxyRenderer->vramProxy = anonymousMemoryMap(SIZE_VRAM); - proxyRenderer->backend->palette = proxyRenderer->paletteProxy; - proxyRenderer->backend->vram = proxyRenderer->vramProxy; - proxyRenderer->backend->oam = &proxyRenderer->oamProxy; + mVideoProxyRendererInit(&proxyRenderer->proxy); + + proxyRenderer->backend->palette = proxyRenderer->proxy.palette; + proxyRenderer->backend->vram = proxyRenderer->proxy.vram; + proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->proxy.oam; proxyRenderer->backend->cache = NULL; proxyRenderer->backend->init(proxyRenderer->backend); - proxyRenderer->vramDirtyBitmap = 0; proxyRenderer->threadState = PROXY_THREAD_IDLE; ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } @@ -93,9 +86,12 @@ void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } - memcpy(&proxyRenderer->oamProxy.raw, &renderer->oam->raw, SIZE_OAM); - memcpy(proxyRenderer->paletteProxy, renderer->palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->vramProxy, renderer->vram, SIZE_VRAM); + memcpy(proxyRenderer->proxy.oam, &renderer->oam->raw, SIZE_OAM); + memcpy(proxyRenderer->proxy.palette, renderer->palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->proxy.vram, renderer->vram, SIZE_VRAM); + + mVideoProxyRendererReset(&proxyRenderer->proxy); + proxyRenderer->backend->reset(proxyRenderer->backend); MutexUnlock(&proxyRenderer->mutex); } @@ -122,7 +118,7 @@ void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) { MutexDeinit(&proxyRenderer->mutex); proxyRenderer->backend->deinit(proxyRenderer->backend); - mappedMemoryFree(proxyRenderer->vramProxy, SIZE_VRAM); + mVideoProxyRendererDeinit(&proxyRenderer->proxy); } void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { @@ -138,7 +134,8 @@ void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -static bool _writeData(struct GBAVideoThreadProxyRenderer* proxyRenderer, void* data, size_t length) { +static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) { mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length); MutexLock(&proxyRenderer->mutex); @@ -154,6 +151,11 @@ static bool _writeData(struct GBAVideoThreadProxyRenderer* proxyRenderer, void* return true; } +static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; + return &proxyRenderer->d.vram[address >> 1]; +} + uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; switch (address) { @@ -178,23 +180,13 @@ uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* return value; } - struct GBAVideoDirtyInfo dirty = { - DIRTY_REGISTER, - address, - value, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererWriteVideoRegister(&proxyRenderer->proxy, address, value); return value; } void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - int bit = 1 << (address >> 12); - if (proxyRenderer->vramDirtyBitmap & bit) { - return; - } - proxyRenderer->vramDirtyBitmap |= bit; + mVideoProxyRendererWriteVRAM(&proxyRenderer->proxy, address); if (renderer->cache) { mTileCacheWriteVRAM(renderer->cache, address); } @@ -202,13 +194,7 @@ void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uin void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - struct GBAVideoDirtyInfo dirty = { - DIRTY_PALETTE, - address, - value, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererWritePalette(&proxyRenderer->proxy, address, value); if (renderer->cache) { mTileCacheWritePalette(renderer->cache, address); } @@ -216,42 +202,12 @@ void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - struct GBAVideoDirtyInfo dirty = { - DIRTY_OAM, - oam, - proxyRenderer->d.oam->raw[oam], - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererWriteOAM(&proxyRenderer->proxy, oam, proxyRenderer->d.oam->raw[oam]); } void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - if (proxyRenderer->vramDirtyBitmap) { - int bitmap = proxyRenderer->vramDirtyBitmap; - proxyRenderer->vramDirtyBitmap = 0; - int j; - for (j = 0; j < 24; ++j) { - if (!(bitmap & (1 << j))) { - continue; - } - struct GBAVideoDirtyInfo dirty = { - DIRTY_VRAM, - j * 0x1000, - 0xABCD, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); - _writeData(proxyRenderer, &proxyRenderer->d.vram[j * 0x800], 0x1000); - } - } - struct GBAVideoDirtyInfo dirty = { - DIRTY_SCANLINE, - y, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererDrawScanline(&proxyRenderer->proxy, y); if ((y & 15) == 15) { ConditionWake(&proxyRenderer->toThreadCond); } @@ -266,19 +222,12 @@ void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { } MutexLock(&proxyRenderer->mutex); // Insert an extra item into the queue to make sure it gets flushed - struct GBAVideoDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererFlush(&proxyRenderer->proxy); do { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } while (proxyRenderer->threadState == PROXY_THREAD_BUSY); proxyRenderer->backend->finishFrame(proxyRenderer->backend); - proxyRenderer->vramDirtyBitmap = 0; MutexUnlock(&proxyRenderer->mutex); } @@ -286,13 +235,7 @@ static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* render struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; MutexLock(&proxyRenderer->mutex); // Insert an extra item into the queue to make sure it gets flushed - struct GBAVideoDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererFlush(&proxyRenderer->proxy); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); @@ -305,13 +248,7 @@ static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* render struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; MutexLock(&proxyRenderer->mutex); // Insert an extra item into the queue to make sure it gets flushed - struct GBAVideoDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - _writeData(proxyRenderer, &dirty, sizeof(dirty)); + mVideoProxyRendererFlush(&proxyRenderer->proxy); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); @@ -325,7 +262,7 @@ static THREAD_ENTRY _proxyThread(void* renderer) { ThreadSetName("Proxy Renderer Thread"); MutexLock(&proxyRenderer->mutex); - struct GBAVideoDirtyInfo item = {0}; + struct mVideoProxyDirtyInfo item = {0}; while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { @@ -340,15 +277,15 @@ static THREAD_ENTRY _proxyThread(void* renderer) { proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value); break; case DIRTY_PALETTE: - proxyRenderer->paletteProxy[item.address >> 1] = item.value; + proxyRenderer->proxy.palette[item.address >> 1] = item.value; proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value); break; case DIRTY_OAM: - proxyRenderer->oamProxy.raw[item.address] = item.value; + proxyRenderer->proxy.oam[item.address] = item.value; proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address); break; case DIRTY_VRAM: - while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->vramProxy[item.address >> 1], 0x1000)) { + while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->proxy.vram[item.address >> 1], 0x1000)) { mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?"); MutexLock(&proxyRenderer->mutex); ConditionWake(&proxyRenderer->fromThreadCond); From eab5ed6e142e75656adbe918f2422d936f67c5c2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 13 Apr 2017 22:13:26 -0700 Subject: [PATCH 073/159] Core: Begin splitting threading out from GBA proxy --- include/mgba/core/video-proxy.h | 9 ++- src/core/video-proxy.c | 22 ++++++- src/gba/renderers/thread-proxy.c | 106 +++++++++++++++++-------------- 3 files changed, 88 insertions(+), 49 deletions(-) diff --git a/include/mgba/core/video-proxy.h b/include/mgba/core/video-proxy.h index 0d6d94def..bd2dd0474 100644 --- a/include/mgba/core/video-proxy.h +++ b/include/mgba/core/video-proxy.h @@ -28,10 +28,13 @@ struct mVideoProxyDirtyInfo { }; struct mVideoProxy { - bool (*writeData)(struct mVideoProxy* proxy, void* data, size_t length); - uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address); + bool (*writeData)(struct mVideoProxy* proxy, const void* data, size_t length); + bool (*readData)(struct mVideoProxy* proxy, void* data, size_t length, bool block); void* context; + bool (*parsePacket)(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet); + uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address); + size_t vramSize; size_t oamSize; size_t paletteSize; @@ -56,6 +59,8 @@ void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, ui void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y); void mVideoProxyRendererFlush(struct mVideoProxy* proxy); +bool mVideoProxyRendererRun(struct mVideoProxy* proxy); + CXX_GUARD_END #endif diff --git a/src/core/video-proxy.c b/src/core/video-proxy.c index 5e736c720..ed3e639ea 100644 --- a/src/core/video-proxy.c +++ b/src/core/video-proxy.c @@ -104,7 +104,6 @@ void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y) { proxy->writeData(proxy, &dirty, sizeof(dirty)); } - void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { struct mVideoProxyDirtyInfo dirty = { DIRTY_FLUSH, @@ -114,3 +113,24 @@ void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { }; proxy->writeData(proxy, &dirty, sizeof(dirty)); } + +bool mVideoProxyRendererRun(struct mVideoProxy* proxy) { + struct mVideoProxyDirtyInfo item = {0}; + while (proxy->readData(proxy, &item, sizeof(item), false)) { + switch (item.type) { + case DIRTY_REGISTER: + case DIRTY_PALETTE: + case DIRTY_OAM: + case DIRTY_VRAM: + case DIRTY_SCANLINE: + case DIRTY_FLUSH: + if (!proxy->parsePacket(proxy, &item)) { + return true; + } + break; + default: + return false; + } + } + return true; +} diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index dc9002ed7..6dc7b51fc 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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 @@ -27,7 +27,9 @@ static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* render static THREAD_ENTRY _proxyThread(void* renderer); -static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length); +static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length); +static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block); +static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet); static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address); void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { @@ -51,6 +53,8 @@ void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* rende renderer->proxy.context = renderer; renderer->proxy.writeData = _writeData; + renderer->proxy.readData = _readData; + renderer->proxy.parsePacket = _parsePacket; renderer->proxy.vramBlock = _vramBlock; renderer->proxy.paletteSize = SIZE_PALETTE_RAM; renderer->proxy.vramSize = SIZE_VRAM; @@ -134,7 +138,7 @@ void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length) { +static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length) { struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) { mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length); @@ -151,6 +155,52 @@ static bool _writeData(struct mVideoProxy* proxy, void* data, size_t length) { return true; } +static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; + bool read = false; + while (true) { + read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length); + if (!block || read) { + break; + } + mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?"); + MutexLock(&proxyRenderer->mutex); + ConditionWake(&proxyRenderer->fromThreadCond); + ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); + MutexUnlock(&proxyRenderer->mutex); + } + return read; +} + +static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* item) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; + switch (item->type) { + case DIRTY_REGISTER: + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_PALETTE: + proxy->palette[item->address >> 1] = item->value; + proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_OAM: + proxy->oam[item->address] = item->value; + proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + break; + case DIRTY_VRAM: + proxy->readData(proxy, &proxy->vram[item->address >> 1], 0x1000, true); + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + break; + case DIRTY_SCANLINE: + proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); + break; + case DIRTY_FLUSH: + return false; + default: + return false; + } + return true; +} + static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) { struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; return &proxyRenderer->d.vram[address >> 1]; @@ -262,55 +312,19 @@ static THREAD_ENTRY _proxyThread(void* renderer) { ThreadSetName("Proxy Renderer Thread"); MutexLock(&proxyRenderer->mutex); - struct mVideoProxyDirtyInfo item = {0}; while (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { break; } - if (RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))) { - proxyRenderer->threadState = PROXY_THREAD_BUSY; - MutexUnlock(&proxyRenderer->mutex); - do { - switch (item.type) { - case DIRTY_REGISTER: - proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item.address, item.value); - break; - case DIRTY_PALETTE: - proxyRenderer->proxy.palette[item.address >> 1] = item.value; - proxyRenderer->backend->writePalette(proxyRenderer->backend, item.address, item.value); - break; - case DIRTY_OAM: - proxyRenderer->proxy.oam[item.address] = item.value; - proxyRenderer->backend->writeOAM(proxyRenderer->backend, item.address); - break; - case DIRTY_VRAM: - while (!RingFIFORead(&proxyRenderer->dirtyQueue, &proxyRenderer->proxy.vram[item.address >> 1], 0x1000)) { - mLOG(GBA_VIDEO, DEBUG, "Proxy thread can't read VRAM. CPU thread asleep?"); - MutexLock(&proxyRenderer->mutex); - ConditionWake(&proxyRenderer->fromThreadCond); - ConditionWait(&proxyRenderer->toThreadCond, &proxyRenderer->mutex); - MutexUnlock(&proxyRenderer->mutex); - } - proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item.address); - break; - case DIRTY_SCANLINE: - proxyRenderer->backend->drawScanline(proxyRenderer->backend, item.address); - break; - case DIRTY_FLUSH: - MutexLock(&proxyRenderer->mutex); - goto out; - default: - // FIFO was corrupted - MutexLock(&proxyRenderer->mutex); - proxyRenderer->threadState = PROXY_THREAD_STOPPED; - mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!"); - goto out; - } - } while (proxyRenderer->threadState == PROXY_THREAD_BUSY && RingFIFORead(&proxyRenderer->dirtyQueue, &item, sizeof(item))); - MutexLock(&proxyRenderer->mutex); + proxyRenderer->threadState = PROXY_THREAD_BUSY; + MutexUnlock(&proxyRenderer->mutex); + if (!mVideoProxyRendererRun(&proxyRenderer->proxy)) { + // FIFO was corrupted + proxyRenderer->threadState = PROXY_THREAD_STOPPED; + mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!"); } - out: + MutexLock(&proxyRenderer->mutex); ConditionWake(&proxyRenderer->fromThreadCond); if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { proxyRenderer->threadState = PROXY_THREAD_IDLE; From bed6ba1fc48a7432c8b45ebb38ea7e403c45bfb0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 14 Apr 2017 14:05:29 -0700 Subject: [PATCH 074/159] GBA Video: Refactor thread proxy out from proxy --- include/mgba/core/video-logger.h | 66 +++++ include/mgba/core/video-proxy.h | 66 ----- include/mgba/internal/gba/renderers/proxy.h | 37 +++ .../internal/gba/renderers/thread-proxy.h | 6 +- src/core/video-logger.c | 136 ++++++++++ src/core/video-proxy.c | 136 ---------- src/gba/core.c | 2 +- src/gba/renderers/proxy.c | 239 ++++++++++++++++++ src/gba/renderers/thread-proxy.c | 236 ++++------------- 9 files changed, 526 insertions(+), 398 deletions(-) create mode 100644 include/mgba/core/video-logger.h delete mode 100644 include/mgba/core/video-proxy.h create mode 100644 include/mgba/internal/gba/renderers/proxy.h create mode 100644 src/core/video-logger.c delete mode 100644 src/core/video-proxy.c create mode 100644 src/gba/renderers/proxy.c diff --git a/include/mgba/core/video-logger.h b/include/mgba/core/video-logger.h new file mode 100644 index 000000000..207385ea2 --- /dev/null +++ b/include/mgba/core/video-logger.h @@ -0,0 +1,66 @@ +/* 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 VIDEO_LOGGER_H +#define VIDEO_LOGGER_H + +#include + +CXX_GUARD_START + +enum mVideoLoggerDirtyType { + DIRTY_DUMMY = 0, + DIRTY_FLUSH, + DIRTY_SCANLINE, + DIRTY_REGISTER, + DIRTY_OAM, + DIRTY_PALETTE, + DIRTY_VRAM +}; + +struct mVideoLoggerDirtyInfo { + enum mVideoLoggerDirtyType type; + uint32_t address; + uint16_t value; + uint32_t padding; +}; + +struct mVideoLogger { + bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length); + bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block); + void* context; + + bool (*parsePacket)(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); + uint16_t* (*vramBlock)(struct mVideoLogger* logger, uint32_t address); + + size_t vramSize; + size_t oamSize; + size_t paletteSize; + + uint32_t* vramDirtyBitmap; + uint32_t* oamDirtyBitmap; + + uint16_t* vram; + uint16_t* oam; + uint16_t* palette; +}; + +void mVideoLoggerRendererInit(struct mVideoLogger* logger); +void mVideoLoggerRendererDeinit(struct mVideoLogger* logger); +void mVideoLoggerRendererReset(struct mVideoLogger* logger); + +void mVideoLoggerRendererWriteVideoRegister(struct mVideoLogger* logger, uint32_t address, uint16_t value); +void mVideoLoggerRendererWriteVRAM(struct mVideoLogger* logger, uint32_t address); +void mVideoLoggerRendererWritePalette(struct mVideoLogger* logger, uint32_t address, uint16_t value); +void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, uint16_t value); + +void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y); +void mVideoLoggerRendererFlush(struct mVideoLogger* logger); + +bool mVideoLoggerRendererRun(struct mVideoLogger* logger); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/core/video-proxy.h b/include/mgba/core/video-proxy.h deleted file mode 100644 index bd2dd0474..000000000 --- a/include/mgba/core/video-proxy.h +++ /dev/null @@ -1,66 +0,0 @@ -/* 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 VIDEO_PROXY_H -#define VIDEO_PROXY_H - -#include - -CXX_GUARD_START - -enum mVideoProxyDirtyType { - DIRTY_DUMMY = 0, - DIRTY_FLUSH, - DIRTY_SCANLINE, - DIRTY_REGISTER, - DIRTY_OAM, - DIRTY_PALETTE, - DIRTY_VRAM -}; - -struct mVideoProxyDirtyInfo { - enum mVideoProxyDirtyType type; - uint32_t address; - uint16_t value; - uint32_t padding; -}; - -struct mVideoProxy { - bool (*writeData)(struct mVideoProxy* proxy, const void* data, size_t length); - bool (*readData)(struct mVideoProxy* proxy, void* data, size_t length, bool block); - void* context; - - bool (*parsePacket)(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet); - uint16_t* (*vramBlock)(struct mVideoProxy* proxy, uint32_t address); - - size_t vramSize; - size_t oamSize; - size_t paletteSize; - - uint32_t* vramDirtyBitmap; - uint32_t* oamDirtyBitmap; - - uint16_t* vram; - uint16_t* oam; - uint16_t* palette; -}; - -void mVideoProxyRendererInit(struct mVideoProxy* proxy); -void mVideoProxyRendererDeinit(struct mVideoProxy* proxy); -void mVideoProxyRendererReset(struct mVideoProxy* proxy); - -void mVideoProxyRendererWriteVideoRegister(struct mVideoProxy* proxy, uint32_t address, uint16_t value); -void mVideoProxyRendererWriteVRAM(struct mVideoProxy* proxy, uint32_t address); -void mVideoProxyRendererWritePalette(struct mVideoProxy* proxy, uint32_t address, uint16_t value); -void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, uint16_t value); - -void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y); -void mVideoProxyRendererFlush(struct mVideoProxy* proxy); - -bool mVideoProxyRendererRun(struct mVideoProxy* proxy); - -CXX_GUARD_END - -#endif diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h new file mode 100644 index 000000000..dd16da688 --- /dev/null +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -0,0 +1,37 @@ +/* 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_VIDEO_PROXY_H +#define GBA_VIDEO_PROXY_H + +#include + +CXX_GUARD_START + +#include +#include + +struct GBAVideoProxyRenderer { + struct GBAVideoRenderer d; + struct GBAVideoRenderer* backend; + struct mVideoLogger logger; + + bool block; + + void (*init)(struct GBAVideoProxyRenderer*); + void (*deinit)(struct GBAVideoProxyRenderer*); + void (*reset)(struct GBAVideoProxyRenderer*); + + void (*lock)(struct GBAVideoProxyRenderer*); + void (*unlock)(struct GBAVideoProxyRenderer*); + void (*wait)(struct GBAVideoProxyRenderer*); + void (*wake)(struct GBAVideoProxyRenderer*, int y); +}; + +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/internal/gba/renderers/thread-proxy.h b/include/mgba/internal/gba/renderers/thread-proxy.h index 07bd93bc1..e47621a25 100644 --- a/include/mgba/internal/gba/renderers/thread-proxy.h +++ b/include/mgba/internal/gba/renderers/thread-proxy.h @@ -10,8 +10,8 @@ CXX_GUARD_START -#include #include +#include #include #include @@ -22,9 +22,7 @@ enum GBAVideoThreadProxyState { }; struct GBAVideoThreadProxyRenderer { - struct GBAVideoRenderer d; - struct GBAVideoRenderer* backend; - struct mVideoProxy proxy; + struct GBAVideoProxyRenderer d; Thread thread; Condition fromThreadCond; diff --git a/src/core/video-logger.c b/src/core/video-logger.c new file mode 100644 index 000000000..507b1ed3e --- /dev/null +++ b/src/core/video-logger.c @@ -0,0 +1,136 @@ +/* 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 + +#include + +static inline size_t _roundUp(size_t value, int shift) { + value += (1 << shift) - 1; + return value >> shift; +} + +void mVideoLoggerRendererInit(struct mVideoLogger* logger) { + logger->palette = anonymousMemoryMap(logger->paletteSize); + logger->vram = anonymousMemoryMap(logger->vramSize); + logger->oam = anonymousMemoryMap(logger->oamSize); + + logger->vramDirtyBitmap = calloc(_roundUp(logger->vramSize, 17), sizeof(uint32_t)); + logger->oamDirtyBitmap = calloc(_roundUp(logger->oamSize, 6), sizeof(uint32_t)); +} + +void mVideoLoggerRendererDeinit(struct mVideoLogger* logger) { + mappedMemoryFree(logger->palette, logger->paletteSize); + mappedMemoryFree(logger->vram, logger->vramSize); + mappedMemoryFree(logger->oam, logger->oamSize); + + free(logger->vramDirtyBitmap); + free(logger->oamDirtyBitmap); +} + +void mVideoLoggerRendererReset(struct mVideoLogger* logger) { + memset(logger->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->vramSize, 17)); + memset(logger->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->oamSize, 6)); +} + +void mVideoLoggerRendererWriteVideoRegister(struct mVideoLogger* logger, uint32_t address, uint16_t value) { + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_REGISTER, + address, + value, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + +void mVideoLoggerRendererWriteVRAM(struct mVideoLogger* logger, uint32_t address) { + int bit = 1 << (address >> 12); + if (logger->vramDirtyBitmap[address >> 17] & bit) { + return; + } + logger->vramDirtyBitmap[address >> 17] |= bit; +} + +void mVideoLoggerRendererWritePalette(struct mVideoLogger* logger, uint32_t address, uint16_t value) { + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_PALETTE, + address, + value, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + +void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, uint16_t value) { + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_OAM, + address, + value, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + +void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { + size_t i; + for (i = 0; i < _roundUp(logger->vramSize, 17); ++i) { + if (logger->vramDirtyBitmap[i]) { + uint32_t bitmap = logger->vramDirtyBitmap[i]; + logger->vramDirtyBitmap[i] = 0; + int j; + for (j = 0; j < 32; ++j) { + if (!(bitmap & (1 << j))) { + continue; + } + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_VRAM, + j * 0x1000, + 0xABCD, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); + logger->writeData(logger, logger->vramBlock(logger, j * 0x1000), 0x1000); + } + } + } + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_SCANLINE, + y, + 0, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + +void mVideoLoggerRendererFlush(struct mVideoLogger* logger) { + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_FLUSH, + 0, + 0, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + +bool mVideoLoggerRendererRun(struct mVideoLogger* logger) { + struct mVideoLoggerDirtyInfo item = {0}; + while (logger->readData(logger, &item, sizeof(item), false)) { + switch (item.type) { + case DIRTY_REGISTER: + case DIRTY_PALETTE: + case DIRTY_OAM: + case DIRTY_VRAM: + case DIRTY_SCANLINE: + case DIRTY_FLUSH: + if (!logger->parsePacket(logger, &item)) { + return true; + } + break; + default: + return false; + } + } + return true; +} diff --git a/src/core/video-proxy.c b/src/core/video-proxy.c deleted file mode 100644 index ed3e639ea..000000000 --- a/src/core/video-proxy.c +++ /dev/null @@ -1,136 +0,0 @@ -/* 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 - -#include - -static inline size_t _roundUp(size_t value, int shift) { - value += (1 << shift) - 1; - return value >> shift; -} - -void mVideoProxyRendererInit(struct mVideoProxy* proxy) { - proxy->palette = anonymousMemoryMap(proxy->paletteSize); - proxy->vram = anonymousMemoryMap(proxy->vramSize); - proxy->oam = anonymousMemoryMap(proxy->oamSize); - - proxy->vramDirtyBitmap = calloc(_roundUp(proxy->vramSize, 17), sizeof(uint32_t)); - proxy->oamDirtyBitmap = calloc(_roundUp(proxy->oamSize, 6), sizeof(uint32_t)); -} - -void mVideoProxyRendererDeinit(struct mVideoProxy* proxy) { - mappedMemoryFree(proxy->palette, proxy->paletteSize); - mappedMemoryFree(proxy->vram, proxy->vramSize); - mappedMemoryFree(proxy->oam, proxy->oamSize); - - free(proxy->vramDirtyBitmap); - free(proxy->oamDirtyBitmap); -} - -void mVideoProxyRendererReset(struct mVideoProxy* proxy) { - memset(proxy->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(proxy->vramSize, 17)); - memset(proxy->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(proxy->oamSize, 6)); -} - -void mVideoProxyRendererWriteVideoRegister(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { - struct mVideoProxyDirtyInfo dirty = { - DIRTY_REGISTER, - address, - value, - 0xDEADBEEF, - }; - proxy->writeData(proxy, &dirty, sizeof(dirty)); -} - -void mVideoProxyRendererWriteVRAM(struct mVideoProxy* proxy, uint32_t address) { - int bit = 1 << (address >> 12); - if (proxy->vramDirtyBitmap[address >> 17] & bit) { - return; - } - proxy->vramDirtyBitmap[address >> 17] |= bit; -} - -void mVideoProxyRendererWritePalette(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { - struct mVideoProxyDirtyInfo dirty = { - DIRTY_PALETTE, - address, - value, - 0xDEADBEEF, - }; - proxy->writeData(proxy, &dirty, sizeof(dirty)); -} - -void mVideoProxyRendererWriteOAM(struct mVideoProxy* proxy, uint32_t address, uint16_t value) { - struct mVideoProxyDirtyInfo dirty = { - DIRTY_OAM, - address, - value, - 0xDEADBEEF, - }; - proxy->writeData(proxy, &dirty, sizeof(dirty)); -} - -void mVideoProxyRendererDrawScanline(struct mVideoProxy* proxy, int y) { - size_t i; - for (i = 0; i < _roundUp(proxy->vramSize, 17); ++i) { - if (proxy->vramDirtyBitmap[i]) { - uint32_t bitmap = proxy->vramDirtyBitmap[i]; - proxy->vramDirtyBitmap[i] = 0; - int j; - for (j = 0; j < 32; ++j) { - if (!(bitmap & (1 << j))) { - continue; - } - struct mVideoProxyDirtyInfo dirty = { - DIRTY_VRAM, - j * 0x1000, - 0xABCD, - 0xDEADBEEF, - }; - proxy->writeData(proxy, &dirty, sizeof(dirty)); - proxy->writeData(proxy, proxy->vramBlock(proxy, j * 0x1000), 0x1000); - } - } - } - struct mVideoProxyDirtyInfo dirty = { - DIRTY_SCANLINE, - y, - 0, - 0xDEADBEEF, - }; - proxy->writeData(proxy, &dirty, sizeof(dirty)); -} - -void mVideoProxyRendererFlush(struct mVideoProxy* proxy) { - struct mVideoProxyDirtyInfo dirty = { - DIRTY_FLUSH, - 0, - 0, - 0xDEADBEEF, - }; - proxy->writeData(proxy, &dirty, sizeof(dirty)); -} - -bool mVideoProxyRendererRun(struct mVideoProxy* proxy) { - struct mVideoProxyDirtyInfo item = {0}; - while (proxy->readData(proxy, &item, sizeof(item), false)) { - switch (item.type) { - case DIRTY_REGISTER: - case DIRTY_PALETTE: - case DIRTY_OAM: - case DIRTY_VRAM: - case DIRTY_SCANLINE: - case DIRTY_FLUSH: - if (!proxy->parsePacket(proxy, &item)) { - return true; - } - break; - default: - return false; - } - } - return true; -} diff --git a/src/gba/core.c b/src/gba/core.c index 9a758afd2..075f87b6e 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -290,7 +290,7 @@ static void _GBACoreReset(struct mCore* core) { struct GBAVideoRenderer* renderer = &gbacore->renderer.d; #ifndef DISABLE_THREADING if (gbacore->threadedVideo) { - renderer = &gbacore->threadProxy.d; + renderer = &gbacore->threadProxy.d.d; } #endif GBAVideoAssociateRenderer(&gba->video, renderer); diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c new file mode 100644 index 000000000..b80d82374 --- /dev/null +++ b/src/gba/renderers/proxy.c @@ -0,0 +1,239 @@ +/* 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 + +#include +#include +#include + +static void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer); +static void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer); +static void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer); +static uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); +static void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); +static void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); +static void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); +static void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer); +static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels); +static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); + +static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); +static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); + +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) { + renderer->d.init = GBAVideoProxyRendererInit; + renderer->d.reset = GBAVideoProxyRendererReset; + renderer->d.deinit = GBAVideoProxyRendererDeinit; + renderer->d.writeVideoRegister = GBAVideoProxyRendererWriteVideoRegister; + renderer->d.writeVRAM = GBAVideoProxyRendererWriteVRAM; + renderer->d.writeOAM = GBAVideoProxyRendererWriteOAM; + renderer->d.writePalette = GBAVideoProxyRendererWritePalette; + renderer->d.drawScanline = GBAVideoProxyRendererDrawScanline; + renderer->d.finishFrame = GBAVideoProxyRendererFinishFrame; + renderer->d.getPixels = GBAVideoProxyRendererGetPixels; + renderer->d.putPixels = GBAVideoProxyRendererPutPixels; + + renderer->d.disableBG[0] = false; + renderer->d.disableBG[1] = false; + renderer->d.disableBG[2] = false; + renderer->d.disableBG[3] = false; + renderer->d.disableOBJ = false; + + renderer->logger.context = renderer; + renderer->logger.writeData = NULL; + renderer->logger.readData = NULL; + renderer->logger.parsePacket = _parsePacket; + renderer->logger.vramBlock = _vramBlock; + renderer->logger.paletteSize = SIZE_PALETTE_RAM; + renderer->logger.vramSize = SIZE_VRAM; + renderer->logger.oamSize = SIZE_OAM; + + renderer->backend = backend; +} + +void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + + mVideoLoggerRendererInit(&proxyRenderer->logger); + + if (proxyRenderer->block) { + proxyRenderer->backend->palette = proxyRenderer->logger.palette; + proxyRenderer->backend->vram = proxyRenderer->logger.vram; + proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger.oam; + proxyRenderer->backend->cache = NULL; + } + + proxyRenderer->init(proxyRenderer); + + proxyRenderer->backend->init(proxyRenderer->backend); +} + +void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + memcpy(proxyRenderer->logger.oam, &renderer->oam->raw, SIZE_OAM); + memcpy(proxyRenderer->logger.palette, renderer->palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger.vram, renderer->vram, SIZE_VRAM); + + mVideoLoggerRendererReset(&proxyRenderer->logger); + + proxyRenderer->reset(proxyRenderer); + + proxyRenderer->backend->reset(proxyRenderer->backend); +} + +void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + + proxyRenderer->deinit(proxyRenderer); + + proxyRenderer->backend->deinit(proxyRenderer->backend); + + mVideoLoggerRendererDeinit(&proxyRenderer->logger); +} + +static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { + struct GBAVideoProxyRenderer* proxyRenderer = logger->context; + switch (item->type) { + case DIRTY_REGISTER: + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_PALETTE: + logger->palette[item->address >> 1] = item->value; + proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_OAM: + logger->oam[item->address] = item->value; + proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + break; + case DIRTY_VRAM: + logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + break; + case DIRTY_SCANLINE: + proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); + break; + case DIRTY_FLUSH: + return false; + default: + return false; + } + return true; +} + +static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) { + struct GBAVideoProxyRenderer* proxyRenderer = logger->context; + return &proxyRenderer->d.vram[address >> 1]; +} + +uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + switch (address) { + case REG_BG0CNT: + case REG_BG1CNT: + case REG_BG2CNT: + case REG_BG3CNT: + value &= 0xFFCF; + break; + case REG_BG0HOFS: + case REG_BG0VOFS: + case REG_BG1HOFS: + case REG_BG1VOFS: + case REG_BG2HOFS: + case REG_BG2VOFS: + case REG_BG3HOFS: + case REG_BG3VOFS: + value &= 0x01FF; + break; + } + if (address > REG_BLDY) { + return value; + } + + mVideoLoggerRendererWriteVideoRegister(&proxyRenderer->logger, address, value); + return value; +} + +void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + mVideoLoggerRendererWriteVRAM(&proxyRenderer->logger, address); + if (!proxyRenderer->block) { + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address); + } + if (renderer->cache) { + mTileCacheWriteVRAM(renderer->cache, address); + } +} + +void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + mVideoLoggerRendererWritePalette(&proxyRenderer->logger, address, value); + if (!proxyRenderer->block) { + proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value); + } + if (renderer->cache) { + mTileCacheWritePalette(renderer->cache, address); + } +} + +void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (!proxyRenderer->block) { + proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam); + } + mVideoLoggerRendererWriteOAM(&proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]); +} + +void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (!proxyRenderer->block) { + proxyRenderer->backend->drawScanline(proxyRenderer->backend, y); + } + mVideoLoggerRendererDrawScanline(&proxyRenderer->logger, y); + if (proxyRenderer->block) { + proxyRenderer->wake(proxyRenderer, y); + } +} + +void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (proxyRenderer->block) { + proxyRenderer->lock(proxyRenderer); + proxyRenderer->wait(proxyRenderer); + } + mVideoLoggerRendererFlush(&proxyRenderer->logger); + if (proxyRenderer->block) { + proxyRenderer->unlock(proxyRenderer); + } +} + +static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (proxyRenderer->block) { + proxyRenderer->lock(proxyRenderer); + // Insert an extra item into the queue to make sure it gets flushed + mVideoLoggerRendererFlush(&proxyRenderer->logger); + proxyRenderer->wait(proxyRenderer); + } + proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); + if (proxyRenderer->block) { + proxyRenderer->unlock(proxyRenderer); + } +} + +static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + if (proxyRenderer->block) { + proxyRenderer->lock(proxyRenderer); + // Insert an extra item into the queue to make sure it gets flushed + mVideoLoggerRendererFlush(&proxyRenderer->logger); + proxyRenderer->wait(proxyRenderer); + } + proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); + if (proxyRenderer->block) { + proxyRenderer->unlock(proxyRenderer); + } +} diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index 6dc7b51fc..80bb84f82 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -9,98 +9,60 @@ #include #include -#include - #ifndef DISABLE_THREADING -static void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer); -static void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer); -static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer); -static uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); -static void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address); -static void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value); -static void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam); -static void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y); -static void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer); -static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels); -static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels); +static void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer); +static void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer); +static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoProxyRenderer* renderer); static THREAD_ENTRY _proxyThread(void* renderer); -static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length); -static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block); -static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* packet); -static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address); +static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length); +static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block); + +static void _lock(struct GBAVideoProxyRenderer* proxyRenderer); +static void _unlock(struct GBAVideoProxyRenderer* proxyRenderer); +static void _wait(struct GBAVideoProxyRenderer* proxyRenderer); +static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y); void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { + renderer->d.block = true; + GBAVideoProxyRendererCreate(&renderer->d, backend); + renderer->d.init = GBAVideoThreadProxyRendererInit; renderer->d.reset = GBAVideoThreadProxyRendererReset; renderer->d.deinit = GBAVideoThreadProxyRendererDeinit; - renderer->d.writeVideoRegister = GBAVideoThreadProxyRendererWriteVideoRegister; - renderer->d.writeVRAM = GBAVideoThreadProxyRendererWriteVRAM; - renderer->d.writeOAM = GBAVideoThreadProxyRendererWriteOAM; - renderer->d.writePalette = GBAVideoThreadProxyRendererWritePalette; - renderer->d.drawScanline = GBAVideoThreadProxyRendererDrawScanline; - renderer->d.finishFrame = GBAVideoThreadProxyRendererFinishFrame; - renderer->d.getPixels = GBAVideoThreadProxyRendererGetPixels; - renderer->d.putPixels = GBAVideoThreadProxyRendererPutPixels; + renderer->d.lock = _lock; + renderer->d.unlock = _unlock; + renderer->d.wait = _wait; + renderer->d.wake = _wake; - renderer->d.disableBG[0] = false; - renderer->d.disableBG[1] = false; - renderer->d.disableBG[2] = false; - renderer->d.disableBG[3] = false; - renderer->d.disableOBJ = false; - - renderer->proxy.context = renderer; - renderer->proxy.writeData = _writeData; - renderer->proxy.readData = _readData; - renderer->proxy.parsePacket = _parsePacket; - renderer->proxy.vramBlock = _vramBlock; - renderer->proxy.paletteSize = SIZE_PALETTE_RAM; - renderer->proxy.vramSize = SIZE_VRAM; - renderer->proxy.oamSize = SIZE_OAM; - - renderer->backend = backend; + renderer->d.logger.writeData = _writeData; + renderer->d.logger.readData = _readData; } -void GBAVideoThreadProxyRendererInit(struct GBAVideoRenderer* renderer) { +void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; ConditionInit(&proxyRenderer->fromThreadCond); ConditionInit(&proxyRenderer->toThreadCond); MutexInit(&proxyRenderer->mutex); RingFIFOInit(&proxyRenderer->dirtyQueue, 0x40000); - mVideoProxyRendererInit(&proxyRenderer->proxy); - - proxyRenderer->backend->palette = proxyRenderer->proxy.palette; - proxyRenderer->backend->vram = proxyRenderer->proxy.vram; - proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->proxy.oam; - proxyRenderer->backend->cache = NULL; - - proxyRenderer->backend->init(proxyRenderer->backend); - proxyRenderer->threadState = PROXY_THREAD_IDLE; ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -void GBAVideoThreadProxyRendererReset(struct GBAVideoRenderer* renderer) { +void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; MutexLock(&proxyRenderer->mutex); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } - memcpy(proxyRenderer->proxy.oam, &renderer->oam->raw, SIZE_OAM); - memcpy(proxyRenderer->proxy.palette, renderer->palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->proxy.vram, renderer->vram, SIZE_VRAM); - - mVideoProxyRendererReset(&proxyRenderer->proxy); - - proxyRenderer->backend->reset(proxyRenderer->backend); MutexUnlock(&proxyRenderer->mutex); } -void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) { +void GBAVideoThreadProxyRendererDeinit(struct GBAVideoProxyRenderer* renderer) { struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; bool waiting = false; MutexLock(&proxyRenderer->mutex); @@ -120,9 +82,6 @@ void GBAVideoThreadProxyRendererDeinit(struct GBAVideoRenderer* renderer) { ConditionDeinit(&proxyRenderer->fromThreadCond); ConditionDeinit(&proxyRenderer->toThreadCond); MutexDeinit(&proxyRenderer->mutex); - proxyRenderer->backend->deinit(proxyRenderer->backend); - - mVideoProxyRendererDeinit(&proxyRenderer->proxy); } void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { @@ -138,8 +97,8 @@ void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t length) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; +static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) { mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length); MutexLock(&proxyRenderer->mutex); @@ -155,8 +114,8 @@ static bool _writeData(struct mVideoProxy* proxy, const void* data, size_t lengt return true; } -static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool block) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; +static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; bool read = false; while (true) { read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length); @@ -172,139 +131,34 @@ static bool _readData(struct mVideoProxy* proxy, void* data, size_t length, bool return read; } -static bool _parsePacket(struct mVideoProxy* proxy, const struct mVideoProxyDirtyInfo* item) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; - switch (item->type) { - case DIRTY_REGISTER: - proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); - break; - case DIRTY_PALETTE: - proxy->palette[item->address >> 1] = item->value; - proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); - break; - case DIRTY_OAM: - proxy->oam[item->address] = item->value; - proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); - break; - case DIRTY_VRAM: - proxy->readData(proxy, &proxy->vram[item->address >> 1], 0x1000, true); - proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); - break; - case DIRTY_SCANLINE: - proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); - break; - case DIRTY_FLUSH: - return false; - default: - return false; - } - return true; +static void _lock(struct GBAVideoProxyRenderer* proxyRenderer) { + struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; + MutexLock(&threadProxy->mutex); } -static uint16_t* _vramBlock(struct mVideoProxy* proxy, uint32_t address) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = proxy->context; - return &proxyRenderer->d.vram[address >> 1]; -} - -uint16_t GBAVideoThreadProxyRendererWriteVideoRegister(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - switch (address) { - case REG_BG0CNT: - case REG_BG1CNT: - case REG_BG2CNT: - case REG_BG3CNT: - value &= 0xFFCF; - break; - case REG_BG0HOFS: - case REG_BG0VOFS: - case REG_BG1HOFS: - case REG_BG1VOFS: - case REG_BG2HOFS: - case REG_BG2VOFS: - case REG_BG3HOFS: - case REG_BG3VOFS: - value &= 0x01FF; - break; - } - if (address > REG_BLDY) { - return value; - } - - mVideoProxyRendererWriteVideoRegister(&proxyRenderer->proxy, address, value); - return value; -} - -void GBAVideoThreadProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - mVideoProxyRendererWriteVRAM(&proxyRenderer->proxy, address); - if (renderer->cache) { - mTileCacheWriteVRAM(renderer->cache, address); - } -} - -void GBAVideoThreadProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - mVideoProxyRendererWritePalette(&proxyRenderer->proxy, address, value); - if (renderer->cache) { - mTileCacheWritePalette(renderer->cache, address); - } -} - -void GBAVideoThreadProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - mVideoProxyRendererWriteOAM(&proxyRenderer->proxy, oam, proxyRenderer->d.oam->raw[oam]); -} - -void GBAVideoThreadProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - mVideoProxyRendererDrawScanline(&proxyRenderer->proxy, y); - if ((y & 15) == 15) { - ConditionWake(&proxyRenderer->toThreadCond); - } -} - -void GBAVideoThreadProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { +static void _wait(struct GBAVideoProxyRenderer* proxyRenderer) { + struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; + if (threadProxy->threadState == PROXY_THREAD_STOPPED) { mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!"); - _proxyThreadRecover(proxyRenderer); + _proxyThreadRecover(threadProxy); return; } - MutexLock(&proxyRenderer->mutex); - // Insert an extra item into the queue to make sure it gets flushed - mVideoProxyRendererFlush(&proxyRenderer->proxy); - do { - ConditionWake(&proxyRenderer->toThreadCond); - ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); - } while (proxyRenderer->threadState == PROXY_THREAD_BUSY); - proxyRenderer->backend->finishFrame(proxyRenderer->backend); - MutexUnlock(&proxyRenderer->mutex); + while (threadProxy->threadState == PROXY_THREAD_BUSY) { + ConditionWake(&threadProxy->toThreadCond); + ConditionWait(&threadProxy->fromThreadCond, &threadProxy->mutex); + } } -static void GBAVideoThreadProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - MutexLock(&proxyRenderer->mutex); - // Insert an extra item into the queue to make sure it gets flushed - mVideoProxyRendererFlush(&proxyRenderer->proxy); - while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { - ConditionWake(&proxyRenderer->toThreadCond); - ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); - } - proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); - MutexUnlock(&proxyRenderer->mutex); +static void _unlock(struct GBAVideoProxyRenderer* proxyRenderer) { + struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; + MutexUnlock(&threadProxy->mutex); } -static void GBAVideoThreadProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; - MutexLock(&proxyRenderer->mutex); - // Insert an extra item into the queue to make sure it gets flushed - mVideoProxyRendererFlush(&proxyRenderer->proxy); - while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { - ConditionWake(&proxyRenderer->toThreadCond); - ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); +static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y) { + struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; + if ((y & 15) == 15) { + ConditionWake(&threadProxy->toThreadCond); } - proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); - MutexUnlock(&proxyRenderer->mutex); } static THREAD_ENTRY _proxyThread(void* renderer) { @@ -319,7 +173,7 @@ static THREAD_ENTRY _proxyThread(void* renderer) { } proxyRenderer->threadState = PROXY_THREAD_BUSY; MutexUnlock(&proxyRenderer->mutex); - if (!mVideoProxyRendererRun(&proxyRenderer->proxy)) { + if (!mVideoLoggerRendererRun(&proxyRenderer->d.logger)) { // FIFO was corrupted proxyRenderer->threadState = PROXY_THREAD_STOPPED; mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!"); From 73947766defe48d122796c602b553af507cdb6d5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 14 Apr 2017 18:03:00 -0700 Subject: [PATCH 075/159] Core: Video log recording --- include/mgba/core/core.h | 8 +- include/mgba/core/video-logger.h | 38 +++++++++ include/mgba/internal/gba/renderers/proxy.h | 2 + src/core/video-logger.c | 89 +++++++++++++++++++++ src/gba/core.c | 36 +++++++++ src/gba/renderers/proxy.c | 75 ++++++++++++++--- src/gba/renderers/thread-proxy.c | 1 + src/platform/qt/GameController.cpp | 23 ++++++ src/platform/qt/GameController.h | 7 ++ src/platform/qt/Window.cpp | 17 ++++ src/platform/qt/Window.h | 2 + 11 files changed, 283 insertions(+), 15 deletions(-) diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 2c226c577..170634bc2 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -26,10 +26,10 @@ CXX_GUARD_START enum mPlatform { PLATFORM_NONE = -1, #ifdef M_CORE_GBA - PLATFORM_GBA, + PLATFORM_GBA = 0, #endif #ifdef M_CORE_GB - PLATFORM_GB, + PLATFORM_GB = 1, #endif }; @@ -40,6 +40,7 @@ enum mCoreChecksumType { struct mCoreConfig; struct mCoreSync; struct mStateExtdata; +struct mVideoLogContext; struct mCore { void* cpu; void* board; @@ -145,6 +146,9 @@ struct mCore { size_t (*listAudioChannels)(const struct mCore*, const struct mCoreChannelInfo**); void (*enableVideoLayer)(struct mCore*, size_t id, bool enable); void (*enableAudioChannel)(struct mCore*, size_t id, bool enable); + + void (*startVideoLog)(struct mCore*, struct mVideoLogContext*); + void (*endVideoLog)(struct mCore*); }; #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 diff --git a/include/mgba/core/video-logger.h b/include/mgba/core/video-logger.h index 207385ea2..f204e9bb5 100644 --- a/include/mgba/core/video-logger.h +++ b/include/mgba/core/video-logger.h @@ -27,6 +27,7 @@ struct mVideoLoggerDirtyInfo { uint32_t padding; }; +struct VFile; struct mVideoLogger { bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length); bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block); @@ -45,8 +46,40 @@ struct mVideoLogger { uint16_t* vram; uint16_t* oam; uint16_t* palette; + + struct VFile* vf; }; +struct mVideoLogChannel { + uint32_t type; + void* initialState; + size_t initialStateSize; + struct VFile* channelData; +}; + +struct mVideoLogContext { + void* initialState; + size_t initialStateSize; + uint32_t nChannels; + struct mVideoLogChannel channels[32]; +}; + +struct mVideoLogHeader { + char magic[4]; + uint32_t platform; + uint32_t nChannels; + uint32_t initialStatePointer; + uint32_t channelPointers[32]; +}; + +struct mVideoLogChannelHeader { + uint32_t type; + uint32_t channelInitialStatePointer; + uint32_t channelSize; + uint32_t reserved; +}; + +void mVideoLoggerRendererCreate(struct mVideoLogger* logger); void mVideoLoggerRendererInit(struct mVideoLogger* logger); void mVideoLoggerRendererDeinit(struct mVideoLogger* logger); void mVideoLoggerRendererReset(struct mVideoLogger* logger); @@ -61,6 +94,11 @@ void mVideoLoggerRendererFlush(struct mVideoLogger* logger); bool mVideoLoggerRendererRun(struct mVideoLogger* logger); +struct mCore; +struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core); +void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext*); +void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext*, struct VFile*); + CXX_GUARD_END #endif diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h index dd16da688..06b8b6bcc 100644 --- a/include/mgba/internal/gba/renderers/proxy.h +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -31,6 +31,8 @@ struct GBAVideoProxyRenderer { }; void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend); +void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); +void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); CXX_GUARD_END diff --git a/src/core/video-logger.c b/src/core/video-logger.c index 507b1ed3e..15359402b 100644 --- a/src/core/video-logger.c +++ b/src/core/video-logger.c @@ -5,13 +5,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include +#include + +const char mVL_MAGIC[] = "mVL\0"; + +static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length); +static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block); static inline size_t _roundUp(size_t value, int shift) { value += (1 << shift) - 1; return value >> shift; } +void mVideoLoggerRendererCreate(struct mVideoLogger* logger) { + logger->writeData = _writeData; + logger->readData = _readData; + logger->vf = NULL; +} + void mVideoLoggerRendererInit(struct mVideoLogger* logger) { logger->palette = anonymousMemoryMap(logger->paletteSize); logger->vram = anonymousMemoryMap(logger->vramSize); @@ -134,3 +147,79 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger) { } return true; } + +static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { + return logger->vf->write(logger->vf, data, length) == (ssize_t) length; +} + +static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { + return logger->vf->read(logger->vf, data, length) == (ssize_t) length || !block; +} + +struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core) { + struct mVideoLogContext* context = malloc(sizeof(*context)); + core->startVideoLog(core, context); + return context; +} + +void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext* context) { + if (core) { + core->endVideoLog(core); + } + free(context); +} + +void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, struct VFile* vf) { + struct mVideoLogHeader header = {{0}}; + memcpy(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)); + + enum mPlatform platform = core->platform(core); + STORE_32LE(platform, 0, &header.platform); + STORE_32LE(context->nChannels, 0, &header.nChannels); + + ssize_t pointer = vf->seek(vf, sizeof(header), SEEK_SET); + if (context->initialStateSize) { + ssize_t written = vf->write(vf, context->initialState, context->initialStateSize); + if (written > 0) { + STORE_32LE(pointer, 0, &header.initialStatePointer); + pointer += written; + } else { + header.initialStatePointer = 0; + } + } else { + header.initialStatePointer = 0; + } + + size_t i; + for (i = 0; i < context->nChannels && i < 32; ++i) { + struct VFile* channel = context->channels[i].channelData; + void* block = channel->map(channel, channel->size(channel), MAP_READ); + + struct mVideoLogChannelHeader chHeader = {0}; + STORE_32LE(context->channels[i].type, 0, &chHeader.type); + STORE_32LE(channel->size(channel), 0, &chHeader.channelSize); + + if (context->channels[i].initialStateSize) { + ssize_t written = vf->write(vf, context->channels[i].initialState, context->channels[i].initialStateSize); + if (written > 0) { + STORE_32LE(pointer, 0, &chHeader.channelInitialStatePointer); + pointer += written; + } else { + chHeader.channelInitialStatePointer = 0; + } + } + STORE_32LE(pointer, 0, &header.channelPointers[i]); + ssize_t written = vf->write(vf, &chHeader, sizeof(chHeader)); + if (written != sizeof(chHeader)) { + continue; + } + pointer += written; + written = vf->write(vf, block, channel->size(channel)); + if (written != channel->size(channel)) { + break; + } + pointer += written; + } + vf->seek(vf, 0, SEEK_SET); + vf->write(vf, &header, sizeof(header)); +} diff --git a/src/gba/core.c b/src/gba/core.c index 075f87b6e..7b1417ab6 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -42,6 +42,8 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = { struct GBACore { struct mCore d; struct GBAVideoSoftwareRenderer renderer; + struct GBAVideoProxyRenderer logProxy; + struct mVideoLogContext* logContext; #ifndef DISABLE_THREADING struct GBAVideoThreadProxyRenderer threadProxy; int threadedVideo; @@ -69,6 +71,7 @@ static bool _GBACoreInit(struct mCore* core) { gbacore->overrides = NULL; gbacore->debuggerPlatform = NULL; gbacore->cheatDevice = NULL; + gbacore->logContext = NULL; GBACreate(gba); // TODO: Restore cheats @@ -646,6 +649,37 @@ static void _GBACoreEnableAudioChannel(struct mCore* core, size_t id, bool enabl } } +static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { + struct GBACore* gbacore = (struct GBACore*) core; + struct GBA* gba = core->board; + gbacore->logContext = context; + + GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer); + + context->initialStateSize = core->stateSize(core); + context->initialState = anonymousMemoryMap(context->initialStateSize); + core->saveState(core, context->initialState); + + struct VFile* vf = VFileMemChunk(NULL, 0); + context->nChannels = 1; + context->channels[0].initialState = NULL; + context->channels[0].initialStateSize = 0; + context->channels[0].channelData = vf; + gbacore->logProxy.logger.vf = vf; + gbacore->logProxy.block = false; + + GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); +} + +static void _GBACoreEndVideoLog(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + struct GBA* gba = core->board; + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + + mappedMemoryFree(gbacore->logContext->initialState, gbacore->logContext->initialStateSize); + gbacore->logContext->channels[0].channelData->close(gbacore->logContext->channels[0].channelData); +} + struct mCore* GBACoreCreate(void) { struct GBACore* gbacore = malloc(sizeof(*gbacore)); struct mCore* core = &gbacore->d; @@ -718,5 +752,7 @@ struct mCore* GBACoreCreate(void) { core->listAudioChannels = _GBACoreListAudioChannels; core->enableVideoLayer = _GBACoreEnableVideoLayer; core->enableAudioChannel = _GBACoreEnableAudioChannel; + core->startVideoLog = _GBACoreStartVideoLog; + core->endVideoLog = _GBACoreEndVideoLog; return core; } diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index b80d82374..6e543c884 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -25,6 +25,8 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) { + mVideoLoggerRendererCreate(&renderer->logger); + renderer->d.init = GBAVideoProxyRendererInit; renderer->d.reset = GBAVideoProxyRendererReset; renderer->d.deinit = GBAVideoProxyRendererDeinit; @@ -43,9 +45,11 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->d.disableBG[3] = false; renderer->d.disableOBJ = false; + renderer->init = NULL; + renderer->deinit = NULL; + renderer->reset = NULL; + renderer->logger.context = renderer; - renderer->logger.writeData = NULL; - renderer->logger.readData = NULL; renderer->logger.parsePacket = _parsePacket; renderer->logger.vramBlock = _vramBlock; renderer->logger.paletteSize = SIZE_PALETTE_RAM; @@ -55,9 +59,7 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->backend = backend; } -void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) { - struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - +static void _init(struct GBAVideoProxyRenderer* proxyRenderer) { mVideoLoggerRendererInit(&proxyRenderer->logger); if (proxyRenderer->block) { @@ -67,20 +69,65 @@ void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) { proxyRenderer->backend->cache = NULL; } - proxyRenderer->init(proxyRenderer); + if (proxyRenderer->init) { + proxyRenderer->init(proxyRenderer); + } +} + +static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { + memcpy(proxyRenderer->logger.oam, &proxyRenderer->d.oam->raw, SIZE_OAM); + memcpy(proxyRenderer->logger.palette, &proxyRenderer->d.palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger.vram, &proxyRenderer->d.vram, SIZE_VRAM); + + mVideoLoggerRendererReset(&proxyRenderer->logger); + + if (proxyRenderer->reset) { + proxyRenderer->reset(proxyRenderer); + } +} + +void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { + if (video->renderer != renderer->backend) { + return; + } + renderer->d.cache = video->renderer->cache; + video->renderer = &renderer->d; + renderer->d.palette = video->palette; + renderer->d.vram = video->vram; + renderer->d.oam = &video->oam; + _init(renderer); + _reset(renderer); +} + +void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { + if (video->renderer != &renderer->d) { + return; + } + renderer->backend->cache = video->renderer->cache; + video->renderer = renderer->backend; + renderer->backend->palette = video->palette; + renderer->backend->vram = video->vram; + renderer->backend->oam = &video->oam; + + if (renderer->deinit) { + renderer->deinit(renderer); + } + + mVideoLoggerRendererDeinit(&renderer->logger); +} + +void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) { + struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; + + _init(proxyRenderer); proxyRenderer->backend->init(proxyRenderer->backend); } void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - memcpy(proxyRenderer->logger.oam, &renderer->oam->raw, SIZE_OAM); - memcpy(proxyRenderer->logger.palette, renderer->palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->logger.vram, renderer->vram, SIZE_VRAM); - mVideoLoggerRendererReset(&proxyRenderer->logger); - - proxyRenderer->reset(proxyRenderer); + _reset(proxyRenderer); proxyRenderer->backend->reset(proxyRenderer->backend); } @@ -88,7 +135,9 @@ void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) { void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - proxyRenderer->deinit(proxyRenderer); + if (proxyRenderer->deinit) { + proxyRenderer->deinit(proxyRenderer); + } proxyRenderer->backend->deinit(proxyRenderer->backend); diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index 80bb84f82..a47a3f727 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -39,6 +39,7 @@ void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* rende renderer->d.logger.writeData = _writeData; renderer->d.logger.readData = _readData; + renderer->d.logger.vf = NULL; } void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer) { diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 8cf59c8f1..7aa4657a5 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #ifdef M_CORE_GBA #include #include @@ -71,6 +72,7 @@ GameController::GameController(QObject* parent) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) , m_preload(false) , m_override(nullptr) + , m_vl(nullptr) { #ifdef M_CORE_GBA m_lux.p = this; @@ -1199,6 +1201,27 @@ void GameController::disableLogLevel(int levels) { m_logLevels &= ~levels; } +void GameController::startVideoLog(const QString& path) { + if (!isLoaded() || m_vl) { + return; + } + m_vlPath = path; + m_vl = mVideoLoggerCreate(m_threadContext.core); +} + +void GameController::endVideoLog() { + if (!m_vl) { + return; + } + if (isLoaded()) { + VFile* vf = VFileDevice::open(m_vlPath, O_WRONLY | O_CREAT | O_TRUNC); + mVideoLoggerWrite(m_threadContext.core, m_vl, vf); + vf->close(vf); + } + mVideoLoggerDestroy(m_threadContext.core, m_vl); + m_vf = nullptr; +} + void GameController::pollEvents() { if (!m_inputController) { return; diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 3fe79d087..66b4b4012 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -29,6 +29,7 @@ struct GBAAudio; struct mCoreConfig; struct mDebugger; struct mTileCache; +struct mVideoLogContext; namespace QGBA { @@ -174,6 +175,9 @@ public slots: void enableLogLevel(int); void disableLogLevel(int); + void startVideoLog(const QString& path); + void endVideoLog(); + private slots: void openGame(bool bios = false); void crashGame(const QString& crashMessage); @@ -242,6 +246,9 @@ private: mAVStream* m_stream; + mVideoLogContext* m_vl; + QString m_vlPath; + struct GameControllerLux : GBALuminanceSource { GameController* p; uint8_t value; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 112ee95bf..17800c082 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -478,6 +478,13 @@ void Window::openAboutScreen() { openView(about); } +void Window::startVideoLog() { + QString filename = GBAApp::app()->getSaveFileName(this, tr("Select video log"), tr("Video logs (*.mvl)")); + if (!filename.isEmpty()) { + m_controller->startVideoLog(filename); + } +} + template std::function Window::openTView(A arg) { return [=]() { @@ -1350,6 +1357,16 @@ void Window::setupMenu(QMenuBar* menubar) { addControlledAction(avMenu, recordGIF, "recordGIF"); #endif + QAction* recordVL = new QAction(tr("Record video log..."), avMenu); + connect(recordVL, SIGNAL(triggered()), this, SLOT(startVideoLog())); + addControlledAction(avMenu, recordVL, "recordVL"); + m_gameActions.append(recordVL); + + QAction* stopVL = new QAction(tr("Stop video log"), avMenu); + connect(stopVL, SIGNAL(triggered()), m_controller, SLOT(endVideoLog())); + addControlledAction(avMenu, stopVL, "stopVL"); + m_gameActions.append(stopVL); + avMenu->addSeparator(); m_videoLayers = avMenu->addMenu(tr("Video layers")); m_shortcutController->addMenu(m_videoLayers, avMenu); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index f224a28bf..bd24621bd 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -81,6 +81,8 @@ public slots: void openSettingsWindow(); void openAboutScreen(); + void startVideoLog(); + #ifdef USE_DEBUGGERS void consoleOpen(); #endif From 5665ac0316df4800bec0e942e4d18ba3fec59310 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Apr 2017 21:12:27 -0700 Subject: [PATCH 076/159] GBA Core: Video log playing --- CMakeLists.txt | 6 +- include/mgba/gba/core.h | 1 + include/mgba/internal/gba/renderers/proxy.h | 4 +- src/core/core.c | 12 +- src/{core => feature}/video-logger.c | 152 +++++++++++++++++- .../mgba/core => src/feature}/video-logger.h | 18 ++- src/gba/core.c | 101 +++++++++++- src/gba/memory.c | 42 ++--- src/gba/renderers/proxy.c | 38 +++-- src/gba/renderers/thread-proxy.c | 2 +- src/gba/video.c | 4 +- src/platform/qt/GameController.cpp | 9 +- src/platform/qt/Window.cpp | 1 + 13 files changed, 329 insertions(+), 61 deletions(-) rename src/{core => feature}/video-logger.c (61%) rename {include/mgba/core => src/feature}/video-logger.h (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04a4f1c06..90222c0f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,7 @@ file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/lockstep.c) file(GLOB GB_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/sio/lockstep.c) file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c) file(GLOB THIRD_PARTY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/inih/*.c) -set(CLI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/commandline.c) +file(GLOB EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/*.c) set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c) set(VFS_SRC) source_group("ARM core" FILES ${ARM_SRC}) @@ -674,7 +674,7 @@ if(NOT MINIMAL_CORE) endif() list(APPEND SRC ${FEATURE_SRC} - ${CLI_SRC}) + ${EXTRA_SRC}) endif() if(NOT SKIP_LIBRARY) @@ -771,7 +771,7 @@ if(BUILD_QT) endif() if(BUILD_PERF) - set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c ${CLI_SRC}) + set(PERF_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/test/perf-main.c) if(UNIX AND NOT APPLE) list(APPEND PERF_LIB rt) endif() diff --git a/include/mgba/gba/core.h b/include/mgba/gba/core.h index cb4c1a862..65a19f447 100644 --- a/include/mgba/gba/core.h +++ b/include/mgba/gba/core.h @@ -12,6 +12,7 @@ CXX_GUARD_START struct mCore; struct mCore* GBACoreCreate(void); +struct mCore* GBAVideoLogPlayerCreate(void); CXX_GUARD_END diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h index 06b8b6bcc..2ff3c8761 100644 --- a/include/mgba/internal/gba/renderers/proxy.h +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -11,7 +11,7 @@ CXX_GUARD_START #include -#include +#include "feature/video-logger.h" struct GBAVideoProxyRenderer { struct GBAVideoRenderer d; @@ -30,7 +30,7 @@ struct GBAVideoProxyRenderer { void (*wake)(struct GBAVideoProxyRenderer*, int y); }; -void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend); +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly); void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); diff --git a/src/core/core.c b/src/core/core.c index 439f89c3e..c07198366 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -18,8 +18,11 @@ #include #include #endif +#ifndef MINIMAL_CORE +#include "feature/video-logger.h" +#endif -static struct mCoreFilter { +const static struct mCoreFilter { bool (*filter)(struct VFile*); struct mCore* (*open)(void); enum mPlatform platform; @@ -37,7 +40,7 @@ struct mCore* mCoreFindVF(struct VFile* vf) { if (!vf) { return NULL; } - struct mCoreFilter* filter; + const struct mCoreFilter* filter; for (filter = &_filters[0]; filter->filter; ++filter) { if (filter->filter(vf)) { break; @@ -46,6 +49,9 @@ struct mCore* mCoreFindVF(struct VFile* vf) { if (filter->open) { return filter->open(); } +#ifndef MINIMAL_CORE + return mVideoLogCoreFind(vf); +#endif return NULL; } @@ -53,7 +59,7 @@ enum mPlatform mCoreIsCompatible(struct VFile* vf) { if (!vf) { return false; } - struct mCoreFilter* filter; + const struct mCoreFilter* filter; for (filter = &_filters[0]; filter->filter; ++filter) { if (filter->filter(vf)) { return filter->platform; diff --git a/src/core/video-logger.c b/src/feature/video-logger.c similarity index 61% rename from src/core/video-logger.c rename to src/feature/video-logger.c index 15359402b..2b9ce2cc8 100644 --- a/src/core/video-logger.c +++ b/src/feature/video-logger.c @@ -3,15 +3,30 @@ * 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 +#include "video-logger.h" #include #include #include +#ifdef M_CORE_GBA +#include +#endif + const char mVL_MAGIC[] = "mVL\0"; +const static struct mVLDescriptor { + enum mPlatform platform; + struct mCore* (*open)(void); +} _descriptors[] = { +#ifdef M_CORE_GBA + { PLATFORM_GBA, GBAVideoLogPlayerCreate }, +#endif + { PLATFORM_NONE, 0 } +}; + static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length); +static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length); static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block); static inline size_t _roundUp(size_t value, int shift) { @@ -19,8 +34,12 @@ static inline size_t _roundUp(size_t value, int shift) { return value >> shift; } -void mVideoLoggerRendererCreate(struct mVideoLogger* logger) { - logger->writeData = _writeData; +void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) { + if (readonly) { + logger->writeData = _writeNull; + } else { + logger->writeData = _writeData; + } logger->readData = _readData; logger->vf = NULL; } @@ -93,7 +112,7 @@ void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { uint32_t bitmap = logger->vramDirtyBitmap[i]; logger->vramDirtyBitmap[i] = 0; int j; - for (j = 0; j < 32; ++j) { + for (j = 0; j < mVL_MAX_CHANNELS; ++j) { if (!(bitmap & (1 << j))) { continue; } @@ -145,20 +164,29 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger) { return false; } } - return true; + return false; } static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { return logger->vf->write(logger->vf, data, length) == (ssize_t) length; } +static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length) { + UNUSED(logger); + UNUSED(data); + UNUSED(length); + return false; +} + static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { - return logger->vf->read(logger->vf, data, length) == (ssize_t) length || !block; + return logger->vf->read(logger->vf, data, length) == (ssize_t) length; } struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core) { struct mVideoLogContext* context = malloc(sizeof(*context)); - core->startVideoLog(core, context); + if (core) { + core->startVideoLog(core, context); + } return context; } @@ -182,6 +210,7 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str ssize_t written = vf->write(vf, context->initialState, context->initialStateSize); if (written > 0) { STORE_32LE(pointer, 0, &header.initialStatePointer); + STORE_32LE(context->initialStateSize, 0, &header.initialStateSize); pointer += written; } else { header.initialStatePointer = 0; @@ -191,7 +220,7 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str } size_t i; - for (i = 0; i < context->nChannels && i < 32; ++i) { + for (i = 0; i < context->nChannels && i < mVL_MAX_CHANNELS; ++i) { struct VFile* channel = context->channels[i].channelData; void* block = channel->map(channel, channel->size(channel), MAP_READ); @@ -203,6 +232,7 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str ssize_t written = vf->write(vf, context->channels[i].initialState, context->channels[i].initialStateSize); if (written > 0) { STORE_32LE(pointer, 0, &chHeader.channelInitialStatePointer); + STORE_32LE(context->channels[i].initialStateSize, 0, &chHeader.channelInitialStateSize); pointer += written; } else { chHeader.channelInitialStatePointer = 0; @@ -223,3 +253,109 @@ void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, str vf->seek(vf, 0, SEEK_SET); vf->write(vf, &header, sizeof(header)); } + +struct mCore* mVideoLogCoreFind(struct VFile* vf) { + if (!vf) { + return NULL; + } + struct mVideoLogHeader header = {{0}}; + vf->seek(vf, 0, SEEK_SET); + ssize_t read = vf->read(vf, &header, sizeof(header)); + if (read != sizeof(header)) { + return NULL; + } + if (memcmp(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)) != 0) { + return NULL; + } + enum mPlatform platform; + LOAD_32LE(platform, 0, &header.platform); + + const struct mVLDescriptor* descriptor; + for (descriptor = &_descriptors[0]; descriptor->platform != PLATFORM_NONE; ++descriptor) { + if (platform == descriptor->platform) { + break; + } + } + struct mCore* core = NULL; + if (descriptor->open) { + core = descriptor->open(); + } + return core; +} + +bool mVideoLogContextLoad(struct VFile* vf, struct mVideoLogContext* context) { + if (!vf) { + return false; + } + struct mVideoLogHeader header = {{0}}; + vf->seek(vf, 0, SEEK_SET); + ssize_t read = vf->read(vf, &header, sizeof(header)); + if (read != sizeof(header)) { + return false; + } + if (memcmp(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)) != 0) { + return false; + } + + // TODO: Error check + uint32_t initialStatePointer; + uint32_t initialStateSize; + LOAD_32LE(initialStatePointer, 0, &header.initialStatePointer); + LOAD_32LE(initialStateSize, 0, &header.initialStateSize); + void* initialState = anonymousMemoryMap(initialStateSize); + vf->read(vf, initialState, initialStateSize); + context->initialState = initialState; + context->initialStateSize = initialStateSize; + + uint32_t nChannels; + LOAD_32LE(nChannels, 0, &header.nChannels); + context->nChannels = nChannels; + + size_t i; + for (i = 0; i < nChannels && i < mVL_MAX_CHANNELS; ++i) { + uint32_t channelPointer; + LOAD_32LE(channelPointer, 0, &header.channelPointers[i]); + vf->seek(vf, channelPointer, SEEK_SET); + + struct mVideoLogChannelHeader chHeader; + vf->read(vf, &chHeader, sizeof(chHeader)); + + LOAD_32LE(context->channels[i].type, 0, &chHeader.type); + LOAD_32LE(context->channels[i].initialStateSize, 0, &chHeader.channelInitialStateSize); + + LOAD_32LE(channelPointer, 0, &chHeader.channelInitialStatePointer); + if (channelPointer) { + off_t position = vf->seek(vf, 0, SEEK_CUR); + vf->seek(vf, channelPointer, SEEK_SET); + + context->channels[i].initialState = anonymousMemoryMap(context->channels[i].initialStateSize); + vf->read(vf, context->channels[i].initialState, context->channels[i].initialStateSize); + vf->seek(vf, position, SEEK_SET); + } + + uint32_t channelSize; + LOAD_32LE(channelSize, 0, &chHeader.channelSize); + struct VFile* vfm = VFileMemChunk(0, channelSize); + + while (channelSize) { + uint8_t buffer[2048]; + ssize_t toRead = channelSize; + if (toRead > (ssize_t) sizeof(buffer)) { + toRead = sizeof(buffer); + } + toRead = vf->read(vf, buffer, toRead); + if (toRead > 0) { + channelSize -= toRead; + } else { + break; + } + vfm->write(vfm, buffer, toRead); + } + context->channels[i].channelData = vfm; + } + + for (; i < mVL_MAX_CHANNELS; ++i) { + context->channels[i].channelData = NULL; + } + return true; +} diff --git a/include/mgba/core/video-logger.h b/src/feature/video-logger.h similarity index 86% rename from include/mgba/core/video-logger.h rename to src/feature/video-logger.h index f204e9bb5..3a08baca1 100644 --- a/include/mgba/core/video-logger.h +++ b/src/feature/video-logger.h @@ -10,6 +10,8 @@ CXX_GUARD_START +#define mVL_MAX_CHANNELS 32 + enum mVideoLoggerDirtyType { DIRTY_DUMMY = 0, DIRTY_FLUSH, @@ -61,25 +63,28 @@ struct mVideoLogContext { void* initialState; size_t initialStateSize; uint32_t nChannels; - struct mVideoLogChannel channels[32]; + struct mVideoLogChannel channels[mVL_MAX_CHANNELS]; }; struct mVideoLogHeader { char magic[4]; + uint32_t reserved; uint32_t platform; - uint32_t nChannels; + uint32_t padding; uint32_t initialStatePointer; - uint32_t channelPointers[32]; + uint32_t initialStateSize; + uint32_t nChannels; + uint32_t channelPointers[mVL_MAX_CHANNELS]; }; struct mVideoLogChannelHeader { uint32_t type; uint32_t channelInitialStatePointer; + uint32_t channelInitialStateSize; uint32_t channelSize; - uint32_t reserved; }; -void mVideoLoggerRendererCreate(struct mVideoLogger* logger); +void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly); void mVideoLoggerRendererInit(struct mVideoLogger* logger); void mVideoLoggerRendererDeinit(struct mVideoLogger* logger); void mVideoLoggerRendererReset(struct mVideoLogger* logger); @@ -99,6 +104,9 @@ struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core); void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext*); void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext*, struct VFile*); +struct mCore* mVideoLogCoreFind(struct VFile*); +bool mVideoLogContextLoad(struct VFile*, struct mVideoLogContext*); + CXX_GUARD_END #endif diff --git a/src/gba/core.c b/src/gba/core.c index 7b1417ab6..26b99ec23 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #ifndef DISABLE_THREADING @@ -44,6 +45,7 @@ struct GBACore { struct GBAVideoSoftwareRenderer renderer; struct GBAVideoProxyRenderer logProxy; struct mVideoLogContext* logContext; + struct mCoreCallbacks logCallbacks; #ifndef DISABLE_THREADING struct GBAVideoThreadProxyRenderer threadProxy; int threadedVideo; @@ -654,17 +656,21 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c struct GBA* gba = core->board; gbacore->logContext = context; - GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer); + GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer, false); context->initialStateSize = core->stateSize(core); context->initialState = anonymousMemoryMap(context->initialStateSize); core->saveState(core, context->initialState); + struct GBASerializedState* state = context->initialState; + state->id = 0; + state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM; struct VFile* vf = VFileMemChunk(NULL, 0); context->nChannels = 1; context->channels[0].initialState = NULL; context->channels[0].initialStateSize = 0; context->channels[0].channelData = vf; + context->channels[0].type = 0; gbacore->logProxy.logger.vf = vf; gbacore->logProxy.block = false; @@ -756,3 +762,96 @@ struct mCore* GBACoreCreate(void) { core->endVideoLog = _GBACoreEndVideoLog; return core; } + +#ifndef MINIMAL_CORE +static void _GBAVLPStartFrameCallback(void *context) { + struct mCore* core = context; + struct GBACore* gbacore = (struct GBACore*) core; + struct GBA* gba = core->board; + + if (!mVideoLoggerRendererRun(&gbacore->logProxy.logger)) { + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); + core->loadState(core, gbacore->logContext->initialState); + GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + + // Make sure CPU loop never spins + GBAHalt(gba); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); + } +} + +static bool _GBAVLPInit(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + GBAVideoProxyRendererCreate(&gbacore->logProxy, NULL, true); + memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks)); + gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback; + gbacore->logCallbacks.context = core; + if (_GBACoreInit(core)) { + core->addCoreCallbacks(core, &gbacore->logCallbacks); + return true; + } + return false; +} + +static void _GBAVLPDeinit(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + if (gbacore->logContext) { + mVideoLoggerDestroy(core, gbacore->logContext); + } + _GBACoreDeinit(core); +} + +static void _GBAVLPReset(struct mCore* core) { + struct GBACore* gbacore = (struct GBACore*) core; + struct GBA* gba = (struct GBA*) core->board; + if (gba->video.renderer == &gbacore->logProxy.d) { + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + } else if (gbacore->renderer.outputBuffer) { + struct GBAVideoRenderer* renderer = &gbacore->renderer.d; + GBAVideoAssociateRenderer(&gba->video, renderer); + } + gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); + + ARMReset(core->cpu); + core->loadState(core, gbacore->logContext->initialState); + GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + + // Make sure CPU loop never spins + GBAHalt(gba); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); +} + +static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { + struct GBACore* gbacore = (struct GBACore*) core; + gbacore->logContext = mVideoLoggerCreate(NULL); + if (!mVideoLogContextLoad(vf, gbacore->logContext)) { + mVideoLoggerDestroy(core, gbacore->logContext); + gbacore->logContext = NULL; + return false; + } + gbacore->logProxy.logger.vf = gbacore->logContext->channels[0].channelData; + return true; +} + +static bool _returnTrue(struct VFile* vf) { + UNUSED(vf); + return true; +} + +struct mCore* GBAVideoLogPlayerCreate(void) { + struct mCore* core = GBACoreCreate(); + core->init = _GBAVLPInit; + core->deinit = _GBAVLPDeinit; + core->reset = _GBAVLPReset; + core->loadROM = _GBAVLPLoadROM; + core->isROM = _returnTrue; + return core; +} +#else +struct mCore* GBAVideoLogPlayerCreate(void) { + return false; +} +#endif diff --git a/src/gba/memory.c b/src/gba/memory.c index a6280254e..db4a30122 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -102,7 +102,7 @@ void GBAMemoryDeinit(struct GBA* gba) { } void GBAMemoryReset(struct GBA* gba) { - if (gba->memory.rom || gba->memory.fullBios) { + if (gba->memory.rom || gba->memory.fullBios || !gba->memory.wram) { // Not multiboot if (gba->memory.wram) { mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM); @@ -274,10 +274,10 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { break; case REGION_VRAM: if (address & 0x10000) { - cpu->memory.activeRegion = (uint32_t*) &gba->video.renderer->vram[0x8000]; + cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000]; cpu->memory.activeMask = 0x00007FFF; } else { - cpu->memory.activeRegion = (uint32_t*) gba->video.renderer->vram; + cpu->memory.activeRegion = (uint32_t*) gba->video.vram; cpu->memory.activeMask = 0x0000FFFF; } break; @@ -377,9 +377,9 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { #define LOAD_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - LOAD_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \ + LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \ } else { \ - LOAD_32(value, address & 0x00017FFC, gba->video.renderer->vram); \ + LOAD_32(value, address & 0x00017FFC, gba->video.vram); \ } \ wait += waitstatesRegion[REGION_VRAM]; @@ -507,9 +507,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + LOAD_16(value, address & 0x0001FFFE, gba->video.vram); } else { - LOAD_16(value, address & 0x00017FFE, gba->video.renderer->vram); + LOAD_16(value, address & 0x00017FFE, gba->video.vram); } break; case REGION_OAM: @@ -608,9 +608,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - value = ((uint8_t*) gba->video.renderer->vram)[address & 0x0001FFFF]; + value = ((uint8_t*) gba->video.vram)[address & 0x0001FFFF]; } else { - value = ((uint8_t*) gba->video.renderer->vram)[address & 0x00017FFF]; + value = ((uint8_t*) gba->video.vram)[address & 0x00017FFF]; } break; case REGION_OAM: @@ -691,11 +691,11 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { #define STORE_VRAM \ if ((address & 0x0001FFFF) < SIZE_VRAM) { \ - STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); \ + STORE_32(value, address & 0x0001FFFC, gba->video.vram); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC) + 2); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x0001FFFC)); \ } else { \ - STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); \ + STORE_32(value, address & 0x00017FFC, gba->video.vram); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) + 2); \ gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC)); \ } \ @@ -796,10 +796,10 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + STORE_16(value, address & 0x0001FFFE, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } else { - STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram); + STORE_16(value, address & 0x00017FFE, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); } break; @@ -1052,11 +1052,11 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_32(oldValue, address & 0x0001FFFC, gba->video.renderer->vram); - STORE_32(value, address & 0x0001FFFC, gba->video.renderer->vram); + LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); + STORE_32(value, address & 0x0001FFFC, gba->video.vram); } else { - LOAD_32(oldValue, address & 0x00017FFC, gba->video.renderer->vram); - STORE_32(value, address & 0x00017FFC, gba->video.renderer->vram); + LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); + STORE_32(value, address & 0x00017FFC, gba->video.vram); } break; case REGION_OAM: @@ -1121,11 +1121,11 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o break; case REGION_VRAM: if ((address & 0x0001FFFF) < SIZE_VRAM) { - LOAD_16(oldValue, address & 0x0001FFFE, gba->video.renderer->vram); - STORE_16(value, address & 0x0001FFFE, gba->video.renderer->vram); + LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); + STORE_16(value, address & 0x0001FFFE, gba->video.vram); } else { - LOAD_16(oldValue, address & 0x00017FFE, gba->video.renderer->vram); - STORE_16(value, address & 0x00017FFE, gba->video.renderer->vram); + LOAD_16(oldValue, address & 0x00017FFE, gba->video.vram); + STORE_16(value, address & 0x00017FFE, gba->video.vram); } break; case REGION_OAM: diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index 6e543c884..f1eaa8bf7 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -24,8 +24,11 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); -void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) { - mVideoLoggerRendererCreate(&renderer->logger); +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly) { + mVideoLoggerRendererCreate(&renderer->logger, readonly); + if (readonly) { + renderer->block = true; + } renderer->d.init = GBAVideoProxyRendererInit; renderer->d.reset = GBAVideoProxyRendererReset; @@ -56,6 +59,11 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->logger.vramSize = SIZE_VRAM; renderer->logger.oamSize = SIZE_OAM; + renderer->lock = NULL; + renderer->unlock = NULL; + renderer->wait = NULL; + renderer->wake = NULL; + renderer->backend = backend; } @@ -76,8 +84,8 @@ static void _init(struct GBAVideoProxyRenderer* proxyRenderer) { static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { memcpy(proxyRenderer->logger.oam, &proxyRenderer->d.oam->raw, SIZE_OAM); - memcpy(proxyRenderer->logger.palette, &proxyRenderer->d.palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->logger.vram, &proxyRenderer->d.vram, SIZE_VRAM); + memcpy(proxyRenderer->logger.palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger.vram, proxyRenderer->d.vram, SIZE_VRAM); mVideoLoggerRendererReset(&proxyRenderer->logger); @@ -87,11 +95,12 @@ static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { } void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { - if (video->renderer != renderer->backend) { + if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) { return; } - renderer->d.cache = video->renderer->cache; + renderer->backend = video->renderer; video->renderer = &renderer->d; + renderer->d.cache = renderer->backend->cache; renderer->d.palette = video->palette; renderer->d.vram = video->vram; renderer->d.oam = &video->oam; @@ -203,6 +212,9 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render } mVideoLoggerRendererWriteVideoRegister(&proxyRenderer->logger, address, value); + if (!proxyRenderer->block) { + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); + } return value; } @@ -242,47 +254,47 @@ void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) proxyRenderer->backend->drawScanline(proxyRenderer->backend, y); } mVideoLoggerRendererDrawScanline(&proxyRenderer->logger, y); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wake) { proxyRenderer->wake(proxyRenderer, y); } } void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->lock(proxyRenderer); proxyRenderer->wait(proxyRenderer); } mVideoLoggerRendererFlush(&proxyRenderer->logger); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->unlock(proxyRenderer); } } static void GBAVideoProxyRendererGetPixels(struct GBAVideoRenderer* renderer, size_t* stride, const void** pixels) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->lock(proxyRenderer); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(&proxyRenderer->logger); proxyRenderer->wait(proxyRenderer); } proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->unlock(proxyRenderer); } } static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->lock(proxyRenderer); // Insert an extra item into the queue to make sure it gets flushed mVideoLoggerRendererFlush(&proxyRenderer->logger); proxyRenderer->wait(proxyRenderer); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->block) { + if (proxyRenderer->block && proxyRenderer->wait) { proxyRenderer->unlock(proxyRenderer); } } diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index a47a3f727..3dc0d6acb 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -27,7 +27,7 @@ static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y); void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { renderer->d.block = true; - GBAVideoProxyRendererCreate(&renderer->d, backend); + GBAVideoProxyRendererCreate(&renderer->d, backend, false); renderer->d.init = GBAVideoThreadProxyRendererInit; renderer->d.reset = GBAVideoThreadProxyRendererReset; diff --git a/src/gba/video.c b/src/gba/video.c index 935d1fcea..db0614342 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -295,7 +295,7 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si } void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) { - memcpy(state->vram, video->renderer->vram, SIZE_VRAM); + memcpy(state->vram, video->vram, SIZE_VRAM); memcpy(state->oam, video->oam.raw, SIZE_OAM); memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent); @@ -303,7 +303,7 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* } void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) { - memcpy(video->renderer->vram, state->vram, SIZE_VRAM); + memcpy(video->vram, state->vram, SIZE_VRAM); uint16_t value; int i; for (i = 0; i < SIZE_OAM; i += 2) { diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 7aa4657a5..3667400a7 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #ifdef M_CORE_GBA #include #include @@ -33,6 +32,7 @@ #include #endif #include +#include "feature/video-logger.h" using namespace QGBA; using namespace std; @@ -158,6 +158,7 @@ GameController::GameController(QObject* parent) } controller->m_patch = QString(); controller->clearOverride(); + controller->endVideoLog(); QMetaObject::invokeMethod(controller->m_audioProcessor, "pause"); @@ -1205,6 +1206,8 @@ void GameController::startVideoLog(const QString& path) { if (!isLoaded() || m_vl) { return; } + + Interrupter interrupter(this); m_vlPath = path; m_vl = mVideoLoggerCreate(m_threadContext.core); } @@ -1213,13 +1216,15 @@ void GameController::endVideoLog() { if (!m_vl) { return; } + + Interrupter interrupter(this); if (isLoaded()) { VFile* vf = VFileDevice::open(m_vlPath, O_WRONLY | O_CREAT | O_TRUNC); mVideoLoggerWrite(m_threadContext.core, m_vl, vf); vf->close(vf); } mVideoLoggerDestroy(m_threadContext.core, m_vl); - m_vf = nullptr; + m_vl = nullptr; } void GameController::pollEvents() { diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 17800c082..04872eb99 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -354,6 +354,7 @@ QString Window::getFilters() const { formats.removeDuplicates(); filters.prepend(tr("All ROMs (%1)").arg(formats.join(QChar(' ')))); + filters.append(tr("%1 Video Logs (*.mvl)").arg(projectName)); return filters.join(";;"); } From fbb02475dae7e39e7b29341a8b62031cb68f2420 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Apr 2017 22:39:50 -0700 Subject: [PATCH 077/159] VFS: Optimize expanding in-memory files --- CHANGES | 1 + src/util/vfs/vfs-mem.c | 29 +++++++++++++++++++---------- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index e24c84992..5c9f39755 100644 --- a/CHANGES +++ b/CHANGES @@ -103,6 +103,7 @@ Misc: - Feature: Make -l option explicit - Core: Ability to enumerate and modify video and audio channels - Debugger: Make attaching a backend idempotent + - VFS: Optimize expanding in-memory files 0.5.2: (2016-12-31) Bugfixes: diff --git a/src/util/vfs/vfs-mem.c b/src/util/vfs/vfs-mem.c index 7264577cf..2ccdc339c 100644 --- a/src/util/vfs/vfs-mem.c +++ b/src/util/vfs/vfs-mem.c @@ -4,12 +4,14 @@ * 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 +#include #include struct VFileMem { struct VFile d; void* mem; size_t size; + size_t bufferSize; size_t offset; }; @@ -40,6 +42,7 @@ struct VFile* VFileFromMemory(void* mem, size_t size) { vfm->mem = mem; vfm->size = size; + vfm->bufferSize = size; vfm->offset = 0; vfm->d.close = _vfmClose; vfm->d.seek = _vfmSeek; @@ -67,6 +70,7 @@ struct VFile* VFileFromConstMemory(const void* mem, size_t size) { vfm->mem = (void*) mem; vfm->size = size; + vfm->bufferSize = size; vfm->offset = 0; vfm->d.close = _vfmClose; vfm->d.seek = _vfmSeek; @@ -89,8 +93,9 @@ struct VFile* VFileMemChunk(const void* mem, size_t size) { } vfm->size = size; + vfm->bufferSize = toPow2(size); if (size) { - vfm->mem = anonymousMemoryMap(size); + vfm->mem = anonymousMemoryMap(vfm->bufferSize); if (mem) { memcpy(vfm->mem, mem, size); } @@ -113,15 +118,19 @@ struct VFile* VFileMemChunk(const void* mem, size_t size) { } void _vfmExpand(struct VFileMem* vfm, size_t newSize) { - void* oldBuf = vfm->mem; - vfm->mem = anonymousMemoryMap(newSize); - if (oldBuf) { - if (newSize < vfm->size) { - memcpy(vfm->mem, oldBuf, newSize); - } else { - memcpy(vfm->mem, oldBuf, vfm->size); + size_t alignedSize = toPow2(newSize); + if (alignedSize > vfm->bufferSize) { + void* oldBuf = vfm->mem; + vfm->mem = anonymousMemoryMap(alignedSize); + if (oldBuf) { + if (newSize < vfm->size) { + memcpy(vfm->mem, oldBuf, newSize); + } else { + memcpy(vfm->mem, oldBuf, vfm->size); + } + mappedMemoryFree(oldBuf, vfm->bufferSize); } - mappedMemoryFree(oldBuf, vfm->size); + vfm->bufferSize = alignedSize; } vfm->size = newSize; } @@ -135,7 +144,7 @@ bool _vfmClose(struct VFile* vf) { bool _vfmCloseFree(struct VFile* vf) { struct VFileMem* vfm = (struct VFileMem*) vf; - mappedMemoryFree(vfm->mem, vfm->size); + mappedMemoryFree(vfm->mem, vfm->bufferSize); vfm->mem = 0; free(vfm); return true; From 3f92b1e67f30abfc056df1df141de7c7ccb6df61 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Apr 2017 02:21:02 -0700 Subject: [PATCH 078/159] GBA Video: Trim down GBAVideoProxyRenderer --- include/mgba/internal/gba/renderers/proxy.h | 11 --- src/feature/video-logger.c | 23 +++++++ src/feature/video-logger.h | 10 +++ src/gba/core.c | 2 +- src/gba/renderers/proxy.c | 74 +++++++-------------- src/gba/renderers/thread-proxy.c | 74 ++++++++++----------- 6 files changed, 94 insertions(+), 100 deletions(-) diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h index 2ff3c8761..82c7b2a99 100644 --- a/include/mgba/internal/gba/renderers/proxy.h +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -17,17 +17,6 @@ struct GBAVideoProxyRenderer { struct GBAVideoRenderer d; struct GBAVideoRenderer* backend; struct mVideoLogger logger; - - bool block; - - void (*init)(struct GBAVideoProxyRenderer*); - void (*deinit)(struct GBAVideoProxyRenderer*); - void (*reset)(struct GBAVideoProxyRenderer*); - - void (*lock)(struct GBAVideoProxyRenderer*); - void (*unlock)(struct GBAVideoProxyRenderer*); - void (*wait)(struct GBAVideoProxyRenderer*); - void (*wake)(struct GBAVideoProxyRenderer*, int y); }; void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly); diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 2b9ce2cc8..cd4e04a25 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -37,11 +37,21 @@ static inline size_t _roundUp(size_t value, int shift) { void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) { if (readonly) { logger->writeData = _writeNull; + logger->block = true; } else { logger->writeData = _writeData; } logger->readData = _readData; logger->vf = NULL; + + logger->init = NULL; + logger->deinit = NULL; + logger->reset = NULL; + + logger->lock = NULL; + logger->unlock = NULL; + logger->wait = NULL; + logger->wake = NULL; } void mVideoLoggerRendererInit(struct mVideoLogger* logger) { @@ -51,9 +61,17 @@ void mVideoLoggerRendererInit(struct mVideoLogger* logger) { logger->vramDirtyBitmap = calloc(_roundUp(logger->vramSize, 17), sizeof(uint32_t)); logger->oamDirtyBitmap = calloc(_roundUp(logger->oamSize, 6), sizeof(uint32_t)); + + if (logger->init) { + logger->init(logger); + } } void mVideoLoggerRendererDeinit(struct mVideoLogger* logger) { + if (logger->deinit) { + logger->deinit(logger); + } + mappedMemoryFree(logger->palette, logger->paletteSize); mappedMemoryFree(logger->vram, logger->vramSize); mappedMemoryFree(logger->oam, logger->oamSize); @@ -65,6 +83,10 @@ void mVideoLoggerRendererDeinit(struct mVideoLogger* logger) { void mVideoLoggerRendererReset(struct mVideoLogger* logger) { memset(logger->vramDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->vramSize, 17)); memset(logger->oamDirtyBitmap, 0, sizeof(uint32_t) * _roundUp(logger->oamSize, 6)); + + if (logger->reset) { + logger->reset(logger); + } } void mVideoLoggerRendererWriteVideoRegister(struct mVideoLogger* logger, uint32_t address, uint16_t value) { @@ -179,6 +201,7 @@ static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t len } static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { + UNUSED(block); return logger->vf->read(logger->vf, data, length) == (ssize_t) length; } diff --git a/src/feature/video-logger.h b/src/feature/video-logger.h index 3a08baca1..e1a1b9594 100644 --- a/src/feature/video-logger.h +++ b/src/feature/video-logger.h @@ -33,6 +33,16 @@ struct VFile; struct mVideoLogger { bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length); bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block); + + bool block; + void (*init)(struct mVideoLogger*); + void (*deinit)(struct mVideoLogger*); + void (*reset)(struct mVideoLogger*); + + void (*lock)(struct mVideoLogger*); + void (*unlock)(struct mVideoLogger*); + void (*wait)(struct mVideoLogger*); + void (*wake)(struct mVideoLogger*, int y); void* context; bool (*parsePacket)(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); diff --git a/src/gba/core.c b/src/gba/core.c index 26b99ec23..b4f414da8 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -672,7 +672,7 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c context->channels[0].channelData = vf; context->channels[0].type = 0; gbacore->logProxy.logger.vf = vf; - gbacore->logProxy.block = false; + gbacore->logProxy.logger.block = false; GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); } diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index f1eaa8bf7..c56d54be1 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -26,9 +26,6 @@ static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly) { mVideoLoggerRendererCreate(&renderer->logger, readonly); - if (readonly) { - renderer->block = true; - } renderer->d.init = GBAVideoProxyRendererInit; renderer->d.reset = GBAVideoProxyRendererReset; @@ -48,10 +45,6 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->d.disableBG[3] = false; renderer->d.disableOBJ = false; - renderer->init = NULL; - renderer->deinit = NULL; - renderer->reset = NULL; - renderer->logger.context = renderer; renderer->logger.parsePacket = _parsePacket; renderer->logger.vramBlock = _vramBlock; @@ -59,27 +52,18 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->logger.vramSize = SIZE_VRAM; renderer->logger.oamSize = SIZE_OAM; - renderer->lock = NULL; - renderer->unlock = NULL; - renderer->wait = NULL; - renderer->wake = NULL; - renderer->backend = backend; } static void _init(struct GBAVideoProxyRenderer* proxyRenderer) { mVideoLoggerRendererInit(&proxyRenderer->logger); - if (proxyRenderer->block) { + if (proxyRenderer->logger.block) { proxyRenderer->backend->palette = proxyRenderer->logger.palette; proxyRenderer->backend->vram = proxyRenderer->logger.vram; proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger.oam; proxyRenderer->backend->cache = NULL; } - - if (proxyRenderer->init) { - proxyRenderer->init(proxyRenderer); - } } static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { @@ -88,10 +72,6 @@ static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { memcpy(proxyRenderer->logger.vram, proxyRenderer->d.vram, SIZE_VRAM); mVideoLoggerRendererReset(&proxyRenderer->logger); - - if (proxyRenderer->reset) { - proxyRenderer->reset(proxyRenderer); - } } void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { @@ -118,10 +98,6 @@ void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRen renderer->backend->vram = video->vram; renderer->backend->oam = &video->oam; - if (renderer->deinit) { - renderer->deinit(renderer); - } - mVideoLoggerRendererDeinit(&renderer->logger); } @@ -144,10 +120,6 @@ void GBAVideoProxyRendererReset(struct GBAVideoRenderer* renderer) { void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->deinit) { - proxyRenderer->deinit(proxyRenderer); - } - proxyRenderer->backend->deinit(proxyRenderer->backend); mVideoLoggerRendererDeinit(&proxyRenderer->logger); @@ -212,7 +184,7 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render } mVideoLoggerRendererWriteVideoRegister(&proxyRenderer->logger, address, value); - if (!proxyRenderer->block) { + if (!proxyRenderer->logger.block) { proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); } return value; @@ -221,7 +193,7 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; mVideoLoggerRendererWriteVRAM(&proxyRenderer->logger, address); - if (!proxyRenderer->block) { + if (!proxyRenderer->logger.block) { proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address); } if (renderer->cache) { @@ -232,7 +204,7 @@ void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; mVideoLoggerRendererWritePalette(&proxyRenderer->logger, address, value); - if (!proxyRenderer->block) { + if (!proxyRenderer->logger.block) { proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value); } if (renderer->cache) { @@ -242,7 +214,7 @@ void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32 void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (!proxyRenderer->block) { + if (!proxyRenderer->logger.block) { proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam); } mVideoLoggerRendererWriteOAM(&proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]); @@ -250,51 +222,51 @@ void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t o void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (!proxyRenderer->block) { + if (!proxyRenderer->logger.block) { proxyRenderer->backend->drawScanline(proxyRenderer->backend, y); } mVideoLoggerRendererDrawScanline(&proxyRenderer->logger, y); - if (proxyRenderer->block && proxyRenderer->wake) { - proxyRenderer->wake(proxyRenderer, y); + if (proxyRenderer->logger.block && proxyRenderer->logger.wake) { + proxyRenderer->logger.wake(&proxyRenderer->logger, y); } } void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block && proxyRenderer->wait) { - proxyRenderer->lock(proxyRenderer); - proxyRenderer->wait(proxyRenderer); + if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { + proxyRenderer->logger.lock(&proxyRenderer->logger); + proxyRenderer->logger.wait(&proxyRenderer->logger); } mVideoLoggerRendererFlush(&proxyRenderer->logger); - if (proxyRenderer->block && proxyRenderer->wait) { - proxyRenderer->unlock(proxyRenderer); + 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->block && proxyRenderer->wait) { - proxyRenderer->lock(proxyRenderer); + 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->wait(proxyRenderer); + proxyRenderer->logger.wait(&proxyRenderer->logger); } proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->block && proxyRenderer->wait) { - proxyRenderer->unlock(proxyRenderer); + if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { + proxyRenderer->logger.unlock(&proxyRenderer->logger); } } static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, size_t stride, const void* pixels) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->block && proxyRenderer->wait) { - proxyRenderer->lock(proxyRenderer); + 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->wait(proxyRenderer); + proxyRenderer->logger.wait(&proxyRenderer->logger); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->block && proxyRenderer->wait) { - proxyRenderer->unlock(proxyRenderer); + if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { + proxyRenderer->logger.unlock(&proxyRenderer->logger); } } diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index 3dc0d6acb..61fcf3fb1 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -11,39 +11,39 @@ #ifndef DISABLE_THREADING -static void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer); -static void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer); -static void GBAVideoThreadProxyRendererDeinit(struct GBAVideoProxyRenderer* renderer); +static void GBAVideoThreadProxyRendererInit(struct mVideoLogger* logger); +static void GBAVideoThreadProxyRendererReset(struct mVideoLogger* logger); +static void GBAVideoThreadProxyRendererDeinit(struct mVideoLogger* logger); static THREAD_ENTRY _proxyThread(void* renderer); static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length); static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block); -static void _lock(struct GBAVideoProxyRenderer* proxyRenderer); -static void _unlock(struct GBAVideoProxyRenderer* proxyRenderer); -static void _wait(struct GBAVideoProxyRenderer* proxyRenderer); -static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y); +static void _lock(struct mVideoLogger* logger); +static void _unlock(struct mVideoLogger* logger); +static void _wait(struct mVideoLogger* logger); +static void _wake(struct mVideoLogger* logger, int y); void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { - renderer->d.block = true; + renderer->d.logger.block = true; GBAVideoProxyRendererCreate(&renderer->d, backend, false); - renderer->d.init = GBAVideoThreadProxyRendererInit; - renderer->d.reset = GBAVideoThreadProxyRendererReset; - renderer->d.deinit = GBAVideoThreadProxyRendererDeinit; - renderer->d.lock = _lock; - renderer->d.unlock = _unlock; - renderer->d.wait = _wait; - renderer->d.wake = _wake; + renderer->d.logger.init = GBAVideoThreadProxyRendererInit; + renderer->d.logger.reset = GBAVideoThreadProxyRendererReset; + renderer->d.logger.deinit = GBAVideoThreadProxyRendererDeinit; + renderer->d.logger.lock = _lock; + renderer->d.logger.unlock = _unlock; + renderer->d.logger.wait = _wait; + renderer->d.logger.wake = _wake; renderer->d.logger.writeData = _writeData; renderer->d.logger.readData = _readData; renderer->d.logger.vf = NULL; } -void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; +void GBAVideoThreadProxyRendererInit(struct mVideoLogger* logger) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; ConditionInit(&proxyRenderer->fromThreadCond); ConditionInit(&proxyRenderer->toThreadCond); MutexInit(&proxyRenderer->mutex); @@ -53,8 +53,8 @@ void GBAVideoThreadProxyRendererInit(struct GBAVideoProxyRenderer* renderer) { ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; +void GBAVideoThreadProxyRendererReset(struct mVideoLogger* logger) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; MutexLock(&proxyRenderer->mutex); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); @@ -63,8 +63,8 @@ void GBAVideoThreadProxyRendererReset(struct GBAVideoProxyRenderer* renderer) { MutexUnlock(&proxyRenderer->mutex); } -void GBAVideoThreadProxyRendererDeinit(struct GBAVideoProxyRenderer* renderer) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = (struct GBAVideoThreadProxyRenderer*) renderer; +void GBAVideoThreadProxyRendererDeinit(struct mVideoLogger* logger) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; bool waiting = false; MutexLock(&proxyRenderer->mutex); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { @@ -132,33 +132,33 @@ static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bo return read; } -static void _lock(struct GBAVideoProxyRenderer* proxyRenderer) { - struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; - MutexLock(&threadProxy->mutex); +static void _lock(struct mVideoLogger* logger) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + MutexLock(&proxyRenderer->mutex); } -static void _wait(struct GBAVideoProxyRenderer* proxyRenderer) { - struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; - if (threadProxy->threadState == PROXY_THREAD_STOPPED) { +static void _wait(struct mVideoLogger* logger) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!"); - _proxyThreadRecover(threadProxy); + _proxyThreadRecover(proxyRenderer); return; } - while (threadProxy->threadState == PROXY_THREAD_BUSY) { - ConditionWake(&threadProxy->toThreadCond); - ConditionWait(&threadProxy->fromThreadCond, &threadProxy->mutex); + while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { + ConditionWake(&proxyRenderer->toThreadCond); + ConditionWait(&proxyRenderer->fromThreadCond, &proxyRenderer->mutex); } } -static void _unlock(struct GBAVideoProxyRenderer* proxyRenderer) { - struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; - MutexUnlock(&threadProxy->mutex); +static void _unlock(struct mVideoLogger* logger) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + MutexUnlock(&proxyRenderer->mutex); } -static void _wake(struct GBAVideoProxyRenderer* proxyRenderer, int y) { - struct GBAVideoThreadProxyRenderer* threadProxy = (struct GBAVideoThreadProxyRenderer*) proxyRenderer; +static void _wake(struct mVideoLogger* logger, int y) { + struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; if ((y & 15) == 15) { - ConditionWake(&threadProxy->toThreadCond); + ConditionWake(&proxyRenderer->toThreadCond); } } From b8593bdb7bd2d2c790be24c7b1e94ee88d4f5390 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Apr 2017 02:55:26 -0700 Subject: [PATCH 079/159] Core: Allow video playback to be non-blocking --- src/feature/video-logger.c | 6 +++--- src/feature/video-logger.h | 2 +- src/gba/core.c | 2 +- src/gba/renderers/thread-proxy.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index cd4e04a25..edb5d1f7c 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -168,9 +168,9 @@ void mVideoLoggerRendererFlush(struct mVideoLogger* logger) { logger->writeData(logger, &dirty, sizeof(dirty)); } -bool mVideoLoggerRendererRun(struct mVideoLogger* logger) { +bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) { struct mVideoLoggerDirtyInfo item = {0}; - while (logger->readData(logger, &item, sizeof(item), false)) { + while (logger->readData(logger, &item, sizeof(item), block)) { switch (item.type) { case DIRTY_REGISTER: case DIRTY_PALETTE: @@ -186,7 +186,7 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger) { return false; } } - return false; + return !block; } static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { diff --git a/src/feature/video-logger.h b/src/feature/video-logger.h index e1a1b9594..0023d3b3c 100644 --- a/src/feature/video-logger.h +++ b/src/feature/video-logger.h @@ -107,7 +107,7 @@ void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y); void mVideoLoggerRendererFlush(struct mVideoLogger* logger); -bool mVideoLoggerRendererRun(struct mVideoLogger* logger); +bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block); struct mCore; struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core); diff --git a/src/gba/core.c b/src/gba/core.c index b4f414da8..bd8e05ba1 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -769,7 +769,7 @@ static void _GBAVLPStartFrameCallback(void *context) { struct GBACore* gbacore = (struct GBACore*) core; struct GBA* gba = core->board; - if (!mVideoLoggerRendererRun(&gbacore->logProxy.logger)) { + if (!mVideoLoggerRendererRun(&gbacore->logProxy.logger, true)) { GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); core->loadState(core, gbacore->logContext->initialState); diff --git a/src/gba/renderers/thread-proxy.c b/src/gba/renderers/thread-proxy.c index 61fcf3fb1..a6038483e 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/gba/renderers/thread-proxy.c @@ -174,7 +174,7 @@ static THREAD_ENTRY _proxyThread(void* renderer) { } proxyRenderer->threadState = PROXY_THREAD_BUSY; MutexUnlock(&proxyRenderer->mutex); - if (!mVideoLoggerRendererRun(&proxyRenderer->d.logger)) { + if (!mVideoLoggerRendererRun(&proxyRenderer->d.logger, false)) { // FIFO was corrupted proxyRenderer->threadState = PROXY_THREAD_STOPPED; mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!"); From 3b69fb767ccb42325e4f6b49b2ab8f39c13e7b40 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Apr 2017 03:48:54 -0700 Subject: [PATCH 080/159] Feature: Make thread proxy generic --- include/mgba/internal/gba/renderers/proxy.h | 4 +- src/{gba/renderers => feature}/thread-proxy.c | 66 ++++++------- .../renderers => src/feature}/thread-proxy.h | 14 +-- src/gba/core.c | 62 +++++++----- src/gba/renderers/proxy.c | 98 +++++++++---------- 5 files changed, 126 insertions(+), 118 deletions(-) rename src/{gba/renderers => feature}/thread-proxy.c (71%) rename {include/mgba/internal/gba/renderers => src/feature}/thread-proxy.h (64%) diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h index 82c7b2a99..5dbe03f74 100644 --- a/include/mgba/internal/gba/renderers/proxy.h +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -16,10 +16,10 @@ CXX_GUARD_START struct GBAVideoProxyRenderer { struct GBAVideoRenderer d; struct GBAVideoRenderer* backend; - struct mVideoLogger logger; + struct mVideoLogger* logger; }; -void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly); +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend); void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer); diff --git a/src/gba/renderers/thread-proxy.c b/src/feature/thread-proxy.c similarity index 71% rename from src/gba/renderers/thread-proxy.c rename to src/feature/thread-proxy.c index a6038483e..91db10f4b 100644 --- a/src/gba/renderers/thread-proxy.c +++ b/src/feature/thread-proxy.c @@ -3,7 +3,7 @@ * 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 +#include "thread-proxy.h" #include #include @@ -11,9 +11,9 @@ #ifndef DISABLE_THREADING -static void GBAVideoThreadProxyRendererInit(struct mVideoLogger* logger); -static void GBAVideoThreadProxyRendererReset(struct mVideoLogger* logger); -static void GBAVideoThreadProxyRendererDeinit(struct mVideoLogger* logger); +static void mVideoThreadProxyInit(struct mVideoLogger* logger); +static void mVideoThreadProxyReset(struct mVideoLogger* logger); +static void mVideoThreadProxyDeinit(struct mVideoLogger* logger); static THREAD_ENTRY _proxyThread(void* renderer); @@ -25,25 +25,25 @@ static void _unlock(struct mVideoLogger* logger); static void _wait(struct mVideoLogger* logger); static void _wake(struct mVideoLogger* logger, int y); -void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend) { - renderer->d.logger.block = true; - GBAVideoProxyRendererCreate(&renderer->d, backend, false); +void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) { + mVideoLoggerRendererCreate(&renderer->d, false); + renderer->d.block = true; - renderer->d.logger.init = GBAVideoThreadProxyRendererInit; - renderer->d.logger.reset = GBAVideoThreadProxyRendererReset; - renderer->d.logger.deinit = GBAVideoThreadProxyRendererDeinit; - renderer->d.logger.lock = _lock; - renderer->d.logger.unlock = _unlock; - renderer->d.logger.wait = _wait; - renderer->d.logger.wake = _wake; + renderer->d.init = mVideoThreadProxyInit; + renderer->d.reset = mVideoThreadProxyReset; + renderer->d.deinit = mVideoThreadProxyDeinit; + renderer->d.lock = _lock; + renderer->d.unlock = _unlock; + renderer->d.wait = _wait; + renderer->d.wake = _wake; - renderer->d.logger.writeData = _writeData; - renderer->d.logger.readData = _readData; - renderer->d.logger.vf = NULL; + renderer->d.writeData = _writeData; + renderer->d.readData = _readData; + renderer->d.vf = NULL; } -void GBAVideoThreadProxyRendererInit(struct mVideoLogger* logger) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; +void mVideoThreadProxyInit(struct mVideoLogger* logger) { + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; ConditionInit(&proxyRenderer->fromThreadCond); ConditionInit(&proxyRenderer->toThreadCond); MutexInit(&proxyRenderer->mutex); @@ -53,8 +53,8 @@ void GBAVideoThreadProxyRendererInit(struct mVideoLogger* logger) { ThreadCreate(&proxyRenderer->thread, _proxyThread, proxyRenderer); } -void GBAVideoThreadProxyRendererReset(struct mVideoLogger* logger) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; +void mVideoThreadProxyReset(struct mVideoLogger* logger) { + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; MutexLock(&proxyRenderer->mutex); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { ConditionWake(&proxyRenderer->toThreadCond); @@ -63,8 +63,8 @@ void GBAVideoThreadProxyRendererReset(struct mVideoLogger* logger) { MutexUnlock(&proxyRenderer->mutex); } -void GBAVideoThreadProxyRendererDeinit(struct mVideoLogger* logger) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; +void mVideoThreadProxyDeinit(struct mVideoLogger* logger) { + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; bool waiting = false; MutexLock(&proxyRenderer->mutex); while (proxyRenderer->threadState == PROXY_THREAD_BUSY) { @@ -85,7 +85,7 @@ void GBAVideoThreadProxyRendererDeinit(struct mVideoLogger* logger) { MutexDeinit(&proxyRenderer->mutex); } -void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { +void _proxyThreadRecover(struct mVideoThreadProxy* proxyRenderer) { MutexLock(&proxyRenderer->mutex); if (proxyRenderer->threadState != PROXY_THREAD_STOPPED) { MutexUnlock(&proxyRenderer->mutex); @@ -99,7 +99,7 @@ void _proxyThreadRecover(struct GBAVideoThreadProxyRenderer* proxyRenderer) { } static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; while (!RingFIFOWrite(&proxyRenderer->dirtyQueue, data, length)) { mLOG(GBA_VIDEO, DEBUG, "Can't write %"PRIz"u bytes. Proxy thread asleep?", length); MutexLock(&proxyRenderer->mutex); @@ -116,7 +116,7 @@ static bool _writeData(struct mVideoLogger* logger, const void* data, size_t len } static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; bool read = false; while (true) { read = RingFIFORead(&proxyRenderer->dirtyQueue, data, length); @@ -133,12 +133,12 @@ static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bo } static void _lock(struct mVideoLogger* logger) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; MutexLock(&proxyRenderer->mutex); } static void _wait(struct mVideoLogger* logger) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; if (proxyRenderer->threadState == PROXY_THREAD_STOPPED) { mLOG(GBA_VIDEO, ERROR, "Proxy thread stopped prematurely!"); _proxyThreadRecover(proxyRenderer); @@ -151,19 +151,19 @@ static void _wait(struct mVideoLogger* logger) { } static void _unlock(struct mVideoLogger* logger) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; MutexUnlock(&proxyRenderer->mutex); } static void _wake(struct mVideoLogger* logger, int y) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = logger->context; + struct mVideoThreadProxy* proxyRenderer = (struct mVideoThreadProxy*) logger; if ((y & 15) == 15) { ConditionWake(&proxyRenderer->toThreadCond); } } -static THREAD_ENTRY _proxyThread(void* renderer) { - struct GBAVideoThreadProxyRenderer* proxyRenderer = renderer; +static THREAD_ENTRY _proxyThread(void* logger) { + struct mVideoThreadProxy* proxyRenderer = logger; ThreadSetName("Proxy Renderer Thread"); MutexLock(&proxyRenderer->mutex); @@ -174,7 +174,7 @@ static THREAD_ENTRY _proxyThread(void* renderer) { } proxyRenderer->threadState = PROXY_THREAD_BUSY; MutexUnlock(&proxyRenderer->mutex); - if (!mVideoLoggerRendererRun(&proxyRenderer->d.logger, false)) { + if (!mVideoLoggerRendererRun(&proxyRenderer->d, false)) { // FIFO was corrupted proxyRenderer->threadState = PROXY_THREAD_STOPPED; mLOG(GBA_VIDEO, ERROR, "Proxy thread queue got corrupted!"); diff --git a/include/mgba/internal/gba/renderers/thread-proxy.h b/src/feature/thread-proxy.h similarity index 64% rename from include/mgba/internal/gba/renderers/thread-proxy.h rename to src/feature/thread-proxy.h index e47621a25..cb663d506 100644 --- a/include/mgba/internal/gba/renderers/thread-proxy.h +++ b/src/feature/thread-proxy.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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 @@ -11,29 +11,29 @@ CXX_GUARD_START #include -#include +#include "video-logger.h" #include #include -enum GBAVideoThreadProxyState { +enum mVideoThreadProxyState { PROXY_THREAD_STOPPED = 0, PROXY_THREAD_IDLE, PROXY_THREAD_BUSY }; -struct GBAVideoThreadProxyRenderer { - struct GBAVideoProxyRenderer d; +struct mVideoThreadProxy { + struct mVideoLogger d; Thread thread; Condition fromThreadCond; Condition toThreadCond; Mutex mutex; - enum GBAVideoThreadProxyState threadState; + enum mVideoThreadProxyState threadState; struct RingFIFO dirtyQueue; }; -void GBAVideoThreadProxyRendererCreate(struct GBAVideoThreadProxyRenderer* renderer, struct GBAVideoRenderer* backend); +void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer); CXX_GUARD_END diff --git a/src/gba/core.c b/src/gba/core.c index bd8e05ba1..5564fa784 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -14,8 +14,9 @@ #include #include #ifndef DISABLE_THREADING -#include +#include "feature/thread-proxy.h" #endif +#include #include #include #include @@ -43,11 +44,11 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = { struct GBACore { struct mCore d; struct GBAVideoSoftwareRenderer renderer; - struct GBAVideoProxyRenderer logProxy; + struct GBAVideoProxyRenderer proxyRenderer; struct mVideoLogContext* logContext; struct mCoreCallbacks logCallbacks; #ifndef DISABLE_THREADING - struct GBAVideoThreadProxyRenderer threadProxy; + struct mVideoThreadProxy threadProxy; int threadedVideo; #endif int keys; @@ -88,8 +89,9 @@ static bool _GBACoreInit(struct mCore* core) { #ifndef DISABLE_THREADING gbacore->threadedVideo = false; - GBAVideoThreadProxyRendererCreate(&gbacore->threadProxy, &gbacore->renderer.d); + mVideoThreadProxyCreate(&gbacore->threadProxy); #endif + gbacore->proxyRenderer.logger = NULL; gbacore->keys = 0; gba->keySource = &gbacore->keys; @@ -295,7 +297,9 @@ static void _GBACoreReset(struct mCore* core) { struct GBAVideoRenderer* renderer = &gbacore->renderer.d; #ifndef DISABLE_THREADING if (gbacore->threadedVideo) { - renderer = &gbacore->threadProxy.d.d; + gbacore->proxyRenderer.logger = &gbacore->threadProxy.d; + GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, renderer); + renderer = &gbacore->proxyRenderer.d; } #endif GBAVideoAssociateRenderer(&gba->video, renderer); @@ -656,8 +660,6 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c struct GBA* gba = core->board; gbacore->logContext = context; - GBAVideoProxyRendererCreate(&gbacore->logProxy, gba->video.renderer, false); - context->initialStateSize = core->stateSize(core); context->initialState = anonymousMemoryMap(context->initialStateSize); core->saveState(core, context->initialState); @@ -671,16 +673,22 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c context->channels[0].initialStateSize = 0; context->channels[0].channelData = vf; context->channels[0].type = 0; - gbacore->logProxy.logger.vf = vf; - gbacore->logProxy.logger.block = false; + gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); + mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false); - GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + gbacore->proxyRenderer.logger->vf = vf; + gbacore->proxyRenderer.logger->block = false; + + GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d); + GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); } static void _GBACoreEndVideoLog(struct mCore* core) { struct GBACore* gbacore = (struct GBACore*) core; struct GBA* gba = core->board; - GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer); + free(gbacore->proxyRenderer.logger); + gbacore->proxyRenderer.logger = NULL; mappedMemoryFree(gbacore->logContext->initialState, gbacore->logContext->initialStateSize); gbacore->logContext->channels[0].channelData->close(gbacore->logContext->channels[0].channelData); @@ -769,11 +777,11 @@ static void _GBAVLPStartFrameCallback(void *context) { struct GBACore* gbacore = (struct GBACore*) core; struct GBA* gba = core->board; - if (!mVideoLoggerRendererRun(&gbacore->logProxy.logger, true)) { - GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); - gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); + if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) { + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer); + gbacore->proxyRenderer.logger->vf->seek(gbacore->proxyRenderer.logger->vf, 0, SEEK_SET); core->loadState(core, gbacore->logContext->initialState); - GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); // Make sure CPU loop never spins GBAHalt(gba); @@ -784,15 +792,17 @@ static void _GBAVLPStartFrameCallback(void *context) { static bool _GBAVLPInit(struct mCore* core) { struct GBACore* gbacore = (struct GBACore*) core; - GBAVideoProxyRendererCreate(&gbacore->logProxy, NULL, true); + if (!_GBACoreInit(core)) { + return false; + } + gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); + mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, true); + GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, NULL); memset(&gbacore->logCallbacks, 0, sizeof(gbacore->logCallbacks)); gbacore->logCallbacks.videoFrameStarted = _GBAVLPStartFrameCallback; gbacore->logCallbacks.context = core; - if (_GBACoreInit(core)) { - core->addCoreCallbacks(core, &gbacore->logCallbacks); - return true; - } - return false; + core->addCoreCallbacks(core, &gbacore->logCallbacks); + return true; } static void _GBAVLPDeinit(struct mCore* core) { @@ -806,17 +816,17 @@ static void _GBAVLPDeinit(struct mCore* core) { static void _GBAVLPReset(struct mCore* core) { struct GBACore* gbacore = (struct GBACore*) core; struct GBA* gba = (struct GBA*) core->board; - if (gba->video.renderer == &gbacore->logProxy.d) { - GBAVideoProxyRendererUnshim(&gba->video, &gbacore->logProxy); + if (gba->video.renderer == &gbacore->proxyRenderer.d) { + GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer); } else if (gbacore->renderer.outputBuffer) { struct GBAVideoRenderer* renderer = &gbacore->renderer.d; GBAVideoAssociateRenderer(&gba->video, renderer); } - gbacore->logProxy.logger.vf->seek(gbacore->logProxy.logger.vf, 0, SEEK_SET); + gbacore->proxyRenderer.logger->vf->seek(gbacore->proxyRenderer.logger->vf, 0, SEEK_SET); ARMReset(core->cpu); core->loadState(core, gbacore->logContext->initialState); - GBAVideoProxyRendererShim(&gba->video, &gbacore->logProxy); + GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); // Make sure CPU loop never spins GBAHalt(gba); @@ -832,7 +842,7 @@ static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { gbacore->logContext = NULL; return false; } - gbacore->logProxy.logger.vf = gbacore->logContext->channels[0].channelData; + gbacore->proxyRenderer.logger->vf = gbacore->logContext->channels[0].channelData; return true; } diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index c56d54be1..4f8f62243 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -24,9 +24,7 @@ static void GBAVideoProxyRendererPutPixels(struct GBAVideoRenderer* renderer, si static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); -void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend, bool readonly) { - mVideoLoggerRendererCreate(&renderer->logger, readonly); - +void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct GBAVideoRenderer* backend) { renderer->d.init = GBAVideoProxyRendererInit; renderer->d.reset = GBAVideoProxyRendererReset; renderer->d.deinit = GBAVideoProxyRendererDeinit; @@ -45,33 +43,33 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->d.disableBG[3] = false; renderer->d.disableOBJ = false; - renderer->logger.context = renderer; - renderer->logger.parsePacket = _parsePacket; - renderer->logger.vramBlock = _vramBlock; - renderer->logger.paletteSize = SIZE_PALETTE_RAM; - renderer->logger.vramSize = SIZE_VRAM; - renderer->logger.oamSize = SIZE_OAM; + renderer->logger->context = renderer; + renderer->logger->parsePacket = _parsePacket; + renderer->logger->vramBlock = _vramBlock; + renderer->logger->paletteSize = SIZE_PALETTE_RAM; + renderer->logger->vramSize = SIZE_VRAM; + renderer->logger->oamSize = SIZE_OAM; renderer->backend = backend; } static void _init(struct GBAVideoProxyRenderer* proxyRenderer) { - mVideoLoggerRendererInit(&proxyRenderer->logger); + mVideoLoggerRendererInit(proxyRenderer->logger); - if (proxyRenderer->logger.block) { - proxyRenderer->backend->palette = proxyRenderer->logger.palette; - proxyRenderer->backend->vram = proxyRenderer->logger.vram; - proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger.oam; + if (proxyRenderer->logger->block) { + proxyRenderer->backend->palette = proxyRenderer->logger->palette; + proxyRenderer->backend->vram = proxyRenderer->logger->vram; + proxyRenderer->backend->oam = (union GBAOAM*) proxyRenderer->logger->oam; proxyRenderer->backend->cache = NULL; } } static void _reset(struct GBAVideoProxyRenderer* proxyRenderer) { - memcpy(proxyRenderer->logger.oam, &proxyRenderer->d.oam->raw, SIZE_OAM); - memcpy(proxyRenderer->logger.palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM); - memcpy(proxyRenderer->logger.vram, proxyRenderer->d.vram, SIZE_VRAM); + memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, SIZE_OAM); + memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, SIZE_VRAM); - mVideoLoggerRendererReset(&proxyRenderer->logger); + mVideoLoggerRendererReset(proxyRenderer->logger); } void GBAVideoProxyRendererShim(struct GBAVideo* video, struct GBAVideoProxyRenderer* renderer) { @@ -98,7 +96,7 @@ void GBAVideoProxyRendererUnshim(struct GBAVideo* video, struct GBAVideoProxyRen renderer->backend->vram = video->vram; renderer->backend->oam = &video->oam; - mVideoLoggerRendererDeinit(&renderer->logger); + mVideoLoggerRendererDeinit(renderer->logger); } void GBAVideoProxyRendererInit(struct GBAVideoRenderer* renderer) { @@ -122,7 +120,7 @@ void GBAVideoProxyRendererDeinit(struct GBAVideoRenderer* renderer) { proxyRenderer->backend->deinit(proxyRenderer->backend); - mVideoLoggerRendererDeinit(&proxyRenderer->logger); + mVideoLoggerRendererDeinit(proxyRenderer->logger); } static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { @@ -183,8 +181,8 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render return value; } - mVideoLoggerRendererWriteVideoRegister(&proxyRenderer->logger, address, value); - if (!proxyRenderer->logger.block) { + mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value); + if (!proxyRenderer->logger->block) { proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); } return value; @@ -192,8 +190,8 @@ uint16_t GBAVideoProxyRendererWriteVideoRegister(struct GBAVideoRenderer* render void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t address) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - mVideoLoggerRendererWriteVRAM(&proxyRenderer->logger, address); - if (!proxyRenderer->logger.block) { + mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address); + if (!proxyRenderer->logger->block) { proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address); } if (renderer->cache) { @@ -203,8 +201,8 @@ void GBAVideoProxyRendererWriteVRAM(struct GBAVideoRenderer* renderer, uint32_t void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32_t address, uint16_t value) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - mVideoLoggerRendererWritePalette(&proxyRenderer->logger, address, value); - if (!proxyRenderer->logger.block) { + mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value); + if (!proxyRenderer->logger->block) { proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value); } if (renderer->cache) { @@ -214,59 +212,59 @@ void GBAVideoProxyRendererWritePalette(struct GBAVideoRenderer* renderer, uint32 void GBAVideoProxyRendererWriteOAM(struct GBAVideoRenderer* renderer, uint32_t oam) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (!proxyRenderer->logger.block) { + if (!proxyRenderer->logger->block) { proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam); } - mVideoLoggerRendererWriteOAM(&proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]); + mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, proxyRenderer->d.oam->raw[oam]); } void GBAVideoProxyRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (!proxyRenderer->logger.block) { + if (!proxyRenderer->logger->block) { proxyRenderer->backend->drawScanline(proxyRenderer->backend, y); } - mVideoLoggerRendererDrawScanline(&proxyRenderer->logger, y); - if (proxyRenderer->logger.block && proxyRenderer->logger.wake) { - proxyRenderer->logger.wake(&proxyRenderer->logger, y); + mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y); + if (proxyRenderer->logger->block && proxyRenderer->logger->wake) { + proxyRenderer->logger->wake(proxyRenderer->logger, y); } } void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { struct GBAVideoProxyRenderer* proxyRenderer = (struct GBAVideoProxyRenderer*) renderer; - if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { - proxyRenderer->logger.lock(&proxyRenderer->logger); - proxyRenderer->logger.wait(&proxyRenderer->logger); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->lock(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); } - mVideoLoggerRendererFlush(&proxyRenderer->logger); - if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { - proxyRenderer->logger.unlock(&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); + 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); + mVideoLoggerRendererFlush(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); } proxyRenderer->backend->getPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { - proxyRenderer->logger.unlock(&proxyRenderer->logger); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); } } static void GBAVideoProxyRendererPutPixels(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); + 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); + mVideoLoggerRendererFlush(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); } proxyRenderer->backend->putPixels(proxyRenderer->backend, stride, pixels); - if (proxyRenderer->logger.block && proxyRenderer->logger.wait) { - proxyRenderer->logger.unlock(&proxyRenderer->logger); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); } } From db4a873d9caab6fbb4d368690b382f7082dd1b12 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 01:51:41 -0700 Subject: [PATCH 081/159] GB: Rearrange some scheduling for better callback ability --- src/gb/video.c | 10 +++++----- src/lr35902/lr35902.c | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gb/video.c b/src/gb/video.c index cefe73cf7..4925b85cd 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -127,7 +127,7 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { video->mode = 1; video->modeEvent.callback = _endMode1; - _updateFrameCount(timing, video, cyclesLate); + mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate); if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); @@ -252,16 +252,16 @@ void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLat video->p->stream->postVideoFrame(video->p->stream, pixels, stride); } + if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { + mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); + } + for (c = 0; c < mCoreCallbacksListSize(&video->p->coreCallbacks); ++c) { struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&video->p->coreCallbacks, c); if (callbacks->videoFrameStarted) { callbacks->videoFrameStarted(callbacks->context); } } - - if (!GBRegisterLCDCIsEnable(video->p->memory.io[REG_LCDC])) { - mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); - } } static void _cleanOAM(struct GBVideo* video, int y) { diff --git a/src/lr35902/lr35902.c b/src/lr35902/lr35902.c index 8d1f3f005..e4100a84a 100644 --- a/src/lr35902/lr35902.c +++ b/src/lr35902/lr35902.c @@ -158,6 +158,10 @@ void LR35902Tick(struct LR35902Core* cpu) { void LR35902Run(struct LR35902Core* cpu) { bool running = true; while (running || cpu->executionState != LR35902_CORE_FETCH) { + if (cpu->cycles >= cpu->nextEvent) { + cpu->irqh.processEvents(cpu); + break; + } _LR35902Step(cpu); if (cpu->cycles + 2 >= cpu->nextEvent) { int32_t diff = cpu->nextEvent - cpu->cycles; @@ -172,9 +176,5 @@ void LR35902Run(struct LR35902Core* cpu) { cpu->executionState = LR35902_CORE_FETCH; cpu->instruction(cpu); ++cpu->cycles; - if (cpu->cycles >= cpu->nextEvent) { - cpu->irqh.processEvents(cpu); - running = false; - } } } From 1b6531e32044481feeb0c631ef01bc61d5ccecc6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 01:53:49 -0700 Subject: [PATCH 082/159] Core: Add more video log types --- src/feature/video-logger.c | 43 ++++++++++++++++++++++++++++++++++++-- src/feature/video-logger.h | 13 +++++++++--- src/gba/renderers/proxy.c | 4 ++++ 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index edb5d1f7c..1ef7e910d 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -127,7 +127,7 @@ void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, logger->writeData(logger, &dirty, sizeof(dirty)); } -void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { +static void _flushVRAM(struct mVideoLogger* logger) { size_t i; for (i = 0; i < _roundUp(logger->vramSize, 17); ++i) { if (logger->vramDirtyBitmap[i]) { @@ -141,7 +141,7 @@ void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { struct mVideoLoggerDirtyInfo dirty = { DIRTY_VRAM, j * 0x1000, - 0xABCD, + 0x1000, 0xDEADBEEF, }; logger->writeData(logger, &dirty, sizeof(dirty)); @@ -149,6 +149,10 @@ void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { } } } +} + +void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { + _flushVRAM(logger); struct mVideoLoggerDirtyInfo dirty = { DIRTY_SCANLINE, y, @@ -158,6 +162,17 @@ void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y) { logger->writeData(logger, &dirty, sizeof(dirty)); } +void mVideoLoggerRendererDrawRange(struct mVideoLogger* logger, int startX, int endX, int y) { + _flushVRAM(logger); + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_RANGE, + y, + startX, + endX, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + void mVideoLoggerRendererFlush(struct mVideoLogger* logger) { struct mVideoLoggerDirtyInfo dirty = { DIRTY_FLUSH, @@ -168,6 +183,27 @@ void mVideoLoggerRendererFlush(struct mVideoLogger* logger) { logger->writeData(logger, &dirty, sizeof(dirty)); } +void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger) { + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_FRAME, + 0, + 0, + 0xDEADBEEF, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); +} + +void mVideoLoggerWriteBuffer(struct mVideoLogger* logger, uint32_t bufferId, uint32_t offset, uint32_t length, const void* data) { + struct mVideoLoggerDirtyInfo dirty = { + DIRTY_BUFFER, + bufferId, + offset, + length, + }; + logger->writeData(logger, &dirty, sizeof(dirty)); + logger->writeData(logger, data, length); +} + bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) { struct mVideoLoggerDirtyInfo item = {0}; while (logger->readData(logger, &item, sizeof(item), block)) { @@ -178,6 +214,9 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) { case DIRTY_VRAM: case DIRTY_SCANLINE: case DIRTY_FLUSH: + case DIRTY_FRAME: + case DIRTY_RANGE: + case DIRTY_BUFFER: if (!logger->parsePacket(logger, &item)) { return true; } diff --git a/src/feature/video-logger.h b/src/feature/video-logger.h index 0023d3b3c..41fb8ff47 100644 --- a/src/feature/video-logger.h +++ b/src/feature/video-logger.h @@ -19,14 +19,17 @@ enum mVideoLoggerDirtyType { DIRTY_REGISTER, DIRTY_OAM, DIRTY_PALETTE, - DIRTY_VRAM + DIRTY_VRAM, + DIRTY_FRAME, + DIRTY_RANGE, + DIRTY_BUFFER, }; struct mVideoLoggerDirtyInfo { enum mVideoLoggerDirtyType type; uint32_t address; - uint16_t value; - uint32_t padding; + uint32_t value; + uint32_t value2; }; struct VFile; @@ -104,8 +107,12 @@ void mVideoLoggerRendererWriteVRAM(struct mVideoLogger* logger, uint32_t address void mVideoLoggerRendererWritePalette(struct mVideoLogger* logger, uint32_t address, uint16_t value); void mVideoLoggerRendererWriteOAM(struct mVideoLogger* logger, uint32_t address, uint16_t value); +void mVideoLoggerWriteBuffer(struct mVideoLogger* logger, uint32_t bufferId, uint32_t offset, uint32_t length, const void* data); + void mVideoLoggerRendererDrawScanline(struct mVideoLogger* logger, int y); +void mVideoLoggerRendererDrawRange(struct mVideoLogger* logger, int startX, int endX, int y); void mVideoLoggerRendererFlush(struct mVideoLogger* logger); +void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger); bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block); diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index 4f8f62243..ef9493379 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -144,6 +144,9 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD case DIRTY_SCANLINE: proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); break; + case DIRTY_FRAME: + proxyRenderer->backend->finishFrame(proxyRenderer->backend); + break; case DIRTY_FLUSH: return false; default: @@ -235,6 +238,7 @@ void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->wait(proxyRenderer->logger); } + mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { proxyRenderer->logger->unlock(proxyRenderer->logger); From d4deaf62923cfcd83ca2e34ec1adfa409a194b95 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 01:55:32 -0700 Subject: [PATCH 083/159] GB: Video tester --- include/mgba/gb/core.h | 1 + include/mgba/internal/gb/renderers/proxy.h | 31 +++ include/mgba/internal/gb/video.h | 1 + src/feature/video-logger.c | 6 + src/gb/core.c | 137 +++++++++++ src/gb/memory.c | 3 + src/gb/renderers/proxy.c | 258 +++++++++++++++++++++ src/gb/renderers/software.c | 8 + src/gb/video.c | 8 + 9 files changed, 453 insertions(+) create mode 100644 include/mgba/internal/gb/renderers/proxy.h create mode 100644 src/gb/renderers/proxy.c diff --git a/include/mgba/gb/core.h b/include/mgba/gb/core.h index 10b284803..621bfe956 100644 --- a/include/mgba/gb/core.h +++ b/include/mgba/gb/core.h @@ -12,6 +12,7 @@ CXX_GUARD_START struct mCore; struct mCore* GBCoreCreate(void); +struct mCore* GBVideoLogPlayerCreate(void); CXX_GUARD_END diff --git a/include/mgba/internal/gb/renderers/proxy.h b/include/mgba/internal/gb/renderers/proxy.h new file mode 100644 index 000000000..59b0aed72 --- /dev/null +++ b/include/mgba/internal/gb/renderers/proxy.h @@ -0,0 +1,31 @@ +/* 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 GB_VIDEO_PROXY_H +#define GB_VIDEO_PROXY_H + +#include + +CXX_GUARD_START + +#include +#include "feature/video-logger.h" + +struct GBVideoProxyRenderer { + struct GBVideoRenderer d; + struct GBVideoRenderer* backend; + struct mVideoLogger* logger; + + struct GBObj objThisLine[40]; + size_t oamMax; +}; + +void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend); +void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer); +void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index fc751d43a..ce111662d 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -61,6 +61,7 @@ struct GBVideoRenderer { uint8_t (*writeVideoRegister)(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); void (*writeVRAM)(struct GBVideoRenderer* renderer, uint16_t address); void (*writePalette)(struct GBVideoRenderer* renderer, int index, uint16_t value); + void (*writeOAM)(struct GBVideoRenderer* renderer, uint16_t oam); void (*drawRange)(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* objOnLine, size_t nObj); void (*finishScanline)(struct GBVideoRenderer* renderer, int y); void (*finishFrame)(struct GBVideoRenderer* renderer); diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 1ef7e910d..c3187a3fc 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -12,6 +12,9 @@ #ifdef M_CORE_GBA #include #endif +#ifdef M_CORE_GB +#include +#endif const char mVL_MAGIC[] = "mVL\0"; @@ -21,6 +24,9 @@ const static struct mVLDescriptor { } _descriptors[] = { #ifdef M_CORE_GBA { PLATFORM_GBA, GBAVideoLogPlayerCreate }, +#endif +#ifdef M_CORE_GB + { PLATFORM_GB, GBVideoLogPlayerCreate }, #endif { PLATFORM_NONE, 0 } }; diff --git a/src/gb/core.c b/src/gb/core.c index 369bcf7b5..5bef4b658 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,9 @@ const static struct mCoreChannelInfo _GBAudioChannels[] = { struct GBCore { struct mCore d; struct GBVideoSoftwareRenderer renderer; + struct GBVideoProxyRenderer proxyRenderer; + struct mVideoLogContext* logContext; + struct mCoreCallbacks logCallbacks; uint8_t keys; struct mCPUComponent* components[CPU_COMPONENT_MAX]; const struct Configuration* overrides; @@ -635,6 +639,42 @@ static void _GBCoreEnableAudioChannel(struct mCore* core, size_t id, bool enable } } +static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* context) { + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; + gbcore->logContext = context; + + context->initialStateSize = core->stateSize(core); + context->initialState = anonymousMemoryMap(context->initialStateSize); + core->saveState(core, context->initialState); + + struct VFile* vf = VFileMemChunk(NULL, 0); + context->nChannels = 1; + context->channels[0].initialState = NULL; + context->channels[0].initialStateSize = 0; + context->channels[0].channelData = vf; + context->channels[0].type = 0; + gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); + mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false); + + gbcore->proxyRenderer.logger->vf = vf; + gbcore->proxyRenderer.logger->block = false; + + GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d); + GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); +} + +static void _GBCoreEndVideoLog(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; + GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); + free(gbcore->proxyRenderer.logger); + gbcore->proxyRenderer.logger = NULL; + + mappedMemoryFree(gbcore->logContext->initialState, gbcore->logContext->initialStateSize); + gbcore->logContext->channels[0].channelData->close(gbcore->logContext->channels[0].channelData); +} + struct mCore* GBCoreCreate(void) { struct GBCore* gbcore = malloc(sizeof(*gbcore)); struct mCore* core = &gbcore->d; @@ -707,5 +747,102 @@ struct mCore* GBCoreCreate(void) { core->listAudioChannels = _GBCoreListAudioChannels; core->enableVideoLayer = _GBCoreEnableVideoLayer; core->enableAudioChannel = _GBCoreEnableAudioChannel; + core->startVideoLog = _GBCoreStartVideoLog; + core->endVideoLog = _GBCoreEndVideoLog; return core; } + +#ifndef MINIMAL_CORE +static void _GBVLPStartFrameCallback(void *context) { + struct mCore* core = context; + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; + + if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) { + GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); + gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET); + core->loadState(core, gbcore->logContext->initialState); + GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); + + // Make sure CPU loop never spins + GBHalt(gb->cpu); + gb->memory.ie = 0; + gb->memory.ime = false; + } +} + +static bool _GBVLPInit(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + if (!_GBCoreInit(core)) { + return false; + } + gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); + mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, true); + GBVideoProxyRendererCreate(&gbcore->proxyRenderer, NULL); + memset(&gbcore->logCallbacks, 0, sizeof(gbcore->logCallbacks)); + gbcore->logCallbacks.videoFrameStarted = _GBVLPStartFrameCallback; + gbcore->logCallbacks.context = core; + core->addCoreCallbacks(core, &gbcore->logCallbacks); + return true; +} + +static void _GBVLPDeinit(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + if (gbcore->logContext) { + mVideoLoggerDestroy(core, gbcore->logContext); + } + _GBCoreDeinit(core); +} + +static void _GBVLPReset(struct mCore* core) { + struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = (struct GB*) core->board; + if (gb->video.renderer == &gbcore->proxyRenderer.d) { + GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); + } else if (gbcore->renderer.outputBuffer) { + struct GBVideoRenderer* renderer = &gbcore->renderer.d; + GBVideoAssociateRenderer(&gb->video, renderer); + } + gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET); + + LR35902Reset(core->cpu); + core->loadState(core, gbcore->logContext->initialState); + GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); + + // Make sure CPU loop never spins + GBHalt(gb->cpu); + gb->memory.ie = 0; + gb->memory.ime = false; +} + +static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) { + struct GBCore* gbcore = (struct GBCore*) core; + gbcore->logContext = mVideoLoggerCreate(NULL); + if (!mVideoLogContextLoad(vf, gbcore->logContext)) { + mVideoLoggerDestroy(core, gbcore->logContext); + gbcore->logContext = NULL; + return false; + } + gbcore->proxyRenderer.logger->vf = gbcore->logContext->channels[0].channelData; + return true; +} + +static bool _returnTrue(struct VFile* vf) { + UNUSED(vf); + return true; +} + +struct mCore* GBVideoLogPlayerCreate(void) { + struct mCore* core = GBCoreCreate(); + core->init = _GBVLPInit; + core->deinit = _GBVLPDeinit; + core->reset = _GBVLPReset; + core->loadROM = _GBVLPLoadROM; + core->isROM = _returnTrue; + return core; +} +#else +struct mCore* GBVideoLogPlayerCreate(void) { + return false; +} +#endif diff --git a/src/gb/memory.c b/src/gb/memory.c index eb709b01b..fcc01b651 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -258,6 +258,7 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { } else if (address < GB_BASE_UNUSABLE) { if (gb->video.mode < 2) { gb->video.oam.raw[address & 0xFF] = value; + gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF); } } else if (address < GB_BASE_IO) { mLOG(GB_MEM, GAME_ERROR, "Attempt to write to unusable memory: %04X:%02X", address, value); @@ -395,6 +396,7 @@ void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesL uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource); // TODO: Can DMA write OAM during modes 2-3? gb->video.oam.raw[gb->memory.dmaDest] = b; + gb->video.renderer->writeOAM(gb->video.renderer, gb->memory.dmaDest); ++gb->memory.dmaSource; ++gb->memory.dmaDest; --gb->memory.dmaRemaining; @@ -559,6 +561,7 @@ void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* o } else if (address < GB_BASE_UNUSABLE) { oldValue = gb->video.oam.raw[address & 0xFF]; gb->video.oam.raw[address & 0xFF] = value; + gb->video.renderer->writeOAM(gb->video.renderer, address & 0xFF); } else if (address < GB_BASE_HRAM) { mLOG(GB_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); return; diff --git a/src/gb/renderers/proxy.c b/src/gb/renderers/proxy.c new file mode 100644 index 000000000..747bcca81 --- /dev/null +++ b/src/gb/renderers/proxy.c @@ -0,0 +1,258 @@ +/* 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 + +#include +#include +#include + +#define BUFFER_OAM 1 + +static void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model); +static void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer); +static uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); +static void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); +static void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); +static void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value); +static void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax); +static void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y); +static void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer); +static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels); +static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels); + +static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* packet); +static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address); + +void GBVideoProxyRendererCreate(struct GBVideoProxyRenderer* renderer, struct GBVideoRenderer* backend) { + renderer->d.init = GBVideoProxyRendererInit; + renderer->d.deinit = GBVideoProxyRendererDeinit; + renderer->d.writeVideoRegister = GBVideoProxyRendererWriteVideoRegister; + renderer->d.writeVRAM = GBVideoProxyRendererWriteVRAM; + renderer->d.writeOAM = GBVideoProxyRendererWriteOAM; + renderer->d.writePalette = GBVideoProxyRendererWritePalette; + renderer->d.drawRange = GBVideoProxyRendererDrawRange; + renderer->d.finishScanline = GBVideoProxyRendererFinishScanline; + renderer->d.finishFrame = GBVideoProxyRendererFinishFrame; + renderer->d.getPixels = GBVideoProxyRendererGetPixels; + renderer->d.putPixels = GBVideoProxyRendererPutPixels; + + renderer->logger->context = renderer; + renderer->logger->parsePacket = _parsePacket; + renderer->logger->vramBlock = _vramBlock; + renderer->logger->paletteSize = 0; + renderer->logger->vramSize = GB_SIZE_VRAM; + renderer->logger->oamSize = GB_SIZE_OAM; + + renderer->backend = backend; +} + +static void _init(struct GBVideoProxyRenderer* proxyRenderer) { + mVideoLoggerRendererInit(proxyRenderer->logger); + + if (proxyRenderer->logger->block) { + proxyRenderer->backend->vram = (uint8_t*) proxyRenderer->logger->vram; + proxyRenderer->backend->oam = (union GBOAM*) proxyRenderer->logger->oam; + proxyRenderer->backend->cache = NULL; + } +} + +static void _reset(struct GBVideoProxyRenderer* proxyRenderer) { + memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM); + memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM); + + mVideoLoggerRendererReset(proxyRenderer->logger); +} + +void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) { + if ((renderer->backend && video->renderer != renderer->backend) || video->renderer == &renderer->d) { + return; + } + renderer->backend = video->renderer; + video->renderer = &renderer->d; + renderer->d.cache = renderer->backend->cache; + renderer->d.vram = video->vram; + renderer->d.oam = &video->oam; + _init(renderer); + _reset(renderer); +} + +void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) { + if (video->renderer != &renderer->d) { + return; + } + renderer->backend->cache = video->renderer->cache; + video->renderer = renderer->backend; + renderer->backend->vram = video->vram; + renderer->backend->oam = &video->oam; + + mVideoLoggerRendererDeinit(renderer->logger); +} + +void GBVideoProxyRendererInit(struct GBVideoRenderer* renderer, enum GBModel model) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + + _init(proxyRenderer); + + proxyRenderer->backend->init(proxyRenderer->backend, model); +} + +void GBVideoProxyRendererDeinit(struct GBVideoRenderer* renderer) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + + proxyRenderer->backend->deinit(proxyRenderer->backend); + + mVideoLoggerRendererDeinit(proxyRenderer->logger); +} + +static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerDirtyInfo* item) { + struct GBVideoProxyRenderer* proxyRenderer = logger->context; + switch (item->type) { + case DIRTY_REGISTER: + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_PALETTE: + proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + break; + case DIRTY_OAM: + logger->oam[item->address] = item->value; + proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + break; + case DIRTY_VRAM: + logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + break; + case DIRTY_SCANLINE: + proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address); + break; + case DIRTY_RANGE: + proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address, proxyRenderer->objThisLine, proxyRenderer->oamMax); + break; + case DIRTY_FRAME: + proxyRenderer->backend->finishFrame(proxyRenderer->backend); + break; + case DIRTY_BUFFER: + switch (item->address) { + case BUFFER_OAM: + proxyRenderer->oamMax = item->value2 / sizeof(struct GBObj); + if (proxyRenderer->oamMax > 40) { + return false; + } + logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true); + } + break; + case DIRTY_FLUSH: + return false; + default: + return false; + } + return true; +} + +static uint16_t* _vramBlock(struct mVideoLogger* logger, uint32_t address) { + struct GBVideoProxyRenderer* proxyRenderer = logger->context; + return (uint16_t*) &proxyRenderer->d.vram[address]; +} + +uint8_t GBVideoProxyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + + mVideoLoggerRendererWriteVideoRegister(proxyRenderer->logger, address, value); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, address, value); + } + return value; +} + +void GBVideoProxyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + mVideoLoggerRendererWriteVRAM(proxyRenderer->logger, address); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, address); + } + if (renderer->cache) { + mTileCacheWriteVRAM(renderer->cache, address); + } +} + +void GBVideoProxyRendererWritePalette(struct GBVideoRenderer* renderer, int address, uint16_t value) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + mVideoLoggerRendererWritePalette(proxyRenderer->logger, address, value); + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writePalette(proxyRenderer->backend, address, value); + } + if (renderer->cache) { + mTileCacheWritePalette(renderer->cache, address); + } +} + +void GBVideoProxyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->writeOAM(proxyRenderer->backend, oam); + } + mVideoLoggerRendererWriteOAM(proxyRenderer->logger, oam, ((uint8_t*) proxyRenderer->d.oam->raw)[oam]); +} + +void GBVideoProxyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->drawRange(proxyRenderer->backend, startX, endX, y, obj, oamMax); + } + mVideoLoggerWriteBuffer(proxyRenderer->logger, BUFFER_OAM, 0, oamMax * sizeof(*obj), obj); + mVideoLoggerRendererDrawRange(proxyRenderer->logger, startX, endX, y); +} + +void GBVideoProxyRendererFinishScanline(struct GBVideoRenderer* renderer, int y) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (!proxyRenderer->logger->block) { + proxyRenderer->backend->finishScanline(proxyRenderer->backend, y); + } + mVideoLoggerRendererDrawScanline(proxyRenderer->logger, y); + if (proxyRenderer->logger->block && proxyRenderer->logger->wake) { + proxyRenderer->logger->wake(proxyRenderer->logger, y); + } +} + +void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) { + struct GBVideoProxyRenderer* proxyRenderer = (struct GBVideoProxyRenderer*) renderer; + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->lock(proxyRenderer->logger); + proxyRenderer->logger->wait(proxyRenderer->logger); + } + mVideoLoggerRendererFinishFrame(proxyRenderer->logger); + mVideoLoggerRendererFlush(proxyRenderer->logger); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); + } +} + +static void GBVideoProxyRendererGetPixels(struct GBVideoRenderer* renderer, size_t* stride, const void** pixels) { + 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->getPixels(proxyRenderer->backend, stride, pixels); + if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { + proxyRenderer->logger->unlock(proxyRenderer->logger); + } +} + +static void GBVideoProxyRendererPutPixels(struct GBVideoRenderer* renderer, size_t stride, const void* pixels) { + 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) { + proxyRenderer->logger->unlock(proxyRenderer->logger); + } +} diff --git a/src/gb/renderers/software.c b/src/gb/renderers/software.c index c8922e0b8..1474a86a5 100644 --- a/src/gb/renderers/software.c +++ b/src/gb/renderers/software.c @@ -14,6 +14,7 @@ static void GBVideoSoftwareRendererDeinit(struct GBVideoRenderer* renderer); static uint8_t GBVideoSoftwareRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static void GBVideoSoftwareRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); +static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax); static void GBVideoSoftwareRendererFinishScanline(struct GBVideoRenderer* renderer, int y); static void GBVideoSoftwareRendererFinishFrame(struct GBVideoRenderer* renderer); @@ -43,6 +44,7 @@ void GBVideoSoftwareRendererCreate(struct GBVideoSoftwareRenderer* renderer) { renderer->d.writeVideoRegister = GBVideoSoftwareRendererWriteVideoRegister; renderer->d.writePalette = GBVideoSoftwareRendererWritePalette; renderer->d.writeVRAM = GBVideoSoftwareRendererWriteVRAM; + renderer->d.writeOAM = GBVideoSoftwareRendererWriteOAM; renderer->d.drawRange = GBVideoSoftwareRendererDrawRange; renderer->d.finishScanline = GBVideoSoftwareRendererFinishScanline; renderer->d.finishFrame = GBVideoSoftwareRendererFinishFrame; @@ -126,6 +128,12 @@ static void GBVideoSoftwareRendererWriteVRAM(struct GBVideoRenderer* renderer, u } } +static void GBVideoSoftwareRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) { + UNUSED(renderer); + UNUSED(oam); + // Nothing to do +} + static void GBVideoSoftwareRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax) { struct GBVideoSoftwareRenderer* softwareRenderer = (struct GBVideoSoftwareRenderer*) renderer; uint8_t* maps = &softwareRenderer->d.vram[GB_BASE_MAP]; diff --git a/src/gb/video.c b/src/gb/video.c index 4925b85cd..cf68e38e5 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -20,6 +20,7 @@ static void GBVideoDummyRendererDeinit(struct GBVideoRenderer* renderer); static uint8_t GBVideoDummyRendererWriteVideoRegister(struct GBVideoRenderer* renderer, uint16_t address, uint8_t value); static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value); static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint16_t address); +static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam); static void GBVideoDummyRendererDrawRange(struct GBVideoRenderer* renderer, int startX, int endX, int y, struct GBObj* obj, size_t oamMax); static void GBVideoDummyRendererFinishScanline(struct GBVideoRenderer* renderer, int y); static void GBVideoDummyRendererFinishFrame(struct GBVideoRenderer* renderer); @@ -39,6 +40,7 @@ static struct GBVideoRenderer dummyRenderer = { .deinit = GBVideoDummyRendererDeinit, .writeVideoRegister = GBVideoDummyRendererWriteVideoRegister, .writeVRAM = GBVideoDummyRendererWriteVRAM, + .writeOAM = GBVideoDummyRendererWriteOAM, .writePalette = GBVideoDummyRendererWritePalette, .drawRange = GBVideoDummyRendererDrawRange, .finishScanline = GBVideoDummyRendererFinishScanline, @@ -469,6 +471,12 @@ static void GBVideoDummyRendererWriteVRAM(struct GBVideoRenderer* renderer, uint } } +static void GBVideoDummyRendererWriteOAM(struct GBVideoRenderer* renderer, uint16_t oam) { + UNUSED(renderer); + UNUSED(oam); + // Nothing to do +} + static void GBVideoDummyRendererWritePalette(struct GBVideoRenderer* renderer, int index, uint16_t value) { UNUSED(value); if (renderer->cache) { From 3dbd49a307fb7f0725b9b7481445578fdf820284 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 02:02:13 -0700 Subject: [PATCH 084/159] GB, GBA Video: Put back missing finishFrame callback --- src/gb/renderers/proxy.c | 1 + src/gba/renderers/proxy.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gb/renderers/proxy.c b/src/gb/renderers/proxy.c index 747bcca81..5f8fafb76 100644 --- a/src/gb/renderers/proxy.c +++ b/src/gb/renderers/proxy.c @@ -222,6 +222,7 @@ void GBVideoProxyRendererFinishFrame(struct GBVideoRenderer* renderer) { proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->wait(proxyRenderer->logger); } + proxyRenderer->backend->finishFrame(proxyRenderer->backend); mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index ef9493379..563f81234 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -238,6 +238,7 @@ void GBAVideoProxyRendererFinishFrame(struct GBAVideoRenderer* renderer) { proxyRenderer->logger->lock(proxyRenderer->logger); proxyRenderer->logger->wait(proxyRenderer->logger); } + proxyRenderer->backend->finishFrame(proxyRenderer->backend); mVideoLoggerRendererFinishFrame(proxyRenderer->logger); mVideoLoggerRendererFlush(proxyRenderer->logger); if (proxyRenderer->logger->block && proxyRenderer->logger->wait) { From 9b0a5e566ec2de97baf5abbe0807922420e34006 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 02:11:24 -0700 Subject: [PATCH 085/159] GB Video: Fix proxy renderer for GBC --- src/gb/renderers/proxy.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gb/renderers/proxy.c b/src/gb/renderers/proxy.c index 5f8fafb76..0cbf5053c 100644 --- a/src/gb/renderers/proxy.c +++ b/src/gb/renderers/proxy.c @@ -59,10 +59,14 @@ static void _init(struct GBVideoProxyRenderer* proxyRenderer) { } } -static void _reset(struct GBVideoProxyRenderer* proxyRenderer) { +static void _reset(struct GBVideoProxyRenderer* proxyRenderer, enum GBModel model) { memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM); memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM); + + proxyRenderer->backend->deinit(proxyRenderer->backend); + proxyRenderer->backend->init(proxyRenderer->backend, model); + mVideoLoggerRendererReset(proxyRenderer->logger); } @@ -76,7 +80,7 @@ void GBVideoProxyRendererShim(struct GBVideo* video, struct GBVideoProxyRenderer renderer->d.vram = video->vram; renderer->d.oam = &video->oam; _init(renderer); - _reset(renderer); + _reset(renderer, video->p->model); } void GBVideoProxyRendererUnshim(struct GBVideo* video, struct GBVideoProxyRenderer* renderer) { From 1731d4f97581d1dfffb7b2aa8aa12661a7758671 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 02:53:22 -0700 Subject: [PATCH 086/159] Test: Don't rely on core for frames elapsed --- CHANGES | 1 + src/platform/test/fuzz-main.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 5c9f39755..fe416bcb4 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Bugfixes: - GBA Hardware: Fix crash if a savestate lies about game hardware - Test: Fix crash when fuzzing fails to load a file - GBA: Fix multiboot loading resulting in too small WRAM + - Test: Don't rely on core for frames elapsed Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index 7e86ec914..533866155 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -176,9 +176,10 @@ loadError: static void _fuzzRunloop(struct mCore* core, int frames) { do { core->runFrame(core); + --frames; blip_clear(core->getAudioChannel(core, 0)); blip_clear(core->getAudioChannel(core, 1)); - } while (core->frameCounter(core) < frames && !_dispatchExiting); + } while (frames > 0 && !_dispatchExiting); } static void _fuzzShutdown(int signal) { From 8d6edf9033c4c25bf755bb6e076bc5d6d2bf6994 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 03:12:38 -0700 Subject: [PATCH 087/159] GB Video: mVL bounds checking --- src/gb/renderers/proxy.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/gb/renderers/proxy.c b/src/gb/renderers/proxy.c index 0cbf5053c..052e93805 100644 --- a/src/gb/renderers/proxy.c +++ b/src/gb/renderers/proxy.c @@ -63,6 +63,7 @@ static void _reset(struct GBVideoProxyRenderer* proxyRenderer, enum GBModel mode memcpy(proxyRenderer->logger->oam, &proxyRenderer->d.oam->raw, GB_SIZE_OAM); memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GB_SIZE_VRAM); + proxyRenderer->oamMax = 0; proxyRenderer->backend->deinit(proxyRenderer->backend); proxyRenderer->backend->init(proxyRenderer->backend, model); @@ -118,21 +119,31 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); break; case DIRTY_PALETTE: - proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + if (item->address < 64) { + proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + } break; case DIRTY_OAM: - logger->oam[item->address] = item->value; - proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + if (item->address < GB_SIZE_OAM) { + logger->oam[item->address] = item->value; + proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + } break; case DIRTY_VRAM: - logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); - proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + if (item->address + 0x1000 <= GB_SIZE_VRAM) { + logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + } break; case DIRTY_SCANLINE: - proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address); + if (item->address < GB_VIDEO_VERTICAL_PIXELS) { + proxyRenderer->backend->finishScanline(proxyRenderer->backend, item->address); + } break; case DIRTY_RANGE: - proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address, proxyRenderer->objThisLine, proxyRenderer->oamMax); + if (item->value < item->value2 && item->value2 <= GB_VIDEO_HORIZONTAL_PIXELS && item->address < GB_VIDEO_VERTICAL_PIXELS) { + proxyRenderer->backend->drawRange(proxyRenderer->backend, item->value, item->value2, item->address, proxyRenderer->objThisLine, proxyRenderer->oamMax); + } break; case DIRTY_FRAME: proxyRenderer->backend->finishFrame(proxyRenderer->backend); @@ -142,6 +153,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD case BUFFER_OAM: proxyRenderer->oamMax = item->value2 / sizeof(struct GBObj); if (proxyRenderer->oamMax > 40) { + proxyRenderer->oamMax = 0; return false; } logger->readData(logger, &proxyRenderer->objThisLine, item->value2, true); From 5b6bf9eb646b10b55424c4112e77295163894dfe Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 03:36:48 -0700 Subject: [PATCH 088/159] Test: Fix crash when loading invalid file --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index fe416bcb4..c7f1702fe 100644 --- a/CHANGES +++ b/CHANGES @@ -50,6 +50,7 @@ Bugfixes: - Test: Fix crash when fuzzing fails to load a file - GBA: Fix multiboot loading resulting in too small WRAM - Test: Don't rely on core for frames elapsed + - Test: Fix crash when loading invalid file Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers From caf59d726f5e8e4b9269e7ada8bc9c59831b08bb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 03:42:37 -0700 Subject: [PATCH 089/159] GBA Video: mVL bounds checking --- src/gba/renderers/proxy.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index 563f81234..98e658dce 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -130,19 +130,27 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); break; case DIRTY_PALETTE: - logger->palette[item->address >> 1] = item->value; - proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + if (item->address < SIZE_PALETTE_RAM) { + logger->palette[item->address >> 1] = item->value; + proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); + } break; case DIRTY_OAM: - logger->oam[item->address] = item->value; - proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + if (item->address < SIZE_PALETTE_RAM) { + logger->oam[item->address] = item->value; + proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); + } break; case DIRTY_VRAM: - logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); - proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + if (item->address + 0x1000 <= SIZE_VRAM) { + logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); + proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); + } break; case DIRTY_SCANLINE: - proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); + if (item->address < VIDEO_VERTICAL_PIXELS) { + proxyRenderer->backend->drawScanline(proxyRenderer->backend, item->address); + } break; case DIRTY_FRAME: proxyRenderer->backend->finishFrame(proxyRenderer->backend); From 716a2453978d149291f9b88caff36f06fa227e95 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 04:02:44 -0700 Subject: [PATCH 090/159] GB, GBA Video: Fix integer overflow --- src/gb/renderers/proxy.c | 2 +- src/gba/renderers/proxy.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gb/renderers/proxy.c b/src/gb/renderers/proxy.c index 052e93805..eab14a7c1 100644 --- a/src/gb/renderers/proxy.c +++ b/src/gb/renderers/proxy.c @@ -130,7 +130,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD } break; case DIRTY_VRAM: - if (item->address + 0x1000 <= GB_SIZE_VRAM) { + if (item->address <= GB_SIZE_VRAM - 0x1000) { logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); } diff --git a/src/gba/renderers/proxy.c b/src/gba/renderers/proxy.c index 98e658dce..e75da94c4 100644 --- a/src/gba/renderers/proxy.c +++ b/src/gba/renderers/proxy.c @@ -142,7 +142,7 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD } break; case DIRTY_VRAM: - if (item->address + 0x1000 <= SIZE_VRAM) { + if (item->address <= SIZE_VRAM - 0x1000) { logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); } From 3021996a49cf72be55d15a1be4b24a13a8b751b9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Apr 2017 17:13:11 -0700 Subject: [PATCH 091/159] All: Cleanup and buildfixing --- CMakeLists.txt | 7 +++++-- include/mgba/core/core.h | 2 ++ include/mgba/gb/core.h | 2 ++ include/mgba/gba/core.h | 2 ++ src/gb/core.c | 2 ++ src/gb/{extra => debugger}/cli.c | 0 src/gb/{renderers => extra}/proxy.c | 0 src/gba/core.c | 2 ++ src/gba/{extra => debugger}/cli.c | 0 src/gba/{renderers => extra}/proxy.c | 0 10 files changed, 15 insertions(+), 2 deletions(-) rename src/gb/{extra => debugger}/cli.c (100%) rename src/gb/{renderers => extra}/proxy.c (100%) rename src/gba/{extra => debugger}/cli.c (100%) rename src/gba/{renderers => extra}/proxy.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90222c0f9..e5490a04b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,8 +54,10 @@ file(GLOB UTIL_TEST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/test/*.c) file(GLOB GUI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/gui/*.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/gui/*.c) file(GLOB GBA_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/renderers/*.c) file(GLOB GBA_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/sio/lockstep.c) +file(GLOB GBA_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/*.c) file(GLOB GB_SIO_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/sio/lockstep.c) file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c) +file(GLOB GB_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/*.c) file(GLOB THIRD_PARTY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/inih/*.c) file(GLOB EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/*.c) set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c) @@ -600,7 +602,7 @@ if(M_CORE_GB) list(APPEND DEBUGGER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/cli-debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/debugger.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/cli.c) + ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c) list(APPEND TEST_SRC ${LR35902_TEST_SRC} ${GB_TEST_SRC}) @@ -617,7 +619,7 @@ if(M_CORE_GBA) ${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/cli-debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/arm/debugger/memory-debugger.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/extra/cli.c) + ${CMAKE_CURRENT_SOURCE_DIR}/src/gba/debugger/cli.c) list(APPEND TEST_SRC ${ARM_TEST_SRC} ${GBA_TEST_SRC}) @@ -670,6 +672,7 @@ if(NOT MINIMAL_CORE) endif() if(M_CORE_GB) list(APPEND SRC + ${GB_EXTRA_SRC} ${GB_SIO_SRC}) endif() list(APPEND SRC diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index 170634bc2..f7238f861 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -147,8 +147,10 @@ struct mCore { void (*enableVideoLayer)(struct mCore*, size_t id, bool enable); void (*enableAudioChannel)(struct mCore*, size_t id, bool enable); +#ifndef MINIMAL_CORE void (*startVideoLog)(struct mCore*, struct mVideoLogContext*); void (*endVideoLog)(struct mCore*); +#endif }; #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 diff --git a/include/mgba/gb/core.h b/include/mgba/gb/core.h index 621bfe956..90ddccad6 100644 --- a/include/mgba/gb/core.h +++ b/include/mgba/gb/core.h @@ -12,7 +12,9 @@ CXX_GUARD_START struct mCore; struct mCore* GBCoreCreate(void); +#ifndef MINIMAL_CORE struct mCore* GBVideoLogPlayerCreate(void); +#endif CXX_GUARD_END diff --git a/include/mgba/gba/core.h b/include/mgba/gba/core.h index 65a19f447..5cb0e468c 100644 --- a/include/mgba/gba/core.h +++ b/include/mgba/gba/core.h @@ -12,7 +12,9 @@ CXX_GUARD_START struct mCore; struct mCore* GBACoreCreate(void); +#ifndef MINIMAL_CORE struct mCore* GBAVideoLogPlayerCreate(void); +#endif CXX_GUARD_END diff --git a/src/gb/core.c b/src/gb/core.c index 5bef4b658..cd77c6f33 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -747,8 +747,10 @@ struct mCore* GBCoreCreate(void) { core->listAudioChannels = _GBCoreListAudioChannels; core->enableVideoLayer = _GBCoreEnableVideoLayer; core->enableAudioChannel = _GBCoreEnableAudioChannel; +#ifndef MINIMAL_CORE core->startVideoLog = _GBCoreStartVideoLog; core->endVideoLog = _GBCoreEndVideoLog; +#endif return core; } diff --git a/src/gb/extra/cli.c b/src/gb/debugger/cli.c similarity index 100% rename from src/gb/extra/cli.c rename to src/gb/debugger/cli.c diff --git a/src/gb/renderers/proxy.c b/src/gb/extra/proxy.c similarity index 100% rename from src/gb/renderers/proxy.c rename to src/gb/extra/proxy.c diff --git a/src/gba/core.c b/src/gba/core.c index 5564fa784..522c8a41b 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -766,8 +766,10 @@ struct mCore* GBACoreCreate(void) { core->listAudioChannels = _GBACoreListAudioChannels; core->enableVideoLayer = _GBACoreEnableVideoLayer; core->enableAudioChannel = _GBACoreEnableAudioChannel; +#ifndef MINIMAL_CORE core->startVideoLog = _GBACoreStartVideoLog; core->endVideoLog = _GBACoreEndVideoLog; +#endif return core; } diff --git a/src/gba/extra/cli.c b/src/gba/debugger/cli.c similarity index 100% rename from src/gba/extra/cli.c rename to src/gba/debugger/cli.c diff --git a/src/gba/renderers/proxy.c b/src/gba/extra/proxy.c similarity index 100% rename from src/gba/renderers/proxy.c rename to src/gba/extra/proxy.c From deffdc79a0e657365b223a168092a4814fd77b97 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Apr 2017 21:41:48 -0700 Subject: [PATCH 092/159] Core: New mVL file format --- include/mgba-util/circle-buffer.h | 1 + src/feature/thread-proxy.c | 1 - src/feature/video-logger.c | 463 +++++++++++++++++++++-------- src/feature/video-logger.h | 56 ++-- src/gb/core.c | 34 +-- src/gba/core.c | 35 +-- src/platform/qt/GameController.cpp | 16 +- src/platform/qt/GameController.h | 2 +- src/util/circle-buffer.c | 28 ++ 9 files changed, 412 insertions(+), 224 deletions(-) diff --git a/include/mgba-util/circle-buffer.h b/include/mgba-util/circle-buffer.h index 28d436e03..dee1f1f2a 100644 --- a/include/mgba-util/circle-buffer.h +++ b/include/mgba-util/circle-buffer.h @@ -26,6 +26,7 @@ void CircleBufferClear(struct CircleBuffer* buffer); int CircleBufferWrite8(struct CircleBuffer* buffer, int8_t value); int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value); int CircleBufferWrite32(struct CircleBuffer* buffer, int32_t value); +size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length); int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value); int CircleBufferRead16(struct CircleBuffer* buffer, int16_t* value); int CircleBufferRead32(struct CircleBuffer* buffer, int32_t* value); diff --git a/src/feature/thread-proxy.c b/src/feature/thread-proxy.c index 91db10f4b..51e807565 100644 --- a/src/feature/thread-proxy.c +++ b/src/feature/thread-proxy.c @@ -39,7 +39,6 @@ void mVideoThreadProxyCreate(struct mVideoThreadProxy* renderer) { renderer->d.writeData = _writeData; renderer->d.readData = _readData; - renderer->d.vf = NULL; } void mVideoThreadProxyInit(struct mVideoLogger* logger) { diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index c3187a3fc..57a028c3c 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -8,6 +8,7 @@ #include #include #include +#include #ifdef M_CORE_GBA #include @@ -16,6 +17,8 @@ #include #endif +#define BUFFER_BASE_SIZE 0x8000 + const char mVL_MAGIC[] = "mVL\0"; const static struct mVLDescriptor { @@ -31,10 +34,64 @@ const static struct mVLDescriptor { { PLATFORM_NONE, 0 } }; +enum mVLBlockType { + mVL_BLOCK_DUMMY = 0, + mVL_BLOCK_INITIAL_STATE, + mVL_BLOCK_CHANNEL_HEADER, + mVL_BLOCK_DATA, + mVL_BLOCK_FOOTER = 0x784C566D +}; + +enum mVLHeaderFlag { + mVL_FLAG_HAS_INITIAL_STATE = 1 +}; + +struct mVLBlockHeader { + uint32_t blockType; + uint32_t length; + uint32_t channelId; + uint32_t flags; +}; + +struct mVideoLogHeader { + char magic[4]; + uint32_t flags; + uint32_t platform; + uint32_t nChannels; +}; + +struct mVideoLogContext; +struct mVideoLogChannel { + struct mVideoLogContext* p; + + uint32_t type; + void* initialState; + size_t initialStateSize; + + off_t currentPointer; + size_t bufferRemaining; + + struct CircleBuffer buffer; +}; + +struct mVideoLogContext { + void* initialState; + size_t initialStateSize; + uint32_t nChannels; + struct mVideoLogChannel channels[mVL_MAX_CHANNELS]; + + uint32_t activeChannel; + struct VFile* backing; +}; + + static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length); static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length); static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block); +static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* data, size_t length); +static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const void* data, size_t length); + static inline size_t _roundUp(size_t value, int shift) { value += (1 << shift) - 1; return value >> shift; @@ -48,7 +105,7 @@ void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly) { logger->writeData = _writeData; } logger->readData = _readData; - logger->vf = NULL; + logger->dataContext = NULL; logger->init = NULL; logger->deinit = NULL; @@ -235,7 +292,8 @@ bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block) { } static bool _writeData(struct mVideoLogger* logger, const void* data, size_t length) { - return logger->vf->write(logger->vf, data, length) == (ssize_t) length; + struct mVideoLogChannel* channel = logger->dataContext; + return mVideoLoggerWriteChannel(channel, data, length) == (ssize_t) length; } static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t length) { @@ -247,92 +305,316 @@ static bool _writeNull(struct mVideoLogger* logger, const void* data, size_t len static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bool block) { UNUSED(block); - return logger->vf->read(logger->vf, data, length) == (ssize_t) length; + struct mVideoLogChannel* channel = logger->dataContext; + return mVideoLoggerReadChannel(channel, data, length) == (ssize_t) length; } -struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core) { +void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId) { + if (channelId >= mVL_MAX_CHANNELS) { + return; + } + logger->dataContext = &context->channels[channelId]; +} + +struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core) { struct mVideoLogContext* context = malloc(sizeof(*context)); + memset(context, 0, sizeof(*context)); + if (core) { + context->initialStateSize = core->stateSize(core); + context->initialState = anonymousMemoryMap(context->initialStateSize); + core->saveState(core, context->initialState); core->startVideoLog(core, context); } + + context->activeChannel = 0; return context; } -void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext* context) { - if (core) { - core->endVideoLog(core); - } - free(context); +void mVideoLogContextSetOutput(struct mVideoLogContext* context, struct VFile* vf) { + context->backing = vf; + vf->truncate(vf, 0); + vf->seek(vf, 0, SEEK_SET); } -void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext* context, struct VFile* vf) { - struct mVideoLogHeader header = {{0}}; - memcpy(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)); - +void mVideoLogContextWriteHeader(struct mVideoLogContext* context, struct mCore* core) { + struct mVideoLogHeader header = { { 0 } }; + memcpy(header.magic, mVL_MAGIC, sizeof(header.magic)); enum mPlatform platform = core->platform(core); STORE_32LE(platform, 0, &header.platform); STORE_32LE(context->nChannels, 0, &header.nChannels); - ssize_t pointer = vf->seek(vf, sizeof(header), SEEK_SET); - if (context->initialStateSize) { - ssize_t written = vf->write(vf, context->initialState, context->initialStateSize); - if (written > 0) { - STORE_32LE(pointer, 0, &header.initialStatePointer); - STORE_32LE(context->initialStateSize, 0, &header.initialStateSize); - pointer += written; - } else { - header.initialStatePointer = 0; - } - } else { - header.initialStatePointer = 0; + uint32_t flags = 0; + if (context->initialState) { + flags |= mVL_FLAG_HAS_INITIAL_STATE; } + STORE_32LE(flags, 0, &header.flags); + context->backing->write(context->backing, &header, sizeof(header)); + if (context->initialState) { + struct mVLBlockHeader chheader = { 0 }; + STORE_32LE(mVL_BLOCK_INITIAL_STATE, 0, &chheader.blockType); + STORE_32LE(context->initialStateSize, 0, &chheader.length); + context->backing->write(context->backing, &chheader, sizeof(chheader)); + context->backing->write(context->backing, context->initialState, context->initialStateSize); + } + + size_t i; + for (i = 0; i < context->nChannels; ++i) { + struct mVLBlockHeader chheader = { 0 }; + STORE_32LE(mVL_BLOCK_CHANNEL_HEADER, 0, &chheader.blockType); + STORE_32LE(i, 0, &chheader.channelId); + context->backing->write(context->backing, &chheader, sizeof(chheader)); + } +} + +bool _readBlockHeader(struct mVideoLogContext* context, struct mVLBlockHeader* header) { + struct mVLBlockHeader buffer; + if (context->backing->read(context->backing, &buffer, sizeof(buffer)) != sizeof(buffer)) { + return false; + } + LOAD_32LE(header->blockType, 0, &buffer.blockType); + LOAD_32LE(header->length, 0, &buffer.length); + LOAD_32LE(header->channelId, 0, &buffer.channelId); + LOAD_32LE(header->flags, 0, &buffer.flags); + return true; +} + +bool _readHeader(struct mVideoLogContext* context) { + struct mVideoLogHeader header; + context->backing->seek(context->backing, 0, SEEK_SET); + if (context->backing->read(context->backing, &header, sizeof(header)) != sizeof(header)) { + return false; + } + if (memcmp(header.magic, mVL_MAGIC, sizeof(header.magic)) != 0) { + return false; + } + + LOAD_32LE(context->nChannels, 0, &header.nChannels); + if (context->nChannels > mVL_MAX_CHANNELS) { + return false; + } + + uint32_t flags; + LOAD_32LE(flags, 0, &header.flags); + if (flags & mVL_FLAG_HAS_INITIAL_STATE) { + struct mVLBlockHeader header; + if (!_readBlockHeader(context, &header)) { + return false; + } + if (header.blockType != mVL_BLOCK_INITIAL_STATE) { + return false; + } + context->initialStateSize = header.length; + context->initialState = anonymousMemoryMap(header.length); + context->backing->read(context->backing, context->initialState, context->initialStateSize); + } + return true; +} + +bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) { + context->backing = vf; + + if (!_readHeader(context)) { + return false; + } + + off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR); size_t i; - for (i = 0; i < context->nChannels && i < mVL_MAX_CHANNELS; ++i) { - struct VFile* channel = context->channels[i].channelData; - void* block = channel->map(channel, channel->size(channel), MAP_READ); + for (i = 0; i < context->nChannels; ++i) { + CircleBufferInit(&context->channels[i].buffer, BUFFER_BASE_SIZE); + context->channels[i].bufferRemaining = 0; + context->channels[i].currentPointer = pointer; + context->channels[i].p = context; + } + return true; +} - struct mVideoLogChannelHeader chHeader = {0}; - STORE_32LE(context->channels[i].type, 0, &chHeader.type); - STORE_32LE(channel->size(channel), 0, &chHeader.channelSize); +static void _flushBuffer(struct mVideoLogContext* context) { + struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer; + if (!CircleBufferSize(buffer)) { + return; + } + struct mVLBlockHeader header = { 0 }; + STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType); + STORE_32LE(CircleBufferSize(buffer), 0, &header.length); + STORE_32LE(context->activeChannel, 0, &header.channelId); + context->backing->write(context->backing, &header, sizeof(header)); - if (context->channels[i].initialStateSize) { - ssize_t written = vf->write(vf, context->channels[i].initialState, context->channels[i].initialStateSize); - if (written > 0) { - STORE_32LE(pointer, 0, &chHeader.channelInitialStatePointer); - STORE_32LE(context->channels[i].initialStateSize, 0, &chHeader.channelInitialStateSize); - pointer += written; - } else { - chHeader.channelInitialStatePointer = 0; - } + uint8_t writeBuffer[0x1000]; + while (CircleBufferSize(buffer)) { + size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer)); + context->backing->write(context->backing, writeBuffer, read); + } +} + +void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* context) { + _flushBuffer(context); + + struct mVLBlockHeader header = { 0 }; + STORE_32LE(mVL_BLOCK_FOOTER, 0, &header.blockType); + context->backing->write(context->backing, &header, sizeof(header)); + + if (core) { + core->endVideoLog(core); + } + if (context->initialState) { + mappedMemoryFree(context->initialState, context->initialStateSize); + } + free(context); +} + +void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core) { + _readHeader(context); + if (core) { + core->loadState(core, context->initialState); + } + + off_t pointer = context->backing->seek(context->backing, 0, SEEK_CUR); + + size_t i; + for (i = 0; i < context->nChannels; ++i) { + CircleBufferClear(&context->channels[i].buffer); + context->channels[i].bufferRemaining = 0; + context->channels[i].currentPointer = pointer; + } +} + +void* mVideoLogContextInitialState(struct mVideoLogContext* context, size_t* size) { + if (size) { + *size = context->initialStateSize; + } + return context->initialState; +} + +int mVideoLoggerAddChannel(struct mVideoLogContext* context) { + if (context->nChannels >= mVL_MAX_CHANNELS) { + return -1; + } + int chid = context->nChannels; + ++context->nChannels; + context->channels[chid].p = context; + CircleBufferInit(&context->channels[chid].buffer, BUFFER_BASE_SIZE); + return chid; +} + +static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) { + uint8_t buffer[0x1000]; + while (length) { + size_t thisRead = sizeof(buffer); + if (thisRead > length) { + thisRead = length; } - STORE_32LE(pointer, 0, &header.channelPointers[i]); - ssize_t written = vf->write(vf, &chHeader, sizeof(chHeader)); - if (written != sizeof(chHeader)) { - continue; + thisRead = vf->read(vf, buffer, thisRead); + if (thisRead <= 0) { + return; } - pointer += written; - written = vf->write(vf, block, channel->size(channel)); - if (written != channel->size(channel)) { + size_t thisWrite = CircleBufferWrite(&channel->buffer, buffer, thisRead); + length -= thisWrite; + channel->bufferRemaining -= thisWrite; + channel->currentPointer += thisWrite; + if (thisWrite < thisRead) { break; } - pointer += written; } - vf->seek(vf, 0, SEEK_SET); - vf->write(vf, &header, sizeof(header)); +} + +static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size_t length) { + struct mVideoLogChannel* channel = &context->channels[channelId]; + context->backing->seek(context->backing, channel->currentPointer, SEEK_SET); + struct mVLBlockHeader header; + while (length) { + size_t bufferRemaining = channel->bufferRemaining; + if (bufferRemaining) { + if (bufferRemaining > length) { + bufferRemaining = length; + } + _readBuffer(context->backing, channel, bufferRemaining); + length -= bufferRemaining; + continue; + } + + if (!_readBlockHeader(context, &header)) { + return false; + } + if (header.blockType == mVL_BLOCK_FOOTER) { + return false; + } + if (header.channelId != channelId || header.blockType != mVL_BLOCK_DATA) { + context->backing->seek(context->backing, header.length, SEEK_CUR); + continue; + } + channel->currentPointer = context->backing->seek(context->backing, 0, SEEK_CUR); + if (!header.length) { + continue; + } + + channel->bufferRemaining = header.length; + } + return true; +} + +static ssize_t mVideoLoggerReadChannel(struct mVideoLogChannel* channel, void* data, size_t length) { + struct mVideoLogContext* context = channel->p; + unsigned channelId = channel - context->channels; + if (channelId >= mVL_MAX_CHANNELS) { + return 0; + } + if (CircleBufferSize(&channel->buffer) >= length) { + return CircleBufferRead(&channel->buffer, data, length); + } + ssize_t size = 0; + if (CircleBufferSize(&channel->buffer)) { + size = CircleBufferRead(&channel->buffer, data, CircleBufferSize(&channel->buffer)); + if (size <= 0) { + return size; + } + data = (uint8_t*) data + size; + length -= size; + } + if (!_fillBuffer(context, channelId, BUFFER_BASE_SIZE)) { + return size; + } + size += CircleBufferRead(&channel->buffer, data, length); + return size; +} + +static ssize_t mVideoLoggerWriteChannel(struct mVideoLogChannel* channel, const void* data, size_t length) { + struct mVideoLogContext* context = channel->p; + unsigned channelId = channel - context->channels; + if (channelId >= mVL_MAX_CHANNELS) { + return 0; + } + if (channelId != context->activeChannel) { + _flushBuffer(context); + context->activeChannel = channelId; + } + if (CircleBufferCapacity(&channel->buffer) - CircleBufferSize(&channel->buffer) < length) { + _flushBuffer(context); + if (CircleBufferCapacity(&channel->buffer) < length) { + CircleBufferDeinit(&channel->buffer); + CircleBufferInit(&channel->buffer, toPow2(length << 1)); + } + } + + ssize_t read = CircleBufferWrite(&channel->buffer, data, length); + if (CircleBufferCapacity(&channel->buffer) == CircleBufferSize(&channel->buffer)) { + _flushBuffer(context); + } + return read; } struct mCore* mVideoLogCoreFind(struct VFile* vf) { if (!vf) { return NULL; } - struct mVideoLogHeader header = {{0}}; + struct mVideoLogHeader header = { { 0 } }; vf->seek(vf, 0, SEEK_SET); ssize_t read = vf->read(vf, &header, sizeof(header)); if (read != sizeof(header)) { return NULL; } - if (memcmp(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)) != 0) { + if (memcmp(header.magic, mVL_MAGIC, sizeof(header.magic)) != 0) { return NULL; } enum mPlatform platform; @@ -350,80 +632,3 @@ struct mCore* mVideoLogCoreFind(struct VFile* vf) { } return core; } - -bool mVideoLogContextLoad(struct VFile* vf, struct mVideoLogContext* context) { - if (!vf) { - return false; - } - struct mVideoLogHeader header = {{0}}; - vf->seek(vf, 0, SEEK_SET); - ssize_t read = vf->read(vf, &header, sizeof(header)); - if (read != sizeof(header)) { - return false; - } - if (memcmp(header.magic, mVL_MAGIC, sizeof(mVL_MAGIC)) != 0) { - return false; - } - - // TODO: Error check - uint32_t initialStatePointer; - uint32_t initialStateSize; - LOAD_32LE(initialStatePointer, 0, &header.initialStatePointer); - LOAD_32LE(initialStateSize, 0, &header.initialStateSize); - void* initialState = anonymousMemoryMap(initialStateSize); - vf->read(vf, initialState, initialStateSize); - context->initialState = initialState; - context->initialStateSize = initialStateSize; - - uint32_t nChannels; - LOAD_32LE(nChannels, 0, &header.nChannels); - context->nChannels = nChannels; - - size_t i; - for (i = 0; i < nChannels && i < mVL_MAX_CHANNELS; ++i) { - uint32_t channelPointer; - LOAD_32LE(channelPointer, 0, &header.channelPointers[i]); - vf->seek(vf, channelPointer, SEEK_SET); - - struct mVideoLogChannelHeader chHeader; - vf->read(vf, &chHeader, sizeof(chHeader)); - - LOAD_32LE(context->channels[i].type, 0, &chHeader.type); - LOAD_32LE(context->channels[i].initialStateSize, 0, &chHeader.channelInitialStateSize); - - LOAD_32LE(channelPointer, 0, &chHeader.channelInitialStatePointer); - if (channelPointer) { - off_t position = vf->seek(vf, 0, SEEK_CUR); - vf->seek(vf, channelPointer, SEEK_SET); - - context->channels[i].initialState = anonymousMemoryMap(context->channels[i].initialStateSize); - vf->read(vf, context->channels[i].initialState, context->channels[i].initialStateSize); - vf->seek(vf, position, SEEK_SET); - } - - uint32_t channelSize; - LOAD_32LE(channelSize, 0, &chHeader.channelSize); - struct VFile* vfm = VFileMemChunk(0, channelSize); - - while (channelSize) { - uint8_t buffer[2048]; - ssize_t toRead = channelSize; - if (toRead > (ssize_t) sizeof(buffer)) { - toRead = sizeof(buffer); - } - toRead = vf->read(vf, buffer, toRead); - if (toRead > 0) { - channelSize -= toRead; - } else { - break; - } - vfm->write(vfm, buffer, toRead); - } - context->channels[i].channelData = vfm; - } - - for (; i < mVL_MAX_CHANNELS; ++i) { - context->channels[i].channelData = NULL; - } - return true; -} diff --git a/src/feature/video-logger.h b/src/feature/video-logger.h index 41fb8ff47..f194433a2 100644 --- a/src/feature/video-logger.h +++ b/src/feature/video-logger.h @@ -10,6 +10,8 @@ CXX_GUARD_START +#include + #define mVL_MAX_CHANNELS 32 enum mVideoLoggerDirtyType { @@ -36,6 +38,7 @@ struct VFile; struct mVideoLogger { bool (*writeData)(struct mVideoLogger* logger, const void* data, size_t length); bool (*readData)(struct mVideoLogger* logger, void* data, size_t length, bool block); + void* dataContext; bool block; void (*init)(struct mVideoLogger*); @@ -61,40 +64,6 @@ struct mVideoLogger { uint16_t* vram; uint16_t* oam; uint16_t* palette; - - struct VFile* vf; -}; - -struct mVideoLogChannel { - uint32_t type; - void* initialState; - size_t initialStateSize; - struct VFile* channelData; -}; - -struct mVideoLogContext { - void* initialState; - size_t initialStateSize; - uint32_t nChannels; - struct mVideoLogChannel channels[mVL_MAX_CHANNELS]; -}; - -struct mVideoLogHeader { - char magic[4]; - uint32_t reserved; - uint32_t platform; - uint32_t padding; - uint32_t initialStatePointer; - uint32_t initialStateSize; - uint32_t nChannels; - uint32_t channelPointers[mVL_MAX_CHANNELS]; -}; - -struct mVideoLogChannelHeader { - uint32_t type; - uint32_t channelInitialStatePointer; - uint32_t channelInitialStateSize; - uint32_t channelSize; }; void mVideoLoggerRendererCreate(struct mVideoLogger* logger, bool readonly); @@ -116,13 +85,24 @@ void mVideoLoggerRendererFinishFrame(struct mVideoLogger* logger); bool mVideoLoggerRendererRun(struct mVideoLogger* logger, bool block); +struct mVideoLogContext; +void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId); + struct mCore; -struct mVideoLogContext* mVideoLoggerCreate(struct mCore* core); -void mVideoLoggerDestroy(struct mCore* core, struct mVideoLogContext*); -void mVideoLoggerWrite(struct mCore* core, struct mVideoLogContext*, struct VFile*); +struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core); + +void mVideoLogContextSetOutput(struct mVideoLogContext*, struct VFile*); +void mVideoLogContextWriteHeader(struct mVideoLogContext*, struct mCore* core); + +bool mVideoLogContextLoad(struct mVideoLogContext*, struct VFile*); +void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext*); + +void mVideoLogContextRewind(struct mVideoLogContext*, struct mCore*); +void* mVideoLogContextInitialState(struct mVideoLogContext*, size_t* size); + +int mVideoLoggerAddChannel(struct mVideoLogContext*); struct mCore* mVideoLogCoreFind(struct VFile*); -bool mVideoLogContextLoad(struct VFile*, struct mVideoLogContext*); CXX_GUARD_END diff --git a/src/gb/core.c b/src/gb/core.c index cd77c6f33..756c9e19d 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -34,6 +34,7 @@ const static struct mCoreChannelInfo _GBAudioChannels[] = { { 3, "ch3", "Channel 3", "Noise" }, }; +struct mVideoLogContext; struct GBCore { struct mCore d; struct GBVideoSoftwareRenderer renderer; @@ -644,20 +645,10 @@ static void _GBCoreStartVideoLog(struct mCore* core, struct mVideoLogContext* co struct GB* gb = core->board; gbcore->logContext = context; - context->initialStateSize = core->stateSize(core); - context->initialState = anonymousMemoryMap(context->initialStateSize); - core->saveState(core, context->initialState); - - struct VFile* vf = VFileMemChunk(NULL, 0); - context->nChannels = 1; - context->channels[0].initialState = NULL; - context->channels[0].initialStateSize = 0; - context->channels[0].channelData = vf; - context->channels[0].type = 0; + int channelId = mVideoLoggerAddChannel(context); gbcore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); mVideoLoggerRendererCreate(gbcore->proxyRenderer.logger, false); - - gbcore->proxyRenderer.logger->vf = vf; + mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, context, channelId); gbcore->proxyRenderer.logger->block = false; GBVideoProxyRendererCreate(&gbcore->proxyRenderer, &gbcore->renderer.d); @@ -670,9 +661,6 @@ static void _GBCoreEndVideoLog(struct mCore* core) { GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); free(gbcore->proxyRenderer.logger); gbcore->proxyRenderer.logger = NULL; - - mappedMemoryFree(gbcore->logContext->initialState, gbcore->logContext->initialStateSize); - gbcore->logContext->channels[0].channelData->close(gbcore->logContext->channels[0].channelData); } struct mCore* GBCoreCreate(void) { @@ -762,8 +750,7 @@ static void _GBVLPStartFrameCallback(void *context) { if (!mVideoLoggerRendererRun(gbcore->proxyRenderer.logger, true)) { GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); - gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET); - core->loadState(core, gbcore->logContext->initialState); + mVideoLogContextRewind(gbcore->logContext, core); GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); // Make sure CPU loop never spins @@ -791,7 +778,7 @@ static bool _GBVLPInit(struct mCore* core) { static void _GBVLPDeinit(struct mCore* core) { struct GBCore* gbcore = (struct GBCore*) core; if (gbcore->logContext) { - mVideoLoggerDestroy(core, gbcore->logContext); + mVideoLogContextDestroy(core, gbcore->logContext); } _GBCoreDeinit(core); } @@ -805,10 +792,9 @@ static void _GBVLPReset(struct mCore* core) { struct GBVideoRenderer* renderer = &gbcore->renderer.d; GBVideoAssociateRenderer(&gb->video, renderer); } - gbcore->proxyRenderer.logger->vf->seek(gbcore->proxyRenderer.logger->vf, 0, SEEK_SET); LR35902Reset(core->cpu); - core->loadState(core, gbcore->logContext->initialState); + mVideoLogContextRewind(gbcore->logContext, core); GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); // Make sure CPU loop never spins @@ -819,13 +805,13 @@ static void _GBVLPReset(struct mCore* core) { static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) { struct GBCore* gbcore = (struct GBCore*) core; - gbcore->logContext = mVideoLoggerCreate(NULL); - if (!mVideoLogContextLoad(vf, gbcore->logContext)) { - mVideoLoggerDestroy(core, gbcore->logContext); + gbcore->logContext = mVideoLogContextCreate(NULL); + if (!mVideoLogContextLoad(gbcore->logContext, vf)) { + mVideoLogContextDestroy(core, gbcore->logContext); gbcore->logContext = NULL; return false; } - gbcore->proxyRenderer.logger->vf = gbcore->logContext->channels[0].channelData; + mVideoLoggerAttachChannel(gbcore->proxyRenderer.logger, gbcore->logContext, 0); return true; } diff --git a/src/gba/core.c b/src/gba/core.c index 522c8a41b..ea09e4d85 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -41,6 +41,7 @@ const static struct mCoreChannelInfo _GBAAudioChannels[] = { { 5, "chB", "FIFO Channel B", NULL }, }; +struct mVideoLogContext; struct GBACore { struct mCore d; struct GBAVideoSoftwareRenderer renderer; @@ -660,23 +661,14 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c struct GBA* gba = core->board; gbacore->logContext = context; - context->initialStateSize = core->stateSize(core); - context->initialState = anonymousMemoryMap(context->initialStateSize); - core->saveState(core, context->initialState); - struct GBASerializedState* state = context->initialState; + struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL); state->id = 0; state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM; - struct VFile* vf = VFileMemChunk(NULL, 0); - context->nChannels = 1; - context->channels[0].initialState = NULL; - context->channels[0].initialStateSize = 0; - context->channels[0].channelData = vf; - context->channels[0].type = 0; + int channelId = mVideoLoggerAddChannel(context); gbacore->proxyRenderer.logger = malloc(sizeof(struct mVideoLogger)); mVideoLoggerRendererCreate(gbacore->proxyRenderer.logger, false); - - gbacore->proxyRenderer.logger->vf = vf; + mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, context, channelId); gbacore->proxyRenderer.logger->block = false; GBAVideoProxyRendererCreate(&gbacore->proxyRenderer, &gbacore->renderer.d); @@ -689,9 +681,6 @@ static void _GBACoreEndVideoLog(struct mCore* core) { GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer); free(gbacore->proxyRenderer.logger); gbacore->proxyRenderer.logger = NULL; - - mappedMemoryFree(gbacore->logContext->initialState, gbacore->logContext->initialStateSize); - gbacore->logContext->channels[0].channelData->close(gbacore->logContext->channels[0].channelData); } struct mCore* GBACoreCreate(void) { @@ -781,8 +770,7 @@ static void _GBAVLPStartFrameCallback(void *context) { if (!mVideoLoggerRendererRun(gbacore->proxyRenderer.logger, true)) { GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer); - gbacore->proxyRenderer.logger->vf->seek(gbacore->proxyRenderer.logger->vf, 0, SEEK_SET); - core->loadState(core, gbacore->logContext->initialState); + mVideoLogContextRewind(gbacore->logContext, core); GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); // Make sure CPU loop never spins @@ -810,7 +798,7 @@ static bool _GBAVLPInit(struct mCore* core) { static void _GBAVLPDeinit(struct mCore* core) { struct GBACore* gbacore = (struct GBACore*) core; if (gbacore->logContext) { - mVideoLoggerDestroy(core, gbacore->logContext); + mVideoLogContextDestroy(core, gbacore->logContext); } _GBACoreDeinit(core); } @@ -824,10 +812,9 @@ static void _GBAVLPReset(struct mCore* core) { struct GBAVideoRenderer* renderer = &gbacore->renderer.d; GBAVideoAssociateRenderer(&gba->video, renderer); } - gbacore->proxyRenderer.logger->vf->seek(gbacore->proxyRenderer.logger->vf, 0, SEEK_SET); ARMReset(core->cpu); - core->loadState(core, gbacore->logContext->initialState); + mVideoLogContextRewind(gbacore->logContext, core); GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); // Make sure CPU loop never spins @@ -838,13 +825,13 @@ static void _GBAVLPReset(struct mCore* core) { static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { struct GBACore* gbacore = (struct GBACore*) core; - gbacore->logContext = mVideoLoggerCreate(NULL); - if (!mVideoLogContextLoad(vf, gbacore->logContext)) { - mVideoLoggerDestroy(core, gbacore->logContext); + gbacore->logContext = mVideoLogContextCreate(NULL); + if (!mVideoLogContextLoad(gbacore->logContext, vf)) { + mVideoLogContextDestroy(core, gbacore->logContext); gbacore->logContext = NULL; return false; } - gbacore->proxyRenderer.logger->vf = gbacore->logContext->channels[0].channelData; + mVideoLoggerAttachChannel(gbacore->proxyRenderer.logger, gbacore->logContext, 0); return true; } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 3667400a7..3c86f96a5 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -73,6 +73,7 @@ GameController::GameController(QObject* parent) , m_preload(false) , m_override(nullptr) , m_vl(nullptr) + , m_vlVf(nullptr) { #ifdef M_CORE_GBA m_lux.p = this; @@ -1208,8 +1209,10 @@ void GameController::startVideoLog(const QString& path) { } Interrupter interrupter(this); - m_vlPath = path; - m_vl = mVideoLoggerCreate(m_threadContext.core); + m_vl = mVideoLogContextCreate(m_threadContext.core); + m_vlVf = VFileDevice::open(path, O_WRONLY | O_CREAT | O_TRUNC); + mVideoLogContextSetOutput(m_vl, m_vlVf); + mVideoLogContextWriteHeader(m_vl, m_threadContext.core); } void GameController::endVideoLog() { @@ -1218,12 +1221,11 @@ void GameController::endVideoLog() { } Interrupter interrupter(this); - if (isLoaded()) { - VFile* vf = VFileDevice::open(m_vlPath, O_WRONLY | O_CREAT | O_TRUNC); - mVideoLoggerWrite(m_threadContext.core, m_vl, vf); - vf->close(vf); + mVideoLogContextDestroy(m_threadContext.core, m_vl); + if (m_vlVf) { + m_vlVf->close(m_vlVf); + m_vlVf = nullptr; } - mVideoLoggerDestroy(m_threadContext.core, m_vl); m_vl = nullptr; } diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 66b4b4012..86f02579d 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -247,7 +247,7 @@ private: mAVStream* m_stream; mVideoLogContext* m_vl; - QString m_vlPath; + VFile* m_vlVf; struct GameControllerLux : GBALuminanceSource { GameController* p; diff --git a/src/util/circle-buffer.c b/src/util/circle-buffer.c index deb426473..c60326e3c 100644 --- a/src/util/circle-buffer.c +++ b/src/util/circle-buffer.c @@ -125,6 +125,34 @@ int CircleBufferWrite16(struct CircleBuffer* buffer, int16_t value) { return 2; } +size_t CircleBufferWrite(struct CircleBuffer* buffer, const void* input, size_t length) { + int8_t* data = buffer->writePtr; + if (buffer->size + sizeof(int16_t) > buffer->capacity) { + return 0; + } + size_t remaining = buffer->capacity - ((int8_t*) data - (int8_t*) buffer->data); + if (length <= remaining) { + memcpy(data, input, length); + if (length == remaining) { + buffer->writePtr = buffer->data; + } else { + buffer->writePtr = (int8_t*) data + length; + } + } else { + memcpy(data, input, remaining); + memcpy(buffer->data, (const int8_t*) input + remaining, length - remaining); + buffer->writePtr = (int8_t*) buffer->data + length - remaining; + } + + buffer->size += length; +#ifndef NDEBUG + if (!_checkIntegrity(buffer)) { + abort(); + } +#endif + return length; +} + int CircleBufferRead8(struct CircleBuffer* buffer, int8_t* value) { int8_t* data = buffer->readPtr; if (buffer->size < sizeof(int8_t)) { From c4aa669cf19b64703d525753c0c736beee56d0e3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 23 Apr 2017 23:24:48 -0700 Subject: [PATCH 093/159] Core: Fix flushing a read video log --- src/feature/video-logger.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 57a028c3c..6697cf9bb 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -80,6 +80,7 @@ struct mVideoLogContext { uint32_t nChannels; struct mVideoLogChannel channels[mVL_MAX_CHANNELS]; + bool write; uint32_t activeChannel; struct VFile* backing; }; @@ -320,6 +321,8 @@ struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core) { struct mVideoLogContext* context = malloc(sizeof(*context)); memset(context, 0, sizeof(*context)); + context->write = !!core; + if (core) { context->initialStateSize = core->stateSize(core); context->initialState = anonymousMemoryMap(context->initialStateSize); @@ -449,11 +452,13 @@ static void _flushBuffer(struct mVideoLogContext* context) { } void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* context) { - _flushBuffer(context); + if (context->write) { + _flushBuffer(context); - struct mVLBlockHeader header = { 0 }; - STORE_32LE(mVL_BLOCK_FOOTER, 0, &header.blockType); - context->backing->write(context->backing, &header, sizeof(header)); + struct mVLBlockHeader header = { 0 }; + STORE_32LE(mVL_BLOCK_FOOTER, 0, &header.blockType); + context->backing->write(context->backing, &header, sizeof(header)); + } if (core) { core->endVideoLog(core); From f82ef55517cf3edede6b5ccd2557f8a1099dbc09 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 01:43:31 -0700 Subject: [PATCH 094/159] Core: Compressed video logs --- src/feature/video-logger.c | 181 ++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 4 deletions(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 6697cf9bb..2770365f3 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -17,7 +17,11 @@ #include #endif -#define BUFFER_BASE_SIZE 0x8000 +#ifdef USE_ZLIB +#include +#endif + +#define BUFFER_BASE_SIZE 0x20000 const char mVL_MAGIC[] = "mVL\0"; @@ -53,6 +57,10 @@ struct mVLBlockHeader { uint32_t flags; }; +enum mVLBlockFlag { + mVL_FLAG_BLOCK_COMPRESSED = 1 +}; + struct mVideoLogHeader { char magic[4]; uint32_t flags; @@ -70,6 +78,10 @@ struct mVideoLogChannel { off_t currentPointer; size_t bufferRemaining; +#ifdef USE_ZLIB + bool inflating; + z_stream inflateStream; +#endif struct CircleBuffer buffer; }; @@ -429,11 +441,82 @@ bool mVideoLogContextLoad(struct mVideoLogContext* context, struct VFile* vf) { context->channels[i].bufferRemaining = 0; context->channels[i].currentPointer = pointer; context->channels[i].p = context; +#ifdef USE_ZLIB + context->channels[i].inflating = false; +#endif } return true; } +#ifdef USE_ZLIB +static void _flushBufferCompressed(struct mVideoLogContext* context) { + struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer; + if (!CircleBufferSize(buffer)) { + return; + } + uint8_t writeBuffer[0x400]; + struct mVLBlockHeader header = { 0 }; + STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType); + + STORE_32LE(context->activeChannel, 0, &header.channelId); + STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &header.flags); + + uint8_t compressBuffer[0x800]; + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.avail_in = 0; + zstr.avail_out = sizeof(compressBuffer); + zstr.next_out = (Bytef*) compressBuffer; + if (deflateInit(&zstr, 9) != Z_OK) { + return; + } + + struct VFile* vfm = VFileMemChunk(NULL, 0); + + while (CircleBufferSize(buffer)) { + size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer)); + zstr.avail_in = read; + zstr.next_in = (Bytef*) writeBuffer; + while (zstr.avail_in) { + if (deflate(&zstr, Z_NO_FLUSH) == Z_STREAM_ERROR) { + break; + } + vfm->write(vfm, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); + zstr.avail_out = sizeof(compressBuffer); + zstr.next_out = (Bytef*) compressBuffer; + } + } + + do { + zstr.avail_out = sizeof(compressBuffer); + zstr.next_out = (Bytef*) compressBuffer; + zstr.avail_in = 0; + int ret = deflate(&zstr, Z_FINISH); + if (ret == Z_STREAM_ERROR) { + break; + } + vfm->write(vfm, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); + } while (sizeof(compressBuffer) - zstr.avail_out); + + size_t size = vfm->size(vfm); + STORE_32LE(size, 0, &header.length); + context->backing->write(context->backing, &header, sizeof(header)); + void* vfmm = vfm->map(vfm, size, MAP_READ); + context->backing->write(context->backing, vfmm, size); + vfm->unmap(vfm, vfmm, size); + vfm->close(vfm); +} +#endif + static void _flushBuffer(struct mVideoLogContext* context) { +#ifdef USE_ZLIB + // TODO: Make optional + _flushBufferCompressed(context); + return; +#endif + struct CircleBuffer* buffer = &context->channels[context->activeChannel].buffer; if (!CircleBufferSize(buffer)) { return; @@ -442,9 +525,10 @@ static void _flushBuffer(struct mVideoLogContext* context) { STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType); STORE_32LE(CircleBufferSize(buffer), 0, &header.length); STORE_32LE(context->activeChannel, 0, &header.channelId); + context->backing->write(context->backing, &header, sizeof(header)); - uint8_t writeBuffer[0x1000]; + uint8_t writeBuffer[0x800]; while (CircleBufferSize(buffer)) { size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer)); context->backing->write(context->backing, writeBuffer, read); @@ -503,8 +587,83 @@ int mVideoLoggerAddChannel(struct mVideoLogContext* context) { return chid; } +#ifdef USE_ZLIB +static size_t _readBufferCompressed(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) { + uint8_t fbuffer[0x400]; + uint8_t zbuffer[0x800]; + size_t read = 0; + + channel->inflateStream.avail_in = 0; + while (length) { + size_t thisWrite = sizeof(zbuffer); + if (thisWrite > length) { + thisWrite = length; + } + + size_t thisRead = 0; + if (channel->inflating && channel->inflateStream.avail_in) { + channel->inflateStream.next_out = zbuffer; + channel->inflateStream.avail_out = thisWrite; + thisRead = channel->inflateStream.avail_in; + } else if (channel->bufferRemaining) { + thisRead = sizeof(fbuffer); + if (thisRead > channel->bufferRemaining) { + thisRead = channel->bufferRemaining; + } + + thisRead = vf->read(vf, fbuffer, thisRead); + if (thisRead <= 0) { + break; + } + + channel->inflateStream.next_in = fbuffer; + channel->inflateStream.avail_in = thisRead; + channel->inflateStream.next_out = zbuffer; + channel->inflateStream.avail_out = thisWrite; + + if (!channel->inflating) { + if (inflateInit(&channel->inflateStream) != Z_OK) { + break; + } + channel->inflating = true; + } + } else { + channel->inflateStream.next_in = Z_NULL; + channel->inflateStream.avail_in = 0; + channel->inflateStream.next_out = zbuffer; + channel->inflateStream.avail_out = thisWrite; + } + + int ret = inflate(&channel->inflateStream, Z_NO_FLUSH); + + if (channel->inflateStream.next_in != Z_NULL) { + thisRead -= channel->inflateStream.avail_in; + channel->currentPointer += thisRead; + channel->bufferRemaining -= thisRead; + } + + if (ret != Z_OK) { + inflateEnd(&channel->inflateStream); + channel->inflating = false; + if (ret != Z_STREAM_END) { + break; + } + } + + thisWrite = CircleBufferWrite(&channel->buffer, zbuffer, thisWrite - channel->inflateStream.avail_out); + length -= thisWrite; + read += thisWrite; + + if (!channel->inflating) { + break; + } + } + return read; +} +#endif + static void _readBuffer(struct VFile* vf, struct mVideoLogChannel* channel, size_t length) { - uint8_t buffer[0x1000]; + uint8_t buffer[0x800]; while (length) { size_t thisRead = sizeof(buffer); if (thisRead > length) { @@ -531,9 +690,16 @@ static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size while (length) { size_t bufferRemaining = channel->bufferRemaining; if (bufferRemaining) { +#ifdef USE_ZLIB + if (channel->inflating) { + length -= _readBufferCompressed(context->backing, channel, length); + continue; + } +#endif if (bufferRemaining > length) { bufferRemaining = length; } + _readBuffer(context->backing, channel, bufferRemaining); length -= bufferRemaining; continue; @@ -553,8 +719,15 @@ static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size if (!header.length) { continue; } - channel->bufferRemaining = header.length; + + if (header.flags & mVL_FLAG_BLOCK_COMPRESSED) { +#ifdef USE_ZLIB + length -= _readBufferCompressed(context->backing, channel, length); +#else + return false; +#endif + } } return true; } From 31fa64efacf2d60f6500fa86861c28e3ae25af96 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 11:46:04 -0700 Subject: [PATCH 095/159] Core: Compressed video log states --- src/feature/video-logger.c | 170 +++++++++++++++++++++++++++++++++++-- 1 file changed, 163 insertions(+), 7 deletions(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 2770365f3..f7b784fc3 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -322,6 +322,133 @@ static bool _readData(struct mVideoLogger* logger, void* data, size_t length, bo return mVideoLoggerReadChannel(channel, data, length) == (ssize_t) length; } +#ifdef USE_ZLIB +static void _copyVf(struct VFile* dest, struct VFile* src) { + size_t size = src->size(src); + void* mem = src->map(src, size, MAP_READ); + dest->write(dest, mem, size); + src->unmap(src, mem, size); +} + +static void _compress(struct VFile* dest, const void* in, size_t size) { + uint8_t writeBuffer[0x800]; + uint8_t compressBuffer[0x400]; + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.avail_in = 0; + zstr.avail_out = sizeof(compressBuffer); + zstr.next_out = (Bytef*) compressBuffer; + if (deflateInit(&zstr, 9) != Z_OK) { + return; + } + + struct VFile* src = VFileFromConstMemory(in, size); + + while (true) { + size_t read = src->read(src, writeBuffer, sizeof(writeBuffer)); + if (!read) { + break; + } + zstr.avail_in = read; + zstr.next_in = (Bytef*) writeBuffer; + while (zstr.avail_in) { + if (deflate(&zstr, Z_NO_FLUSH) == Z_STREAM_ERROR) { + break; + } + dest->write(dest, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); + zstr.avail_out = sizeof(compressBuffer); + zstr.next_out = (Bytef*) compressBuffer; + } + } + + do { + zstr.avail_out = sizeof(compressBuffer); + zstr.next_out = (Bytef*) compressBuffer; + zstr.avail_in = 0; + int ret = deflate(&zstr, Z_FINISH); + if (ret == Z_STREAM_ERROR) { + break; + } + dest->write(dest, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); + } while (sizeof(compressBuffer) - zstr.avail_out); + src->close(src); +} + +static bool _decompress(struct VFile* dest, struct VFile* src, size_t compressedLength) { + uint8_t fbuffer[0x400]; + uint8_t zbuffer[0x800]; + z_stream zstr; + zstr.zalloc = Z_NULL; + zstr.zfree = Z_NULL; + zstr.opaque = Z_NULL; + zstr.avail_in = 0; + zstr.avail_out = sizeof(zbuffer); + zstr.next_out = (Bytef*) zbuffer; + bool started = false; + + while (true) { + size_t thisWrite = sizeof(zbuffer); + size_t thisRead = 0; + if (zstr.avail_in) { + zstr.next_out = zbuffer; + zstr.avail_out = thisWrite; + thisRead = zstr.avail_in; + } else if (compressedLength) { + thisRead = sizeof(fbuffer); + if (thisRead > compressedLength) { + thisRead = compressedLength; + } + + thisRead = src->read(src, fbuffer, thisRead); + if (thisRead <= 0) { + break; + } + + zstr.next_in = fbuffer; + zstr.avail_in = thisRead; + zstr.next_out = zbuffer; + zstr.avail_out = thisWrite; + + if (!started) { + if (inflateInit(&zstr) != Z_OK) { + break; + } + started = true; + } + } else { + zstr.next_in = Z_NULL; + zstr.avail_in = 0; + zstr.next_out = zbuffer; + zstr.avail_out = thisWrite; + } + + int ret = inflate(&zstr, Z_NO_FLUSH); + + if (zstr.next_in != Z_NULL) { + thisRead -= zstr.avail_in; + compressedLength -= thisRead; + } + + if (ret != Z_OK) { + inflateEnd(&zstr); + started = false; + if (ret != Z_STREAM_END) { + break; + } + } + + thisWrite = dest->write(dest, zbuffer, thisWrite - zstr.avail_out); + + if (!started) { + break; + } + } + return !compressedLength; +} +#endif + void mVideoLoggerAttachChannel(struct mVideoLogger* logger, struct mVideoLogContext* context, size_t channelId) { if (channelId >= mVL_MAX_CHANNELS) { return; @@ -368,9 +495,20 @@ void mVideoLogContextWriteHeader(struct mVideoLogContext* context, struct mCore* if (context->initialState) { struct mVLBlockHeader chheader = { 0 }; STORE_32LE(mVL_BLOCK_INITIAL_STATE, 0, &chheader.blockType); +#ifdef USE_ZLIB + STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &chheader.flags); + + struct VFile* vfm = VFileMemChunk(NULL, 0); + _compress(vfm, context->initialState, context->initialStateSize); + STORE_32LE(vfm->size(vfm), 0, &chheader.length); + context->backing->write(context->backing, &chheader, sizeof(chheader)); + _copyVf(context->backing, vfm); + vfm->close(vfm); +#else STORE_32LE(context->initialStateSize, 0, &chheader.length); context->backing->write(context->backing, &chheader, sizeof(chheader)); context->backing->write(context->backing, context->initialState, context->initialStateSize); +#endif } size_t i; @@ -419,9 +557,27 @@ bool _readHeader(struct mVideoLogContext* context) { if (header.blockType != mVL_BLOCK_INITIAL_STATE) { return false; } - context->initialStateSize = header.length; - context->initialState = anonymousMemoryMap(header.length); - context->backing->read(context->backing, context->initialState, context->initialStateSize); + if (header.flags & mVL_FLAG_BLOCK_COMPRESSED) { +#ifdef USE_ZLIB + struct VFile* vfm = VFileMemChunk(NULL, 0); + if (!_decompress(vfm, context->backing, header.length)) { + vfm->close(vfm); + return false; + } + context->initialStateSize = vfm->size(vfm); + context->initialState = anonymousMemoryMap(context->initialStateSize); + void* mem = vfm->map(vfm, context->initialStateSize, MAP_READ); + memcpy(context->initialState, mem, context->initialStateSize); + vfm->unmap(vfm, mem, context->initialStateSize); + vfm->close(vfm); +#else + return false; +#endif + } else { + context->initialStateSize = header.length; + context->initialState = anonymousMemoryMap(header.length); + context->backing->read(context->backing, context->initialState, context->initialStateSize); + } } return true; } @@ -461,6 +617,7 @@ static void _flushBufferCompressed(struct mVideoLogContext* context) { STORE_32LE(context->activeChannel, 0, &header.channelId); STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &header.flags); + // TODO: Share with _compress uint8_t compressBuffer[0x800]; z_stream zstr; zstr.zalloc = Z_NULL; @@ -503,9 +660,7 @@ static void _flushBufferCompressed(struct mVideoLogContext* context) { size_t size = vfm->size(vfm); STORE_32LE(size, 0, &header.length); context->backing->write(context->backing, &header, sizeof(header)); - void* vfmm = vfm->map(vfm, size, MAP_READ); - context->backing->write(context->backing, vfmm, size); - vfm->unmap(vfm, vfmm, size); + _copyVf(context->backing, vfm); vfm->close(vfm); } #endif @@ -555,7 +710,7 @@ void mVideoLogContextDestroy(struct mCore* core, struct mVideoLogContext* contex void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core) { _readHeader(context); - if (core) { + if (core && core->stateSize(core) == context->initialStateSize) { core->loadState(core, context->initialState); } @@ -593,6 +748,7 @@ static size_t _readBufferCompressed(struct VFile* vf, struct mVideoLogChannel* c uint8_t zbuffer[0x800]; size_t read = 0; + // TODO: Share with _decompress channel->inflateStream.avail_in = 0; while (length) { size_t thisWrite = sizeof(zbuffer); From 6b547899a8ba7c34556708f9736af1d8abbbe702 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 12:10:31 -0700 Subject: [PATCH 096/159] VFS: Add VFileFIFO for operating on circle buffers --- CHANGES | 1 + CMakeLists.txt | 2 +- include/mgba-util/vfs.h | 3 ++ src/util/vfs/vfs-fifo.c | 102 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/util/vfs/vfs-fifo.c diff --git a/CHANGES b/CHANGES index c7f1702fe..2598f6fcb 100644 --- a/CHANGES +++ b/CHANGES @@ -106,6 +106,7 @@ Misc: - Core: Ability to enumerate and modify video and audio channels - Debugger: Make attaching a backend idempotent - VFS: Optimize expanding in-memory files + - VFS: Add VFileFIFO for operating on circle buffers 0.5.2: (2016-12-31) Bugfixes: diff --git a/CMakeLists.txt b/CMakeLists.txt index e5490a04b..c75114dc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ file(GLOB GB_RENDERER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/renderers/*.c) file(GLOB GB_EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/extra/*.c) file(GLOB THIRD_PARTY_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/third-party/inih/*.c) file(GLOB EXTRA_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/*.c) -set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c) +set(CORE_VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-mem.c ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-fifo.c) set(VFS_SRC) source_group("ARM core" FILES ${ARM_SRC}) source_group("LR35902 core" FILES ${LR35902_SRC}) diff --git a/include/mgba-util/vfs.h b/include/mgba-util/vfs.h index 02c6d94e1..562fa52b5 100644 --- a/include/mgba-util/vfs.h +++ b/include/mgba-util/vfs.h @@ -73,6 +73,9 @@ struct VFile* VFileFromMemory(void* mem, size_t size); struct VFile* VFileFromConstMemory(const void* mem, size_t size); struct VFile* VFileMemChunk(const void* mem, size_t size); +struct CircleBuffer; +struct VFile* VFileFIFO(struct CircleBuffer* backing); + struct VDir* VDirOpen(const char* path); struct VDir* VDirOpenArchive(const char* path); diff --git a/src/util/vfs/vfs-fifo.c b/src/util/vfs/vfs-fifo.c new file mode 100644 index 000000000..efff7bdab --- /dev/null +++ b/src/util/vfs/vfs-fifo.c @@ -0,0 +1,102 @@ +/* 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 +#include + +struct VFileFIFO { + struct VFile d; + struct CircleBuffer* backing; +}; + +static bool _vffClose(struct VFile* vf); +static off_t _vffSeek(struct VFile* vf, off_t offset, int whence); +static ssize_t _vffRead(struct VFile* vf, void* buffer, size_t size); +static ssize_t _vffWrite(struct VFile* vf, const void* buffer, size_t size); +static void* _vffMap(struct VFile* vf, size_t size, int flags); +static void _vffUnmap(struct VFile* vf, void* memory, size_t size); +static void _vffTruncate(struct VFile* vf, size_t size); +static ssize_t _vffSize(struct VFile* vf); +static bool _vffSync(struct VFile* vf, const void* buffer, size_t size); + +struct VFile* VFileFIFO(struct CircleBuffer* backing) { + if (!backing) { + return NULL; + } + + struct VFileFIFO* vff = malloc(sizeof(*vff)); + if (!vff) { + return NULL; + } + + vff->backing = backing; + vff->d.close = _vffClose; + vff->d.seek = _vffSeek; + vff->d.read = _vffRead; + vff->d.readline = VFileReadline; + vff->d.write = _vffWrite; + vff->d.map = _vffMap; + vff->d.unmap = _vffUnmap; + vff->d.truncate = _vffTruncate; + vff->d.size = _vffSize; + vff->d.sync = _vffSync; + + return &vff->d; +} + + +static bool _vffClose(struct VFile* vf) { + free(vf); + return true; +} + +static off_t _vffSeek(struct VFile* vf, off_t offset, int whence) { + UNUSED(vf); + UNUSED(offset); + UNUSED(whence); + return 0; +} + +static ssize_t _vffRead(struct VFile* vf, void* buffer, size_t size) { + struct VFileFIFO* vff = (struct VFileFIFO*) vf; + return CircleBufferRead(vff->backing, buffer, size); +} + +static ssize_t _vffWrite(struct VFile* vf, const void* buffer, size_t size) { + struct VFileFIFO* vff = (struct VFileFIFO*) vf; + return CircleBufferWrite(vff->backing, buffer, size); +} + +static void* _vffMap(struct VFile* vf, size_t size, int flags) { + UNUSED(vf); + UNUSED(size); + UNUSED(flags); + return NULL; +} + +static void _vffUnmap(struct VFile* vf, void* memory, size_t size) { + UNUSED(vf); + UNUSED(memory); + UNUSED(size); +} + +static void _vffTruncate(struct VFile* vf, size_t size) { + struct VFileFIFO* vff = (struct VFileFIFO*) vf; + if (!size) { + CircleBufferClear(vff->backing); + } +} + +static ssize_t _vffSize(struct VFile* vf) { + struct VFileFIFO* vff = (struct VFileFIFO*) vf; + return CircleBufferSize(vff->backing); +} + +static bool _vffSync(struct VFile* vf, const void* buffer, size_t size) { + UNUSED(vf); + UNUSED(buffer); + UNUSED(size); + return true; +} From f67052eebcba6caf00a8a9d33e20a226e3c5a0d0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 12:10:46 -0700 Subject: [PATCH 097/159] Core: Clean up mVL compression --- src/feature/video-logger.c | 63 ++++++++------------------------------ 1 file changed, 13 insertions(+), 50 deletions(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index f7b784fc3..0a9299c6f 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -330,7 +330,7 @@ static void _copyVf(struct VFile* dest, struct VFile* src) { src->unmap(src, mem, size); } -static void _compress(struct VFile* dest, const void* in, size_t size) { +static void _compress(struct VFile* dest, struct VFile* src) { uint8_t writeBuffer[0x800]; uint8_t compressBuffer[0x400]; z_stream zstr; @@ -344,8 +344,6 @@ static void _compress(struct VFile* dest, const void* in, size_t size) { return; } - struct VFile* src = VFileFromConstMemory(in, size); - while (true) { size_t read = src->read(src, writeBuffer, sizeof(writeBuffer)); if (!read) { @@ -373,7 +371,6 @@ static void _compress(struct VFile* dest, const void* in, size_t size) { } dest->write(dest, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); } while (sizeof(compressBuffer) - zstr.avail_out); - src->close(src); } static bool _decompress(struct VFile* dest, struct VFile* src, size_t compressedLength) { @@ -499,7 +496,9 @@ void mVideoLogContextWriteHeader(struct mVideoLogContext* context, struct mCore* STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &chheader.flags); struct VFile* vfm = VFileMemChunk(NULL, 0); - _compress(vfm, context->initialState, context->initialStateSize); + struct VFile* src = VFileFromConstMemory(context->initialState, context->initialStateSize); + _compress(vfm, src); + src->close(src); STORE_32LE(vfm->size(vfm), 0, &chheader.length); context->backing->write(context->backing, &chheader, sizeof(chheader)); _copyVf(context->backing, vfm); @@ -610,55 +609,19 @@ static void _flushBufferCompressed(struct mVideoLogContext* context) { if (!CircleBufferSize(buffer)) { return; } - uint8_t writeBuffer[0x400]; - struct mVLBlockHeader header = { 0 }; - STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType); - - STORE_32LE(context->activeChannel, 0, &header.channelId); - STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &header.flags); - - // TODO: Share with _compress - uint8_t compressBuffer[0x800]; - z_stream zstr; - zstr.zalloc = Z_NULL; - zstr.zfree = Z_NULL; - zstr.opaque = Z_NULL; - zstr.avail_in = 0; - zstr.avail_out = sizeof(compressBuffer); - zstr.next_out = (Bytef*) compressBuffer; - if (deflateInit(&zstr, 9) != Z_OK) { - return; - } - struct VFile* vfm = VFileMemChunk(NULL, 0); - - while (CircleBufferSize(buffer)) { - size_t read = CircleBufferRead(buffer, writeBuffer, sizeof(writeBuffer)); - zstr.avail_in = read; - zstr.next_in = (Bytef*) writeBuffer; - while (zstr.avail_in) { - if (deflate(&zstr, Z_NO_FLUSH) == Z_STREAM_ERROR) { - break; - } - vfm->write(vfm, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); - zstr.avail_out = sizeof(compressBuffer); - zstr.next_out = (Bytef*) compressBuffer; - } - } - - do { - zstr.avail_out = sizeof(compressBuffer); - zstr.next_out = (Bytef*) compressBuffer; - zstr.avail_in = 0; - int ret = deflate(&zstr, Z_FINISH); - if (ret == Z_STREAM_ERROR) { - break; - } - vfm->write(vfm, compressBuffer, sizeof(compressBuffer) - zstr.avail_out); - } while (sizeof(compressBuffer) - zstr.avail_out); + struct VFile* src = VFileFIFO(buffer); + _compress(vfm, src); + src->close(src); size_t size = vfm->size(vfm); + + struct mVLBlockHeader header = { 0 }; + STORE_32LE(mVL_BLOCK_DATA, 0, &header.blockType); + STORE_32LE(context->activeChannel, 0, &header.channelId); + STORE_32LE(mVL_FLAG_BLOCK_COMPRESSED, 0, &header.flags); STORE_32LE(size, 0, &header.length); + context->backing->write(context->backing, &header, sizeof(header)); _copyVf(context->backing, vfm); vfm->close(vfm); From 63ed7421ce05174608dd1799f8e7b0a5f01d5c3d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 12:42:46 -0700 Subject: [PATCH 098/159] Core: Fix rewinding video logs when decompressing --- src/feature/video-logger.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 0a9299c6f..ee4dcdedf 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -684,6 +684,12 @@ void mVideoLogContextRewind(struct mVideoLogContext* context, struct mCore* core CircleBufferClear(&context->channels[i].buffer); context->channels[i].bufferRemaining = 0; context->channels[i].currentPointer = pointer; +#ifdef USE_ZLIB + if (context->channels[i].inflating) { + inflateEnd(&context->channels[i].inflateStream); + context->channels[i].inflating = false; + } +#endif } } From 25beafcc11af80cdd3192ec3a363ba5e45f4d9b2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 12:43:12 -0700 Subject: [PATCH 099/159] GB, GBA Core: Only deserialize needed parts of savestates for mVL --- src/gb/core.c | 33 +++++++++++++++++++++++---------- src/gba/core.c | 28 ++++++++++++++++++---------- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/gb/core.c b/src/gb/core.c index 756c9e19d..6707b8416 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -752,11 +753,6 @@ static void _GBVLPStartFrameCallback(void *context) { GBVideoProxyRendererUnshim(&gb->video, &gbcore->proxyRenderer); mVideoLogContextRewind(gbcore->logContext, core); GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); - - // Make sure CPU loop never spins - GBHalt(gb->cpu); - gb->memory.ie = 0; - gb->memory.ime = false; } } @@ -796,11 +792,6 @@ static void _GBVLPReset(struct mCore* core) { LR35902Reset(core->cpu); mVideoLogContextRewind(gbcore->logContext, core); GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); - - // Make sure CPU loop never spins - GBHalt(gb->cpu); - gb->memory.ie = 0; - gb->memory.ime = false; } static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) { @@ -815,6 +806,27 @@ static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) { return true; } +static bool _GBVLPLoadState(struct mCore* core, const void* buffer) { + struct GB* gb = (struct GB*) core->board; + const struct GBSerializedState* state = buffer; + + gb->timing.root = NULL; + gb->model = state->model; + + gb->cpu->pc = GB_BASE_HRAM; + gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); + + GBVideoDeserialize(&gb->video, state); + GBIODeserialize(gb, state); + + // Make sure CPU loop never spins + GBHalt(gb->cpu); + gb->memory.ie = 0; + gb->memory.ime = false; + + return true; +} + static bool _returnTrue(struct VFile* vf) { UNUSED(vf); return true; @@ -826,6 +838,7 @@ struct mCore* GBVideoLogPlayerCreate(void) { core->deinit = _GBVLPDeinit; core->reset = _GBVLPReset; core->loadROM = _GBVLPLoadROM; + core->loadState = _GBVLPLoadState; core->isROM = _returnTrue; return core; } diff --git a/src/gba/core.c b/src/gba/core.c index ea09e4d85..185598e53 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -772,11 +772,6 @@ static void _GBAVLPStartFrameCallback(void *context) { GBAVideoProxyRendererUnshim(&gba->video, &gbacore->proxyRenderer); mVideoLogContextRewind(gbacore->logContext, core); GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); - - // Make sure CPU loop never spins - GBAHalt(gba); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); } } @@ -816,11 +811,6 @@ static void _GBAVLPReset(struct mCore* core) { ARMReset(core->cpu); mVideoLogContextRewind(gbacore->logContext, core); GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); - - // Make sure CPU loop never spins - GBAHalt(gba); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); - gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); } static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { @@ -835,6 +825,23 @@ static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { return true; } +static bool _GBAVLPLoadState(struct mCore* core, const void* state) { + struct GBA* gba = (struct GBA*) core->board; + + gba->timing.root = NULL; + gba->cpu->gprs[ARM_PC] = BASE_WORKING_RAM; + gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); + + // Make sure CPU loop never spins + GBAHalt(gba); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); + GBAVideoDeserialize(&gba->video, state); + GBAIODeserialize(gba, state); + + return true; +} + static bool _returnTrue(struct VFile* vf) { UNUSED(vf); return true; @@ -846,6 +853,7 @@ struct mCore* GBAVideoLogPlayerCreate(void) { core->deinit = _GBAVLPDeinit; core->reset = _GBAVLPReset; core->loadROM = _GBAVLPLoadROM; + core->loadState = _GBAVLPLoadState; core->isROM = _returnTrue; return core; } From aec8b8275622c3b54ee2bebfd903d5fc051255bc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 12:44:51 -0700 Subject: [PATCH 100/159] GB, GBA Core: Reset audio when loading mVL states so audio sync works --- src/gb/core.c | 1 + src/gba/core.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/gb/core.c b/src/gb/core.c index 6707b8416..97b48e7da 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -818,6 +818,7 @@ static bool _GBVLPLoadState(struct mCore* core, const void* buffer) { GBVideoDeserialize(&gb->video, state); GBIODeserialize(gb, state); + GBAudioReset(&gb->audio); // Make sure CPU loop never spins GBHalt(gb->cpu); diff --git a/src/gba/core.c b/src/gba/core.c index 185598e53..4558ccd77 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -838,6 +838,7 @@ static bool _GBAVLPLoadState(struct mCore* core, const void* state) { gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); GBAVideoDeserialize(&gba->video, state); GBAIODeserialize(gba, state); + GBAAudioReset(&gba->audio); return true; } From 5cda42077c114080b6b91dd7ece27fb12b08d7c8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 12:56:24 -0700 Subject: [PATCH 101/159] Core: Fix missing tailing bytes of video logs --- src/feature/video-logger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index ee4dcdedf..ffad97a57 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -834,7 +834,7 @@ static bool _fillBuffer(struct mVideoLogContext* context, size_t channelId, size return false; } if (header.blockType == mVL_BLOCK_FOOTER) { - return false; + return true; } if (header.channelId != channelId || header.blockType != mVL_BLOCK_DATA) { context->backing->seek(context->backing, header.length, SEEK_CUR); From 91491e704e21343f8d7a908d1a8119cafecd6de2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 13:14:31 -0700 Subject: [PATCH 102/159] GB, GBA Core: Ensure the core is halted even if a state is not loaded --- src/gb/core.c | 5 +++++ src/gba/core.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/gb/core.c b/src/gb/core.c index 97b48e7da..0bcc2aa9d 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -792,6 +792,11 @@ static void _GBVLPReset(struct mCore* core) { LR35902Reset(core->cpu); mVideoLogContextRewind(gbcore->logContext, core); GBVideoProxyRendererShim(&gb->video, &gbcore->proxyRenderer); + + // Make sure CPU loop never spins + GBHalt(gb->cpu); + gb->memory.ie = 0; + gb->memory.ime = false; } static bool _GBVLPLoadROM(struct mCore* core, struct VFile* vf) { diff --git a/src/gba/core.c b/src/gba/core.c index 4558ccd77..dc764d6e7 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -811,6 +811,11 @@ static void _GBAVLPReset(struct mCore* core) { ARMReset(core->cpu); mVideoLogContextRewind(gbacore->logContext, core); GBAVideoProxyRendererShim(&gba->video, &gbacore->proxyRenderer); + + // Make sure CPU loop never spins + GBAHalt(gba); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, BASE_IO | REG_IE, 0, NULL); } static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { From 367a12674842bfe469618eb364732129809b5837 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 13:34:57 -0700 Subject: [PATCH 103/159] GBA Hardware: Fix crash if a savestate lies about game hardware --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 2598f6fcb..ff8b35359 100644 --- a/CHANGES +++ b/CHANGES @@ -51,6 +51,7 @@ Bugfixes: - GBA: Fix multiboot loading resulting in too small WRAM - Test: Don't rely on core for frames elapsed - Test: Fix crash when loading invalid file + - GBA Hardware: Fix crash if a savestate lies about game hardware Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers From 8e79530cc60712b0c0c6f74d1f6a50b0e81ad167 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 13:35:28 -0700 Subject: [PATCH 104/159] Test: Fix crash when fuzzing fails to load a file --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index ff8b35359..cb06bb5af 100644 --- a/CHANGES +++ b/CHANGES @@ -52,6 +52,7 @@ Bugfixes: - Test: Don't rely on core for frames elapsed - Test: Fix crash when loading invalid file - GBA Hardware: Fix crash if a savestate lies about game hardware + - Test: Fix crash when fuzzing fails to load a file Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers From 909886d2e14f3ca429958fb2cf78edf7980044dd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 14:00:59 -0700 Subject: [PATCH 105/159] Core: Fix memory leak when reloading mVL header --- src/feature/video-logger.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index ffad97a57..ee773795b 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -458,6 +458,8 @@ struct mVideoLogContext* mVideoLogContextCreate(struct mCore* core) { memset(context, 0, sizeof(*context)); context->write = !!core; + context->initialStateSize = 0; + context->initialState = NULL; if (core) { context->initialStateSize = core->stateSize(core); @@ -556,6 +558,11 @@ bool _readHeader(struct mVideoLogContext* context) { if (header.blockType != mVL_BLOCK_INITIAL_STATE) { return false; } + if (context->initialState) { + mappedMemoryFree(context->initialState, context->initialStateSize); + context->initialState = NULL; + context->initialStateSize = 0; + } if (header.flags & mVL_FLAG_BLOCK_COMPRESSED) { #ifdef USE_ZLIB struct VFile* vfm = VFileMemChunk(NULL, 0); From 6a14c30e3bbf5f665eeab5d54723ddc6690d2978 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 14:01:14 -0700 Subject: [PATCH 106/159] Core: Enforce max mVL block size --- src/feature/video-logger.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index ee773795b..98b6956db 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -22,6 +22,7 @@ #endif #define BUFFER_BASE_SIZE 0x20000 +#define MAX_BLOCK_SIZE 0x800000 const char mVL_MAGIC[] = "mVL\0"; @@ -530,6 +531,12 @@ bool _readBlockHeader(struct mVideoLogContext* context, struct mVLBlockHeader* h LOAD_32LE(header->length, 0, &buffer.length); LOAD_32LE(header->channelId, 0, &buffer.channelId); LOAD_32LE(header->flags, 0, &buffer.flags); + + if (header->length > MAX_BLOCK_SIZE) { + // Pre-emptively reject blocks that are too big. + // If we encounter one, the file is probably corrupted. + return false; + } return true; } From 0ed5973f84d27eb54a1ad775ac9cc8940c577dbd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 24 Apr 2017 15:29:50 -0700 Subject: [PATCH 107/159] Core: Reject mVL with zero-length initial states --- src/feature/video-logger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 98b6956db..10cb5200c 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -562,7 +562,7 @@ bool _readHeader(struct mVideoLogContext* context) { if (!_readBlockHeader(context, &header)) { return false; } - if (header.blockType != mVL_BLOCK_INITIAL_STATE) { + if (header.blockType != mVL_BLOCK_INITIAL_STATE || !header.length) { return false; } if (context->initialState) { From 8358586cc6a0eb2606e991f6d38194c53f342c00 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 26 Apr 2017 18:20:08 -0700 Subject: [PATCH 108/159] GB Video: Fix resetting proxy backend unnecessarily --- src/gb/extra/proxy.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gb/extra/proxy.c b/src/gb/extra/proxy.c index eab14a7c1..2b015ae6c 100644 --- a/src/gb/extra/proxy.c +++ b/src/gb/extra/proxy.c @@ -65,9 +65,6 @@ static void _reset(struct GBVideoProxyRenderer* proxyRenderer, enum GBModel mode proxyRenderer->oamMax = 0; - proxyRenderer->backend->deinit(proxyRenderer->backend); - proxyRenderer->backend->init(proxyRenderer->backend, model); - mVideoLoggerRendererReset(proxyRenderer->logger); } From ad203c17bd7f204a9e5df8499c76c909c75a93e0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 26 Apr 2017 18:28:54 -0700 Subject: [PATCH 109/159] All: Update CHANGES --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index cb06bb5af..93d8b515f 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Features: - Add option to allow preloading the entire ROM before running - GB: Video/audio channel enabling/disabling - Add option to lock video to integer scaling + - Video log recording for testing and bug reporting Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior From 2fdb5a1ff950ed4a7b8b9134a1c76a4087bc4997 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 26 Apr 2017 18:41:45 -0700 Subject: [PATCH 110/159] Feature: Move several headers into include/ --- {src => include/mgba}/feature/commandline.h | 0 {src => include/mgba}/feature/thread-proxy.h | 0 {src => include/mgba}/feature/video-logger.h | 0 include/mgba/internal/gb/renderers/proxy.h | 2 +- include/mgba/internal/gba/renderers/proxy.h | 2 +- src/core/core.c | 2 +- src/feature/commandline.c | 2 +- src/feature/thread-proxy.c | 2 +- src/feature/video-logger.c | 2 +- src/gba/core.c | 2 +- src/platform/example/client-server/server.c | 2 +- src/platform/qt/ConfigController.cpp | 2 +- src/platform/qt/ConfigController.h | 2 +- src/platform/qt/GameController.cpp | 2 +- src/platform/qt/Window.cpp | 2 +- src/platform/sdl/main.c | 2 +- src/platform/test/fuzz-main.c | 2 +- src/platform/test/perf-main.c | 2 +- 18 files changed, 15 insertions(+), 15 deletions(-) rename {src => include/mgba}/feature/commandline.h (100%) rename {src => include/mgba}/feature/thread-proxy.h (100%) rename {src => include/mgba}/feature/video-logger.h (100%) diff --git a/src/feature/commandline.h b/include/mgba/feature/commandline.h similarity index 100% rename from src/feature/commandline.h rename to include/mgba/feature/commandline.h diff --git a/src/feature/thread-proxy.h b/include/mgba/feature/thread-proxy.h similarity index 100% rename from src/feature/thread-proxy.h rename to include/mgba/feature/thread-proxy.h diff --git a/src/feature/video-logger.h b/include/mgba/feature/video-logger.h similarity index 100% rename from src/feature/video-logger.h rename to include/mgba/feature/video-logger.h diff --git a/include/mgba/internal/gb/renderers/proxy.h b/include/mgba/internal/gb/renderers/proxy.h index 59b0aed72..244c5e39b 100644 --- a/include/mgba/internal/gb/renderers/proxy.h +++ b/include/mgba/internal/gb/renderers/proxy.h @@ -11,7 +11,7 @@ CXX_GUARD_START #include -#include "feature/video-logger.h" +#include struct GBVideoProxyRenderer { struct GBVideoRenderer d; diff --git a/include/mgba/internal/gba/renderers/proxy.h b/include/mgba/internal/gba/renderers/proxy.h index 5dbe03f74..c1044c77e 100644 --- a/include/mgba/internal/gba/renderers/proxy.h +++ b/include/mgba/internal/gba/renderers/proxy.h @@ -11,7 +11,7 @@ CXX_GUARD_START #include -#include "feature/video-logger.h" +#include struct GBAVideoProxyRenderer { struct GBAVideoRenderer d; diff --git a/src/core/core.c b/src/core/core.c index c07198366..b82bab950 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -19,7 +19,7 @@ #include #endif #ifndef MINIMAL_CORE -#include "feature/video-logger.h" +#include #endif const static struct mCoreFilter { diff --git a/src/feature/commandline.c b/src/feature/commandline.c index 07da69760..ccb44f105 100644 --- a/src/feature/commandline.c +++ b/src/feature/commandline.c @@ -3,7 +3,7 @@ * 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 "commandline.h" +#include #include #include diff --git a/src/feature/thread-proxy.c b/src/feature/thread-proxy.c index 51e807565..16b3db70c 100644 --- a/src/feature/thread-proxy.c +++ b/src/feature/thread-proxy.c @@ -3,7 +3,7 @@ * 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 "thread-proxy.h" +#include #include #include diff --git a/src/feature/video-logger.c b/src/feature/video-logger.c index 10cb5200c..865bd24ea 100644 --- a/src/feature/video-logger.c +++ b/src/feature/video-logger.c @@ -3,7 +3,7 @@ * 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 "video-logger.h" +#include #include #include diff --git a/src/gba/core.c b/src/gba/core.c index dc764d6e7..6888594ba 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -14,7 +14,7 @@ #include #include #ifndef DISABLE_THREADING -#include "feature/thread-proxy.h" +#include #endif #include #include diff --git a/src/platform/example/client-server/server.c b/src/platform/example/client-server/server.c index 75af4edb1..fce25888c 100644 --- a/src/platform/example/client-server/server.c +++ b/src/platform/example/client-server/server.c @@ -1,6 +1,6 @@ // This source file is placed into the public domain. #include -#include "feature/commandline.h" +#include #include #define DEFAULT_PORT 13721 diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 862cb3d06..f8c168167 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -11,7 +11,7 @@ #include #include -#include "feature/commandline.h" +#include using namespace QGBA; diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 2c8512c5e..c3a2e7759 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -17,7 +17,7 @@ #include #include -#include "feature/commandline.h" +#include class QAction; class QMenu; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 3c86f96a5..f0364512e 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -32,7 +32,7 @@ #include #endif #include -#include "feature/video-logger.h" +#include using namespace QGBA; using namespace std; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 04872eb99..98bfa13b1 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -53,7 +53,7 @@ #include #include #endif -#include "feature/commandline.h" +#include #include "feature/sqlite3/no-intro.h" #include diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index f5ec5fe53..386689695 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -20,7 +20,7 @@ #include #include -#include "feature/commandline.h" +#include #include #include diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index 533866155..fb27bce57 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -12,7 +12,7 @@ #include #include -#include "feature/commandline.h" +#include #include #include #include diff --git a/src/platform/test/perf-main.c b/src/platform/test/perf-main.c index 03fe093ce..aff3c30d8 100644 --- a/src/platform/test/perf-main.c +++ b/src/platform/test/perf-main.c @@ -11,7 +11,7 @@ #include #include -#include "feature/commandline.h" +#include #include #include #include From 417012632c19be49e5e4d28b70a6f76a392f0b1a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 26 Apr 2017 19:40:54 -0700 Subject: [PATCH 111/159] GBA Serialize: Remove unused function --- include/mgba/internal/gba/serialize.h | 3 --- src/gba/serialize.c | 8 -------- 2 files changed, 11 deletions(-) diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 7f1e5e128..14bddf6e2 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -334,9 +334,6 @@ struct VDir; void GBASerialize(struct GBA* gba, struct GBASerializedState* state); bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state); -struct GBASerializedState* GBAAllocateState(void); -void GBADeallocateState(struct GBASerializedState* state); - CXX_GUARD_END #endif diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 59c6ecc34..663fcc44c 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -206,11 +206,3 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { } return true; } - -struct GBASerializedState* GBAAllocateState(void) { - return anonymousMemoryMap(sizeof(struct GBASerializedState)); -} - -void GBADeallocateState(struct GBASerializedState* state) { - mappedMemoryFree(state, sizeof(struct GBASerializedState)); -} From 27c70880385877006cbc85e3d7345be694110a1f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 28 Apr 2017 23:17:58 -0700 Subject: [PATCH 112/159] Qt: Fix mCoreConfig leaking --- src/platform/qt/GameController.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index f0364512e..8d59cc0d6 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -651,6 +651,7 @@ void GameController::cleanGame() { delete[] m_drawContext; delete[] m_frontBuffer; + mCoreConfigDeinit(&m_threadContext.core->config); m_threadContext.core->deinit(m_threadContext.core); m_threadContext.core = nullptr; m_gameOpen = false; From 29e5e3b42dfc00df8500664b74077160a82b40dc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 28 Apr 2017 19:45:52 -0700 Subject: [PATCH 113/159] Core: Move rewind diffing to its own thread --- CHANGES | 1 + include/mgba/core/rewind.h | 12 +++- src/core/rewind.c | 101 +++++++++++++++++++++++++---- src/core/thread.c | 2 +- src/platform/qt/GameController.cpp | 4 +- 5 files changed, 104 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 93d8b515f..f619979ba 100644 --- a/CHANGES +++ b/CHANGES @@ -110,6 +110,7 @@ Misc: - Debugger: Make attaching a backend idempotent - VFS: Optimize expanding in-memory files - VFS: Add VFileFIFO for operating on circle buffers + - Core: Move rewind diffing to its own thread 0.5.2: (2016-12-31) Bugfixes: diff --git a/include/mgba/core/rewind.h b/include/mgba/core/rewind.h index 8762ff76a..e839209fc 100644 --- a/include/mgba/core/rewind.h +++ b/include/mgba/core/rewind.h @@ -11,6 +11,9 @@ CXX_GUARD_START #include +#ifndef DISABLE_THREADING +#include +#endif DECLARE_VECTOR(mCoreRewindPatches, struct PatchFast); @@ -22,9 +25,16 @@ struct mCoreRewindContext { int stateFlags; struct VFile* previousState; struct VFile* currentState; + +#ifndef DISABLE_THREADING + bool onThread; + Thread thread; + Condition cond; + Mutex mutex; +#endif }; -void mCoreRewindContextInit(struct mCoreRewindContext*, size_t entries); +void mCoreRewindContextInit(struct mCoreRewindContext*, size_t entries, bool onThread); void mCoreRewindContextDeinit(struct mCoreRewindContext*); struct mCore; diff --git a/src/core/rewind.c b/src/core/rewind.c index 53ee67188..15ac69a95 100644 --- a/src/core/rewind.c +++ b/src/core/rewind.c @@ -12,7 +12,13 @@ DEFINE_VECTOR(mCoreRewindPatches, struct PatchFast); -void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) { +void _rewindDiff(struct mCoreRewindContext* context); + +#ifndef DISABLE_THREADING +THREAD_ENTRY _rewindThread(void* context); +#endif + +void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries, bool onThread) { mCoreRewindPatchesInit(&context->patchMemory, entries); size_t e; for (e = 0; e < entries; ++e) { @@ -22,9 +28,30 @@ void mCoreRewindContextInit(struct mCoreRewindContext* context, size_t entries) context->currentState = VFileMemChunk(0, 0); context->size = 0; context->stateFlags = SAVESTATE_SAVEDATA; +#ifndef DISABLE_THREADING + context->onThread = onThread; + if (onThread) { + MutexInit(&context->mutex); + ConditionInit(&context->cond); + ThreadCreate(&context->thread, _rewindThread, context); + } +#else + UNUSED(onThread); +#endif } void mCoreRewindContextDeinit(struct mCoreRewindContext* context) { +#ifndef DISABLE_THREADING + if (context->onThread) { + MutexLock(&context->mutex); + context->onThread = false; + MutexUnlock(&context->mutex); + ConditionWake(&context->cond); + ThreadJoin(context->thread); + MutexDeinit(&context->mutex); + ConditionDeinit(&context->cond); + } +#endif context->previousState->close(context->previousState); context->currentState->close(context->currentState); size_t s; @@ -35,7 +62,26 @@ void mCoreRewindContextDeinit(struct mCoreRewindContext* context) { } void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) { +#ifndef DISABLE_THREADING + if (context->onThread) { + MutexLock(&context->mutex); + } +#endif struct VFile* nextState = context->previousState; + mCoreSaveStateNamed(core, nextState, context->stateFlags); + context->previousState = context->currentState; + context->currentState = nextState; +#ifndef DISABLE_THREADING + if (context->onThread) { + ConditionWake(&context->cond); + MutexUnlock(&context->mutex); + return; + } +#endif + _rewindDiff(context); +} + +void _rewindDiff(struct mCoreRewindContext* context) { ++context->current; if (context->size < mCoreRewindPatchesSize(&context->patchMemory)) { ++context->size; @@ -43,27 +89,34 @@ void mCoreRewindAppend(struct mCoreRewindContext* context, struct mCore* core) { if (context->current >= mCoreRewindPatchesSize(&context->patchMemory)) { context->current = 0; } - mCoreSaveStateNamed(core, nextState, context->stateFlags); struct PatchFast* patch = mCoreRewindPatchesGetPointer(&context->patchMemory, context->current); - size_t size2 = nextState->size(nextState); - size_t size = context->currentState->size(context->currentState); + size_t size2 = context->currentState->size(context->currentState); + size_t size = context->previousState->size(context->previousState); if (size2 > size) { - context->currentState->truncate(context->currentState, size2); + context->previousState->truncate(context->previousState, size2); size = size2; } else if (size > size2) { - nextState->truncate(nextState, size); + context->currentState->truncate(context->currentState, size); } - void* current = context->currentState->map(context->currentState, size, MAP_READ); - void* next = nextState->map(nextState, size, MAP_READ); + void* current = context->previousState->map(context->previousState, size, MAP_READ); + void* next = context->currentState->map(context->currentState, size, MAP_READ); diffPatchFast(patch, current, next, size); - context->currentState->unmap(context->currentState, current, size); - nextState->unmap(next, nextState, size); - context->previousState = context->currentState; - context->currentState = nextState; + context->previousState->unmap(context->previousState, current, size); + context->currentState->unmap(context->currentState, next, size); } bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) { +#ifndef DISABLE_THREADING + if (context->onThread) { + MutexLock(&context->mutex); + } +#endif if (!context->size) { +#ifndef DISABLE_THREADING + if (context->onThread) { + MutexUnlock(&context->mutex); + } +#endif return false; } --context->size; @@ -88,5 +141,29 @@ bool mCoreRewindRestore(struct mCoreRewindContext* context, struct mCore* core) context->current = mCoreRewindPatchesSize(&context->patchMemory); } --context->current; +#ifndef DISABLE_THREADING + if (context->onThread) { + MutexUnlock(&context->mutex); + } +#endif return true; } + +#ifndef DISABLE_THREADING +THREAD_ENTRY _rewindThread(void* context) { + struct mCoreRewindContext* rewindContext = context; + ThreadSetName("Rewind Diff Thread"); + MutexLock(&rewindContext->mutex); + struct VFile* state = rewindContext->currentState; + while (rewindContext->onThread) { + if (rewindContext->currentState != state) { + _rewindDiff(rewindContext); + state = rewindContext->currentState; + } + ConditionWait(&rewindContext->cond, &rewindContext->mutex); + } + MutexUnlock(&rewindContext->mutex); + return 0; +} +#endif + diff --git a/src/core/thread.c b/src/core/thread.c index 652561b8c..424260b7e 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -168,7 +168,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } if (core->opts.rewindEnable && core->opts.rewindBufferCapacity > 0) { - mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity); + mCoreRewindContextInit(&threadContext->rewind, core->opts.rewindBufferCapacity, true); threadContext->rewind.stateFlags = core->opts.rewindSave ? SAVESTATE_SAVEDATA : 0; } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 8d59cc0d6..3bddec45a 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -741,8 +741,8 @@ void GameController::setRewind(bool enable, int capacity, bool rewindSave) { m_threadContext.core->opts.rewindBufferCapacity = capacity; m_threadContext.core->opts.rewindSave = rewindSave; if (enable && capacity > 0) { - mCoreRewindContextInit(&m_threadContext.rewind, capacity); - m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0; + mCoreRewindContextInit(&m_threadContext.rewind, capacity, true); + m_threadContext.rewind.stateFlags = rewindSave ? SAVESTATE_SAVEDATA : 0; } } } From 669075582d19bac1e67286013bdab26ffa1efab0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 28 Apr 2017 23:45:08 -0700 Subject: [PATCH 114/159] Util: Tune patch-fast extent sizes --- CHANGES | 1 + include/mgba-util/patch/fast.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index f619979ba..3128c6f46 100644 --- a/CHANGES +++ b/CHANGES @@ -111,6 +111,7 @@ Misc: - VFS: Optimize expanding in-memory files - VFS: Add VFileFIFO for operating on circle buffers - Core: Move rewind diffing to its own thread + - Util: Tune patch-fast extent sizes 0.5.2: (2016-12-31) Bugfixes: diff --git a/include/mgba-util/patch/fast.h b/include/mgba-util/patch/fast.h index 6657a0419..ee59cd23f 100644 --- a/include/mgba-util/patch/fast.h +++ b/include/mgba-util/patch/fast.h @@ -13,7 +13,7 @@ CXX_GUARD_START #include #include -#define PATCH_FAST_EXTENT 256 +#define PATCH_FAST_EXTENT 128 struct PatchFastExtent { size_t length; From 87170f9b774d616291810661c34a7d2e5218963a Mon Sep 17 00:00:00 2001 From: Lothar Serra Mari Date: Sat, 29 Apr 2017 16:30:00 +0200 Subject: [PATCH 115/159] Qt: Update German GUI translation Added translations for strings related to the new Video Log feature. Also improved some older translations. --- src/platform/qt/ts/mgba-de.ts | 346 ++++++++++++++++++---------------- 1 file changed, 186 insertions(+), 160 deletions(-) diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index e8318c194..a5ec7e42c 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -1045,28 +1045,28 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::GameController - - + + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen - + Failed to start audio processor Fehler beim Starten des Audio-Prozessors @@ -2629,54 +2629,54 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::ObjView - - + + 0x%0 0x%0 - + Off Aus - + Normal Normal - + Trans Trans - + OBJWIN OBJWIN - + Invalid Ungültig - - + + N/A N/A - + Export sprite Sprite exportieren - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - + Failed to open output PNG file: %1 Fehler beim Öffnen der Ausgabe-PNG-Datei: %1 @@ -2884,52 +2884,67 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Alle ROMs (%1) - + + %1 Video Logs (*.mvl) + %1 Video-Logs (*.mvl) + + + Archives (%1) Archive (%1) - - - + + + Select ROM ROM auswählen - + Game Boy Advance save files (%1) Game Boy Advance-Speicherdateien (%1) - - - + + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Patches (*.ips *.ups *.bps) - - + + GameShark saves (*.sps *.xps) GameShark-Speicherdaten (*.sps *.xps) - + + Select video log + Video-Log auswählen + + + + Video logs (*.mvl) + Video-Logs (*.mvl) + + + Crash Absturz - + The game has crashed with the following error: %1 @@ -2938,613 +2953,623 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - + Couldn't Load Konnte nicht geladen werden - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Sind Sie sicher, dass es im korrekten Format vorliegt? - + Unimplemented BIOS call - Nichtimplementierter BIOS-Aufruf + Nicht implementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 - + &File &Datei - + Load &ROM... &ROM laden... - + Load ROM in archive... ROM aus Archiv laden... - + Load temporary save... Temporäre Speicherdatei laden... - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate &laden - + F10 F10 - + &Save state Savestate &speichern - + Shift+F10 Umschalt+F10 - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Stand - + Undo load state Laden des Savestate rückgängig machen - + F11 F11 - + Undo save state Speichern des Savestate rückgängig machen - + Shift+F11 Umschalt+F11 - - + + State &%1 Savestate &%1 - + F%1 F%1 - + Shift+F%1 Umschalt+F%1 - + Import GameShark Save Importiere GameShark-Speicherstand - + Export GameShark Save Exportiere GameShark-Speicherstand - + New multiplayer window Neues Multiplayer-Fenster - + About Über - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Ctrl+R Strg+R - + Sh&utdown B&eenden - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + Ctrl+P Strg+P - + &Next frame &Nächstes Bild - + Ctrl+N Strg+N - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Shift+Tab Umschalt+Tab - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + ~ ~ - + Step backwards Schrittweiser Rücklauf - + Ctrl+B Strg+B - + Sync to &video Mit &Video synchronisieren - + Sync to &audio Mit &Audio synchronisieren - + Solar sensor Solar-Sensor - + Increase solar level Sonnen-Level erhöhen - + Decrease solar level Sonnen-Level verringern - + Brightest solar level Hellster Sonnen-Level - + Darkest solar level Dunkelster Sonnen-Level - + Brightness %1 Helligkeit %1 - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + %1x %1x - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Frame&skip Frame&skip - + Shader options... Shader-Optionen... - + Mute Stummschalten - + FPS target Bildwiederholrate - + 15 15 - + 30 30 - + 45 45 - + Native (59.7) Nativ (59.7) - + 60 60 - + 90 90 - + 120 120 - + 240 240 - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Record output... Ausgabe aufzeichen... - + Record GIF... GIF aufzeichen... - + + Record video log... + Video-Log aufzeichnen... + + + + Stop video log + Video-Log beenden + + + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + Game &Pak sensors... Game &Pak-Sensoren... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole äffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... - + Select folder Ordner auswählen - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Bilinear filtering Bilineare Filterung - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View memory... Speicher betrachten... - + View &I/O registers... &I/O-Register betrachten... - + Exit fullscreen Vollbildmodus beenden - + Autofire Autofeuer - + Autofire A Autofeuer A - + Autofire B Autofeuer B - + Autofire L Autofeuer L - + Autofire R Autofeuer R - + Autofire Start Autofeuer Start - + Autofire Select Autofeuer Select - + Autofire Up Autofeuer nach oben - + Autofire Right Autofeuer rechts - + Autofire Down Autofeuer nach unten - + Autofire Left Autofeuer links @@ -3904,7 +3929,8 @@ in Arbeitsspeicher vorladen Use BIOS file if found - BIOS-Datei verwenden, wenn vorhanden + BIOS-Datei verwenden, +sofern vorhanden @@ -4007,17 +4033,17 @@ in Arbeitsspeicher vorladen GB BIOS file: - GB-BIOS: + Datei mit GB-BIOS: GBA BIOS file: - GBA-BIOS: + Datei mit GBA-BIOS: GBC BIOS file: - GBC-BIOS: + Datei mit GBC-BIOS: From e1b681f6036706a402f5ab581b61a0f9dcc354ec Mon Sep 17 00:00:00 2001 From: "Eevee (Lexy Munroe)" Date: Fri, 5 May 2017 19:07:42 -0700 Subject: [PATCH 116/159] Qt: Fix race condition when opening sprites window --- src/platform/qt/GameController.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 3bddec45a..f5f921c56 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -1258,10 +1258,10 @@ std::shared_ptr GameController::tileCache() { if (m_tileCache) { return m_tileCache; } + Interrupter interrupter(this); switch (platform()) { #ifdef M_CORE_GBA case PLATFORM_GBA: { - Interrupter interrupter(this); GBA* gba = static_cast(m_threadContext.core->board); m_tileCache = std::make_shared(); GBAVideoTileCacheInit(m_tileCache.get()); @@ -1272,7 +1272,6 @@ std::shared_ptr GameController::tileCache() { #endif #ifdef M_CORE_GB case PLATFORM_GB: { - Interrupter interrupter(this); GB* gb = static_cast(m_threadContext.core->board); m_tileCache = std::make_shared(); GBVideoTileCacheInit(m_tileCache.get()); From 63197308b659349a3e9ce2317b2dc581dace79cd Mon Sep 17 00:00:00 2001 From: "Eevee (Lexy Munroe)" Date: Fri, 5 May 2017 19:08:46 -0700 Subject: [PATCH 117/159] Core: Fix maximum length of INI section names It was a generous 128, but the inih library has its own maximum length of 50. Any longer keys were truncated by inih, so they appeared to be missing when mgba tried to read the longer key again later. This was causing the configuration for my Nintendo Wii Remote Pro Controller to be effectively lost; the key name ended up at 53 characters. Arguably inih should truncate when reading, too, but... --- src/core/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/input.c b/src/core/input.c index fe2096ccd..c80930b3a 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -11,7 +11,7 @@ #include -#define SECTION_NAME_MAX 128 +#define SECTION_NAME_MAX 50 #define KEY_NAME_MAX 32 #define KEY_VALUE_MAX 16 #define AXIS_INFO_MAX 12 From b58d216868189f71475404d75839f426cb086d22 Mon Sep 17 00:00:00 2001 From: "Eevee (Lexy Munroe)" Date: Fri, 5 May 2017 19:10:45 -0700 Subject: [PATCH 118/159] Qt: Fix resetting selected gamepad when opening settings dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have a Wacom tablet always plugged in. For unrelated asinine reasons, SDL detects both the pen and the tablet itself as (completely useless) gamepads, so they always show up in the list first. I also have a Wii U Bluetooth controller, which shows up third. When the settings dialog is spawned, selectGamepad is bound to the currentIndexChanged event, so picking a controller from the combobox will call it. The current index is initially -1 (because the combobox is empty), and when updateJoysticks is called to populate it, the index automatically changes to 0. The event is fired and the current gamepad is changed to the first one, which in my case is my tablet pen. updateJoysticks tries to avoid this by explicitly reselecting the active gamepad after refreshing the combobox, but at this point the "active" gamepad has already changed. The upshot of this was that opening the settings dialog for any reason would disable my controller, unless I remembered to go change it back. I've also seen the button configuration for the wacom pen or controller be saved under each others' names. The fix, which works on my machine™, is to disable signals altogether while mucking with the contents of the combobox, explicitly reselect the right gamepad, and then explicitly call selectGamepad. (I dropped the last two lines because the same thing is already done in refresh, which is called by selectGamepad.) Arguably this is still a little wrong: - If I unplugged my Wacom tablet, my controller would shift upwards to slot 0. This code would fail to notice and keep trying to use joystick 2. That's a pretty obscure case, though, and maybe difficult to fix correctly. - This panel makes changes immediately, but it should wait for OK or Apply. --- src/platform/qt/GBAKeyEditor.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 1f5aafeed..4e157c85c 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -396,14 +396,14 @@ void GBAKeyEditor::updateJoysticks() { m_controller->updateJoysticks(); m_controller->recalibrateAxes(); + // Block the currentIndexChanged signal while rearranging the combo box + auto wasBlocked = m_profileSelect->blockSignals(true); m_profileSelect->clear(); m_profileSelect->addItems(m_controller->connectedGamepads(m_type)); int activeGamepad = m_controller->gamepad(m_type); + m_profileSelect->setCurrentIndex(activeGamepad); + m_profileSelect->blockSignals(wasBlocked); + selectGamepad(activeGamepad); - if (activeGamepad > 0) { - m_profileSelect->setCurrentIndex(activeGamepad); - } - lookupAxes(m_controller->map()); - lookupHats(m_controller->map()); } #endif From 3889fd4d3f893b9a2445cea2057dbd23c997bf3c Mon Sep 17 00:00:00 2001 From: Anty-Lemon Date: Sat, 6 May 2017 18:59:53 -0400 Subject: [PATCH 119/159] SDL: Add integer scaling --- src/platform/sdl/gl-sdl.c | 1 + src/platform/sdl/gles2-sdl.c | 1 + src/platform/sdl/main.c | 1 + src/platform/sdl/main.h | 1 + 4 files changed, 4 insertions(+) diff --git a/src/platform/sdl/gl-sdl.c b/src/platform/sdl/gl-sdl.c index 0ecf67ecd..b723a495c 100644 --- a/src/platform/sdl/gl-sdl.c +++ b/src/platform/sdl/gl-sdl.c @@ -38,6 +38,7 @@ bool mSDLGLInit(struct mSDLRenderer* renderer) { mGLContextCreate(&renderer->gl); renderer->gl.d.user = renderer; renderer->gl.d.lockAspectRatio = renderer->lockAspectRatio; + renderer->gl.d.lockIntegerScaling = renderer->lockIntegerScaling; renderer->gl.d.filter = renderer->filter; renderer->gl.d.swap = mSDLGLCommonSwap; renderer->gl.d.init(&renderer->gl.d, 0); diff --git a/src/platform/sdl/gles2-sdl.c b/src/platform/sdl/gles2-sdl.c index 041315b8e..9f9778c3f 100644 --- a/src/platform/sdl/gles2-sdl.c +++ b/src/platform/sdl/gles2-sdl.c @@ -109,6 +109,7 @@ bool mSDLGLES2Init(struct mSDLRenderer* renderer) { mGLES2ContextCreate(&renderer->gl2); renderer->gl2.d.user = renderer; renderer->gl2.d.lockAspectRatio = renderer->lockAspectRatio; + renderer->gl2.d.lockIntegerScaling = renderer->lockIntegerScaling; renderer->gl2.d.filter = renderer->filter; renderer->gl2.d.swap = mSDLGLCommonSwap; renderer->gl2.d.init(&renderer->gl2.d, 0); diff --git a/src/platform/sdl/main.c b/src/platform/sdl/main.c index 386689695..84efaade4 100644 --- a/src/platform/sdl/main.c +++ b/src/platform/sdl/main.c @@ -114,6 +114,7 @@ int main(int argc, char** argv) { #endif renderer.lockAspectRatio = renderer.core->opts.lockAspectRatio; + renderer.lockIntegerScaling = renderer.core->opts.lockIntegerScaling; renderer.filter = renderer.core->opts.resampleVideo; if (!mSDLInit(&renderer)) { diff --git a/src/platform/sdl/main.h b/src/platform/sdl/main.h index 0477692cb..eb435a1c7 100644 --- a/src/platform/sdl/main.h +++ b/src/platform/sdl/main.h @@ -65,6 +65,7 @@ struct mSDLRenderer { int ratio; bool lockAspectRatio; + bool lockIntegerScaling; bool filter; #ifdef BUILD_GL From 86cf8a90a35bd1b210aa21e8252164dc6f9928c2 Mon Sep 17 00:00:00 2001 From: Anty-Lemon Date: Sat, 6 May 2017 19:41:07 -0400 Subject: [PATCH 120/159] SDL: Prevent resizing below 1x resolution with integer scaling --- src/platform/sdl/gl-common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/platform/sdl/gl-common.c b/src/platform/sdl/gl-common.c index 7ed9aa95b..13c20811b 100644 --- a/src/platform/sdl/gl-common.c +++ b/src/platform/sdl/gl-common.c @@ -38,6 +38,9 @@ void mSDLGLCommonInit(struct mSDLRenderer* renderer) { SDL_GL_SetSwapInterval(1); SDL_GetWindowSize(renderer->window, &renderer->viewportWidth, &renderer->viewportHeight); renderer->player.window = renderer->window; + if (renderer->lockIntegerScaling) { + SDL_SetWindowMinimumSize(renderer->window, renderer->width, renderer->height); + } #else SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); #ifdef COLOR_16_BIT From aa4423a68fb70027c2e1288a326e3c5f55dae232 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Sun, 14 May 2017 21:22:17 -0400 Subject: [PATCH 121/159] Core Input: Correct a memset size within mInputUnbindHat() Previously the binding struct wouldn't be fully invalidated. --- src/core/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/input.c b/src/core/input.c index c80930b3a..6d391ee3b 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -563,7 +563,7 @@ void mInputUnbindHat(struct mInputMap* map, uint32_t type, int id) { mInputHatListResize(&impl->hats, -1); } else { struct mInputHatBindings* description = mInputHatListGetPointer(&impl->hats, id); - memset(description, -1, sizeof(&description)); + memset(description, -1, sizeof(*description)); } } From 2fff4eac674acf0b936557c68ed624ddbcf29a6f Mon Sep 17 00:00:00 2001 From: drummyfish Date: Mon, 15 May 2017 20:40:54 +0200 Subject: [PATCH 122/159] add new shader --- res/shaders/fish.shader/fish.fs | 181 +++++++++++++++++++++++++++ res/shaders/fish.shader/manifest.ini | 16 +++ 2 files changed, 197 insertions(+) create mode 100644 res/shaders/fish.shader/fish.fs create mode 100644 res/shaders/fish.shader/manifest.ini diff --git a/res/shaders/fish.shader/fish.fs b/res/shaders/fish.shader/fish.fs new file mode 100644 index 000000000..f54a3bf8b --- /dev/null +++ b/res/shaders/fish.shader/fish.fs @@ -0,0 +1,181 @@ +/* + fish shader + + algorithm and original implementation by Miloslav "drummyfish" Ciz + (tastyfish@seznam.cz) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +precision highp float; + +varying vec2 texCoord; +uniform sampler2D tex; +uniform vec2 texSize; + +uniform float similarity_threshold; + +#define screen_res 240,160 + +vec4 texel_fetch(sampler2D t, ivec2 c) // because GLSL TexelFetch is not supported + { + return texture2D(tex, (2 * vec2(c) + vec2(1,1)) / (2 * vec2(screen_res)) ); + } + +float pixel_brightness(vec4 pixel) + { + return 0.21 * pixel.x + 0.72 * pixel.y + 0.07 * pixel.z; + } + +bool pixel_is_brighter(vec4 pixel1, vec4 pixel2) + { + return pixel_brightness(pixel1) > pixel_brightness(pixel2); + } + +vec3 pixel_to_yuv(vec4 pixel) + { + float y = 0.299 * pixel.x + 0.587 * pixel.y + 0.114 * pixel.z; + return vec3(y, 0.492 * (pixel.z - y), 0.877 * (pixel.x - y)); + } + +bool yuvs_are_similar(vec3 yuv1, vec3 yuv2) + { + vec3 yuv_difference = abs(yuv1 - yuv2); + return yuv_difference.x <= similarity_threshold && yuv_difference.y <= similarity_threshold && yuv_difference.z <= similarity_threshold; + } + +bool pixels_are_similar(vec4 pixel1, vec4 pixel2) + { + vec3 yuv1 = pixel_to_yuv(pixel1); + vec3 yuv2 = pixel_to_yuv(pixel2); + + return yuvs_are_similar(yuv1, yuv2); + } + +vec4 interpolate_nondiagonal(vec4 neighbour1, vec4 neighbour2) + { + if (pixels_are_similar(neighbour1,neighbour2)) + return mix(neighbour1,neighbour2,0.5); + else + return pixel_is_brighter(neighbour1, neighbour2) ? neighbour1 : neighbour2; + } + +vec4 mix3(vec4 value1, vec4 value2, vec4 value3) + { + return (value1 + value2 + value3) / 3.0; + } + +vec4 straight_line(vec4 p0, vec4 p1, vec4 p2, vec4 p3) + { + return pixel_is_brighter(p2,p0) ? mix(p2,p3,0.5) : mix(p0,p1,0.5); + } + +vec4 corner(vec4 p0, vec4 p1, vec4 p2, vec4 p3) + { + return pixel_is_brighter(p1,p0) ? mix3(p1,p2,p3) : mix3(p0,p1,p2); + } + +vec4 interpolate_diagonal(vec4 a, vec4 b, vec4 c, vec4 d) + { + // a b + // c d + + vec3 a_yuv = pixel_to_yuv(a); + vec3 b_yuv = pixel_to_yuv(b); + vec3 c_yuv = pixel_to_yuv(c); + vec3 d_yuv = pixel_to_yuv(d); + + bool ad = yuvs_are_similar(a_yuv,d_yuv); + bool bc = yuvs_are_similar(b_yuv,c_yuv); + bool ab = yuvs_are_similar(a_yuv,b_yuv); + bool cd = yuvs_are_similar(c_yuv,d_yuv); + bool ac = yuvs_are_similar(a_yuv,c_yuv); + bool bd = yuvs_are_similar(b_yuv,d_yuv); + + if (ad && cd && ab) // all pixels are equal? + return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) ); + + else if (ac && cd && ! ab) // corner 1? + return corner(b,a,d,c); + else if (bd && cd && ! ab) // corner 2? + return corner(a,b,c,d); + else if (ac && ab && ! bd) // corner 3? + return corner(d,c,b,a); + else if (ab && bd && ! ac) // corner 4? + return corner(c,a,d,b); + + else if (ad && (!bc || pixel_is_brighter(b,a))) // diagonal line 1? + return mix(a,d,0.5); + else if (bc && (!ad || pixel_is_brighter(a,b))) // diagonal line 2? + return mix(b,c,0.5); + + else if (ab) // horizontal line 1? + return straight_line(a,b,c,d); + else if (cd) // horizontal line 2? + return straight_line(c,d,a,b); + + else if (ac) // horizontal line 3? + return straight_line(a,c,b,d); + else if (bd) // horizontal line 4? + return straight_line(b,d,a,c); + + return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) ); + } + +void main() + { + ivec2 pixel_coords2 = ivec2(texCoord * vec2(screen_res) * 2); + ivec2 pixel_coords = pixel_coords2 / 2; + + bool x_even = mod(pixel_coords2.x,2) == 0; + bool y_even = mod(pixel_coords2.y,2) == 0; + + if (x_even) + { + if (y_even) + { + + gl_FragColor = interpolate_diagonal( + texel_fetch(tex, pixel_coords + ivec2(-1,-1)), + texel_fetch(tex, pixel_coords + ivec2(0,-1)), + texel_fetch(tex, pixel_coords + ivec2(-1,0)), + texel_fetch(tex, pixel_coords + ivec2(0,0)) + ); + + } + else + { + gl_FragColor = interpolate_nondiagonal + ( + texel_fetch(tex, pixel_coords + ivec2(-1,0)), + texel_fetch(tex, pixel_coords) + ); + } + } + else if (y_even) + { + gl_FragColor = interpolate_nondiagonal + ( + texel_fetch(tex, pixel_coords + ivec2(0,-1)), + texel_fetch(tex, pixel_coords) + ); + } + else + gl_FragColor = texel_fetch(tex, pixel_coords); + } diff --git a/res/shaders/fish.shader/manifest.ini b/res/shaders/fish.shader/manifest.ini new file mode 100644 index 000000000..0a47be032 --- /dev/null +++ b/res/shaders/fish.shader/manifest.ini @@ -0,0 +1,16 @@ +[shader] +name=fish +author=Drummyfish +description=Attempts to keep thin lines thin. +passes=1 + +[pass.0] +fragmentShader=fish.fs +integerScaling=1 + +[pass.0.uniform.similarity_threshold] +type=float +default=0.2 +readableName=Similarity Threshold +min=0 +max=1 From 3851a8230bdf641ba9df41691bd76c6366b9a0df Mon Sep 17 00:00:00 2001 From: drummyfish Date: Mon, 15 May 2017 20:42:38 +0200 Subject: [PATCH 123/159] comment fix --- res/shaders/fish.shader/fish.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/shaders/fish.shader/fish.fs b/res/shaders/fish.shader/fish.fs index f54a3bf8b..7d750fd7b 100644 --- a/res/shaders/fish.shader/fish.fs +++ b/res/shaders/fish.shader/fish.fs @@ -130,9 +130,9 @@ vec4 interpolate_diagonal(vec4 a, vec4 b, vec4 c, vec4 d) else if (cd) // horizontal line 2? return straight_line(c,d,a,b); - else if (ac) // horizontal line 3? + else if (ac) // vertical line 1? return straight_line(a,c,b,d); - else if (bd) // horizontal line 4? + else if (bd) // vertical line 2? return straight_line(b,d,a,c); return( mix(mix(a,b,0.5), mix(c,d,0.5), 0.5) ); From 78e4083a562a1ce34a29dee8ecdb315401c9fb1a Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Mon, 15 May 2017 19:27:29 -0400 Subject: [PATCH 124/159] Qt: New library system (#549) --- CHANGES | 1 + src/platform/qt/ArchiveInspector.cpp | 7 +- src/platform/qt/ArchiveInspector.ui | 6 +- src/platform/qt/CMakeLists.txt | 11 +- src/platform/qt/LibraryModel.cpp | 320 ------------------ src/platform/qt/LibraryModel.h | 115 ------- src/platform/qt/LibraryView.cpp | 58 ---- src/platform/qt/LibraryView.h | 45 --- src/platform/qt/LibraryView.ui | 49 --- src/platform/qt/SettingsView.cpp | 1 + src/platform/qt/SettingsView.ui | 77 +++-- src/platform/qt/Window.cpp | 17 +- src/platform/qt/Window.h | 4 +- src/platform/qt/library/LibraryController.cpp | 193 +++++++++++ src/platform/qt/library/LibraryController.h | 126 +++++++ src/platform/qt/library/LibraryGrid.cpp | 79 +++++ src/platform/qt/library/LibraryGrid.h | 50 +++ src/platform/qt/library/LibraryTree.cpp | 179 ++++++++++ src/platform/qt/library/LibraryTree.h | 57 ++++ src/platform/qt/utils.cpp | 40 +++ src/platform/qt/utils.h | 20 ++ 21 files changed, 820 insertions(+), 635 deletions(-) delete mode 100644 src/platform/qt/LibraryModel.cpp delete mode 100644 src/platform/qt/LibraryModel.h delete mode 100644 src/platform/qt/LibraryView.cpp delete mode 100644 src/platform/qt/LibraryView.h delete mode 100644 src/platform/qt/LibraryView.ui create mode 100644 src/platform/qt/library/LibraryController.cpp create mode 100644 src/platform/qt/library/LibraryController.h create mode 100644 src/platform/qt/library/LibraryGrid.cpp create mode 100644 src/platform/qt/library/LibraryGrid.h create mode 100644 src/platform/qt/library/LibraryTree.cpp create mode 100644 src/platform/qt/library/LibraryTree.h create mode 100644 src/platform/qt/utils.cpp create mode 100644 src/platform/qt/utils.h diff --git a/CHANGES b/CHANGES index 3128c6f46..a3dfca799 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Features: - GB: Video/audio channel enabling/disabling - Add option to lock video to integer scaling - Video log recording for testing and bug reporting + - Library view Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/src/platform/qt/ArchiveInspector.cpp b/src/platform/qt/ArchiveInspector.cpp index a9d346121..200abac2a 100644 --- a/src/platform/qt/ArchiveInspector.cpp +++ b/src/platform/qt/ArchiveInspector.cpp @@ -13,11 +13,12 @@ ArchiveInspector::ArchiveInspector(const QString& filename, QWidget* parent) : QDialog(parent) { m_ui.setupUi(this); - connect(m_ui.archiveView, &LibraryView::doneLoading, [this]() { + connect(m_ui.archiveView, &LibraryController::doneLoading, [this]() { m_ui.loading->hide(); }); - connect(m_ui.archiveView, SIGNAL(accepted()), this, SIGNAL(accepted())); - m_ui.archiveView->setDirectory(filename); + connect(m_ui.archiveView, &LibraryController::startGame, this, &ArchiveInspector::accepted); + m_ui.archiveView->setViewStyle(LibraryStyle::STYLE_LIST); + m_ui.archiveView->addDirectory(filename); } VFile* ArchiveInspector::selectedVFile() const { diff --git a/src/platform/qt/ArchiveInspector.ui b/src/platform/qt/ArchiveInspector.ui index 55e0cbe6e..405c2748e 100644 --- a/src/platform/qt/ArchiveInspector.ui +++ b/src/platform/qt/ArchiveInspector.ui @@ -29,15 +29,15 @@ - + - QGBA::LibraryView + QGBA::LibraryController QWidget -
LibraryView.h
+
library/LibraryController.h
1
diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 464b12652..25069a1dc 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -105,6 +105,7 @@ set(SOURCE_FILES Swatch.cpp TilePainter.cpp TileView.cpp + utils.cpp Window.cpp VFileDevice.cpp VideoView.cpp) @@ -117,7 +118,6 @@ set(UI_FILES DebuggerConsole.ui GIFView.ui IOViewer.ui - LibraryView.ui LoadSaveState.ui LogView.ui MemoryView.ui @@ -138,8 +138,6 @@ set(GBA_SRC set(GB_SRC GBOverride.cpp) -qt5_wrap_ui(UI_SRC ${UI_FILES}) - set(QT_LIBRARIES) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5,libqt5opengl5") @@ -189,8 +187,9 @@ endif() if(USE_SQLITE3) list(APPEND SOURCE_FILES ArchiveInspector.cpp - LibraryModel.cpp - LibraryView.cpp) + library/LibraryController.cpp + library/LibraryGrid.cpp + library/LibraryTree.cpp) endif() qt5_add_resources(RESOURCES resources.qrc) @@ -239,6 +238,8 @@ if(Qt5LinguistTools_FOUND) list(APPEND RESOURCES ${TRANSLATION_RESOURCES}) endif() +qt5_wrap_ui(UI_SRC ${UI_FILES}) + add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES}) set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}") diff --git a/src/platform/qt/LibraryModel.cpp b/src/platform/qt/LibraryModel.cpp deleted file mode 100644 index bea45c4d6..000000000 --- a/src/platform/qt/LibraryModel.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* Copyright (c) 2013-2016 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 "LibraryModel.h" - -#include - -#include - -using namespace QGBA; - -Q_DECLARE_METATYPE(mLibraryEntry); - -QMap LibraryModel::s_handles; -QMap LibraryModel::s_columns; - -LibraryModel::LibraryModel(const QString& path, QObject* parent) - : QAbstractItemModel(parent) -{ - if (s_columns.empty()) { - s_columns["name"] = { - tr("Name"), - [](const mLibraryEntry& e) -> QString { - if (e.title) { - return QString::fromUtf8(e.title); - } - return QString::fromUtf8(e.filename); - } - }; - s_columns["filename"] = { - tr("Filename"), - [](const mLibraryEntry& e) -> QString { - return QString::fromUtf8(e.filename); - } - }; - s_columns["size"] = { - tr("Size"), - [](const mLibraryEntry& e) -> QString { - double size = e.filesize; - QString unit = "B"; - if (size >= 1024.0) { - size /= 1024.0; - unit = "kiB"; - } - if (size >= 1024.0) { - size /= 1024.0; - unit = "MiB"; - } - return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit); - }, - Qt::AlignRight - }; - s_columns["platform"] = { - tr("Platform"), - [](const mLibraryEntry& e) -> QString { - int platform = e.platform; - switch (platform) { -#ifdef M_CORE_GBA - case PLATFORM_GBA: - return tr("GBA"); -#endif -#ifdef M_CORE_GB - case PLATFORM_GB: - return tr("GB"); -#endif - default: - return tr("?"); - } - } - }; - s_columns["location"] = { - tr("Location"), - [](const mLibraryEntry& e) -> QString { - return QString::fromUtf8(e.base); - } - }; - s_columns["crc32"] = { - tr("CRC32"), - [](const mLibraryEntry& e) -> QString { - return QString("%0").arg(e.crc32, 8, 16, QChar('0')); - } - }; - } - if (!path.isNull()) { - if (s_handles.contains(path)) { - m_library = s_handles[path]; - m_library->ref(); - } else { - m_library = new LibraryHandle(mLibraryLoad(path.toUtf8().constData()), path); - if (m_library->library) { - s_handles[path] = m_library; - } else { - delete m_library; - m_library = new LibraryHandle(mLibraryCreateEmpty()); - } - } - } else { - m_library = new LibraryHandle(mLibraryCreateEmpty()); - } - mLibraryListingInit(&m_listings, 0); - memset(&m_constraints, 0, sizeof(m_constraints)); - m_constraints.platform = PLATFORM_NONE; - m_columns.append(s_columns["name"]); - m_columns.append(s_columns["location"]); - m_columns.append(s_columns["platform"]); - m_columns.append(s_columns["size"]); - m_columns.append(s_columns["crc32"]); - - connect(m_library->loader, SIGNAL(directoryLoaded(const QString&)), this, SLOT(directoryLoaded(const QString&))); -} - -LibraryModel::~LibraryModel() { - clearConstraints(); - mLibraryListingDeinit(&m_listings); - if (!m_library->deref()) { - s_handles.remove(m_library->path); - delete m_library; - } -} - -void LibraryModel::loadDirectory(const QString& path) { - m_queue.append(path); - QMetaObject::invokeMethod(m_library->loader, "loadDirectory", Q_ARG(const QString&, path)); -} - -bool LibraryModel::entryAt(int row, mLibraryEntry* out) const { - if (mLibraryListingSize(&m_listings) <= row) { - return false; - } - *out = *mLibraryListingGetConstPointer(&m_listings, row); - return true; -} - -VFile* LibraryModel::openVFile(const QModelIndex& index) const { - mLibraryEntry entry; - if (!entryAt(index.row(), &entry)) { - return nullptr; - } - return mLibraryOpenVFile(m_library->library, &entry); -} - -QString LibraryModel::filename(const QModelIndex& index) const { - mLibraryEntry entry; - if (!entryAt(index.row(), &entry)) { - return QString(); - } - return QString::fromUtf8(entry.filename); -} - -QString LibraryModel::location(const QModelIndex& index) const { - mLibraryEntry entry; - if (!entryAt(index.row(), &entry)) { - return QString(); - } - return QString::fromUtf8(entry.base); -} - -QVariant LibraryModel::data(const QModelIndex& index, int role) const { - if (!index.isValid()) { - return QVariant(); - } - mLibraryEntry entry; - if (!entryAt(index.row(), &entry)) { - return QVariant(); - } - if (role == Qt::UserRole) { - return QVariant::fromValue(entry); - } - if (index.column() >= m_columns.count()) { - return QVariant(); - } - switch (role) { - case Qt::DisplayRole: - return m_columns[index.column()].value(entry); - case Qt::SizeHintRole: { - QFontMetrics fm((QFont())); - return fm.size(Qt::TextSingleLine, m_columns[index.column()].value(entry)); - } - case Qt::TextAlignmentRole: - return m_columns[index.column()].alignment; - default: - return QVariant(); - } -} - -QVariant LibraryModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (role != Qt::DisplayRole) { - return QAbstractItemModel::headerData(section, orientation, role); - } - if (orientation == Qt::Horizontal) { - if (section >= m_columns.count()) { - return QVariant(); - } - return m_columns[section].name; - } - return section; -} - -QModelIndex LibraryModel::index(int row, int column, const QModelIndex& parent) const { - if (parent.isValid()) { - return QModelIndex(); - } - return createIndex(row, column, nullptr); -} - -QModelIndex LibraryModel::parent(const QModelIndex&) const { - return QModelIndex(); -} - -int LibraryModel::columnCount(const QModelIndex& parent) const { - if (parent.isValid()) { - return 0; - } - return m_columns.count(); -} - -int LibraryModel::rowCount(const QModelIndex& parent) const { - if (parent.isValid()) { - return 0; - } - return mLibraryCount(m_library->library, &m_constraints); -} - -void LibraryModel::attachGameDB(const NoIntroDB* gameDB) { - mLibraryAttachGameDB(m_library->library, gameDB); -} - -void LibraryModel::constrainBase(const QString& path) { - clearConstraints(); - if (m_constraints.base) { - free(const_cast(m_constraints.base)); - } - m_constraints.base = strdup(path.toUtf8().constData()); - reload(); -} - -void LibraryModel::clearConstraints() { - if (m_constraints.base) { - free(const_cast(m_constraints.base)); - } - if (m_constraints.filename) { - free(const_cast(m_constraints.filename)); - } - if (m_constraints.title) { - free(const_cast(m_constraints.title)); - } - memset(&m_constraints, 0, sizeof(m_constraints)); - size_t i; - for (i = 0; i < mLibraryListingSize(&m_listings); ++i) { - mLibraryEntryFree(mLibraryListingGetPointer(&m_listings, i)); - } - mLibraryListingClear(&m_listings); -} - -void LibraryModel::reload() { - mLibraryGetEntries(m_library->library, &m_listings, 0, 0, m_constraints.base ? &m_constraints : nullptr); -} - -void LibraryModel::directoryLoaded(const QString& path) { - m_queue.removeOne(path); - beginResetModel(); - endResetModel(); - if (m_queue.empty()) { - emit doneLoading(); - } -} - -LibraryModel::LibraryColumn::LibraryColumn() { -} - -LibraryModel::LibraryColumn::LibraryColumn(const QString& name, std::function value, int alignment) - : name(name) - , value(value) - , alignment(alignment) -{ -} - -LibraryModel::LibraryHandle::LibraryHandle(mLibrary* lib, const QString& p) - : library(lib) - , loader(new LibraryLoader(library)) - , path(p) - , m_ref(1) -{ - if (!library) { - return; - } - loader->moveToThread(&m_loaderThread); - m_loaderThread.setObjectName("Library Loader Thread"); - m_loaderThread.start(); -} - -LibraryModel::LibraryHandle::~LibraryHandle() { - m_loaderThread.quit(); - m_loaderThread.wait(); - if (library) { - mLibraryDestroy(library); - } -} - -void LibraryModel::LibraryHandle::ref() { - ++m_ref; -} - -bool LibraryModel::LibraryHandle::deref() { - --m_ref; - return m_ref > 0; -} - -LibraryLoader::LibraryLoader(mLibrary* library, QObject* parent) - : QObject(parent) - , m_library(library) -{ -} - -void LibraryLoader::loadDirectory(const QString& path) { - mLibraryLoadDirectory(m_library, path.toUtf8().constData()); - emit directoryLoaded(path); -} diff --git a/src/platform/qt/LibraryModel.h b/src/platform/qt/LibraryModel.h deleted file mode 100644 index 970b08f87..000000000 --- a/src/platform/qt/LibraryModel.h +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (c) 2013-2016 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 QGBA_LIBRARY_MODEL -#define QGBA_LIBRARY_MODEL - -#include -#include -#include - -#include - -#include - -struct VDir; -struct VFile; -struct NoIntroDB; - -namespace QGBA { - -class LibraryLoader; -class LibraryModel : public QAbstractItemModel { -Q_OBJECT - -public: - LibraryModel(const QString& path, QObject* parent = nullptr); - virtual ~LibraryModel(); - - bool entryAt(int row, mLibraryEntry* out) const; - VFile* openVFile(const QModelIndex& index) const; - QString filename(const QModelIndex& index) const; - QString location(const QModelIndex& index) const; - - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - - virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; - virtual QModelIndex parent(const QModelIndex& index) const override; - - virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; - virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; - - void attachGameDB(const NoIntroDB* gameDB); - -signals: - void doneLoading(); - -public slots: - void loadDirectory(const QString& path); - - void constrainBase(const QString& path); - void clearConstraints(); - void reload(); - -private slots: - void directoryLoaded(const QString& path); - -private: - struct LibraryColumn { - LibraryColumn(); - LibraryColumn(const QString&, std::function, int = Qt::AlignLeft); - QString name; - std::function value; - int alignment; - }; - - class LibraryHandle { - public: - LibraryHandle(mLibrary*, const QString& path = QString()); - ~LibraryHandle(); - - mLibrary* const library; - LibraryLoader* const loader; - const QString path; - - void ref(); - bool deref(); - - private: - QThread m_loaderThread; - size_t m_ref; - }; - - LibraryHandle* m_library; - static QMap s_handles; - - mLibraryEntry m_constraints; - mLibraryListing m_listings; - QStringList m_queue; - - QList m_columns; - static QMap s_columns; -}; - -class LibraryLoader : public QObject { -Q_OBJECT - -public: - LibraryLoader(mLibrary* library, QObject* parent = nullptr); - -public slots: - void loadDirectory(const QString& path); - -signals: - void directoryLoaded(const QString& path); - -private: - mLibrary* m_library; -}; - -} - -#endif diff --git a/src/platform/qt/LibraryView.cpp b/src/platform/qt/LibraryView.cpp deleted file mode 100644 index 44b447e0a..000000000 --- a/src/platform/qt/LibraryView.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* 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 "LibraryView.h" - -#include - -#include "ConfigController.h" -#include "GBAApp.h" - -using namespace QGBA; - -LibraryView::LibraryView(QWidget* parent) - : QWidget(parent) - , m_model(ConfigController::configDir() + "/library.sqlite3") -{ - m_ui.setupUi(this); - m_model.attachGameDB(GBAApp::app()->gameDB()); - connect(&m_model, SIGNAL(doneLoading()), this, SIGNAL(doneLoading())); - connect(&m_model, SIGNAL(doneLoading()), this, SLOT(resizeColumns())); - connect(m_ui.listing, SIGNAL(activated(const QModelIndex&)), this, SIGNAL(accepted())); - m_ui.listing->horizontalHeader()->setSectionsMovable(true); - m_ui.listing->setModel(&m_model); - m_ui.listing->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - m_model.reload(); - resizeColumns(); -} - -void LibraryView::setDirectory(const QString& filename) { - m_model.loadDirectory(filename); - m_model.constrainBase(filename); -} - -void LibraryView::addDirectory(const QString& filename) { - m_model.loadDirectory(filename); -} - -VFile* LibraryView::selectedVFile() const { - QModelIndex index = m_ui.listing->selectionModel()->currentIndex(); - if (!index.isValid()) { - return nullptr; - } - return m_model.openVFile(index); -} - -QPair LibraryView::selectedPath() const { - QModelIndex index = m_ui.listing->selectionModel()->currentIndex(); - if (!index.isValid()) { - return qMakePair(QString(), QString()); - } - return qMakePair(m_model.filename(index), m_model.location(index)); -} - -void LibraryView::resizeColumns() { - m_ui.listing->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); -} diff --git a/src/platform/qt/LibraryView.h b/src/platform/qt/LibraryView.h deleted file mode 100644 index d56ea7ada..000000000 --- a/src/platform/qt/LibraryView.h +++ /dev/null @@ -1,45 +0,0 @@ -/* 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 QGBA_LIBRARY_VIEW -#define QGBA_LIBRARY_VIEW - -#include "LibraryModel.h" - -#include "ui_LibraryView.h" - -struct VFile; - -namespace QGBA { - -class LibraryView : public QWidget { -Q_OBJECT - -public: - LibraryView(QWidget* parent = nullptr); - - VFile* selectedVFile() const; - QPair selectedPath() const; - -signals: - void doneLoading(); - void accepted(); - -public slots: - void setDirectory(const QString&); - void addDirectory(const QString&); - -private slots: - void resizeColumns(); - -private: - Ui::LibraryView m_ui; - - LibraryModel m_model; -}; - -} - -#endif diff --git a/src/platform/qt/LibraryView.ui b/src/platform/qt/LibraryView.ui deleted file mode 100644 index 96c2a2bf6..000000000 --- a/src/platform/qt/LibraryView.ui +++ /dev/null @@ -1,49 +0,0 @@ - - - LibraryView - - - - 0 - 0 - 400 - 300 - - - - Library - - - - 0 - - - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SelectRows - - - false - - - true - - - false - - - 0 - - - - - - - - diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 9f5b7527c..6848e4cd6 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -204,6 +204,7 @@ void SettingsView::updateConfig() { saveSetting("savestatePath", m_ui.savestatePath); saveSetting("screenshotPath", m_ui.screenshotPath); saveSetting("patchPath", m_ui.patchPath); + saveSetting("libraryStyle", m_ui.libraryStyle->currentIndex()); saveSetting("showLibrary", m_ui.showLibrary); saveSetting("preload", m_ui.preload); diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index caabf1c4c..8fca6efaf 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -398,28 +398,28 @@ - - + + - Allow opposing input directions - - - - - - - Suspend screensaver + Show when no game open true - - - - Pause when inactive - + + + + + List view + + + + + Tree view + + @@ -429,21 +429,7 @@ - - - - Show when no game open - - - - - - - Qt::Horizontal - - - - + false @@ -453,6 +439,37 @@ + + + + Qt::Horizontal + + + + + + + Allow opposing input directions + + + + + + + Suspend screensaver + + + true + + + + + + + Pause when inactive + + + diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 98bfa13b1..d6f24e2e5 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -14,10 +14,12 @@ #include #include -#include "AboutScreen.h" #ifdef USE_SQLITE3 #include "ArchiveInspector.h" +#include "library/LibraryController.h" #endif + +#include "AboutScreen.h" #include "CheatsView.h" #include "ConfigController.h" #include "DebuggerConsole.h" @@ -108,7 +110,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) i = m_savedScale; } #ifdef USE_SQLITE3 - m_libraryView = new LibraryView(); + m_libraryView = new LibraryController(nullptr, ConfigController::configDir() + "/library.sqlite3", m_config); ConfigOption* showLibrary = m_config->addOption("showLibrary"); showLibrary->connect([this](const QVariant& value) { if (value.toBool()) { @@ -122,12 +124,17 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) } }, this); m_config->updateOption("showLibrary"); + ConfigOption* libraryStyle = m_config->addOption("libraryStyle"); + libraryStyle->connect([this](const QVariant& value) { + m_libraryView->setViewStyle(static_cast(value.toInt())); + }, this); + m_config->updateOption("libraryStyle"); - connect(m_libraryView, &LibraryView::accepted, [this]() { + connect(m_libraryView, &LibraryController::startGame, [this]() { VFile* output = m_libraryView->selectedVFile(); - QPair path = m_libraryView->selectedPath(); if (output) { - m_controller->loadGame(output, path.first, path.second); + QPair path = m_libraryView->selectedPath(); + m_controller->loadGame(output, path.second, path.first); } }); #elif defined(M_CORE_GBA) diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index bd24621bd..c9a5d465c 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -28,7 +28,7 @@ class Display; class GameController; class GDBController; class GIFView; -class LibraryView; +class LibraryController; class LogView; class ShaderSelector; class ShortcutController; @@ -199,7 +199,7 @@ private: #endif #ifdef USE_SQLITE3 - LibraryView* m_libraryView; + LibraryController* m_libraryView; #endif }; diff --git a/src/platform/qt/library/LibraryController.cpp b/src/platform/qt/library/LibraryController.cpp new file mode 100644 index 000000000..ecb8a1fab --- /dev/null +++ b/src/platform/qt/library/LibraryController.cpp @@ -0,0 +1,193 @@ +/* Copyright (c) 2014-2017 waddlesplash + * + * 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 "LibraryController.h" + +#include "../GBAApp.h" +#include "LibraryGrid.h" +#include "LibraryTree.h" + +namespace QGBA { + +LibraryEntry::LibraryEntry(mLibraryEntry* entry) + : entry(entry) + , m_fullpath(QString("%1/%2").arg(entry->base, entry->filename)) +{ +} + +void AbstractGameList::addEntries(QList items) { + for (LibraryEntryRef o : items) { + addEntry(o); + } +} +void AbstractGameList::removeEntries(QList items) { + for (LibraryEntryRef o : items) { + addEntry(o); + } +} + +LibraryLoaderThread::LibraryLoaderThread(QObject* parent) + : QThread(parent) +{ +} + +void LibraryLoaderThread::run() { + mLibraryLoadDirectory(m_library, m_directory.toUtf8().constData()); + m_directory = QString(); +} + +LibraryController::LibraryController(QWidget* parent, const QString& path, ConfigController* config) + : QStackedWidget(parent) + , m_config(config) +{ + mLibraryListingInit(&m_listing, 0); + + if (!path.isNull()) { + m_library = mLibraryLoad(path.toUtf8().constData()); + } else { + m_library = mLibraryCreateEmpty(); + } + + mLibraryAttachGameDB(m_library, GBAApp::app()->gameDB()); + + m_libraryTree = new LibraryTree(this); + addWidget(m_libraryTree->widget()); + + m_libraryGrid = new LibraryGrid(this); + addWidget(m_libraryGrid->widget()); + + connect(&m_loaderThread, &QThread::finished, this, &LibraryController::refresh, Qt::QueuedConnection); + + setViewStyle(LibraryStyle::STYLE_LIST); + refresh(); +} + +LibraryController::~LibraryController() { + mLibraryListingDeinit(&m_listing); + + if (m_loaderThread.isRunning()) { + m_loaderThread.wait(); + } + if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { + m_library = m_loaderThread.m_library; + m_loaderThread.m_library = nullptr; + } + if (m_library) { + mLibraryDestroy(m_library); + } +} + +void LibraryController::setViewStyle(LibraryStyle newStyle) { + m_currentStyle = newStyle; + + AbstractGameList* newCurrentList = nullptr; + if (newStyle == LibraryStyle::STYLE_LIST || newStyle == LibraryStyle::STYLE_TREE) { + newCurrentList = m_libraryTree; + } else { + newCurrentList = m_libraryGrid; + } + newCurrentList->selectEntry(selectedEntry()); + newCurrentList->setViewStyle(newStyle); + setCurrentWidget(newCurrentList->widget()); + m_currentList = newCurrentList; +} + +void LibraryController::selectEntry(LibraryEntryRef entry) { + if (!m_currentList) { + return; + } + m_currentList->selectEntry(entry); +} + +LibraryEntryRef LibraryController::selectedEntry() { + if (!m_currentList) { + return LibraryEntryRef(); + } + return m_currentList->selectedEntry(); +} + +VFile* LibraryController::selectedVFile() { + LibraryEntryRef entry = selectedEntry(); + if (entry) { + return mLibraryOpenVFile(m_library, entry->entry); + } else { + return nullptr; + } +} + +QPair LibraryController::selectedPath() { + LibraryEntryRef e = selectedEntry(); + return e ? qMakePair(e->base(), e->filename()) : qMakePair("", ""); +} + +void LibraryController::addDirectory(const QString& dir) { + m_loaderThread.m_directory = dir; + m_loaderThread.m_library = m_library; + // The m_loaderThread temporarily owns the library + m_library = nullptr; + m_loaderThread.start(); +} + +void LibraryController::refresh() { + if (!m_library) { + if (!m_loaderThread.isRunning() && m_loaderThread.m_library) { + m_library = m_loaderThread.m_library; + m_loaderThread.m_library = nullptr; + } else { + return; + } + } + + setDisabled(true); + + QStringList allEntries; + QList newEntries; + + mLibraryListingClear(&m_listing); + mLibraryGetEntries(m_library, &m_listing, 0, 0, nullptr); + for (size_t i = 0; i < mLibraryListingSize(&m_listing); i++) { + mLibraryEntry* entry = mLibraryListingGetPointer(&m_listing, i); + QString fullpath = QString("%1/%2").arg(entry->base, entry->filename); + if (m_entries.contains(fullpath)) { + m_entries.value(fullpath)->entry = entry; + } else { + LibraryEntryRef libentry = std::make_shared(entry); + m_entries.insert(fullpath, libentry); + newEntries.append(libentry); + } + allEntries.append(fullpath); + } + + // Check for entries that were removed + QList removedEntries; + for (QString& path : m_entries.keys()) { + if (!allEntries.contains(path)) { + removedEntries.append(m_entries.value(path)); + m_entries.remove(path); + } + } + + m_libraryTree->addEntries(newEntries); + m_libraryGrid->addEntries(newEntries); + + m_libraryTree->removeEntries(removedEntries); + m_libraryGrid->removeEntries(removedEntries); + + setDisabled(false); + selectLastBootedGame(); + emit doneLoading(); +} + +void LibraryController::selectLastBootedGame() { + if (!m_config) { + return; + } + const QString lastfile = m_config->getMRU().first(); + if (m_entries.contains(lastfile)) { + selectEntry(m_entries.value(lastfile)); + } +} + +} diff --git a/src/platform/qt/library/LibraryController.h b/src/platform/qt/library/LibraryController.h new file mode 100644 index 000000000..3cc82c8b5 --- /dev/null +++ b/src/platform/qt/library/LibraryController.h @@ -0,0 +1,126 @@ +/* Copyright (c) 2014-2017 waddlesplash + * + * 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 QGBA_LIBRARY_CONTROLLER +#define QGBA_LIBRARY_CONTROLLER + +#include + +#include +#include +#include +#include + +#include + +namespace QGBA { + +// Predefinitions +class LibraryGrid; +class LibraryTree; +class ConfigController; + +enum class LibraryStyle { + STYLE_LIST = 0, + STYLE_TREE, + STYLE_GRID, + STYLE_ICON +}; + +class LibraryEntry final { +public: + LibraryEntry(mLibraryEntry* entry); + + QString displayTitle() const { return title().isNull() ? filename() : title(); } + + QString base() const { return QString(entry->base); } + QString filename() const { return QString(entry->filename); } + QString fullpath() const { return m_fullpath; } + QString title() const { return QString(entry->title); } + QByteArray internalTitle() const { return QByteArray(entry->internalTitle); } + QByteArray internalCode() const { return QByteArray(entry->internalCode); } + mPlatform platform() const { return entry->platform; } + size_t filesize() const { return entry->filesize; } + uint32_t crc32() const { return entry->crc32; } + + const mLibraryEntry* entry; +private: + const QString m_fullpath; +}; +typedef std::shared_ptr LibraryEntryRef; + +class AbstractGameList { +public: + virtual LibraryEntryRef selectedEntry() = 0; + virtual void selectEntry(LibraryEntryRef game) = 0; + + virtual void setViewStyle(LibraryStyle newStyle) = 0; + + virtual void addEntry(LibraryEntryRef item) = 0; + virtual void addEntries(QList items); + + virtual void removeEntry(LibraryEntryRef item) = 0; + virtual void removeEntries(QList items); + + virtual QWidget* widget() = 0; +}; + +class LibraryLoaderThread final : public QThread { +Q_OBJECT + +public: + LibraryLoaderThread(QObject* parent = nullptr); + + mLibrary* m_library = nullptr; + QString m_directory; + +protected: + virtual void run() override; +}; + +class LibraryController final : public QStackedWidget { +Q_OBJECT + +public: + LibraryController(QWidget* parent = nullptr, const QString& path = QString(), + ConfigController* config = nullptr); + ~LibraryController(); + + LibraryStyle viewStyle() const { return m_currentStyle; } + void setViewStyle(LibraryStyle newStyle); + + void selectEntry(LibraryEntryRef entry); + LibraryEntryRef selectedEntry(); + VFile* selectedVFile(); + QPair selectedPath(); + + void selectLastBootedGame(); + + void addDirectory(const QString& dir); + +signals: + void startGame(); + void doneLoading(); + +private slots: + void refresh(); + +private: + ConfigController* m_config = nullptr; + LibraryLoaderThread m_loaderThread; + mLibrary* m_library = nullptr; + mLibraryListing m_listing; + QMap m_entries; + + LibraryStyle m_currentStyle; + AbstractGameList* m_currentList = nullptr; + + LibraryGrid* m_libraryGrid = nullptr; + LibraryTree* m_libraryTree = nullptr; +}; + +} + +#endif diff --git a/src/platform/qt/library/LibraryGrid.cpp b/src/platform/qt/library/LibraryGrid.cpp new file mode 100644 index 000000000..d4e4acacf --- /dev/null +++ b/src/platform/qt/library/LibraryGrid.cpp @@ -0,0 +1,79 @@ +/* Copyright (c) 2014-2017 waddlesplash + * + * 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 "LibraryGrid.h" + +namespace QGBA { + +LibraryGrid::LibraryGrid(LibraryController* parent) + : m_widget(new QListWidget(parent)) +{ + m_widget->setObjectName("LibraryGrid"); + m_widget->setWrapping(true); + m_widget->setResizeMode(QListView::Adjust); + m_widget->setUniformItemSizes(true); + setViewStyle(LibraryStyle::STYLE_GRID); + + QObject::connect(m_widget, &QListWidget::itemActivated, parent, &LibraryController::startGame); +} + +LibraryGrid::~LibraryGrid() { + delete m_widget; +} + +LibraryEntryRef LibraryGrid::selectedEntry() { + if (!m_widget->selectedItems().empty()) { + return m_items.key(m_widget->selectedItems().at(0)); + } else { + return LibraryEntryRef(); + } +} + +void LibraryGrid::selectEntry(LibraryEntryRef game) { + if (!game) { + return; + } + if (!m_widget->selectedItems().empty()) { + m_widget->selectedItems().at(0)->setSelected(false); + } + m_items.value(game)->setSelected(true); +} + +void LibraryGrid::setViewStyle(LibraryStyle newStyle) { + if (newStyle == LibraryStyle::STYLE_GRID) { + m_currentStyle = LibraryStyle::STYLE_GRID; + m_widget->setIconSize(QSize(GRID_BANNER_WIDTH, GRID_BANNER_HEIGHT)); + m_widget->setViewMode(QListView::IconMode); + } else { + m_currentStyle = LibraryStyle::STYLE_ICON; + m_widget->setIconSize(QSize(ICON_BANNER_WIDTH, ICON_BANNER_HEIGHT)); + m_widget->setViewMode(QListView::ListMode); + } + + // QListView resets this when you change the view mode, so let's set it again + m_widget->setDragEnabled(false); +} + +void LibraryGrid::addEntry(LibraryEntryRef item) { + if (m_items.contains(item)) { + return; + } + + QListWidgetItem* i = new QListWidgetItem; + i->setText(item->displayTitle()); + + m_widget->addItem(i); + m_items.insert(item, i); +} + +void LibraryGrid::removeEntry(LibraryEntryRef entry) { + if (!m_items.contains(entry)) { + return; + } + + delete m_items.take(entry); +} + +} diff --git a/src/platform/qt/library/LibraryGrid.h b/src/platform/qt/library/LibraryGrid.h new file mode 100644 index 000000000..98a0df630 --- /dev/null +++ b/src/platform/qt/library/LibraryGrid.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2014-2017 waddlesplash + * + * 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 QGBA_LIBRARY_GRID +#define QGBA_LIBRARY_GRID + +#include + +#include "LibraryController.h" + +namespace QGBA { + +class LibraryGrid final : public AbstractGameList { +public: + explicit LibraryGrid(LibraryController* parent = nullptr); + ~LibraryGrid(); + + // AbstractGameList stuff + virtual LibraryEntryRef selectedEntry() override; + virtual void selectEntry(LibraryEntryRef game) override; + + virtual void setViewStyle(LibraryStyle newStyle) override; + + virtual void addEntry(LibraryEntryRef item) override; + virtual void removeEntry(LibraryEntryRef entry) override; + + virtual QWidget* widget() override { return m_widget; } + +signals: + void startGame(); + +private: + QListWidget* m_widget; + + // Game banner image size + const quint32 GRID_BANNER_WIDTH = 320; + const quint32 GRID_BANNER_HEIGHT = 240; + + const quint32 ICON_BANNER_WIDTH = 64; + const quint32 ICON_BANNER_HEIGHT = 64; + + QMap m_items; + LibraryStyle m_currentStyle; +}; + +} + +#endif diff --git a/src/platform/qt/library/LibraryTree.cpp b/src/platform/qt/library/LibraryTree.cpp new file mode 100644 index 000000000..45b972d77 --- /dev/null +++ b/src/platform/qt/library/LibraryTree.cpp @@ -0,0 +1,179 @@ +/* Copyright (c) 2014-2017 waddlesplash + * + * 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 "LibraryTree.h" + +#include "utils.h" + +#include +#include + +namespace QGBA { + +class TreeWidgetItem : public QTreeWidgetItem { +public: + TreeWidgetItem(QTreeWidget* parent = nullptr) : QTreeWidgetItem(parent) {} + void setFilesize(size_t size); + + virtual bool operator<(const QTreeWidgetItem& other) const override; +protected: + size_t m_size = 0; +}; + +void TreeWidgetItem::setFilesize(size_t size) { + m_size = size; + setText(LibraryTree::COL_SIZE, niceSizeFormat(size)); +} + +bool TreeWidgetItem::operator<(const QTreeWidgetItem& other) const { + const int column = treeWidget()->sortColumn(); + return ((column == LibraryTree::COL_SIZE) ? + m_size < dynamic_cast(&other)->m_size : + QTreeWidgetItem::operator<(other)); +} + +LibraryTree::LibraryTree(LibraryController* parent) + : m_widget(new QTreeWidget(parent)) + , m_controller(parent) +{ + m_widget->setObjectName("LibraryTree"); + m_widget->setSortingEnabled(true); + m_widget->setAlternatingRowColors(true); + + QTreeWidgetItem* header = new QTreeWidgetItem({ + QApplication::translate("LibraryTree", "Name", nullptr), + QApplication::translate("LibraryTree", "Location", nullptr), + QApplication::translate("LibraryTree", "Platform", nullptr), + QApplication::translate("LibraryTree", "Size", nullptr), + QApplication::translate("LibraryTree", "CRC32", nullptr), + }); + header->setTextAlignment(3, Qt::AlignTrailing | Qt::AlignVCenter); + m_widget->setHeaderItem(header); + + setViewStyle(LibraryStyle::STYLE_TREE); + m_widget->sortByColumn(COL_NAME, Qt::AscendingOrder); + + QObject::connect(m_widget, &QTreeWidget::itemActivated, [this](QTreeWidgetItem* item, int) -> void { + if (!m_pathNodes.values().contains(item)) { + emit m_controller->startGame(); + } + }); +} + +void LibraryTree::resizeAllCols() { + for (int i = 0; i < m_widget->columnCount(); i++) { + m_widget->resizeColumnToContents(i); + } +} + +LibraryEntryRef LibraryTree::selectedEntry() { + if (!m_widget->selectedItems().empty()) { + return m_items.key(m_widget->selectedItems().at(0)); + } else { + return LibraryEntryRef(); + } +} + +void LibraryTree::selectEntry(LibraryEntryRef game) { + if (!game) { + return; + } + if (!m_widget->selectedItems().empty()) { + m_widget->selectedItems().at(0)->setSelected(false); + } + m_items.value(game)->setSelected(true); +} + +void LibraryTree::setViewStyle(LibraryStyle newStyle) { + if (newStyle == LibraryStyle::STYLE_LIST) { + m_currentStyle = LibraryStyle::STYLE_LIST; + m_widget->setIndentation(0); + rebuildTree(); + } else { + m_currentStyle = LibraryStyle::STYLE_TREE; + m_widget->setIndentation(20); + rebuildTree(); + } +} + +void LibraryTree::addEntries(QList items) { + m_deferredTreeRebuild = true; + AbstractGameList::addEntries(items); + m_deferredTreeRebuild = false; + rebuildTree(); +} + +void LibraryTree::addEntry(LibraryEntryRef item) { + if (m_items.contains(item)) { + return; + } + + QString folder = item->base(); + if (!m_pathNodes.contains(folder)) { + QTreeWidgetItem* i = new TreeWidgetItem; + i->setText(0, folder.section("/", -1)); + m_pathNodes.insert(folder, i); + if (m_currentStyle == LibraryStyle::STYLE_TREE) { + m_widget->addTopLevelItem(i); + } + } + + TreeWidgetItem* i = new TreeWidgetItem; + i->setText(COL_NAME, item->displayTitle()); + i->setText(COL_LOCATION, QDir::toNativeSeparators(item->base())); + i->setText(COL_PLATFORM, nicePlatformFormat(item->platform())); + i->setFilesize(item->filesize()); + i->setTextAlignment(COL_SIZE, Qt::AlignRight); + i->setText(COL_CRC32, QString("%0").arg(item->crc32(), 8, 16, QChar('0'))); + m_items.insert(item, i); + + rebuildTree(); +} + +void LibraryTree::removeEntry(LibraryEntryRef item) { + if (!m_items.contains(item)) { + return; + } + delete m_items.take(item); +} + +void LibraryTree::rebuildTree() { + if (m_deferredTreeRebuild) { + return; + } + + LibraryEntryRef currentGame = selectedEntry(); + + int count = m_widget->topLevelItemCount(); + for (int a = 0; a < count; a++) { + m_widget->takeTopLevelItem(0); + } + + for (QTreeWidgetItem* i : m_pathNodes.values()) { + count = i->childCount(); + for (int a = 0; a < count; a++) { + i->takeChild(0); + } + } + + if (m_currentStyle == LibraryStyle::STYLE_TREE) { + for (QTreeWidgetItem* i : m_pathNodes.values()) { + m_widget->addTopLevelItem(i); + } + for (QTreeWidgetItem* i : m_items.values()) { + m_pathNodes.value(m_items.key(i)->base())->addChild(i); + } + } else { + for (QTreeWidgetItem* i : m_items.values()) { + m_widget->addTopLevelItem(i); + } + } + + m_widget->expandAll(); + resizeAllCols(); + selectEntry(currentGame); +} + +} diff --git a/src/platform/qt/library/LibraryTree.h b/src/platform/qt/library/LibraryTree.h new file mode 100644 index 000000000..80e3b461d --- /dev/null +++ b/src/platform/qt/library/LibraryTree.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2014-2017 waddlesplash + * + * 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 QGBA_LIBRARY_TREE +#define QGBA_LIBRARY_TREE + +#include + +#include "LibraryController.h" + +namespace QGBA { + +class LibraryTree final : public AbstractGameList { + +public: + enum Columns { + COL_NAME = 0, + COL_LOCATION = 1, + COL_PLATFORM = 2, + COL_SIZE = 3, + COL_CRC32 = 4, + }; + + explicit LibraryTree(LibraryController* parent = nullptr); + ~LibraryTree(); + + // AbstractGameList stuff + virtual LibraryEntryRef selectedEntry() override; + virtual void selectEntry(LibraryEntryRef game) override; + + virtual void setViewStyle(LibraryStyle newStyle) override; + + virtual void addEntries(QList items) override; + virtual void addEntry(LibraryEntryRef item) override; + virtual void removeEntry(LibraryEntryRef item) override; + + virtual QWidget* widget() override { return m_widget; } + +private: + QTreeWidget* m_widget; + LibraryStyle m_currentStyle; + + LibraryController* m_controller; + + bool m_deferredTreeRebuild = false; + QMap m_items; + QMap m_pathNodes; + + void rebuildTree(); + void resizeAllCols(); +}; + +} + +#endif diff --git a/src/platform/qt/utils.cpp b/src/platform/qt/utils.cpp new file mode 100644 index 000000000..1c16df932 --- /dev/null +++ b/src/platform/qt/utils.cpp @@ -0,0 +1,40 @@ +/* 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 "utils.h" + +#include + +namespace QGBA { + +QString niceSizeFormat(size_t filesize) { + double size = filesize; + QString unit = "B"; + if (size >= 1024.0) { + size /= 1024.0; + unit = "kiB"; + } + if (size >= 1024.0) { + size /= 1024.0; + unit = "MiB"; + } + return QString("%0 %1").arg(size, 0, 'f', 1).arg(unit); +} +QString nicePlatformFormat(mPlatform platform) { + switch (platform) { +#ifdef M_CORE_GBA + case PLATFORM_GBA: + return QObject::tr("GBA"); +#endif +#ifdef M_CORE_GB + case PLATFORM_GB: + return QObject::tr("GB"); +#endif + default: + return QObject::tr("?"); + } +} + +} diff --git a/src/platform/qt/utils.h b/src/platform/qt/utils.h new file mode 100644 index 000000000..210e1f85a --- /dev/null +++ b/src/platform/qt/utils.h @@ -0,0 +1,20 @@ +/* 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 QGBA_UTILS_H +#define QGBA_UTILS_H + +#include + +#include + +namespace QGBA { + +QString niceSizeFormat(size_t filesize); +QString nicePlatformFormat(mPlatform platform); + +} + +#endif From 2f23829b66712f5d0a41e9e6089c006fb3fb5752 Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Mon, 15 May 2017 22:20:49 -0400 Subject: [PATCH 125/159] Qt: Switch to the C++11 connect() syntax. A few connect()s have not been migrated because the best way to migrate them requires somewhat invasive changes. Other than that, everything has been moved over. --- src/platform/qt/AssetTile.cpp | 2 +- src/platform/qt/AssetView.cpp | 7 +- src/platform/qt/CheatsView.cpp | 12 +- src/platform/qt/ConfigController.cpp | 6 +- src/platform/qt/DebuggerConsole.cpp | 8 +- src/platform/qt/DebuggerController.cpp | 2 +- src/platform/qt/Display.cpp | 2 +- src/platform/qt/DisplayGL.cpp | 2 +- src/platform/qt/GBAKeyEditor.cpp | 13 +- src/platform/qt/GDBWindow.cpp | 22 ++-- src/platform/qt/GIFView.cpp | 13 +- src/platform/qt/GameController.cpp | 16 +-- src/platform/qt/IOViewer.cpp | 13 +- src/platform/qt/LoadSaveState.cpp | 2 +- src/platform/qt/LogController.cpp | 8 +- src/platform/qt/LogView.cpp | 13 +- src/platform/qt/MemoryModel.cpp | 12 +- src/platform/qt/MemoryModel.h | 2 +- src/platform/qt/MemoryView.cpp | 31 ++--- src/platform/qt/MessagePainter.cpp | 2 +- src/platform/qt/ObjView.cpp | 6 +- src/platform/qt/OverrideView.cpp | 30 ++--- src/platform/qt/PaletteView.cpp | 6 +- src/platform/qt/SensorView.cpp | 11 +- src/platform/qt/SettingsView.cpp | 6 +- src/platform/qt/ShaderSelector.cpp | 12 +- src/platform/qt/ShortcutView.cpp | 8 +- src/platform/qt/TileView.cpp | 4 +- src/platform/qt/VideoView.cpp | 12 +- src/platform/qt/Window.cpp | 164 ++++++++++++------------- 30 files changed, 227 insertions(+), 220 deletions(-) diff --git a/src/platform/qt/AssetTile.cpp b/src/platform/qt/AssetTile.cpp index 74dcdf961..9ac748120 100644 --- a/src/platform/qt/AssetTile.cpp +++ b/src/platform/qt/AssetTile.cpp @@ -32,7 +32,7 @@ AssetTile::AssetTile(QWidget* parent) m_ui.color->setDimensions(QSize(1, 1)); m_ui.color->setSize(50); - connect(m_ui.preview, SIGNAL(indexPressed(int)), this, SLOT(selectColor(int))); + connect(m_ui.preview, &Swatch::indexPressed, this, &AssetTile::selectColor); const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); diff --git a/src/platform/qt/AssetView.cpp b/src/platform/qt/AssetView.cpp index 964b40761..198776601 100644 --- a/src/platform/qt/AssetView.cpp +++ b/src/platform/qt/AssetView.cpp @@ -20,9 +20,10 @@ AssetView::AssetView(GameController* controller, QWidget* parent) m_updateTimer.setInterval(1); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateTiles())); - connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), &m_updateTimer, SLOT(start())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), &m_updateTimer, SLOT(stop())); + connect(m_controller, &GameController::frameAvailable, &m_updateTimer, + static_cast(&QTimer::start)); + connect(m_controller, &GameController::gameStopped, this, &AssetView::close); + connect(m_controller, &GameController::gameStopped, &m_updateTimer, &QTimer::stop); } void AssetView::updateTiles(bool force) { diff --git a/src/platform/qt/CheatsView.cpp b/src/platform/qt/CheatsView.cpp index f8ae9b77d..73ffa09ab 100644 --- a/src/platform/qt/CheatsView.cpp +++ b/src/platform/qt/CheatsView.cpp @@ -31,12 +31,12 @@ CheatsView::CheatsView(GameController* controller, QWidget* parent) m_ui.cheatList->installEventFilter(this); m_ui.cheatList->setModel(&m_model); - connect(m_ui.load, SIGNAL(clicked()), this, SLOT(load())); - connect(m_ui.save, SIGNAL(clicked()), this, SLOT(save())); - connect(m_ui.addSet, SIGNAL(clicked()), this, SLOT(addSet())); - connect(m_ui.remove, SIGNAL(clicked()), this, SLOT(removeSet())); - connect(controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close())); - connect(controller, SIGNAL(stateLoaded(mCoreThread*)), &m_model, SLOT(invalidated())); + connect(m_ui.load, &QPushButton::clicked, this, &CheatsView::load); + connect(m_ui.save, &QPushButton::clicked, this, &CheatsView::save); + connect(m_ui.addSet, &QPushButton::clicked, this, &CheatsView::addSet); + connect(m_ui.remove, &QPushButton::clicked, this, &CheatsView::removeSet); + connect(controller, &GameController::gameStopped, this, &CheatsView::close); + connect(controller, &GameController::stateLoaded, &m_model, &CheatsModel::invalidated); QPushButton* add; switch (controller->platform()) { diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index f8c168167..d32b66eba 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -76,14 +76,12 @@ void ConfigOption::setValue(const char* value) { } void ConfigOption::setValue(const QVariant& value) { - QPair action; - foreach (action, m_actions) { + for (QPair& action : m_actions) { bool signalsEnabled = action.first->blockSignals(true); action.first->setChecked(value == action.second); action.first->blockSignals(signalsEnabled); } - std::function slot; - foreach(slot, m_slots.values()) { + for (std::function& slot : m_slots.values()) { slot(value); } } diff --git a/src/platform/qt/DebuggerConsole.cpp b/src/platform/qt/DebuggerConsole.cpp index 612e86e23..a9c505da8 100644 --- a/src/platform/qt/DebuggerConsole.cpp +++ b/src/platform/qt/DebuggerConsole.cpp @@ -17,10 +17,10 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget* { m_ui.setupUi(this); - connect(m_ui.prompt, SIGNAL(returnPressed()), this, SLOT(postLine())); - connect(controller, SIGNAL(log(const QString&)), this, SLOT(log(const QString&))); - connect(m_ui.breakpoint, SIGNAL(clicked()), controller, SLOT(attach())); - connect(m_ui.breakpoint, SIGNAL(clicked()), controller, SLOT(breakInto())); + connect(m_ui.prompt, &QLineEdit::returnPressed, this, &DebuggerConsole::postLine); + connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log); + connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::attach); + connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::breakInto); } void DebuggerConsole::log(const QString& line) { diff --git a/src/platform/qt/DebuggerController.cpp b/src/platform/qt/DebuggerController.cpp index dcec4efb2..34f00f558 100644 --- a/src/platform/qt/DebuggerController.cpp +++ b/src/platform/qt/DebuggerController.cpp @@ -30,7 +30,7 @@ void DebuggerController::attach() { mDebuggerEnter(m_debugger, DEBUGGER_ENTER_ATTACHED, 0); } else { QObject::disconnect(m_autoattach); - m_autoattach = connect(m_gameController, SIGNAL(gameStarted(mCoreThread*, const QString&)), this, SLOT(attach())); + m_autoattach = connect(m_gameController, &GameController::gameStarted, this, &DebuggerController::attach); } } diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index a6719a845..6c40ce01f 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -63,7 +63,7 @@ Display::Display(QWidget* parent) #elif defined(M_CORE_GBA) setMinimumSize(VIDEO_HORIZONTAL_PIXELS, VIDEO_VERTICAL_PIXELS); #endif - connect(&m_mouseTimer, SIGNAL(timeout()), this, SIGNAL(hideCursor())); + connect(&m_mouseTimer, &QTimer::timeout, this, &Display::hideCursor); m_mouseTimer.setSingleShot(true); m_mouseTimer.setInterval(MOUSE_DISAPPEAR_TIMER); setMouseTracking(true); diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 27d06df93..da3333fd4 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -68,7 +68,7 @@ void DisplayGL::startDrawing(mCoreThread* thread) { m_gl->context()->doneCurrent(); m_gl->context()->moveToThread(m_drawThread); m_painter->moveToThread(m_drawThread); - connect(m_drawThread, SIGNAL(started()), m_painter, SLOT(start())); + connect(m_drawThread, &QThread::started, m_painter, &PainterGL::start); m_drawThread->start(); mCoreSyncSetVideoSync(&m_context->sync, false); diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 4e157c85c..c3aca14f1 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -56,7 +56,8 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& #ifdef BUILD_SDL if (type == SDL_BINDING_BUTTON) { m_profileSelect = new QComboBox(this); - connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int))); + connect(m_profileSelect, static_cast(&QComboBox::currentIndexChanged), + this, &GBAKeyEditor::selectGamepad); updateJoysticks(); @@ -90,7 +91,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& QPushButton* updateJoysticksButton = new QPushButton(tr("Refresh")); layout->addWidget(updateJoysticksButton); - connect(updateJoysticksButton, SIGNAL(pressed()), this, SLOT(updateJoysticks())); + connect(updateJoysticksButton, &QAbstractButton::pressed, this, &GBAKeyEditor::updateJoysticks); } #endif @@ -99,7 +100,7 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& m_buttons->setLayout(layout); QPushButton* setAll = new QPushButton(tr("Set all")); - connect(setAll, SIGNAL(pressed()), this, SLOT(setAll())); + connect(setAll, &QAbstractButton::pressed, this, &GBAKeyEditor::setAll); layout->addWidget(setAll); layout->setSpacing(6); @@ -118,9 +119,9 @@ GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& }; for (auto& key : m_keyOrder) { - connect(key, SIGNAL(valueChanged(int)), this, SLOT(setNext())); - connect(key, SIGNAL(axisChanged(int, int)), this, SLOT(setNext())); - connect(key, SIGNAL(hatChanged(int, int)), this, SLOT(setNext())); + connect(key, &KeyEditor::valueChanged, this, &GBAKeyEditor::setNext); + connect(key, &KeyEditor::axisChanged, this, &GBAKeyEditor::setNext); + connect(key, &KeyEditor::hatChanged, this, &GBAKeyEditor::setNext); key->installEventFilter(this); } diff --git a/src/platform/qt/GDBWindow.cpp b/src/platform/qt/GDBWindow.cpp index 98bc89801..c32d7a52d 100644 --- a/src/platform/qt/GDBWindow.cpp +++ b/src/platform/qt/GDBWindow.cpp @@ -38,12 +38,12 @@ GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) m_portEdit = new QLineEdit("2345"); m_portEdit->setMaxLength(5); - connect(m_portEdit, SIGNAL(textChanged(const QString&)), this, SLOT(portChanged(const QString&))); + connect(m_portEdit, &QLineEdit::textChanged, this, &GDBWindow::portChanged); settingsGrid->addWidget(m_portEdit, 0, 1, Qt::AlignLeft); m_bindAddressEdit = new QLineEdit("0.0.0.0"); m_bindAddressEdit->setMaxLength(15); - connect(m_bindAddressEdit, SIGNAL(textChanged(const QString&)), this, SLOT(bindAddressChanged(const QString&))); + connect(m_bindAddressEdit, &QLineEdit::textChanged, this, &GDBWindow::bindAddressChanged); settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft); QHBoxLayout* buttons = new QHBoxLayout; @@ -57,9 +57,9 @@ GDBWindow::GDBWindow(GDBController* controller, QWidget* parent) mainSegment->addLayout(buttons); - connect(m_gdbController, SIGNAL(listening()), this, SLOT(started())); - connect(m_gdbController, SIGNAL(listenFailed()), this, SLOT(failed())); - connect(m_breakButton, SIGNAL(clicked()), controller, SLOT(breakInto())); + connect(m_gdbController, &GDBController::listening, this, &GDBWindow::started); + connect(m_gdbController, &GDBController::listenFailed, this, &GDBWindow::failed); + connect(m_breakButton, &QAbstractButton::clicked, controller, &DebuggerController::breakInto); if (m_gdbController->isAttached()) { started(); @@ -103,9 +103,9 @@ void GDBWindow::started() { m_bindAddressEdit->setEnabled(false); m_startStopButton->setText(tr("Stop")); m_breakButton->setEnabled(true); - disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen())); - connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach())); - connect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped())); + disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &GDBController::listen); + connect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &DebuggerController::detach); + connect(m_startStopButton, &QAbstractButton::clicked, this, &GDBWindow::stopped); } void GDBWindow::stopped() { @@ -113,9 +113,9 @@ void GDBWindow::stopped() { m_bindAddressEdit->setEnabled(true); m_startStopButton->setText(tr("Start")); m_breakButton->setEnabled(false); - disconnect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(detach())); - disconnect(m_startStopButton, SIGNAL(clicked()), this, SLOT(stopped())); - connect(m_startStopButton, SIGNAL(clicked()), m_gdbController, SLOT(listen())); + disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &DebuggerController::detach); + disconnect(m_startStopButton, &QAbstractButton::clicked, this, &GDBWindow::stopped); + connect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &GDBController::listen); } void GDBWindow::failed() { diff --git a/src/platform/qt/GIFView.cpp b/src/platform/qt/GIFView.cpp index e72475150..1b9e0156c 100644 --- a/src/platform/qt/GIFView.cpp +++ b/src/platform/qt/GIFView.cpp @@ -22,14 +22,15 @@ GIFView::GIFView(QWidget* parent) { m_ui.setupUi(this); - connect(m_ui.start, SIGNAL(clicked()), this, SLOT(startRecording())); - connect(m_ui.stop, SIGNAL(clicked()), this, SLOT(stopRecording())); + connect(m_ui.start, &QAbstractButton::clicked, this, &GIFView::startRecording); + connect(m_ui.stop, &QAbstractButton::clicked, this, &GIFView::stopRecording); - connect(m_ui.selectFile, SIGNAL(clicked()), this, SLOT(selectFile())); - connect(m_ui.filename, SIGNAL(textChanged(const QString&)), this, SLOT(setFilename(const QString&))); + connect(m_ui.selectFile, &QAbstractButton::clicked, this, &GIFView::selectFile); + connect(m_ui.filename, &QLineEdit::textChanged, this, &GIFView::setFilename); - connect(m_ui.frameskip, SIGNAL(valueChanged(int)), this, SLOT(updateDelay())); - connect(m_ui.delayAuto, SIGNAL(clicked(bool)), this, SLOT(updateDelay())); + connect(m_ui.frameskip, static_cast(&QSpinBox::valueChanged), + this, &GIFView::updateDelay); + connect(m_ui.delayAuto, &QAbstractButton::clicked, this, &GIFView::updateDelay); ImageMagickGIFEncoderInit(&m_encoder); } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index f5f921c56..dcc466330 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -277,10 +277,10 @@ GameController::GameController(QObject* parent) m_threadContext.userData = this; - connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause())); - connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*))); - connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents())); - connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updateAutofire())); + connect(this, &GameController::gamePaused, m_audioProcessor, &AudioProcessor::pause); + connect(this, &GameController::gameStarted, m_audioProcessor, &AudioProcessor::setInput); + connect(this, &GameController::frameAvailable, this, &GameController::pollEvents); + connect(this, &GameController::frameAvailable, this, &GameController::updateAutofire); } GameController::~GameController() { @@ -939,8 +939,8 @@ void GameController::loadState(int slot) { } mCoreLoadStateNamed(context->core, controller->m_backupLoadState, controller->m_saveStateFlags); if (mCoreLoadState(context->core, controller->m_stateSlot, controller->m_loadStateFlags)) { - controller->frameAvailable(controller->m_drawContext); - controller->stateLoaded(context); + emit controller->frameAvailable(controller->m_drawContext); + emit controller->stateLoaded(context); } }); } @@ -1108,8 +1108,8 @@ void GameController::reloadAudioDriver() { if (sampleRate) { m_audioProcessor->requestSampleRate(sampleRate); } - connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause())); - connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*))); + connect(this, &GameController::gamePaused, m_audioProcessor, &AudioProcessor::pause); + connect(this, &GameController::gameStarted, m_audioProcessor, &AudioProcessor::setInput); if (isLoaded()) { m_audioProcessor->setInput(&m_threadContext); startAudio(); diff --git a/src/platform/qt/IOViewer.cpp b/src/platform/qt/IOViewer.cpp index b308de6a4..030ead19f 100644 --- a/src/platform/qt/IOViewer.cpp +++ b/src/platform/qt/IOViewer.cpp @@ -1040,9 +1040,10 @@ IOViewer::IOViewer(GameController* controller, QWidget* parent) const QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont); m_ui.regValue->setFont(font); - connect(m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonPressed(QAbstractButton*))); - connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close())); - connect(m_ui.regSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectRegister())); + connect(m_ui.buttonBox, &QDialogButtonBox::clicked, this, &IOViewer::buttonPressed); + connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); + connect(m_ui.regSelect, &QComboBox::currentTextChanged, + this, static_cast(&IOViewer::selectRegister)); m_b[0] = m_ui.b0; m_b[1] = m_ui.b1; @@ -1062,7 +1063,7 @@ IOViewer::IOViewer(GameController* controller, QWidget* parent) m_b[15] = m_ui.bF; for (int i = 0; i < 16; ++i) { - connect(m_b[i], SIGNAL(toggled(bool)), this, SLOT(bitFlipped())); + connect(m_b[i], &QAbstractButton::toggled, this, &IOViewer::bitFlipped); } selectRegister(0); @@ -1128,8 +1129,8 @@ void IOViewer::selectRegister(unsigned address) { QCheckBox* check = new QCheckBox; check->setEnabled(!ri.readonly); box->addWidget(check, i, 1, Qt::AlignRight); - connect(check, SIGNAL(toggled(bool)), m_b[ri.start], SLOT(setChecked(bool))); - connect(m_b[ri.start], SIGNAL(toggled(bool)), check, SLOT(setChecked(bool))); + connect(check, &QAbstractButton::toggled, m_b[ri.start], &QAbstractButton::setChecked); + connect(m_b[ri.start], &QAbstractButton::toggled, check, &QAbstractButton::setChecked); } else if (ri.items.empty()) { QSpinBox* sbox = new QSpinBox; sbox->setEnabled(!ri.readonly); diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 122cb133e..820b5f2ed 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -57,7 +57,7 @@ LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent) } QAction* escape = new QAction(this); - escape->connect(escape, SIGNAL(triggered()), this, SLOT(close())); + connect(escape, &QAction::triggered, this, &QWidget::close); escape->setShortcut(QKeySequence("Esc")); escape->setShortcutContext(Qt::WidgetWithChildrenShortcut); addAction(escape); diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp index e54922a10..61208f0cc 100644 --- a/src/platform/qt/LogController.cpp +++ b/src/platform/qt/LogController.cpp @@ -14,10 +14,10 @@ LogController::LogController(int levels, QObject* parent) , m_logLevel(levels) { if (this != &s_global) { - connect(&s_global, SIGNAL(logPosted(int, int, const QString&)), this, SLOT(postLog(int, int, const QString&))); - connect(this, SIGNAL(levelsSet(int)), &s_global, SLOT(setLevels(int))); - connect(this, SIGNAL(levelsEnabled(int)), &s_global, SLOT(enableLevels(int))); - connect(this, SIGNAL(levelsDisabled(int)), &s_global, SLOT(disableLevels(int))); + connect(&s_global, &LogController::logPosted, this, &LogController::postLog); + connect(this, &LogController::levelsSet, &s_global, &LogController::setLevels); + connect(this, &LogController::levelsEnabled, &s_global, &LogController::enableLevels); + connect(this, &LogController::levelsDisabled, &s_global, &LogController::disableLevels); } } diff --git a/src/platform/qt/LogView.cpp b/src/platform/qt/LogView.cpp index dc9a2ed84..958b5e937 100644 --- a/src/platform/qt/LogView.cpp +++ b/src/platform/qt/LogView.cpp @@ -39,12 +39,13 @@ LogView::LogView(LogController* log, QWidget* parent) connect(m_ui.levelGameError, &QAbstractButton::toggled, [this](bool set) { setLevel(mLOG_GAME_ERROR, set); }); - connect(m_ui.clear, SIGNAL(clicked()), this, SLOT(clear())); - connect(m_ui.maxLines, SIGNAL(valueChanged(int)), this, SLOT(setMaxLines(int))); + connect(m_ui.clear, &QAbstractButton::clicked, this, &LogView::clear); + connect(m_ui.maxLines, static_cast(&QSpinBox::valueChanged), + this, &LogView::setMaxLines); m_ui.maxLines->setValue(DEFAULT_LINE_LIMIT); - connect(log, SIGNAL(logPosted(int, int, const QString&)), this, SLOT(postLog(int, int, const QString&))); - connect(log, SIGNAL(levelsSet(int)), this, SLOT(setLevels(int))); + connect(log, &LogController::logPosted, this, &LogView::postLog); + connect(log, &LogController::levelsSet, this, &LogView::setLevels); connect(log, &LogController::levelsEnabled, [this](int level) { bool s = blockSignals(true); setLevel(level, true); @@ -55,8 +56,8 @@ LogView::LogView(LogController* log, QWidget* parent) setLevel(level, false); blockSignals(s); }); - connect(this, SIGNAL(levelsEnabled(int)), log, SLOT(enableLevels(int))); - connect(this, SIGNAL(levelsDisabled(int)), log, SLOT(disableLevels(int))); + connect(this, &LogView::levelsEnabled, log, &LogController::enableLevels); + connect(this, &LogView::levelsDisabled, log, &LogController::disableLevels); } void LogView::postLog(int level, int category, const QString& log) { diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index e7f4d126e..215576b9f 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -49,22 +49,22 @@ MemoryModel::MemoryModel(QWidget* parent) QAction* copy = new QAction(tr("Copy selection"), this); copy->setShortcut(QKeySequence::Copy); - connect(copy, SIGNAL(triggered()), this, SLOT(copy())); + connect(copy, &QAction::triggered, this, &MemoryModel::copy); addAction(copy); QAction* save = new QAction(tr("Save selection"), this); save->setShortcut(QKeySequence::Save); - connect(save, SIGNAL(triggered()), this, SLOT(save())); + connect(save, &QAction::triggered, this, &MemoryModel::save); addAction(save); QAction* paste = new QAction(tr("Paste"), this); paste->setShortcut(QKeySequence::Paste); - connect(paste, SIGNAL(triggered()), this, SLOT(paste())); + connect(paste, &QAction::triggered, this, &MemoryModel::paste); addAction(paste); QAction* load = new QAction(tr("Load"), this); load->setShortcut(QKeySequence::Open); - connect(load, SIGNAL(triggered()), this, SLOT(load())); + connect(load, &QAction::triggered, this, &MemoryModel::load); addAction(load); static QString arg("%0"); @@ -128,7 +128,7 @@ void MemoryModel::setAlignment(int width) { viewport()->update(); } -void MemoryModel::loadTBL(const QString& path) { +void MemoryModel::loadTBLFromPath(const QString& path) { VFile* vf = VFileDevice::open(path, O_RDONLY); if (!vf) { return; @@ -143,7 +143,7 @@ void MemoryModel::loadTBL() { if (filename.isNull()) { return; } - loadTBL(filename); + loadTBLFromPath(filename); } void MemoryModel::jumpToAddress(const QString& hex) { diff --git a/src/platform/qt/MemoryModel.h b/src/platform/qt/MemoryModel.h index a32bb8c36..07d442527 100644 --- a/src/platform/qt/MemoryModel.h +++ b/src/platform/qt/MemoryModel.h @@ -43,7 +43,7 @@ public slots: void jumpToAddress(const QString& hex); void jumpToAddress(uint32_t); - void loadTBL(const QString& path); + void loadTBLFromPath(const QString& path); void loadTBL(); void copy(); diff --git a/src/platform/qt/MemoryView.cpp b/src/platform/qt/MemoryView.cpp index 20a6dce16..b343060fa 100644 --- a/src/platform/qt/MemoryView.cpp +++ b/src/platform/qt/MemoryView.cpp @@ -81,8 +81,10 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent) break; } - connect(m_ui.regions, SIGNAL(currentIndexChanged(int)), this, SLOT(setIndex(int))); - connect(m_ui.segments, SIGNAL(valueChanged(int)), this, SLOT(setSegment(int))); + connect(m_ui.regions, static_cast(&QComboBox::currentIndexChanged), + this, &MemoryView::setIndex); + connect(m_ui.segments, static_cast(&QSpinBox::valueChanged), + this, &MemoryView::setSegment); if (info) { for (size_t i = 0; info[i].name; ++i) { @@ -93,22 +95,23 @@ MemoryView::MemoryView(GameController* controller, QWidget* parent) connect(m_ui.width8, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(1); }); connect(m_ui.width16, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(2); }); connect(m_ui.width32, &QAbstractButton::clicked, [this]() { m_ui.hexfield->setAlignment(4); }); - connect(m_ui.setAddress, SIGNAL(valueChanged(const QString&)), m_ui.hexfield, SLOT(jumpToAddress(const QString&))); - connect(m_ui.hexfield, SIGNAL(selectionChanged(uint32_t, uint32_t)), this, SLOT(updateSelection(uint32_t, uint32_t))); + connect(m_ui.setAddress, static_cast(&QSpinBox::valueChanged), + m_ui.hexfield, static_cast(&MemoryModel::jumpToAddress)); + connect(m_ui.hexfield, &MemoryModel::selectionChanged, this, &MemoryView::updateSelection); - connect(controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close())); + connect(controller, &GameController::gameStopped, this, &QWidget::close); - connect(controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(update())); - connect(controller, SIGNAL(gamePaused(mCoreThread*)), this, SLOT(update())); - connect(controller, SIGNAL(stateLoaded(mCoreThread*)), this, SLOT(update())); - connect(controller, SIGNAL(rewound(mCoreThread*)), this, SLOT(update())); + connect(controller, &GameController::frameAvailable, this, &MemoryView::update); + connect(controller, &GameController::gamePaused, this, &MemoryView::update); + connect(controller, &GameController::stateLoaded, this, &MemoryView::update); + connect(controller, &GameController::rewound, this, &MemoryView::update); - connect(m_ui.copy, SIGNAL(clicked()), m_ui.hexfield, SLOT(copy())); - connect(m_ui.save, SIGNAL(clicked()), m_ui.hexfield, SLOT(save())); - connect(m_ui.paste, SIGNAL(clicked()), m_ui.hexfield, SLOT(paste())); - connect(m_ui.load, SIGNAL(clicked()), m_ui.hexfield, SLOT(load())); + connect(m_ui.copy, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::copy); + connect(m_ui.save, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::save); + connect(m_ui.paste, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::paste); + connect(m_ui.load, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::load); - connect(m_ui.loadTBL, SIGNAL(clicked()), m_ui.hexfield, SLOT(loadTBL())); + connect(m_ui.loadTBL, &QAbstractButton::clicked, m_ui.hexfield, &MemoryModel::loadTBL); } void MemoryView::setIndex(int index) { diff --git a/src/platform/qt/MessagePainter.cpp b/src/platform/qt/MessagePainter.cpp index 629ce4878..c7d73fcfa 100644 --- a/src/platform/qt/MessagePainter.cpp +++ b/src/platform/qt/MessagePainter.cpp @@ -21,7 +21,7 @@ MessagePainter::MessagePainter(QObject* parent) m_messageFont.setFamily("Source Code Pro"); m_messageFont.setStyleHint(QFont::Monospace); m_messageFont.setPixelSize(13); - connect(&m_messageTimer, SIGNAL(timeout()), this, SLOT(clearMessage())); + connect(&m_messageTimer, &QTimer::timeout, this, &MessagePainter::clearMessage); m_messageTimer.setSingleShot(true); m_messageTimer.setInterval(5000); diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index ae76ecf76..cdaf8a21b 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -46,13 +46,13 @@ ObjView::ObjView(GameController* controller, QWidget* parent) m_ui.transform->setFont(font); m_ui.mode->setFont(font); - connect(m_ui.tiles, SIGNAL(indexPressed(int)), this, SLOT(translateIndex(int))); - connect(m_ui.objId, SIGNAL(valueChanged(int)), this, SLOT(selectObj(int))); + connect(m_ui.tiles, &TilePainter::indexPressed, this, &ObjView::translateIndex); + connect(m_ui.objId, static_cast(&QSpinBox::valueChanged), this, &ObjView::selectObj); connect(m_ui.magnification, static_cast(&QSpinBox::valueChanged), [this]() { updateTiles(true); }); #ifdef USE_PNG - connect(m_ui.exportButton, SIGNAL(clicked()), this, SLOT(exportObj())); + connect(m_ui.exportButton, &QAbstractButton::clicked, this, &ObjView::exportObj); #else m_ui.exportButton->setVisible(false); #endif diff --git a/src/platform/qt/OverrideView.cpp b/src/platform/qt/OverrideView.cpp index 39f4e5ce9..42f90f5eb 100644 --- a/src/platform/qt/OverrideView.cpp +++ b/src/platform/qt/OverrideView.cpp @@ -56,8 +56,8 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config, #endif m_ui.setupUi(this); - connect(controller, SIGNAL(gameStarted(mCoreThread*, const QString&)), this, SLOT(gameStarted(mCoreThread*))); - connect(controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(gameStopped())); + connect(controller, &GameController::gameStarted, this, &OverrideView::gameStarted); + connect(controller, &GameController::gameStopped, this, &OverrideView::gameStopped); connect(m_ui.hwAutodetect, &QAbstractButton::toggled, [this] (bool enabled) { m_ui.hwRTC->setEnabled(!enabled); @@ -67,19 +67,19 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config, m_ui.hwRumble->setEnabled(!enabled); }); - connect(m_ui.savetype, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOverrides())); - connect(m_ui.hwAutodetect, SIGNAL(clicked()), this, SLOT(updateOverrides())); - connect(m_ui.hwRTC, SIGNAL(clicked()), this, SLOT(updateOverrides())); - connect(m_ui.hwGyro, SIGNAL(clicked()), this, SLOT(updateOverrides())); - connect(m_ui.hwLight, SIGNAL(clicked()), this, SLOT(updateOverrides())); - connect(m_ui.hwTilt, SIGNAL(clicked()), this, SLOT(updateOverrides())); - connect(m_ui.hwRumble, SIGNAL(clicked()), this, SLOT(updateOverrides())); - connect(m_ui.hwGBPlayer, SIGNAL(clicked()), this, SLOT(updateOverrides())); + connect(m_ui.savetype, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); + connect(m_ui.hwAutodetect, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); + connect(m_ui.hwRTC, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); + connect(m_ui.hwGyro, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); + connect(m_ui.hwLight, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); + connect(m_ui.hwTilt, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); + connect(m_ui.hwRumble, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); + connect(m_ui.hwGBPlayer, &QAbstractButton::clicked, this, &OverrideView::updateOverrides); - connect(m_ui.gbModel, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOverrides())); - connect(m_ui.mbc, SIGNAL(currentIndexChanged(int)), this, SLOT(updateOverrides())); + connect(m_ui.gbModel, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); + connect(m_ui.mbc, &QComboBox::currentTextChanged, this, &OverrideView::updateOverrides); - connect(m_ui.tabWidget, SIGNAL(currentChanged(int)), this, SLOT(updateOverrides())); + connect(m_ui.tabWidget, &QTabWidget::currentChanged, this, &OverrideView::updateOverrides); #ifndef M_CORE_GBA m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGBA)); #endif @@ -87,8 +87,8 @@ OverrideView::OverrideView(GameController* controller, ConfigController* config, m_ui.tabWidget->removeTab(m_ui.tabWidget->indexOf(m_ui.tabGB)); #endif - connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(saveOverride())); - connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close())); + connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &OverrideView::saveOverride); + connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close); m_ui.buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); if (controller->isLoaded()) { diff --git a/src/platform/qt/PaletteView.cpp b/src/platform/qt/PaletteView.cpp index 53a8aaa6e..f669168de 100644 --- a/src/platform/qt/PaletteView.cpp +++ b/src/platform/qt/PaletteView.cpp @@ -30,7 +30,7 @@ PaletteView::PaletteView(GameController* controller, QWidget* parent) { m_ui.setupUi(this); - connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(updatePalette())); + connect(m_controller, &GameController::frameAvailable, this, &PaletteView::updatePalette); m_ui.bgGrid->setDimensions(QSize(16, 16)); m_ui.objGrid->setDimensions(QSize(16, 16)); int count = 256; @@ -56,12 +56,12 @@ PaletteView::PaletteView(GameController* controller, QWidget* parent) m_ui.g->setFont(font); m_ui.b->setFont(font); - connect(m_ui.bgGrid, SIGNAL(indexPressed(int)), this, SLOT(selectIndex(int))); + connect(m_ui.bgGrid, &Swatch::indexPressed, this, &PaletteView::selectIndex); connect(m_ui.objGrid, &Swatch::indexPressed, [this, count] (int index) { selectIndex(index + count); }); connect(m_ui.exportBG, &QAbstractButton::clicked, [this, count] () { exportPalette(0, count); }); connect(m_ui.exportOBJ, &QAbstractButton::clicked, [this, count] () { exportPalette(count, count); }); - connect(controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(close())); + connect(controller, &GameController::gameStopped, this, &QWidget::close); } void PaletteView::updatePalette() { diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index b0e86cc64..8f107df46 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -22,10 +22,11 @@ SensorView::SensorView(GameController* controller, InputController* input, QWidg { m_ui.setupUi(this); - connect(m_ui.lightSpin, SIGNAL(valueChanged(int)), this, SLOT(setLuminanceValue(int))); - connect(m_ui.lightSlide, SIGNAL(valueChanged(int)), this, SLOT(setLuminanceValue(int))); + connect(m_ui.lightSpin, static_cast(&QSpinBox::valueChanged), + this, &SensorView::setLuminanceValue); + connect(m_ui.lightSlide, &QAbstractSlider::valueChanged, this, &SensorView::setLuminanceValue); - connect(m_ui.timeNoOverride, SIGNAL(clicked()), controller, SLOT(setRealTime())); + connect(m_ui.timeNoOverride, &QAbstractButton::clicked, controller, &GameController::setRealTime); connect(m_ui.timeFixed, &QRadioButton::clicked, [controller, this] () { controller->setFixedTime(m_ui.time->dateTime()); }); @@ -39,10 +40,10 @@ SensorView::SensorView(GameController* controller, InputController* input, QWidg m_ui.time->setDateTime(QDateTime::currentDateTime()); }); - connect(m_controller, SIGNAL(luminanceValueChanged(int)), this, SLOT(luminanceValueChanged(int))); + connect(m_controller, &GameController::luminanceValueChanged, this, &SensorView::luminanceValueChanged); m_timer.setInterval(2); - connect(&m_timer, SIGNAL(timeout()), this, SLOT(updateSensors())); + connect(&m_timer, &QTimer::timeout, this, &SensorView::updateSensors); if (!m_rotation || !m_rotation->readTiltX || !m_rotation->readTiltY) { m_ui.tilt->hide(); } else { diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 6848e4cd6..953be8d73 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -140,7 +140,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this); m_ui.stackedWidget->addWidget(editor); m_ui.tabs->addItem(tr("Keyboard")); - connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save())); + connect(m_ui.buttonBox, &QDialogButtonBox::accepted, editor, &GBAKeyEditor::save); GBAKeyEditor* buttonEditor = nullptr; #ifdef BUILD_SDL @@ -149,10 +149,10 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); m_ui.stackedWidget->addWidget(buttonEditor); m_ui.tabs->addItem(tr("Controllers")); - connect(m_ui.buttonBox, SIGNAL(accepted()), buttonEditor, SLOT(save())); + connect(m_ui.buttonBox, &QDialogButtonBox::accepted, buttonEditor, &GBAKeyEditor::save); #endif - connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig())); + connect(m_ui.buttonBox, &QDialogButtonBox::accepted, this, &SettingsView::updateConfig); connect(m_ui.buttonBox, &QDialogButtonBox::clicked, [this, editor, buttonEditor](QAbstractButton* button) { if (m_ui.buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) { updateConfig(); diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp index 4eb743fce..fb3366888 100644 --- a/src/platform/qt/ShaderSelector.cpp +++ b/src/platform/qt/ShaderSelector.cpp @@ -37,9 +37,9 @@ ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidg refreshShaders(); - connect(m_ui.load, SIGNAL(clicked()), this, SLOT(selectShader())); - connect(m_ui.unload, SIGNAL(clicked()), this, SLOT(clearShader())); - connect(m_ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonPressed(QAbstractButton*))); + connect(m_ui.load, &QAbstractButton::clicked, this, &ShaderSelector::selectShader); + connect(m_ui.unload, &QAbstractButton::clicked, this, &ShaderSelector::clearShader); + connect(m_ui.buttonBox, &QDialogButtonBox::clicked, this, &ShaderSelector::buttonPressed); } ShaderSelector::~ShaderSelector() { @@ -112,9 +112,9 @@ void ShaderSelector::refreshShaders() { m_ui.author->clear(); } - disconnect(this, SIGNAL(saved()), 0, 0); - disconnect(this, SIGNAL(reset()), 0, 0); - disconnect(this, SIGNAL(resetToDefault()), 0, 0); + disconnect(this, &ShaderSelector::saved, 0, 0); + disconnect(this, &ShaderSelector::reset, 0, 0); + disconnect(this, &ShaderSelector::resetToDefault, 0, 0); #if !defined(_WIN32) || defined(USE_EPOXY) if (m_shaders->preprocessShader) { diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index 9046f043c..6a3a3edfb 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -31,10 +31,10 @@ ShortcutView::ShortcutView(QWidget* parent) m_ui.keyEdit->setValueKey(0); m_ui.keyEdit->blockSignals(signalsBlocked); }); - connect(m_ui.keyEdit, SIGNAL(valueChanged(int)), this, SLOT(updateButton(int))); - connect(m_ui.keyEdit, SIGNAL(axisChanged(int, int)), this, SLOT(updateAxis(int, int))); - connect(m_ui.shortcutTable, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(load(const QModelIndex&))); - connect(m_ui.clearButton, SIGNAL(clicked()), this, SLOT(clear())); + connect(m_ui.keyEdit, &KeyEditor::valueChanged, this, &ShortcutView::updateButton); + connect(m_ui.keyEdit, &KeyEditor::axisChanged, this, &ShortcutView::updateAxis); + connect(m_ui.shortcutTable, &QAbstractItemView::doubleClicked, this, &ShortcutView::load); + connect(m_ui.clearButton, &QAbstractButton::clicked, this, &ShortcutView::clear); } ShortcutView::~ShortcutView() { diff --git a/src/platform/qt/TileView.cpp b/src/platform/qt/TileView.cpp index eb4dbd18d..6551efcb9 100644 --- a/src/platform/qt/TileView.cpp +++ b/src/platform/qt/TileView.cpp @@ -25,8 +25,8 @@ TileView::TileView(GameController* controller, QWidget* parent) m_ui.setupUi(this); m_ui.tile->setController(controller); - connect(m_ui.tiles, SIGNAL(indexPressed(int)), m_ui.tile, SLOT(selectIndex(int))); - connect(m_ui.paletteId, SIGNAL(valueChanged(int)), this, SLOT(updatePalette(int))); + connect(m_ui.tiles, &TilePainter::indexPressed, m_ui.tile, &AssetTile::selectIndex); + connect(m_ui.paletteId, &QAbstractSlider::valueChanged, this, &TileView::updatePalette); int max = 1024; int boundary = 1024; diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index 681371217..3011293c6 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -75,12 +75,12 @@ VideoView::VideoView(QWidget* parent) s_containerMap["mkv"] = "matroska"; } - connect(m_ui.buttonBox, SIGNAL(rejected()), this, SLOT(close())); - connect(m_ui.start, SIGNAL(clicked()), this, SLOT(startRecording())); - connect(m_ui.stop, SIGNAL(clicked()), this, SLOT(stopRecording())); + connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &VideoView::close); + connect(m_ui.start, &QAbstractButton::clicked, this, &VideoView::startRecording); + connect(m_ui.stop, &QAbstractButton::clicked, this, &VideoView::stopRecording); - connect(m_ui.selectFile, SIGNAL(clicked()), this, SLOT(selectFile())); - connect(m_ui.filename, SIGNAL(textChanged(const QString&)), this, SLOT(setFilename(const QString&))); + connect(m_ui.selectFile, &QAbstractButton::clicked, this, &VideoView::selectFile); + connect(m_ui.filename, &QLineEdit::textChanged, this, &VideoView::setFilename); connect(m_ui.audio, SIGNAL(activated(const QString&)), this, SLOT(setAudioCodec(const QString&))); connect(m_ui.video, SIGNAL(activated(const QString&)), this, SLOT(setVideoCodec(const QString&))); @@ -98,7 +98,7 @@ VideoView::VideoView(QWidget* parent) connect(m_ui.wratio, SIGNAL(valueChanged(int)), this, SLOT(setAspectWidth(int))); connect(m_ui.hratio, SIGNAL(valueChanged(int)), this, SLOT(setAspectHeight(int))); - connect(m_ui.showAdvanced, SIGNAL(clicked(bool)), this, SLOT(showAdvanced(bool))); + connect(m_ui.showAdvanced, &QAbstractButton::clicked, this, &VideoView::showAdvanced); FFmpegEncoderInit(&m_encoder); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index d6f24e2e5..96eb04548 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -145,13 +145,13 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) m_screenWidget->setLockIntegerScaling(false); setCentralWidget(m_screenWidget); - connect(m_controller, SIGNAL(gameStarted(mCoreThread*, const QString&)), this, SLOT(gameStarted(mCoreThread*, const QString&))); - connect(m_controller, SIGNAL(gameStarted(mCoreThread*, const QString&)), &m_inputController, SLOT(suspendScreensaver())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_display, SLOT(stopDrawing())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), this, SLOT(gameStopped())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), &m_inputController, SLOT(resumeScreensaver())); - connect(m_controller, SIGNAL(stateLoaded(mCoreThread*)), m_display, SLOT(forceDraw())); - connect(m_controller, SIGNAL(rewound(mCoreThread*)), m_display, SLOT(forceDraw())); + connect(m_controller, &GameController::gameStarted, this, &Window::gameStarted); + connect(m_controller, &GameController::gameStarted, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller, &GameController::gameStopped, m_display, &Display::stopDrawing); + connect(m_controller, &GameController::gameStopped, this, &Window::gameStopped); + connect(m_controller, &GameController::gameStopped, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller, &GameController::stateLoaded, m_display, &Display::forceDraw); + connect(m_controller, &GameController::rewound, m_display, &Display::forceDraw); connect(m_controller, &GameController::gamePaused, [this](mCoreThread* context) { unsigned width, height; context->core->desiredVideoDimensions(context->core, &width, &height); @@ -162,38 +162,38 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) m_screenWidget->setPixmap(pixmap); m_screenWidget->setLockAspectRatio(width, height); }); - connect(m_controller, SIGNAL(gamePaused(mCoreThread*)), m_display, SLOT(pauseDrawing())); + connect(m_controller, &GameController::gamePaused, m_display, &Display::pauseDrawing); #ifndef Q_OS_MAC - connect(m_controller, SIGNAL(gamePaused(mCoreThread*)), menuBar(), SLOT(show())); + connect(m_controller, &GameController::gamePaused, menuBar(), &QWidget::show); connect(m_controller, &GameController::gameUnpaused, [this]() { if(isFullScreen()) { menuBar()->hide(); } }); #endif - connect(m_controller, SIGNAL(gamePaused(mCoreThread*)), &m_inputController, SLOT(resumeScreensaver())); - connect(m_controller, SIGNAL(gameUnpaused(mCoreThread*)), m_display, SLOT(unpauseDrawing())); - connect(m_controller, SIGNAL(gameUnpaused(mCoreThread*)), &m_inputController, SLOT(suspendScreensaver())); - connect(m_controller, SIGNAL(postLog(int, int, const QString&)), &m_log, SLOT(postLog(int, int, const QString&))); - connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(recordFrame())); - connect(m_controller, SIGNAL(frameAvailable(const uint32_t*)), m_display, SLOT(framePosted(const uint32_t*))); - connect(m_controller, SIGNAL(gameCrashed(const QString&)), this, SLOT(gameCrashed(const QString&))); - connect(m_controller, SIGNAL(gameFailed()), this, SLOT(gameFailed())); - connect(m_controller, SIGNAL(unimplementedBiosCall(int)), this, SLOT(unimplementedBiosCall(int))); - connect(m_controller, SIGNAL(statusPosted(const QString&)), m_display, SLOT(showMessage(const QString&))); - connect(&m_log, SIGNAL(levelsSet(int)), m_controller, SLOT(setLogLevel(int))); - connect(&m_log, SIGNAL(levelsEnabled(int)), m_controller, SLOT(enableLogLevel(int))); - connect(&m_log, SIGNAL(levelsDisabled(int)), m_controller, SLOT(disableLogLevel(int))); - connect(this, SIGNAL(startDrawing(mCoreThread*)), m_display, SLOT(startDrawing(mCoreThread*)), Qt::QueuedConnection); - connect(this, SIGNAL(shutdown()), m_display, SLOT(stopDrawing())); - connect(this, SIGNAL(shutdown()), m_controller, SLOT(closeGame())); - connect(this, SIGNAL(shutdown()), m_logView, SLOT(hide())); - connect(this, SIGNAL(shutdown()), m_shaderView, SLOT(hide())); - connect(this, SIGNAL(audioBufferSamplesChanged(int)), m_controller, SLOT(setAudioBufferSamples(int))); - connect(this, SIGNAL(sampleRateChanged(unsigned)), m_controller, SLOT(setAudioSampleRate(unsigned))); - connect(this, SIGNAL(fpsTargetChanged(float)), m_controller, SLOT(setFPSTarget(float))); - connect(&m_fpsTimer, SIGNAL(timeout()), this, SLOT(showFPS())); - connect(&m_focusCheck, SIGNAL(timeout()), this, SLOT(focusCheck())); + connect(m_controller, &GameController::gamePaused, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller, &GameController::gameUnpaused, m_display, &Display::unpauseDrawing); + connect(m_controller, &GameController::gameUnpaused, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller, &GameController::postLog, &m_log, &LogController::postLog); + connect(m_controller, &GameController::frameAvailable, this, &Window::recordFrame); + connect(m_controller, &GameController::frameAvailable, m_display, &Display::framePosted); + connect(m_controller, &GameController::gameCrashed, this, &Window::gameCrashed); + connect(m_controller, &GameController::gameFailed, this, &Window::gameFailed); + connect(m_controller, &GameController::unimplementedBiosCall, this, &Window::unimplementedBiosCall); + connect(m_controller, &GameController::statusPosted, m_display, &Display::showMessage); + connect(&m_log, &LogController::levelsSet, m_controller, &GameController::setLogLevel); + connect(&m_log, &LogController::levelsEnabled, m_controller, &GameController::enableLogLevel); + connect(&m_log, &LogController::levelsDisabled, m_controller, &GameController::disableLogLevel); + connect(this, &Window::startDrawing, m_display, &Display::startDrawing, Qt::QueuedConnection); + connect(this, &Window::shutdown, m_display, &Display::stopDrawing); + connect(this, &Window::shutdown, m_controller, &GameController::closeGame); + connect(this, &Window::shutdown, m_logView, &QWidget::hide); + connect(this, &Window::shutdown, m_shaderView, &QWidget::hide); + connect(this, &Window::audioBufferSamplesChanged, m_controller, &GameController::setAudioBufferSamples); + connect(this, &Window::sampleRateChanged, m_controller, &GameController::setAudioSampleRate); + connect(this, &Window::fpsTargetChanged, m_controller, &GameController::setFPSTarget); + connect(&m_fpsTimer, &QTimer::timeout, this, &Window::showFPS); + connect(&m_focusCheck, &QTimer::timeout, this, &Window::focusCheck); connect(m_display, &Display::hideCursor, [this]() { if (static_cast(m_screenWidget->layout())->currentWidget() == m_display) { m_screenWidget->setCursor(Qt::BlankCursor); @@ -202,7 +202,7 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) connect(m_display, &Display::showCursor, [this]() { m_screenWidget->unsetCursor(); }); - connect(&m_inputController, SIGNAL(profileLoaded(const QString&)), m_shortcutController, SLOT(loadProfile(const QString&))); + connect(&m_inputController, &InputController::profileLoaded, m_shortcutController, &ShortcutController::loadProfile); m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL); m_fpsTimer.setInterval(FPS_TIMER_INTERVAL); @@ -452,8 +452,8 @@ void Window::selectPatch() { } void Window::openView(QWidget* widget) { - connect(this, SIGNAL(shutdown()), widget, SLOT(close())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), widget, SLOT(close())); + connect(this, &Window::shutdown, widget, &QWidget::close); + connect(m_controller, &GameController::gameStopped, widget, &QWidget::close); widget->setAttribute(Qt::WA_DeleteOnClose); widget->show(); } @@ -474,10 +474,10 @@ void Window::exportSharkport() { void Window::openSettingsWindow() { SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController); - connect(settingsWindow, SIGNAL(biosLoaded(int, const QString&)), m_controller, SLOT(loadBIOS(int, const QString&))); - connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver())); - connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart())); - connect(settingsWindow, SIGNAL(pathsChanged()), this, SLOT(reloadConfig())); + connect(settingsWindow, &SettingsView::biosLoaded, m_controller, &GameController::loadBIOS); + connect(settingsWindow, &SettingsView::audioDriverChanged, m_controller, &GameController::reloadAudioDriver); + connect(settingsWindow, &SettingsView::displayDriverChanged, this, &Window::mustRestart); + connect(settingsWindow, &SettingsView::pathsChanged, this, &Window::reloadConfig); openView(settingsWindow); } @@ -513,17 +513,17 @@ std::function Window::openTView() { void Window::openVideoWindow() { if (!m_videoView) { m_videoView = new VideoView(); - connect(m_videoView, SIGNAL(recordingStarted(mAVStream*)), m_controller, SLOT(setAVStream(mAVStream*))); - connect(m_videoView, SIGNAL(recordingStopped()), m_controller, SLOT(clearAVStream()), Qt::DirectConnection); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_videoView, SLOT(stopRecording())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_videoView, SLOT(close())); + connect(m_videoView, &VideoView::recordingStarted, m_controller, &GameController::setAVStream); + connect(m_videoView, &VideoView::recordingStopped, m_controller, &GameController::clearAVStream, Qt::DirectConnection); + connect(m_controller, &GameController::gameStopped, m_videoView, &VideoView::stopRecording); + connect(m_controller, &GameController::gameStopped, m_videoView, &QWidget::close); connect(m_controller, &GameController::gameStarted, [this]() { m_videoView->setNativeResolution(m_controller->screenDimensions()); }); if (m_controller->isLoaded()) { m_videoView->setNativeResolution(m_controller->screenDimensions()); } - connect(this, SIGNAL(shutdown()), m_videoView, SLOT(close())); + connect(this, &Window::shutdown, m_videoView, &QWidget::close); } m_videoView->show(); } @@ -533,11 +533,11 @@ void Window::openVideoWindow() { void Window::openGIFWindow() { if (!m_gifView) { m_gifView = new GIFView(); - connect(m_gifView, SIGNAL(recordingStarted(mAVStream*)), m_controller, SLOT(setAVStream(mAVStream*))); - connect(m_gifView, SIGNAL(recordingStopped()), m_controller, SLOT(clearAVStream()), Qt::DirectConnection); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_gifView, SLOT(stopRecording())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_gifView, SLOT(close())); - connect(this, SIGNAL(shutdown()), m_gifView, SLOT(close())); + connect(m_gifView, &GIFView::recordingStarted, m_controller, &GameController::setAVStream); + connect(m_gifView, &GIFView::recordingStopped, m_controller, &GameController::clearAVStream, Qt::DirectConnection); + connect(m_controller, &GameController::gameStopped, m_gifView, &GIFView::stopRecording); + connect(m_controller, &GameController::gameStopped, m_gifView, &QWidget::close); + connect(this, &Window::shutdown, m_gifView, &QWidget::close); } m_gifView->show(); } @@ -730,11 +730,11 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { return; } MutexUnlock(&context->stateMutex); - foreach (QAction* action, m_gameActions) { + for (QAction* action : m_gameActions) { action->setDisabled(false); } #ifdef M_CORE_GBA - foreach (QAction* action, m_gbaActions) { + for (QAction* action : m_gbaActions) { action->setDisabled(context->core->platform(context->core) != PLATFORM_GBA); } #endif @@ -800,11 +800,11 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) { void Window::gameStopped() { #ifdef M_CORE_GBA - foreach (QAction* action, m_gbaActions) { + for (QAction* action : m_gbaActions) { action->setDisabled(false); } #endif - foreach (QAction* action, m_gameActions) { + for (QAction* action : m_gameActions) { action->setDisabled(true); } setWindowFilePath(QString()); @@ -863,7 +863,7 @@ void Window::tryMakePortable() { tr("This will make the emulator load its configuration from the same directory as the executable. Do you want to continue?"), QMessageBox::Yes | QMessageBox::Cancel, this, Qt::Sheet); confirm->setAttribute(Qt::WA_DeleteOnClose); - connect(confirm->button(QMessageBox::Yes), SIGNAL(clicked()), m_config, SLOT(makePortable())); + connect(confirm->button(QMessageBox::Yes), &QAbstractButton::clicked, m_config, &ConfigController::makePortable); confirm->show(); } @@ -945,8 +945,8 @@ void Window::openStateWindow(LoadSave ls) { } bool wasPaused = m_controller->isPaused(); m_stateWindow = new LoadSaveState(m_controller); - connect(this, SIGNAL(shutdown()), m_stateWindow, SLOT(close())); - connect(m_controller, SIGNAL(gameStopped(mCoreThread*)), m_stateWindow, SLOT(close())); + connect(this, &Window::shutdown, m_stateWindow, &QWidget::close); + connect(m_controller, &GameController::gameStopped, m_stateWindow, &QWidget::close); connect(m_stateWindow, &LoadSaveState::closed, [this]() { detachWidget(m_stateWindow); m_stateWindow = nullptr; @@ -1024,13 +1024,13 @@ void Window::setupMenu(QMenuBar* menubar) { m_shortcutController->addMenu(quickSaveMenu); QAction* quickLoad = new QAction(tr("Load recent"), quickLoadMenu); - connect(quickLoad, SIGNAL(triggered()), m_controller, SLOT(loadState())); + connect(quickLoad, &QAction::triggered, m_controller, &GameController::loadState); m_gameActions.append(quickLoad); m_nonMpActions.append(quickLoad); addControlledAction(quickLoadMenu, quickLoad, "quickLoad"); QAction* quickSave = new QAction(tr("Save recent"), quickSaveMenu); - connect(quickSave, SIGNAL(triggered()), m_controller, SLOT(saveState())); + connect(quickSave, &QAction::triggered, m_controller, &GameController::saveState); m_gameActions.append(quickSave); m_nonMpActions.append(quickSave); addControlledAction(quickSaveMenu, quickSave, "quickSave"); @@ -1040,14 +1040,14 @@ void Window::setupMenu(QMenuBar* menubar) { QAction* undoLoadState = new QAction(tr("Undo load state"), quickLoadMenu); undoLoadState->setShortcut(tr("F11")); - connect(undoLoadState, SIGNAL(triggered()), m_controller, SLOT(loadBackupState())); + connect(undoLoadState, &QAction::triggered, m_controller, &GameController::loadBackupState); m_gameActions.append(undoLoadState); m_nonMpActions.append(undoLoadState); addControlledAction(quickLoadMenu, undoLoadState, "undoLoadState"); QAction* undoSaveState = new QAction(tr("Undo save state"), quickSaveMenu); undoSaveState->setShortcut(tr("Shift+F11")); - connect(undoSaveState, SIGNAL(triggered()), m_controller, SLOT(saveBackupState())); + connect(undoSaveState, &QAction::triggered, m_controller, &GameController::saveBackupState); m_gameActions.append(undoSaveState); m_nonMpActions.append(undoSaveState); addControlledAction(quickSaveMenu, undoSaveState, "undoSaveState"); @@ -1075,13 +1075,13 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef M_CORE_GBA fileMenu->addSeparator(); QAction* importShark = new QAction(tr("Import GameShark Save"), fileMenu); - connect(importShark, SIGNAL(triggered()), this, SLOT(importSharkport())); + connect(importShark, &QAction::triggered, this, &Window::importSharkport); m_gameActions.append(importShark); m_gbaActions.append(importShark); addControlledAction(fileMenu, importShark, "importShark"); QAction* exportShark = new QAction(tr("Export GameShark Save"), fileMenu); - connect(exportShark, SIGNAL(triggered()), this, SLOT(exportSharkport())); + connect(exportShark, &QAction::triggered, this, &Window::exportSharkport); m_gameActions.append(exportShark); m_gbaActions.append(exportShark); addControlledAction(fileMenu, exportShark, "exportShark"); @@ -1099,7 +1099,7 @@ void Window::setupMenu(QMenuBar* menubar) { #endif QAction* about = new QAction(tr("About"), fileMenu); - connect(about, SIGNAL(triggered()), this, SLOT(openAboutScreen())); + connect(about, &QAction::triggered, this, &Window::openAboutScreen); fileMenu->addAction(about); #ifndef Q_OS_MAC @@ -1110,18 +1110,18 @@ void Window::setupMenu(QMenuBar* menubar) { m_shortcutController->addMenu(emulationMenu); QAction* reset = new QAction(tr("&Reset"), emulationMenu); reset->setShortcut(tr("Ctrl+R")); - connect(reset, SIGNAL(triggered()), m_controller, SLOT(reset())); + connect(reset, &QAction::triggered, m_controller, &GameController::reset); m_gameActions.append(reset); addControlledAction(emulationMenu, reset, "reset"); QAction* shutdown = new QAction(tr("Sh&utdown"), emulationMenu); - connect(shutdown, SIGNAL(triggered()), m_controller, SLOT(closeGame())); + connect(shutdown, &QAction::triggered, m_controller, &GameController::closeGame); m_gameActions.append(shutdown); addControlledAction(emulationMenu, shutdown, "shutdown"); #ifdef M_CORE_GBA QAction* yank = new QAction(tr("Yank game pak"), emulationMenu); - connect(yank, SIGNAL(triggered()), m_controller, SLOT(yankPak())); + connect(yank, &QAction::triggered, m_controller, &GameController::yankPak); m_gameActions.append(yank); m_gbaActions.append(yank); addControlledAction(emulationMenu, yank, "yank"); @@ -1132,7 +1132,7 @@ void Window::setupMenu(QMenuBar* menubar) { pause->setChecked(false); pause->setCheckable(true); pause->setShortcut(tr("Ctrl+P")); - connect(pause, SIGNAL(triggered(bool)), m_controller, SLOT(setPaused(bool))); + connect(pause, &QAction::triggered, m_controller, &GameController::setPaused); connect(m_controller, &GameController::gamePaused, [this, pause]() { pause->setChecked(true); }); @@ -1142,7 +1142,7 @@ void Window::setupMenu(QMenuBar* menubar) { QAction* frameAdvance = new QAction(tr("&Next frame"), emulationMenu); frameAdvance->setShortcut(tr("Ctrl+N")); - connect(frameAdvance, SIGNAL(triggered()), m_controller, SLOT(frameAdvance())); + connect(frameAdvance, &QAction::triggered, m_controller, &GameController::frameAdvance); m_gameActions.append(frameAdvance); addControlledAction(emulationMenu, frameAdvance, "frameAdvance"); @@ -1182,7 +1182,7 @@ void Window::setupMenu(QMenuBar* menubar) { QAction* rewind = new QAction(tr("Re&wind"), emulationMenu); rewind->setShortcut(tr("~")); - connect(rewind, SIGNAL(triggered()), m_controller, SLOT(rewind())); + connect(rewind, &QAction::triggered, m_controller, &GameController::rewind); m_gameActions.append(rewind); m_nonMpActions.append(rewind); addControlledAction(emulationMenu, rewind, "rewind"); @@ -1215,11 +1215,11 @@ void Window::setupMenu(QMenuBar* menubar) { QMenu* solarMenu = emulationMenu->addMenu(tr("Solar sensor")); m_shortcutController->addMenu(solarMenu); QAction* solarIncrease = new QAction(tr("Increase solar level"), solarMenu); - connect(solarIncrease, SIGNAL(triggered()), m_controller, SLOT(increaseLuminanceLevel())); + connect(solarIncrease, &QAction::triggered, m_controller, &GameController::increaseLuminanceLevel); addControlledAction(solarMenu, solarIncrease, "increaseLuminanceLevel"); QAction* solarDecrease = new QAction(tr("Decrease solar level"), solarMenu); - connect(solarDecrease, SIGNAL(triggered()), m_controller, SLOT(decreaseLuminanceLevel())); + connect(solarDecrease, &QAction::triggered, m_controller, &GameController::decreaseLuminanceLevel); addControlledAction(solarMenu, solarDecrease, "decreaseLuminanceLevel"); QAction* maxSolar = new QAction(tr("Brightest solar level"), solarMenu); @@ -1309,7 +1309,7 @@ void Window::setupMenu(QMenuBar* menubar) { m_config->updateOption("frameskip"); QAction* shaderView = new QAction(tr("Shader options..."), avMenu); - connect(shaderView, SIGNAL(triggered()), m_shaderView, SLOT(show())); + connect(shaderView, &QAction::triggered, m_shaderView, &QWidget::show); if (!m_display->supportsShaders()) { shaderView->setEnabled(false); } @@ -1347,31 +1347,31 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef USE_PNG QAction* screenshot = new QAction(tr("Take &screenshot"), avMenu); screenshot->setShortcut(tr("F12")); - connect(screenshot, SIGNAL(triggered()), m_controller, SLOT(screenshot())); + connect(screenshot, &QAction::triggered, m_controller, &GameController::screenshot); m_gameActions.append(screenshot); addControlledAction(avMenu, screenshot, "screenshot"); #endif #ifdef USE_FFMPEG QAction* recordOutput = new QAction(tr("Record output..."), avMenu); - connect(recordOutput, SIGNAL(triggered()), this, SLOT(openVideoWindow())); + connect(recordOutput, &QAction::triggered, this, &Window::openVideoWindow); addControlledAction(avMenu, recordOutput, "recordOutput"); m_gameActions.append(recordOutput); #endif #ifdef USE_MAGICK QAction* recordGIF = new QAction(tr("Record GIF..."), avMenu); - connect(recordGIF, SIGNAL(triggered()), this, SLOT(openGIFWindow())); + connect(recordGIF, &QAction::triggered, this, &Window::openGIFWindow); addControlledAction(avMenu, recordGIF, "recordGIF"); #endif QAction* recordVL = new QAction(tr("Record video log..."), avMenu); - connect(recordVL, SIGNAL(triggered()), this, SLOT(startVideoLog())); + connect(recordVL, &QAction::triggered, this, &Window::startVideoLog); addControlledAction(avMenu, recordVL, "recordVL"); m_gameActions.append(recordVL); QAction* stopVL = new QAction(tr("Stop video log"), avMenu); - connect(stopVL, SIGNAL(triggered()), m_controller, SLOT(endVideoLog())); + connect(stopVL, &QAction::triggered, m_controller, &GameController::endVideoLog); addControlledAction(avMenu, stopVL, "stopVL"); m_gameActions.append(stopVL); @@ -1385,7 +1385,7 @@ void Window::setupMenu(QMenuBar* menubar) { QMenu* toolsMenu = menubar->addMenu(tr("&Tools")); m_shortcutController->addMenu(toolsMenu); QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu); - connect(viewLogs, SIGNAL(triggered()), m_logView, SLOT(show())); + connect(viewLogs, &QAction::triggered, m_logView, &QWidget::show); addControlledAction(toolsMenu, viewLogs, "viewLogs"); QAction* overrides = new QAction(tr("Game &overrides..."), toolsMenu); @@ -1409,13 +1409,13 @@ void Window::setupMenu(QMenuBar* menubar) { #ifdef USE_DEBUGGERS QAction* consoleWindow = new QAction(tr("Open debugger console..."), toolsMenu); - connect(consoleWindow, SIGNAL(triggered()), this, SLOT(consoleOpen())); + connect(consoleWindow, &QAction::triggered, this, &Window::consoleOpen); addControlledAction(toolsMenu, consoleWindow, "debuggerWindow"); #endif #ifdef USE_GDB_STUB QAction* gdbWindow = new QAction(tr("Start &GDB server..."), toolsMenu); - connect(gdbWindow, SIGNAL(triggered()), this, SLOT(gdbOpen())); + connect(gdbWindow, &QAction::triggered, this, &Window::gdbOpen); m_gbaActions.append(gdbWindow); addControlledAction(toolsMenu, gdbWindow, "gdbWindow"); #endif @@ -1513,7 +1513,7 @@ void Window::setupMenu(QMenuBar* menubar) { m_config->updateOption("preload"); QAction* exitFullScreen = new QAction(tr("Exit fullscreen"), frameMenu); - connect(exitFullScreen, SIGNAL(triggered()), this, SLOT(exitFullScreen())); + connect(exitFullScreen, &QAction::triggered, this, &Window::exitFullScreen); exitFullScreen->setShortcut(QKeySequence("Esc")); addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen"); @@ -1580,7 +1580,7 @@ void Window::setupMenu(QMenuBar* menubar) { m_controller->setAutofire(GBA_KEY_LEFT, false); }, QKeySequence(), tr("Autofire Left"), "autofireLeft"); - foreach (QAction* action, m_gameActions) { + for (QAction* action : m_gameActions) { action->setDisabled(true); } } From 11afd59cdaa630be06bdcd5b795a062806bb6042 Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Tue, 16 May 2017 14:54:57 -0400 Subject: [PATCH 126/159] Qt: Disable "New multiplayer window" when MAX_GBAS is reached. Fixes #107. --- src/platform/qt/GBAApp.cpp | 6 ++++++ src/platform/qt/Window.cpp | 6 +++--- src/platform/qt/Window.h | 3 +++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 088d490cc..b316f41ab 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -127,6 +127,9 @@ Window* GBAApp::newWindow() { int windowId = m_multiplayer.attached(); connect(w, &Window::destroyed, [this, w]() { m_windows.removeAll(w); + for (Window* w : m_windows) { + w->updateMultiplayerStatus(m_windows.count() < MAX_GBAS); + } }); m_windows.append(w); w->setAttribute(Qt::WA_DeleteOnClose); @@ -134,6 +137,9 @@ Window* GBAApp::newWindow() { w->show(); w->controller()->setMultiplayerController(&m_multiplayer); w->multiplayerChanged(); + for (Window* w : m_windows) { + w->updateMultiplayerStatus(m_windows.count() < MAX_GBAS); + } return w; } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 96eb04548..78956942e 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1088,11 +1088,11 @@ void Window::setupMenu(QMenuBar* menubar) { #endif fileMenu->addSeparator(); - QAction* multiWindow = new QAction(tr("New multiplayer window"), fileMenu); - connect(multiWindow, &QAction::triggered, [this]() { + m_multiWindow = new QAction(tr("New multiplayer window"), fileMenu); + connect(m_multiWindow, &QAction::triggered, [this]() { GBAApp::app()->newWindow(); }); - addControlledAction(fileMenu, multiWindow, "multiWindow"); + addControlledAction(fileMenu, m_multiWindow, "multiWindow"); #ifndef Q_OS_MAC fileMenu->addSeparator(); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index c9a5d465c..6875759d1 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -49,6 +49,8 @@ public: void resizeFrame(const QSize& size); + void updateMultiplayerStatus(bool canOpenAnother) { m_multiWindow->setEnabled(canOpenAnother); } + signals: void startDrawing(mCoreThread*); void shutdown(); @@ -160,6 +162,7 @@ private: #ifdef M_CORE_GBA QList m_gbaActions; #endif + QAction* m_multiWindow; QMap m_frameSizes; LogController m_log; LogView* m_logView; From 36fb807687480a75085559889d4796e093185d1b Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Tue, 16 May 2017 16:29:47 -0400 Subject: [PATCH 127/159] Qt: Relax hard dependency on OpenGL. --- src/platform/qt/CMakeLists.txt | 13 +++++++------ src/platform/qt/DisplayGL.cpp | 4 ++++ src/platform/qt/DisplayGL.h | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 25069a1dc..3c1d7e665 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -35,12 +35,10 @@ find_package(Qt5OpenGL) find_package(Qt5Widgets) if(NOT BUILD_GL AND NOT BUILD_GLES2) - message(WARNING "OpenGL is required to build the Qt port") - set(BUILD_QT OFF PARENT_SCOPE) - return() + message(WARNING "OpenGL is recommended to build the Qt port") endif() -if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND) +if(NOT Qt5Widgets_FOUND) message(WARNING "Cannot find Qt modules") set(BUILD_QT OFF PARENT_SCOPE) return() @@ -243,8 +241,11 @@ qt5_wrap_ui(UI_SRC ${UI_FILES}) add_executable(${BINARY_NAME}-qt WIN32 MACOSX_BUNDLE main.cpp ${CMAKE_SOURCE_DIR}/res/mgba.icns ${SOURCE_FILES} ${PLATFORM_SRC} ${UI_SRC} ${AUDIO_SRC} ${RESOURCES}) set_target_properties(${BINARY_NAME}-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/info.plist.in COMPILE_DEFINITIONS "${FEATURE_DEFINES};${FUNCTION_DEFINES};${OS_DEFINES};${QT_DEFINES}") -list(APPEND QT_LIBRARIES Qt5::Widgets Qt5::OpenGL) -target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES} ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) +list(APPEND QT_LIBRARIES Qt5::Widgets) +if(BUILD_GL OR BUILD_GLES2) + list(APPEND QT_LIBRARIES Qt5::OpenGL ${OPENGL_LIBRARY} ${OPENGLES2_LIBRARY}) +endif() +target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES}) set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS}" PARENT_SCOPE) install(TARGETS ${BINARY_NAME}-qt diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index da3333fd4..26dd88d65 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DisplayGL.h" +#if defined(BUILD_GL) || defined(BUILD_GLES) + #include #include #include @@ -483,3 +485,5 @@ void PainterGL::clearShaders() { VideoShader* PainterGL::shaders() { return &m_shader; } + +#endif diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 0a7227c01..437bea7a0 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -6,6 +6,8 @@ #ifndef QGBA_DISPLAY_GL #define QGBA_DISPLAY_GL +#if defined(BUILD_GL) || defined(BUILD_GLES) + #include "Display.h" #ifdef USE_EPOXY @@ -128,3 +130,5 @@ private: } #endif + +#endif From d04767eae23748c6937709b4f371702b84153675 Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Tue, 16 May 2017 16:52:18 -0400 Subject: [PATCH 128/159] All: Don't use enable_language(CXX) in the Qt subdirectory. Doing so causes CMake to create a pseudo-subproject when generating build files, which can cause all sorts of random, hard-to-diagnose issues. --- CMakeLists.txt | 4 ++-- src/platform/qt/CMakeLists.txt | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c75114dc3..ba6da81f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ -cmake_minimum_required(VERSION 2.6) -project(mGBA C) +cmake_minimum_required(VERSION 2.8.11) +project(mGBA) set(BINARY_NAME mgba CACHE INTERNAL "Name of output binaries") if(NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-missing-field-initializers -std=c99") diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3c1d7e665..b46a83a77 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -1,6 +1,3 @@ -cmake_minimum_required(VERSION 2.8.11) -enable_language(CXX) - if(NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") endif() From ccfb9f24f53c4d5d0d78573d46fe7bcea60126bd Mon Sep 17 00:00:00 2001 From: waddlesplash Date: Tue, 16 May 2017 16:57:36 -0400 Subject: [PATCH 129/159] Update CHANGES appropriately. --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index a3dfca799..756ea057b 100644 --- a/CHANGES +++ b/CHANGES @@ -55,6 +55,7 @@ Bugfixes: - Test: Fix crash when loading invalid file - GBA Hardware: Fix crash if a savestate lies about game hardware - Test: Fix crash when fuzzing fails to load a file + - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers @@ -113,6 +114,7 @@ Misc: - VFS: Add VFileFIFO for operating on circle buffers - Core: Move rewind diffing to its own thread - Util: Tune patch-fast extent sizes + - Qt: Relax hard dependency on OpenGL 0.5.2: (2016-12-31) Bugfixes: From 686380b6c4d8acf219a704a8932668b11efc47c2 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 May 2017 21:14:11 -0400 Subject: [PATCH 130/159] Qt: Resolve -Wreorder warnings --- src/platform/qt/AssetView.cpp | 2 +- src/platform/qt/DebuggerController.cpp | 2 +- src/platform/qt/GameController.cpp | 8 ++++---- src/platform/qt/IOViewer.h | 14 +++++++------- src/platform/qt/InputController.cpp | 10 +++++----- src/platform/qt/InputProfile.cpp | 4 ++-- src/platform/qt/KeyEditor.cpp | 4 ++-- src/platform/qt/LoadSaveState.cpp | 2 +- src/platform/qt/LogController.cpp | 4 ++-- src/platform/qt/MemoryModel.cpp | 2 +- src/platform/qt/ShortcutController.cpp | 2 +- src/platform/qt/Window.cpp | 16 ++++++++-------- 12 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/platform/qt/AssetView.cpp b/src/platform/qt/AssetView.cpp index 198776601..0077cae8a 100644 --- a/src/platform/qt/AssetView.cpp +++ b/src/platform/qt/AssetView.cpp @@ -13,8 +13,8 @@ using namespace QGBA; AssetView::AssetView(GameController* controller, QWidget* parent) : QWidget(parent) - , m_controller(controller) , m_tileCache(controller->tileCache()) + , m_controller(controller) { m_updateTimer.setSingleShot(true); m_updateTimer.setInterval(1); diff --git a/src/platform/qt/DebuggerController.cpp b/src/platform/qt/DebuggerController.cpp index 34f00f558..a1a7a96c5 100644 --- a/src/platform/qt/DebuggerController.cpp +++ b/src/platform/qt/DebuggerController.cpp @@ -11,8 +11,8 @@ using namespace QGBA; DebuggerController::DebuggerController(GameController* controller, mDebugger* debugger, QObject* parent) : QObject(parent) - , m_gameController(controller) , m_debugger(debugger) + , m_gameController(controller) { } diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index dcc466330..3ae7edc7c 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -48,6 +48,7 @@ GameController::GameController(QObject* parent) , m_gameOpen(false) , m_vf(nullptr) , m_useBios(false) + , m_override(nullptr) , m_audioProcessor(AudioProcessor::create()) , m_pauseAfterFrame(false) , m_sync(true) @@ -62,16 +63,15 @@ GameController::GameController(QObject* parent) , m_videoLayers() , m_autofire{} , m_autofireStatus{} - , m_inputController(nullptr) - , m_multiplayer(nullptr) - , m_stream(nullptr) , m_stateSlot(1) , m_backupLoadState(nullptr) , m_backupSaveState(nullptr) , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) , m_preload(false) - , m_override(nullptr) + , m_inputController(nullptr) + , m_multiplayer(nullptr) + , m_stream(nullptr) , m_vl(nullptr) , m_vlVf(nullptr) { diff --git a/src/platform/qt/IOViewer.h b/src/platform/qt/IOViewer.h index 9186cd4d2..c2451e08d 100644 --- a/src/platform/qt/IOViewer.h +++ b/src/platform/qt/IOViewer.h @@ -21,16 +21,16 @@ Q_OBJECT public: struct RegisterItem { RegisterItem(const QString& description, uint start, uint size = 1, bool readonly = false) - : description(description) - , start(start) + : start(start) , size(size) - , readonly(readonly) {} + , readonly(readonly) + , description(description) {} RegisterItem(const QString& description, uint start, uint size, QStringList items, bool readonly = false) - : description(description) - , start(start) + : start(start) , size(size) - , items(items) - , readonly(readonly) {} + , readonly(readonly) + , description(description) + , items(items) {} uint start; uint size; bool readonly; diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 2d8d8025d..35bf0c11f 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -26,16 +26,16 @@ mSDLEvents InputController::s_sdlEvents; InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) - , m_playerId(playerId) , m_config(nullptr) - , m_gamepadTimer(nullptr) + , m_playerId(playerId) + , m_allowOpposing(false) + , m_topLevel(topLevel) + , m_focusParent(topLevel) #ifdef BUILD_SDL , m_sdlPlayer{} , m_playerAttached(false) #endif - , m_allowOpposing(false) - , m_topLevel(topLevel) - , m_focusParent(topLevel) + , m_gamepadTimer(nullptr) { mInputMapInit(&m_inputMap, &GBAInputInfo); diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index 80aece552..aedc47f6e 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -179,8 +179,6 @@ constexpr InputProfile::InputProfile(const char* name, keys.keyR, keys.keyL, } - , m_shortcutButtons(shortcutButtons) - , m_shortcutAxes(shortcutAxes) , m_axes { axes.keyA, axes.keyB, @@ -193,6 +191,8 @@ constexpr InputProfile::InputProfile(const char* name, axes.keyR, axes.keyL, } + , m_shortcutButtons(shortcutButtons) + , m_shortcutAxes(shortcutAxes) , m_tiltAxis(tiltAxis) , m_gyroAxis(gyroAxis) , m_gyroSensitivity(gyroSensitivity) diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 65b3345a1..64463630a 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -16,12 +16,12 @@ using namespace QGBA; KeyEditor::KeyEditor(QWidget* parent) : QLineEdit(parent) - , m_direction(GamepadAxisEvent::NEUTRAL) - , m_hatDirection(GamepadHatEvent::CENTER) , m_key(-1) , m_axis(-1) , m_hat(-1) , m_button(false) + , m_direction(GamepadAxisEvent::NEUTRAL) + , m_hatDirection(GamepadHatEvent::CENTER) { setAlignment(Qt::AlignCenter); setFocusPolicy(Qt::ClickFocus); diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 820b5f2ed..40777000f 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -23,8 +23,8 @@ using namespace QGBA; LoadSaveState::LoadSaveState(GameController* controller, QWidget* parent) : QWidget(parent) , m_controller(controller) - , m_currentFocus(controller->stateSlot() - 1) , m_mode(LoadSave::LOAD) + , m_currentFocus(controller->stateSlot() - 1) { setAttribute(Qt::WA_TranslucentBackground); m_ui.setupUi(this); diff --git a/src/platform/qt/LogController.cpp b/src/platform/qt/LogController.cpp index 61208f0cc..5de4c74e5 100644 --- a/src/platform/qt/LogController.cpp +++ b/src/platform/qt/LogController.cpp @@ -72,9 +72,9 @@ QString LogController::toString(int level) { } LogController::Stream::Stream(LogController* controller, int level, int category) - : m_log(controller) - , m_level(level) + : m_level(level) , m_category(category) + , m_log(controller) { } diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index 215576b9f..70e0abe72 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -27,11 +27,11 @@ using namespace QGBA; MemoryModel::MemoryModel(QWidget* parent) : QAbstractScrollArea(parent) , m_core(nullptr) + , m_codec(nullptr) , m_top(0) , m_align(1) , m_selection(0, 0) , m_selectionAnchor(0) - , m_codec(nullptr) { m_font.setFamily("Source Code Pro"); m_font.setStyleHint(QFont::Monospace); diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index 8dce4ad8a..c207197eb 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -555,8 +555,8 @@ ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& n ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) : m_action(nullptr) , m_shortcut(shortcut) - , m_functions(functions) , m_menu(nullptr) + , m_functions(functions) , m_name(name) , m_visibleName(visibleName) , m_button(-1) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 78956942e..e048bbe3f 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -65,11 +65,19 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) : QMainWindow(parent) , m_log(0) , m_logView(new LogView(&m_log)) +#ifdef USE_DEBUGGERS + , m_console(nullptr) +#endif , m_stateWindow(nullptr) , m_screenWidget(new WindowBackground()) , m_logo(":/res/mgba-1024.png") , m_config(config) , m_inputController(playerId, this) + , m_mruMenu(nullptr) + , m_shortcutController(new ShortcutController(this)) + , m_fullscreenOnStart(false) + , m_autoresume(false) + , m_wasOpened(false) #ifdef USE_FFMPEG , m_videoView(nullptr) #endif @@ -79,14 +87,6 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent) #ifdef USE_GDB_STUB , m_gdbController(nullptr) #endif -#ifdef USE_DEBUGGERS - , m_console(nullptr) -#endif - , m_mruMenu(nullptr) - , m_shortcutController(new ShortcutController(this)) - , m_fullscreenOnStart(false) - , m_autoresume(false) - , m_wasOpened(false) { setFocusPolicy(Qt::StrongFocus); setAcceptDrops(true); From f6f3cb5d3d8b91dd603772ea0eebb2513562a0cf Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 16 May 2017 22:13:02 -0400 Subject: [PATCH 131/159] Qt: Use C++11 in-class initialization where applicable --- src/platform/qt/AssetTile.cpp | 4 -- src/platform/qt/AssetTile.h | 6 +-- src/platform/qt/AudioProcessor.cpp | 2 - src/platform/qt/AudioProcessor.h | 4 +- src/platform/qt/AudioProcessorQt.cpp | 3 -- src/platform/qt/AudioProcessorQt.h | 6 +-- src/platform/qt/AudioProcessorSDL.cpp | 1 - src/platform/qt/AudioProcessorSDL.h | 2 +- src/platform/qt/ConfigController.cpp | 1 - src/platform/qt/ConfigController.h | 2 +- src/platform/qt/Display.cpp | 3 -- src/platform/qt/Display.h | 6 +-- src/platform/qt/DisplayGL.cpp | 3 -- src/platform/qt/DisplayGL.h | 6 +-- src/platform/qt/DisplayQt.cpp | 2 - src/platform/qt/DisplayQt.h | 4 +- src/platform/qt/GBAApp.cpp | 1 - src/platform/qt/GBAApp.h | 2 +- src/platform/qt/GBAKeyEditor.cpp | 2 - src/platform/qt/GBAKeyEditor.h | 4 +- src/platform/qt/GDBController.cpp | 1 - src/platform/qt/GDBController.h | 2 +- src/platform/qt/GameController.cpp | 32 ------------- src/platform/qt/GameController.h | 62 +++++++++++++------------- src/platform/qt/InputController.cpp | 7 --- src/platform/qt/InputController.h | 10 ++--- src/platform/qt/KeyEditor.cpp | 4 -- src/platform/qt/KeyEditor.h | 8 ++-- src/platform/qt/LogView.cpp | 2 - src/platform/qt/LogView.h | 4 +- src/platform/qt/MemoryModel.cpp | 6 --- src/platform/qt/MemoryModel.h | 10 ++--- src/platform/qt/MessagePainter.cpp | 2 - src/platform/qt/MessagePainter.h | 4 +- src/platform/qt/ObjView.cpp | 3 -- src/platform/qt/ObjView.h | 6 +-- src/platform/qt/ShaderSelector.cpp | 1 - src/platform/qt/ShortcutController.cpp | 18 +------- src/platform/qt/ShortcutController.h | 16 +++---- src/platform/qt/ShortcutView.cpp | 2 - src/platform/qt/ShortcutView.h | 4 +- src/platform/qt/Swatch.cpp | 1 - src/platform/qt/Swatch.h | 2 +- src/platform/qt/TilePainter.cpp | 2 - src/platform/qt/TilePainter.h | 4 +- src/platform/qt/TileView.cpp | 2 - src/platform/qt/TileView.h | 4 +- src/platform/qt/VideoView.cpp | 7 --- src/platform/qt/VideoView.h | 14 +++--- src/platform/qt/Window.cpp | 19 -------- src/platform/qt/Window.h | 22 ++++----- 51 files changed, 109 insertions(+), 236 deletions(-) diff --git a/src/platform/qt/AssetTile.cpp b/src/platform/qt/AssetTile.cpp index 9ac748120..a97895c4b 100644 --- a/src/platform/qt/AssetTile.cpp +++ b/src/platform/qt/AssetTile.cpp @@ -21,10 +21,6 @@ using namespace QGBA; AssetTile::AssetTile(QWidget* parent) : QGroupBox(parent) - , m_tileCache(nullptr) - , m_paletteId(0) - , m_paletteSet(0) - , m_index(0) { m_ui.setupUi(this); diff --git a/src/platform/qt/AssetTile.h b/src/platform/qt/AssetTile.h index c3bf729f0..beb42a21a 100644 --- a/src/platform/qt/AssetTile.h +++ b/src/platform/qt/AssetTile.h @@ -31,9 +31,9 @@ private: Ui::AssetTile m_ui; std::shared_ptr m_tileCache; - int m_paletteId; - int m_paletteSet; - int m_index; + int m_paletteId = 0; + int m_paletteSet = 0; + int m_index = 0; int m_addressWidth; int m_addressBase; diff --git a/src/platform/qt/AudioProcessor.cpp b/src/platform/qt/AudioProcessor.cpp index da32a522d..6f0b49cbe 100644 --- a/src/platform/qt/AudioProcessor.cpp +++ b/src/platform/qt/AudioProcessor.cpp @@ -44,8 +44,6 @@ AudioProcessor* AudioProcessor::create() { AudioProcessor::AudioProcessor(QObject* parent) : QObject(parent) - , m_context(nullptr) - , m_samples(2048) { } diff --git a/src/platform/qt/AudioProcessor.h b/src/platform/qt/AudioProcessor.h index 4db45ac29..9636f0bd4 100644 --- a/src/platform/qt/AudioProcessor.h +++ b/src/platform/qt/AudioProcessor.h @@ -47,8 +47,8 @@ protected: mCoreThread* input() { return m_context; } private: - mCoreThread* m_context; - int m_samples; + mCoreThread* m_context = nullptr; + int m_samples = 2048; static Driver s_driver; }; diff --git a/src/platform/qt/AudioProcessorQt.cpp b/src/platform/qt/AudioProcessorQt.cpp index 334c82ecd..b802d825b 100644 --- a/src/platform/qt/AudioProcessorQt.cpp +++ b/src/platform/qt/AudioProcessorQt.cpp @@ -17,9 +17,6 @@ using namespace QGBA; AudioProcessorQt::AudioProcessorQt(QObject* parent) : AudioProcessor(parent) - , m_audioOutput(nullptr) - , m_device(nullptr) - , m_sampleRate(44100) { } diff --git a/src/platform/qt/AudioProcessorQt.h b/src/platform/qt/AudioProcessorQt.h index cec1fdd0c..ecf517a15 100644 --- a/src/platform/qt/AudioProcessorQt.h +++ b/src/platform/qt/AudioProcessorQt.h @@ -32,9 +32,9 @@ public slots: virtual void requestSampleRate(unsigned) override; private: - QAudioOutput* m_audioOutput; - AudioDevice* m_device; - unsigned m_sampleRate; + QAudioOutput* m_audioOutput = nullptr; + AudioDevice* m_device = nullptr; + unsigned m_sampleRate = 44100; }; } diff --git a/src/platform/qt/AudioProcessorSDL.cpp b/src/platform/qt/AudioProcessorSDL.cpp index 0e8d50af6..3980108e7 100644 --- a/src/platform/qt/AudioProcessorSDL.cpp +++ b/src/platform/qt/AudioProcessorSDL.cpp @@ -13,7 +13,6 @@ using namespace QGBA; AudioProcessorSDL::AudioProcessorSDL(QObject* parent) : AudioProcessor(parent) - , m_audio{ 2048, 44100 } { } diff --git a/src/platform/qt/AudioProcessorSDL.h b/src/platform/qt/AudioProcessorSDL.h index cd1d53957..e0e354729 100644 --- a/src/platform/qt/AudioProcessorSDL.h +++ b/src/platform/qt/AudioProcessorSDL.h @@ -33,7 +33,7 @@ public slots: virtual void requestSampleRate(unsigned) override; private: - mSDLAudio m_audio; + mSDLAudio m_audio{2048, 44100}; }; } diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index d32b66eba..7832b052c 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -90,7 +90,6 @@ QString ConfigController::s_configDir; ConfigController::ConfigController(QObject* parent) : QObject(parent) - , m_opts{} { QString fileName = configDir(); fileName.append(QDir::separator()); diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index c3a2e7759..3c3a9bca3 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -102,7 +102,7 @@ private: Configuration* defaults() { return &m_config.defaultsTable; } mCoreConfig m_config; - mCoreOptions m_opts; + mCoreOptions m_opts{}; QMap m_optionSet; QSettings* m_settings; diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 6c40ce01f..e300b9574 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -53,9 +53,6 @@ Display* Display::create(QWidget* parent) { Display::Display(QWidget* parent) : QWidget(parent) - , m_lockAspectRatio(false) - , m_lockIntegerScaling(false) - , m_filter(false) { setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); #ifdef M_CORE_GB diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index 1e3dc4e22..b7e04d263 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -75,9 +75,9 @@ private: static const int MOUSE_DISAPPEAR_TIMER = 1000; MessagePainter m_messagePainter; - bool m_lockAspectRatio; - bool m_lockIntegerScaling; - bool m_filter; + bool m_lockAspectRatio = false; + bool m_lockIntegerScaling = false; + bool m_filter = false; QTimer m_mouseTimer; }; diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 26dd88d65..af1cc6d51 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -27,10 +27,7 @@ using namespace QGBA; DisplayGL::DisplayGL(const QGLFormat& format, QWidget* parent) : Display(parent) - , m_isDrawing(false) , m_gl(new EmptyGLWidget(format, this)) - , m_drawThread(nullptr) - , m_context(nullptr) { m_painter = new PainterGL(format.majorVersion() < 2 ? 1 : m_gl->format().majorVersion(), m_gl); m_gl->setMouseTracking(true); diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 437bea7a0..f2f6dbc7c 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -70,11 +70,11 @@ protected: private: void resizePainter(); - bool m_isDrawing; + bool m_isDrawing = false; QGLWidget* m_gl; PainterGL* m_painter; - QThread* m_drawThread; - mCoreThread* m_context; + QThread* m_drawThread = nullptr; + mCoreThread* m_context = nullptr; }; class PainterGL : public QObject { diff --git a/src/platform/qt/DisplayQt.cpp b/src/platform/qt/DisplayQt.cpp index db17469d0..824df243e 100644 --- a/src/platform/qt/DisplayQt.cpp +++ b/src/platform/qt/DisplayQt.cpp @@ -14,8 +14,6 @@ using namespace QGBA; DisplayQt::DisplayQt(QWidget* parent) : Display(parent) - , m_isDrawing(false) - , m_backing(nullptr) { } diff --git a/src/platform/qt/DisplayQt.h b/src/platform/qt/DisplayQt.h index eb7ec5723..6fcab1b7a 100644 --- a/src/platform/qt/DisplayQt.h +++ b/src/platform/qt/DisplayQt.h @@ -40,10 +40,10 @@ protected: virtual void paintEvent(QPaintEvent*) override; private: - bool m_isDrawing; + bool m_isDrawing = false; unsigned m_width; unsigned m_height; - QImage m_backing; + QImage m_backing{nullptr}; }; } diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index b316f41ab..8abb6a21b 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -33,7 +33,6 @@ mLOG_DEFINE_CATEGORY(QT, "Qt", "platform.qt"); GBAApp::GBAApp(int& argc, char* argv[]) : QApplication(argc, argv) - , m_db(nullptr) { g_app = this; diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index be0e620d4..fec6056f3 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -71,7 +71,7 @@ private: QList m_windows; MultiplayerController m_multiplayer; - NoIntroDB* m_db; + NoIntroDB* m_db = nullptr; #ifdef USE_SQLITE3 QThread m_parseThread; #endif diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index c3aca14f1..0b58760eb 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -28,8 +28,6 @@ const qreal GBAKeyEditor::DPAD_HEIGHT = 0.12; GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent) : QWidget(parent) - , m_profileSelect(nullptr) - , m_clear(nullptr) , m_type(type) , m_profile(profile) , m_controller(controller) diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 1df22ff70..6070d7f50 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -68,8 +68,8 @@ private: KeyEditor* keyById(GBAKey); - QComboBox* m_profileSelect; - QWidget* m_clear; + QComboBox* m_profileSelect = nullptr; + QWidget* m_clear = nullptr; QWidget* m_buttons; KeyEditor* m_keyDU; KeyEditor* m_keyDD; diff --git a/src/platform/qt/GDBController.cpp b/src/platform/qt/GDBController.cpp index 402357154..5a72420a0 100644 --- a/src/platform/qt/GDBController.cpp +++ b/src/platform/qt/GDBController.cpp @@ -11,7 +11,6 @@ using namespace QGBA; GDBController::GDBController(GameController* controller, QObject* parent) : DebuggerController(controller, &m_gdbStub.d, parent) - , m_port(2345) , m_bindAddress({ IPV4, 0 }) { GDBStubCreate(&m_gdbStub); diff --git a/src/platform/qt/GDBController.h b/src/platform/qt/GDBController.h index 206c8a27e..fc8a6e6a1 100644 --- a/src/platform/qt/GDBController.h +++ b/src/platform/qt/GDBController.h @@ -40,7 +40,7 @@ private: GDBStub m_gdbStub; - ushort m_port; + ushort m_port = 2345; Address m_bindAddress; }; diff --git a/src/platform/qt/GameController.cpp b/src/platform/qt/GameController.cpp index 3ae7edc7c..0b48ac302 100644 --- a/src/platform/qt/GameController.cpp +++ b/src/platform/qt/GameController.cpp @@ -39,41 +39,9 @@ using namespace std; GameController::GameController(QObject* parent) : QObject(parent) - , m_drawContext(nullptr) - , m_frontBuffer(nullptr) - , m_threadContext() - , m_activeKeys(0) - , m_inactiveKeys(0) - , m_logLevels(0) - , m_gameOpen(false) - , m_vf(nullptr) - , m_useBios(false) - , m_override(nullptr) , m_audioProcessor(AudioProcessor::create()) - , m_pauseAfterFrame(false) - , m_sync(true) - , m_videoSync(VIDEO_SYNC) - , m_audioSync(AUDIO_SYNC) - , m_fpsTarget(-1) - , m_turbo(false) - , m_turboForced(false) - , m_turboSpeed(-1) - , m_wasPaused(false) - , m_audioChannels() - , m_videoLayers() - , m_autofire{} - , m_autofireStatus{} - , m_stateSlot(1) - , m_backupLoadState(nullptr) - , m_backupSaveState(nullptr) , m_saveStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_SAVEDATA | SAVESTATE_CHEATS | SAVESTATE_RTC) , m_loadStateFlags(SAVESTATE_SCREENSHOT | SAVESTATE_RTC) - , m_preload(false) - , m_inputController(nullptr) - , m_multiplayer(nullptr) - , m_stream(nullptr) - , m_vl(nullptr) - , m_vlVf(nullptr) { #ifdef M_CORE_GBA m_lux.p = this; diff --git a/src/platform/qt/GameController.h b/src/platform/qt/GameController.h index 86f02579d..111cbdada 100644 --- a/src/platform/qt/GameController.h +++ b/src/platform/qt/GameController.h @@ -191,63 +191,63 @@ private: void redoSamples(int samples); void enableTurbo(); - uint32_t* m_drawContext; - uint32_t* m_frontBuffer; - mCoreThread m_threadContext; + uint32_t* m_drawContext = nullptr; + uint32_t* m_frontBuffer = nullptr; + mCoreThread m_threadContext{}; const mCoreConfig* m_config; mCheatDevice* m_cheatDevice; - int m_activeKeys; - int m_activeButtons; - int m_inactiveKeys; - int m_logLevels; + int m_activeKeys = 0; + int m_activeButtons = 0; + int m_inactiveKeys = 0; + int m_logLevels = 0; - bool m_gameOpen; + bool m_gameOpen = false; QString m_fname; QString m_fsub; - VFile* m_vf; + VFile* m_vf = nullptr; QString m_bios; - bool m_useBios; + bool m_useBios = false; QString m_patch; - Override* m_override; + Override* m_override = nullptr; AudioProcessor* m_audioProcessor; - QAtomicInt m_pauseAfterFrame; + QAtomicInt m_pauseAfterFrame{false}; QList> m_resetActions; - bool m_sync; - bool m_videoSync; - bool m_audioSync; - float m_fpsTarget; - bool m_turbo; - bool m_turboForced; - float m_turboSpeed; - bool m_wasPaused; + bool m_sync = true; + bool m_videoSync = VIDEO_SYNC; + bool m_audioSync = AUDIO_SYNC; + float m_fpsTarget = -1; + bool m_turbo = false; + bool m_turboForced = false; + float m_turboSpeed = -1; + bool m_wasPaused = false; std::shared_ptr m_tileCache; QList m_audioChannels; QList m_videoLayers; - bool m_autofire[GBA_KEY_MAX]; - int m_autofireStatus[GBA_KEY_MAX]; + bool m_autofire[GBA_KEY_MAX] = {}; + int m_autofireStatus[GBA_KEY_MAX] = {}; - int m_stateSlot; - struct VFile* m_backupLoadState; - QByteArray m_backupSaveState; + int m_stateSlot = 1; + struct VFile* m_backupLoadState = nullptr; + QByteArray m_backupSaveState{nullptr}; int m_saveStateFlags; int m_loadStateFlags; - bool m_preload; + bool m_preload = false; - InputController* m_inputController; - MultiplayerController* m_multiplayer; + InputController* m_inputController = nullptr; + MultiplayerController* m_multiplayer = nullptr; - mAVStream* m_stream; + mAVStream* m_stream = nullptr; - mVideoLogContext* m_vl; - VFile* m_vlVf; + mVideoLogContext* m_vl = nullptr; + VFile* m_vlVf = nullptr; struct GameControllerLux : GBALuminanceSource { GameController* p; diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 35bf0c11f..8a643854f 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -26,16 +26,9 @@ mSDLEvents InputController::s_sdlEvents; InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) - , m_config(nullptr) , m_playerId(playerId) - , m_allowOpposing(false) , m_topLevel(topLevel) , m_focusParent(topLevel) -#ifdef BUILD_SDL - , m_sdlPlayer{} - , m_playerAttached(false) -#endif - , m_gamepadTimer(nullptr) { mInputMapInit(&m_inputMap, &GBAInputInfo); diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 95382ce80..460973426 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -104,17 +104,17 @@ private: void sendGamepadEvent(QEvent*); mInputMap m_inputMap; - ConfigController* m_config; + ConfigController* m_config = nullptr; int m_playerId; - bool m_allowOpposing; + bool m_allowOpposing = false; QWidget* m_topLevel; QWidget* m_focusParent; #ifdef BUILD_SDL static int s_sdlInited; static mSDLEvents s_sdlEvents; - mSDLPlayer m_sdlPlayer; - bool m_playerAttached; + mSDLPlayer m_sdlPlayer{}; + bool m_playerAttached = false; #endif QVector m_deadzones; @@ -122,7 +122,7 @@ private: QSet m_activeButtons; QSet> m_activeAxes; QSet> m_activeHats; - QTimer m_gamepadTimer; + QTimer m_gamepadTimer{nullptr}; QSet m_pendingEvents; }; diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 64463630a..002ba490f 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -16,10 +16,6 @@ using namespace QGBA; KeyEditor::KeyEditor(QWidget* parent) : QLineEdit(parent) - , m_key(-1) - , m_axis(-1) - , m_hat(-1) - , m_button(false) , m_direction(GamepadAxisEvent::NEUTRAL) , m_hatDirection(GamepadHatEvent::CENTER) { diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index 6fd097a85..e1a7fa949 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -54,10 +54,10 @@ private: void updateButtonText(); - int m_key; - int m_axis; - int m_hat; - bool m_button; + int m_key = -1; + int m_axis = -1; + int m_hat = -1; + bool m_button = false; GamepadAxisEvent::Direction m_direction; GamepadHatEvent::Direction m_hatDirection; QTimer m_lastKey; diff --git a/src/platform/qt/LogView.cpp b/src/platform/qt/LogView.cpp index 958b5e937..7ed748e78 100644 --- a/src/platform/qt/LogView.cpp +++ b/src/platform/qt/LogView.cpp @@ -14,8 +14,6 @@ using namespace QGBA; LogView::LogView(LogController* log, QWidget* parent) : QWidget(parent) - , m_lines(0) - , m_lineLimit(DEFAULT_LINE_LIMIT) { m_ui.setupUi(this); connect(m_ui.levelDebug, &QAbstractButton::toggled, [this](bool set) { diff --git a/src/platform/qt/LogView.h b/src/platform/qt/LogView.h index 8fa23d212..8151b6da8 100644 --- a/src/platform/qt/LogView.h +++ b/src/platform/qt/LogView.h @@ -40,8 +40,8 @@ private: static const int DEFAULT_LINE_LIMIT = 1000; Ui::LogView m_ui; - int m_lines; - int m_lineLimit; + int m_lines = 0; + int m_lineLimit = DEFAULT_LINE_LIMIT; QQueue m_pendingLines; void setLevel(int level, bool); diff --git a/src/platform/qt/MemoryModel.cpp b/src/platform/qt/MemoryModel.cpp index 70e0abe72..8b7a08dd7 100644 --- a/src/platform/qt/MemoryModel.cpp +++ b/src/platform/qt/MemoryModel.cpp @@ -26,12 +26,6 @@ using namespace QGBA; MemoryModel::MemoryModel(QWidget* parent) : QAbstractScrollArea(parent) - , m_core(nullptr) - , m_codec(nullptr) - , m_top(0) - , m_align(1) - , m_selection(0, 0) - , m_selectionAnchor(0) { m_font.setFamily("Source Code Pro"); m_font.setStyleHint(QFont::Monospace); diff --git a/src/platform/qt/MemoryModel.h b/src/platform/qt/MemoryModel.h index 07d442527..f74078528 100644 --- a/src/platform/qt/MemoryModel.h +++ b/src/platform/qt/MemoryModel.h @@ -76,22 +76,22 @@ private: void operator()(TextCodec*); }; - mCore* m_core; + mCore* m_core = nullptr; std::unique_ptr m_codec; QFont m_font; int m_cellHeight; int m_letterWidth; uint32_t m_base; uint32_t m_size; - int m_top; - int m_align; + int m_top = 0; + int m_align = 1; QMargins m_margins; QVector m_staticNumbers; QVector m_staticLatin1; QStaticText m_regionName; QSizeF m_cellSize; - QPair m_selection; - uint32_t m_selectionAnchor; + QPair m_selection{0, 0}; + uint32_t m_selectionAnchor = 0; uint32_t m_buffer; int m_bufferedNybbles; int m_currentBank; diff --git a/src/platform/qt/MessagePainter.cpp b/src/platform/qt/MessagePainter.cpp index c7d73fcfa..e62afe8f4 100644 --- a/src/platform/qt/MessagePainter.cpp +++ b/src/platform/qt/MessagePainter.cpp @@ -15,8 +15,6 @@ using namespace QGBA; MessagePainter::MessagePainter(QObject* parent) : QObject(parent) - , m_messageTimer(this) - , m_scaleFactor(1) { m_messageFont.setFamily("Source Code Pro"); m_messageFont.setStyleHint(QFont::Monospace); diff --git a/src/platform/qt/MessagePainter.h b/src/platform/qt/MessagePainter.h index 7e0bff53b..24a48b3a5 100644 --- a/src/platform/qt/MessagePainter.h +++ b/src/platform/qt/MessagePainter.h @@ -35,11 +35,11 @@ private: QStaticText m_message; QPixmap m_pixmap; QPixmap m_pixmapBuffer; - QTimer m_messageTimer; + QTimer m_messageTimer{this}; QPoint m_local; QTransform m_world; QFont m_messageFont; - qreal m_scaleFactor; + qreal m_scaleFactor = 1; }; } diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index cdaf8a21b..0fa19df8c 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -27,9 +27,6 @@ using namespace QGBA; ObjView::ObjView(GameController* controller, QWidget* parent) : AssetView(controller, parent) , m_controller(controller) - , m_tileStatus{} - , m_objId(0) - , m_objInfo{} { m_ui.setupUi(this); m_ui.tile->setController(controller); diff --git a/src/platform/qt/ObjView.h b/src/platform/qt/ObjView.h index 487a54264..0e03ce29c 100644 --- a/src/platform/qt/ObjView.h +++ b/src/platform/qt/ObjView.h @@ -41,8 +41,8 @@ private: Ui::ObjView m_ui; GameController* m_controller; - mTileCacheEntry m_tileStatus[1024 * 32]; // TODO: Correct size - int m_objId; + mTileCacheEntry m_tileStatus[1024 * 32] = {}; // TODO: Correct size + int m_objId = 0; struct ObjInfo { unsigned tile; unsigned width; @@ -53,7 +53,7 @@ private: unsigned bits; bool operator!=(const ObjInfo&); - } m_objInfo; + } m_objInfo = {}; int m_tileOffset; }; diff --git a/src/platform/qt/ShaderSelector.cpp b/src/platform/qt/ShaderSelector.cpp index fb3366888..9c3a9ce77 100644 --- a/src/platform/qt/ShaderSelector.cpp +++ b/src/platform/qt/ShaderSelector.cpp @@ -31,7 +31,6 @@ ShaderSelector::ShaderSelector(Display* display, ConfigController* config, QWidg : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint) , m_display(display) , m_config(config) - , m_shaderPath("") { m_ui.setupUi(this); diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index c207197eb..a8364c4af 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -17,9 +17,6 @@ using namespace QGBA; ShortcutController::ShortcutController(QObject* parent) : QAbstractItemModel(parent) - , m_rootMenu(nullptr) - , m_config(nullptr) - , m_profile(nullptr) { } @@ -540,10 +537,7 @@ int ShortcutController::toModifierKey(int key) { ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent) : m_action(action) , m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0]) - , m_menu(nullptr) , m_name(name) - , m_button(-1) - , m_axis(-1) , m_direction(GamepadAxisEvent::NEUTRAL) , m_parent(parent) { @@ -553,25 +547,17 @@ ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& n } ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent) - : m_action(nullptr) - , m_shortcut(shortcut) - , m_menu(nullptr) + : m_shortcut(shortcut) , m_functions(functions) , m_name(name) , m_visibleName(visibleName) - , m_button(-1) - , m_axis(-1) , m_direction(GamepadAxisEvent::NEUTRAL) , m_parent(parent) { } ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent) - : m_action(nullptr) - , m_shortcut(0) - , m_menu(menu) - , m_button(-1) - , m_axis(-1) + : m_menu(menu) , m_direction(GamepadAxisEvent::NEUTRAL) , m_parent(parent) { diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index b128d91e6..f0cc90148 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -69,14 +69,14 @@ private: } private: - QAction* m_action; - int m_shortcut; - QMenu* m_menu; + QAction* m_action = nullptr; + int m_shortcut = 0; + QMenu* m_menu = nullptr; Functions m_functions; QString m_name; QString m_visibleName; - int m_button; - int m_axis; + int m_button = -1; + int m_axis = -1; GamepadAxisEvent::Direction m_direction; QList m_items; ShortcutItem* m_parent; @@ -133,14 +133,14 @@ private: void onSubitems(ShortcutItem*, std::function func); void updateKey(ShortcutItem* item, int keySequence); - ShortcutItem m_rootMenu; + ShortcutItem m_rootMenu{nullptr}; QMap m_menuMap; QMap m_buttons; QMap, ShortcutItem*> m_axes; QMap m_heldKeys; - ConfigController* m_config; + ConfigController* m_config = nullptr; QString m_profileName; - const InputProfile* m_profile; + const InputProfile* m_profile = nullptr; }; } diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index 6a3a3edfb..d9ef44e95 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -15,8 +15,6 @@ using namespace QGBA; ShortcutView::ShortcutView(QWidget* parent) : QWidget(parent) - , m_controller(nullptr) - , m_input(nullptr) { m_ui.setupUi(this); m_ui.keyEdit->setValueKey(0); diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index 945f21d81..94314a84b 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -40,8 +40,8 @@ private slots: private: Ui::ShortcutView m_ui; - ShortcutController* m_controller; - InputController* m_input; + ShortcutController* m_controller = nullptr; + InputController* m_input = nullptr; }; } diff --git a/src/platform/qt/Swatch.cpp b/src/platform/qt/Swatch.cpp index 8c0e64212..111245413 100644 --- a/src/platform/qt/Swatch.cpp +++ b/src/platform/qt/Swatch.cpp @@ -15,7 +15,6 @@ using namespace QGBA; Swatch::Swatch(QWidget* parent) : QWidget(parent) { - m_size = 10; } void Swatch::setSize(int size) { diff --git a/src/platform/qt/Swatch.h b/src/platform/qt/Swatch.h index 266b3dca2..57fd08510 100644 --- a/src/platform/qt/Swatch.h +++ b/src/platform/qt/Swatch.h @@ -32,7 +32,7 @@ protected: void mousePressEvent(QMouseEvent*) override; private: - int m_size; + int m_size = 10; QVector m_colors; QPixmap m_backing; QSize m_dims; diff --git a/src/platform/qt/TilePainter.cpp b/src/platform/qt/TilePainter.cpp index 5354c48eb..a2d99d4af 100644 --- a/src/platform/qt/TilePainter.cpp +++ b/src/platform/qt/TilePainter.cpp @@ -13,9 +13,7 @@ using namespace QGBA; TilePainter::TilePainter(QWidget* parent) : QWidget(parent) - , m_size(8) { - m_backing = QPixmap(256, 768); m_backing.fill(Qt::transparent); resize(256, 768); setTileCount(3072); diff --git a/src/platform/qt/TilePainter.h b/src/platform/qt/TilePainter.h index 1c61c5e97..e76af6ae3 100644 --- a/src/platform/qt/TilePainter.h +++ b/src/platform/qt/TilePainter.h @@ -32,8 +32,8 @@ protected: void resizeEvent(QResizeEvent*) override; private: - QPixmap m_backing; - int m_size; + QPixmap m_backing{256, 768}; + int m_size = 8; int m_tileCount; }; diff --git a/src/platform/qt/TileView.cpp b/src/platform/qt/TileView.cpp index 6551efcb9..eb74a57a6 100644 --- a/src/platform/qt/TileView.cpp +++ b/src/platform/qt/TileView.cpp @@ -19,8 +19,6 @@ using namespace QGBA; TileView::TileView(GameController* controller, QWidget* parent) : AssetView(controller, parent) , m_controller(controller) - , m_tileStatus{} - , m_paletteId(0) { m_ui.setupUi(this); m_ui.tile->setController(controller); diff --git a/src/platform/qt/TileView.h b/src/platform/qt/TileView.h index 623d83a47..6e35df355 100644 --- a/src/platform/qt/TileView.h +++ b/src/platform/qt/TileView.h @@ -35,8 +35,8 @@ private: Ui::TileView m_ui; GameController* m_controller; - mTileCacheEntry m_tileStatus[3072 * 32]; // TODO: Correct size - int m_paletteId; + mTileCacheEntry m_tileStatus[3072 * 32] = {}; // TODO: Correct size + int m_paletteId = 0; }; } diff --git a/src/platform/qt/VideoView.cpp b/src/platform/qt/VideoView.cpp index 3011293c6..e5f28458e 100644 --- a/src/platform/qt/VideoView.cpp +++ b/src/platform/qt/VideoView.cpp @@ -45,13 +45,6 @@ bool VideoView::Preset::compatible(const Preset& other) const { VideoView::VideoView(QWidget* parent) : QWidget(parent) - , m_audioCodecCstr(nullptr) - , m_videoCodecCstr(nullptr) - , m_containerCstr(nullptr) - , m_nativeWidth(0) - , m_nativeHeight(0) - , m_width(1) - , m_height(1) { m_ui.setupUi(this); diff --git a/src/platform/qt/VideoView.h b/src/platform/qt/VideoView.h index aeb4c0b02..6f3ef3ecb 100644 --- a/src/platform/qt/VideoView.h +++ b/src/platform/qt/VideoView.h @@ -86,18 +86,18 @@ private: QString m_audioCodec; QString m_videoCodec; QString m_container; - char* m_audioCodecCstr; - char* m_videoCodecCstr; - char* m_containerCstr; + char* m_audioCodecCstr = nullptr; + char* m_videoCodecCstr = nullptr; + char* m_containerCstr = nullptr; int m_abr; int m_vbr; - int m_width; - int m_height; + int m_width = 1; + int m_height = 1; - int m_nativeWidth; - int m_nativeHeight; + int m_nativeWidth = 0; + int m_nativeHeight = 0; QMap m_presets; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index e048bbe3f..27a4dcaae 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -63,30 +63,11 @@ using namespace QGBA; Window::Window(ConfigController* config, int playerId, QWidget* parent) : QMainWindow(parent) - , m_log(0) , m_logView(new LogView(&m_log)) -#ifdef USE_DEBUGGERS - , m_console(nullptr) -#endif - , m_stateWindow(nullptr) , m_screenWidget(new WindowBackground()) - , m_logo(":/res/mgba-1024.png") , m_config(config) , m_inputController(playerId, this) - , m_mruMenu(nullptr) , m_shortcutController(new ShortcutController(this)) - , m_fullscreenOnStart(false) - , m_autoresume(false) - , m_wasOpened(false) -#ifdef USE_FFMPEG - , m_videoView(nullptr) -#endif -#ifdef USE_MAGICK - , m_gifView(nullptr) -#endif -#ifdef USE_GDB_STUB - , m_gdbController(nullptr) -#endif { setFocusPolicy(Qt::StrongFocus); setAcceptDrops(true); diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 6875759d1..7b1fec72a 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -164,41 +164,41 @@ private: #endif QAction* m_multiWindow; QMap m_frameSizes; - LogController m_log; + LogController m_log{0}; LogView* m_logView; #ifdef USE_DEBUGGERS - DebuggerConsoleController* m_console; + DebuggerConsoleController* m_console = nullptr; #endif - LoadSaveState* m_stateWindow; + LoadSaveState* m_stateWindow = nullptr; WindowBackground* m_screenWidget; - QPixmap m_logo; + QPixmap m_logo{":/res/mgba-1024.png"}; ConfigController* m_config; InputController m_inputController; QList m_frameList; QTimer m_fpsTimer; QList m_mruFiles; - QMenu* m_mruMenu; + QMenu* m_mruMenu = nullptr; QMenu* m_videoLayers; QMenu* m_audioChannels; ShortcutController* m_shortcutController; ShaderSelector* m_shaderView; - bool m_fullscreenOnStart; + bool m_fullscreenOnStart = false; QTimer m_focusCheck; - bool m_autoresume; - bool m_wasOpened; + bool m_autoresume = false; + bool m_wasOpened = false; bool m_hitUnimplementedBiosCall; #ifdef USE_FFMPEG - VideoView* m_videoView; + VideoView* m_videoView = nullptr; #endif #ifdef USE_MAGICK - GIFView* m_gifView; + GIFView* m_gifView = nullptr; #endif #ifdef USE_GDB_STUB - GDBController* m_gdbController; + GDBController* m_gdbController = nullptr; #endif #ifdef USE_SQLITE3 From 96ac3752767923535052840ec978bbd3d873236c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 19 May 2017 16:02:52 -0700 Subject: [PATCH 132/159] Qt: Work around cmake#16907 (fixes #739) --- src/platform/qt/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index b46a83a77..711e15b8c 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -289,3 +289,7 @@ if(APPLE) install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")") endif() endif() +if(WIN32 AND CMAKE_MAJOR_VERSION GREATER 2 AND CMAKE_MINOR_VERSION GREATER 7) + # Work around CMake issue #16907 + set_target_properties(${BINARY_NAME}-qt PROPERTIES AUTORCC ON SKIP_AUTORCC ON) +endif() From 5031ef7c39843dea17fc2df03f3d7e704c5261ff Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 17 May 2017 13:47:43 -0700 Subject: [PATCH 133/159] LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes #735) --- CHANGES | 1 + src/lr35902/decoder.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 756ea057b..95a5196a1 100644 --- a/CHANGES +++ b/CHANGES @@ -56,6 +56,7 @@ Bugfixes: - GBA Hardware: Fix crash if a savestate lies about game hardware - Test: Fix crash when fuzzing fails to load a file - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) + - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/lr35902/decoder.c b/src/lr35902/decoder.c index 6abefc6cc..796a50b17 100644 --- a/src/lr35902/decoder.c +++ b/src/lr35902/decoder.c @@ -66,8 +66,7 @@ DEFINE_DECODER_LR35902(NOP, info->mnemonic = LR35902_MN_NOP;) DEFINE_LD_DECODER_LR35902_MEM(NAME, HL) \ DEFINE_LD_DECODER_LR35902_MEM_2(NAME, HL) \ DEFINE_DECODER_LR35902(LD ## NAME ## _, info->mnemonic = LR35902_MN_LD; \ - info->op1.reg = LR35902_REG_A; \ - info->op1.flags = LR35902_OP_FLAG_IMPLICIT; \ + info->op1.reg = LR35902_REG_ ## NAME; \ return 1;) \ DEFINE_LD_DECODER_LR35902_NOHL(NAME) @@ -500,7 +499,10 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) { strncpy(buffer, "(", blen - 1); ADVANCE(1); } - if (op.immediate) { + if (op.reg) { + int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]); + ADVANCE(written); + } else { int written = snprintf(buffer, blen - 1, "$%02X", op.immediate); ADVANCE(written); if (op.reg) { @@ -508,10 +510,6 @@ static int _decodeOperand(struct LR35902Operand op, char* buffer, int blen) { ADVANCE(1); } } - if (op.reg) { - int written = snprintf(buffer, blen - 1, "%s", _lr35902Registers[op.reg]); - ADVANCE(written); - } if (op.flags & LR35902_OP_FLAG_INCREMENT) { strncpy(buffer, "+", blen - 1); ADVANCE(1); @@ -546,10 +544,12 @@ int LR35902Disassemble(struct LR35902InstructionInfo* info, char* buffer, int bl } } - written = _decodeOperand(info->op1, buffer, blen); - ADVANCE(written); + if (info->op1.reg || info->op1.immediate) { + written = _decodeOperand(info->op1, buffer, blen); + ADVANCE(written); + } - if (info->op2.reg || info->op2.immediate) { + if (info->op2.reg || (!info->op1.immediate && info->opcodeSize > 1 && info->opcode[0] != 0xCB)) { if (written) { strncpy(buffer, ", ", blen - 1); ADVANCE(2); From 53bcea63735d973c4fe09221d29dbb58545405ba Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 16 May 2017 23:37:20 -0700 Subject: [PATCH 134/159] GB: Fix STAT blocking --- CHANGES | 1 + src/gb/video.c | 76 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index 95a5196a1..36f78ebdd 100644 --- a/CHANGES +++ b/CHANGES @@ -57,6 +57,7 @@ Bugfixes: - Test: Fix crash when fuzzing fails to load a file - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) + - GB: Fix STAT blocking Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers diff --git a/src/gb/video.c b/src/gb/video.c index cf68e38e5..6b4ec735f 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -106,6 +106,33 @@ void GBVideoAssociateRenderer(struct GBVideo* video, struct GBVideoRenderer* ren video->renderer->init(video->renderer, video->p->model); } +static bool _statIRQAsserted(struct GBVideo* video, GBRegisterSTAT stat) { + // TODO: variable for the IRQ line value? + if (GBRegisterSTATIsLYCIRQ(stat) && GBRegisterSTATIsLYC(stat)) { + return true; + } + switch (GBRegisterSTATGetMode(stat)) { + case 0: + if (GBRegisterSTATIsHblankIRQ(stat)) { + return true; + } + break; + case 1: + if (GBRegisterSTATIsVblankIRQ(stat)) { + return true; + } + break; + case 2: + if (GBRegisterSTATIsOAMIRQ(stat)) { + return true; + } + break; + case 3: + break; + } + return false; +} + void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBVideo* video = context; if (video->frameskipCounter <= 0) { @@ -115,15 +142,13 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { int32_t next; ++video->ly; video->p->memory.io[REG_LY] = video->ly; + GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->ly); if (video->ly < GB_VIDEO_VERTICAL_PIXELS) { // TODO: Cache SCX & 7 in case it changes during mode 2 next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7); video->mode = 2; video->modeEvent.callback = _endMode2; - if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsOAMIRQ(video->stat)) { - video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); - } } else { next = GB_VIDEO_HORIZONTAL_LENGTH; video->mode = 1; @@ -131,16 +156,16 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { mTimingSchedule(&video->p->timing, &video->frameEvent, -cyclesLate); - if (GBRegisterSTATIsVblankIRQ(video->stat) || GBRegisterSTATIsOAMIRQ(video->stat)) { + if (!_statIRQAsserted(video, oldStat) && GBRegisterSTATIsOAMIRQ(video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } video->p->memory.io[REG_IF] |= (1 << GB_IRQ_VBLANK); } - if (!GBRegisterSTATIsHblankIRQ(video->stat) && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) { + video->stat = GBRegisterSTATSetMode(video->stat, video->mode); + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } GBUpdateIRQs(video->p); - video->stat = GBRegisterSTATSetMode(video->stat, video->mode); video->p->memory.io[REG_STAT] = video->stat; mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); } @@ -160,10 +185,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) { next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7); video->mode = 2; video->modeEvent.callback = _endMode2; - if (GBRegisterSTATIsOAMIRQ(video->stat)) { - video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); - GBUpdateIRQs(video->p); - } if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { video->p->memory.rotation->sample(video->p->memory.rotation); } @@ -178,9 +199,10 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) { next = GB_VIDEO_HORIZONTAL_LENGTH; } + GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetMode(video->stat, video->mode); video->stat = GBRegisterSTATSetLYC(video->stat, lyc == video->p->memory.io[REG_LY]); - if (video->ly && GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->p->memory.io[REG_LY]) { + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } @@ -196,7 +218,12 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) { int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7); video->mode = 3; video->modeEvent.callback = _endMode3; + GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetMode(video->stat, video->mode); + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { + video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); + GBUpdateIRQs(video->p); + } video->p->memory.io[REG_STAT] = video->stat; mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); } @@ -204,10 +231,6 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GBVideo* video = context; GBVideoProcessDots(video); - if (GBRegisterSTATIsHblankIRQ(video->stat)) { - video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); - GBUpdateIRQs(video->p); - } if (video->ly < GB_VIDEO_VERTICAL_PIXELS && video->p->memory.isHdma && video->p->memory.io[REG_HDMA5] != 0xFF) { video->p->memory.hdmaRemaining = 0x10; mTimingDeschedule(timing, &video->p->memory.hdmaEvent); @@ -215,7 +238,12 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) { } video->mode = 0; video->modeEvent.callback = _endMode0; + GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetMode(video->stat, video->mode); + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { + video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); + GBUpdateIRQs(video->p); + } video->p->memory.io[REG_STAT] = video->stat; int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11; mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); @@ -318,9 +346,10 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { video->ly = 0; video->p->memory.io[REG_LY] = 0; // TODO: Does this read as 0 for 4 T-cycles? + GBRegisterSTAT oldStat = video->stat; video->stat = GBRegisterSTATSetMode(video->stat, 2); video->stat = GBRegisterSTATSetLYC(video->stat, video->ly == video->p->memory.io[REG_LYC]); - if (GBRegisterSTATIsLYCIRQ(video->stat) && video->ly == video->p->memory.io[REG_LYC]) { + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } @@ -344,20 +373,21 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) { } void GBVideoWriteSTAT(struct GBVideo* video, GBRegisterSTAT value) { + GBRegisterSTAT oldStat = video->stat; video->stat = (video->stat & 0x7) | (value & 0x78); - if (video->p->model == GB_MODEL_DMG && video->mode == 1) { + if (video->p->model == GB_MODEL_DMG && !_statIRQAsserted(video, oldStat) && video->mode < 3) { + // TODO: variable for the IRQ line value? video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); GBUpdateIRQs(video->p); } } void GBVideoWriteLYC(struct GBVideo* video, uint8_t value) { - if (video->mode == 2) { - video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly); - if (GBRegisterSTATIsLYCIRQ(video->stat) && value == video->ly) { - video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); - GBUpdateIRQs(video->p); - } + GBRegisterSTAT oldStat = video->stat; + video->stat = GBRegisterSTATSetLYC(video->stat, value == video->ly); + if (!_statIRQAsserted(video, oldStat) && _statIRQAsserted(video, video->stat)) { + video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); + GBUpdateIRQs(video->p); } } From b5c350bc5bfdf3845779b430496712d7b6ebf004 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 17 May 2017 00:00:29 -0700 Subject: [PATCH 135/159] GB Video: Improved video timings (again) --- CHANGES | 1 + include/mgba/internal/gb/video.h | 6 +++--- src/gb/video.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 36f78ebdd..069d8fac6 100644 --- a/CHANGES +++ b/CHANGES @@ -117,6 +117,7 @@ Misc: - Core: Move rewind diffing to its own thread - Util: Tune patch-fast extent sizes - Qt: Relax hard dependency on OpenGL + - GB Video: Improved video timings 0.5.2: (2016-12-31) Bugfixes: diff --git a/include/mgba/internal/gb/video.h b/include/mgba/internal/gb/video.h index ce111662d..60534636b 100644 --- a/include/mgba/internal/gb/video.h +++ b/include/mgba/internal/gb/video.h @@ -20,9 +20,9 @@ enum { GB_VIDEO_VERTICAL_TOTAL_PIXELS = 154, // TODO: Figure out exact lengths - GB_VIDEO_MODE_2_LENGTH = 76, - GB_VIDEO_MODE_3_LENGTH_BASE = 171, - GB_VIDEO_MODE_0_LENGTH_BASE = 209, + GB_VIDEO_MODE_2_LENGTH = 80, + GB_VIDEO_MODE_3_LENGTH_BASE = 172, + GB_VIDEO_MODE_0_LENGTH_BASE = 204, GB_VIDEO_HORIZONTAL_LENGTH = 456, diff --git a/src/gb/video.c b/src/gb/video.c index 6b4ec735f..8794a953c 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -215,7 +215,7 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) { _cleanOAM(video, video->ly); video->x = 0; video->dotClock = timing->masterCycles - cyclesLate; - int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 11 - (video->p->memory.io[REG_SCX] & 7); + int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 6 - (video->p->memory.io[REG_SCX] & 7); video->mode = 3; video->modeEvent.callback = _endMode3; GBRegisterSTAT oldStat = video->stat; @@ -245,7 +245,7 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) { GBUpdateIRQs(video->p); } video->p->memory.io[REG_STAT] = video->stat; - int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 11; + int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 6; mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); } From 65d70f474deb78803859c6626da66222ff95a467 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 19 May 2017 16:20:56 -0700 Subject: [PATCH 136/159] All: Kludge buildfix for ports --- CMakeLists.txt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba6da81f1..cd754fd90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,6 +303,7 @@ else() endif() set(DISABLE_FRONTENDS ON) set(MINIMAL_CORE ON) + set(ENABLE_EXTRA ON) endif() check_function_exists(chmod HAVE_CHMOD) @@ -664,19 +665,30 @@ list(APPEND TEST_SRC ${UTIL_TEST_SRC}) set(SRC ${CORE_SRC} ${VFS_SRC}) if(NOT MINIMAL_CORE) + set(ENABLE_EXTRA ON) if(M_CORE_GBA) list(APPEND SRC - ${GBA_RR_SRC} - ${GBA_EXTRA_SRC} ${GBA_SIO_SRC}) endif() if(M_CORE_GB) list(APPEND SRC - ${GB_EXTRA_SRC} ${GB_SIO_SRC}) endif() list(APPEND SRC - ${FEATURE_SRC} + ${FEATURE_SRC}) +endif() + +if(ENABLE_EXTRA) + if(M_CORE_GBA) + list(APPEND SRC + ${GBA_RR_SRC} + ${GBA_EXTRA_SRC}) + endif() + if(M_CORE_GB) + list(APPEND SRC + ${GB_EXTRA_SRC}) + endif() + list(APPEND SRC ${EXTRA_SRC}) endif() From 29d1da69ad8a15ccb7b8586560891a6f8bce6b35 Mon Sep 17 00:00:00 2001 From: Lothar Serra Mari Date: Sat, 20 May 2017 09:00:22 +0200 Subject: [PATCH 137/159] Qt: Update German GUI translation Add some strings for the improved library view. --- src/platform/qt/ts/mgba-de.ts | 622 +++++++++++++++++----------------- 1 file changed, 311 insertions(+), 311 deletions(-) diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index a5ec7e42c..e88df58d0 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -302,11 +302,31 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - LibraryView + LibraryTree - - Library - Bibliothek + + Name + Name + + + + Location + Ort + + + + Platform + Plattform + + + + Size + Größe + + + + CRC32 + CRC32 @@ -903,14 +923,14 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::AssetTile - + %0%1%2 %0%1%2 - - - + + + 0x%0 (%1) 0x%0 (%1) @@ -961,22 +981,22 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::GBAKeyEditor - + Clear Button Button löschen - + Clear Analog Analog löschen - + Refresh Aktualisieren - + Set all Alle belegen @@ -1027,17 +1047,17 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::GIFView - + Failed to open output GIF file: %1 Fehler beim Öffnen der Ausgabe-GIF-Datei: %1 - + Select output file Ausgabedatei auswählen - + Graphics Interchange Format (*.gif) Graphics Interchange Format (*.gif) @@ -1045,28 +1065,28 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::GameController - - + + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open snapshot file for reading: %1 Konnte Snapshot-Datei %1 nicht zum Lesen öffnen - + Failed to open snapshot file for writing: %1 Konnte Snapshot-Datei %1 nicht zum Schreiben öffnen - + Failed to start audio processor Fehler beim Starten des Audio-Prozessors @@ -1265,7 +1285,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. Overflow wraps - Umbrüche + Umbrüche @@ -1309,7 +1329,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. Integer part - Ganzzahl-Anteil + Ganzzahl-Anteil @@ -1317,7 +1337,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. Integer part (bottom) - Ganzzahl-Anteil (unten) + Ganzzahl-Anteil (unten) @@ -1325,7 +1345,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. Integer part (top) - Ganzzahl-Anteil (oben) + Ganzzahl-Anteil (oben) @@ -1414,32 +1434,32 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. Outside window enable BG 0 - Äußeres Fenster: aktiviere BG 0 + Äußeres Fenster: aktiviere BG 0 Outside window enable BG 1 - Äußeres Fenster: aktiviere BG 1 + Äußeres Fenster: aktiviere BG 1 Outside window enable BG 2 - Äußeres Fenster: aktiviere BG 2 + Äußeres Fenster: aktiviere BG 2 Outside window enable BG 3 - Äußeres Fenster: aktiviere BG 3 + Äußeres Fenster: aktiviere BG 3 Outside window enable OBJ - Äußeres Fenster: aktiviere OBJ + Äußeres Fenster: aktiviere OBJ Outside window enable blend - Äußeres Fenster: aktiviere Überblendung + Äußeres Fenster: aktiviere Überblendung @@ -2442,60 +2462,12 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::KeyEditor - - + + --- --- - - QGBA::LibraryModel - - - Name - Name - - - - Filename - Dateiname - - - - Size - Größe - - - - Platform - Plattform - - - - GBA - GBA - - - - GB - GB - - - - ? - ? - - - - Location - Ort - - - - CRC32 - CRC32 - - QGBA::LoadSaveState @@ -2565,63 +2537,63 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::MemoryModel - + Copy selection Auswahl kopieren - + Save selection Auswahl speichern - + Paste Einfügen - + Load Laden - - + + All Alle - + Load TBL TBL laden - + Save selected memory Ausgewählten Speicher abspeichern - + Failed to open output file: %1 Fehler beim Öffnen der Ausgabedatei: %1 - + Load memory Lade Speicher - + Failed to open input file: %1 Fehler beim Laden der Eingabedatei: %1 - + TBL TBL - + ISO-8859-1 ISO-8859-1 @@ -2629,54 +2601,54 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::ObjView - - + + 0x%0 0x%0 - + Off Aus - + Normal Normal - + Trans Trans - + OBJWIN OBJWIN - + Invalid Ungültig - - + + N/A N/A - + Export sprite Sprite exportieren - + Portable Network Graphics (*.png) Portable Network Graphics (*.png) - + Failed to open output PNG file: %1 Fehler beim Öffnen der Ausgabe-PNG-Datei: %1 @@ -2795,37 +2767,37 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::ShaderSelector - + No shader active Kein Shader aktiv - + Load shader Shader laden - + %1 Shader (%.shader) %1 Shader (%.shader) - + No shader loaded Kein Shader geladen - + by %1 von %1 - + Preprocessing Vorbehandlung - + Pass %1 Durchlauf %1 @@ -2833,17 +2805,17 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::ShortcutController - + Action Aktion - + Keyboard Tastatur - + Gamepad Gamepad @@ -2851,17 +2823,17 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::VideoView - + Failed to open output video file: %1 Fehler beim Öffnen der Ausgabe-Videodatei: %1 - + Native (%0x%1) Nativ (%0x%1) - + Select output file Ausgabedatei auswählen @@ -2869,82 +2841,82 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. QGBA::Window - + Game Boy Advance ROMs (%1) Game Boy Advance-ROMs (%1) - + Game Boy ROMs (%1) Game Boy-ROMs (%1) - + All ROMs (%1) Alle ROMs (%1) - + %1 Video Logs (*.mvl) %1 Video-Logs (*.mvl) - + Archives (%1) Archive (%1) - - - + + + Select ROM ROM auswählen - + Game Boy Advance save files (%1) Game Boy Advance-Speicherdateien (%1) - - - + + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Patches (*.ips *.ups *.bps) - - + + GameShark saves (*.sps *.xps) GameShark-Speicherdaten (*.sps *.xps) - + Select video log Video-Log auswählen - + Video logs (*.mvl) Video-Logs (*.mvl) - + Crash Absturz - + The game has crashed with the following error: %1 @@ -2953,627 +2925,645 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - + Couldn't Load Konnte nicht geladen werden - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Sind Sie sicher, dass es im korrekten Format vorliegt? - + Unimplemented BIOS call Nicht implementierter BIOS-Aufruf - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Dieses Spiel verwendet einen BIOS-Aufruf, der nicht implementiert ist. Bitte verwenden Sie für die beste Spielerfahrung das offizielle BIOS. - + Really make portable? Portablen Modus wirklich aktivieren? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Diese Einstellung wird den Emulator so konfigurieren, dass er seine Konfiguration aus dem gleichen Verzeichnis wie die Programmdatei lädt. Möchten Sie fortfahren? - + Restart needed Neustart benötigt - + Some changes will not take effect until the emulator is restarted. Einige Änderungen werden erst übernommen, wenn der Emulator neu gestartet wurde. - + - Player %1 of %2 - Spieler %1 von %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 Bilder/Sekunde) - %4 - + &File &Datei - + Load &ROM... &ROM laden... - + Load ROM in archive... ROM aus Archiv laden... - + Load temporary save... Temporäre Speicherdatei laden... - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate &laden - + F10 F10 - + &Save state Savestate &speichern - + Shift+F10 Umschalt+F10 - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Stand - + Undo load state Laden des Savestate rückgängig machen - + F11 F11 - + Undo save state Speichern des Savestate rückgängig machen - + Shift+F11 Umschalt+F11 - - + + State &%1 Savestate &%1 - + F%1 F%1 - + Shift+F%1 Umschalt+F%1 - + Import GameShark Save Importiere GameShark-Speicherstand - + Export GameShark Save Exportiere GameShark-Speicherstand - + New multiplayer window Neues Multiplayer-Fenster - + About Über - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Ctrl+R Strg+R - + Sh&utdown B&eenden - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + Ctrl+P Strg+P - + &Next frame &Nächstes Bild - + Ctrl+N Strg+N - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Shift+Tab Umschalt+Tab - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + ~ ~ - + Step backwards Schrittweiser Rücklauf - + Ctrl+B Strg+B - + Sync to &video Mit &Video synchronisieren - + Sync to &audio Mit &Audio synchronisieren - + Solar sensor Solar-Sensor - + Increase solar level Sonnen-Level erhöhen - + Decrease solar level Sonnen-Level verringern - + Brightest solar level Hellster Sonnen-Level - + Darkest solar level Dunkelster Sonnen-Level - + Brightness %1 Helligkeit %1 - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + %1x %1x - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Frame&skip Frame&skip - + Shader options... Shader-Optionen... - + Mute Stummschalten - + FPS target Bildwiederholrate - + 15 15 - + 30 30 - + 45 45 - + Native (59.7) Nativ (59.7) - + 60 60 - + 90 90 - + 120 120 - + 240 240 - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Record output... Ausgabe aufzeichen... - + Record GIF... GIF aufzeichen... - + Record video log... Video-Log aufzeichnen... - + Stop video log Video-Log beenden - + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + Game &Pak sensors... Game &Pak-Sensoren... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole äffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... - + Select folder Ordner auswählen - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Bilinear filtering Bilineare Filterung - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View memory... Speicher betrachten... - + View &I/O registers... &I/O-Register betrachten... - + Exit fullscreen Vollbildmodus beenden - + Autofire Autofeuer - + Autofire A Autofeuer A - + Autofire B Autofeuer B - + Autofire L Autofeuer L - + Autofire R Autofeuer R - + Autofire Start Autofeuer Start - + Autofire Select Autofeuer Select - + Autofire Up Autofeuer nach oben - + Autofire Right Autofeuer rechts - + Autofire Down Autofeuer nach unten - + Autofire Left Autofeuer links + + QObject + + + GBA + GBA + + + + GB + GB + + + + ? + ? + + ROMInfo @@ -3843,7 +3833,7 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd. - + frames Bilder @@ -3883,72 +3873,82 @@ Game Boy Advance ist ein eingetragenes Warenzeichen von Nintendo Co., Ltd.Erzwinge pixelgenaue Skalierung (Integer scaling) + + + List view + Listenansicht + + + + Tree view + Baumansicht + Library: Bibliothek: - + Show when no game open Anzeigen, wenn kein Spiel geöffnet ist - + Clear cache Cache leeren - + Fast forward speed: Vorlauf-Geschwindigkeit: - + Rewind affects save data Rücklauf beeinflusst Speicherdaten - + Preload entire ROM into memory ROM-Datei vollständig in Arbeitsspeicher vorladen - - - - - - - + + + + + + + Browse Durchsuchen - + Use BIOS file if found - BIOS-Datei verwenden, -sofern vorhanden + BIOS-Datei verwenden, +wenn vorhanden - + Skip BIOS intro BIOS-Intro überspringen - + × × - + Unbounded unbegrenzt - + Suspend screensaver Bildschirmschoner deaktivieren @@ -3958,50 +3958,50 @@ sofern vorhanden BIOS - + Pause when inactive Pause, wenn inaktiv - + Run all Alle ausführen - + Remove known Bekannte entfernen - + Detect and remove Erkennen und entfernen - + Allow opposing input directions Gegensätzliche Eingaberichtungen erlauben - - + + Screenshot Screenshot - - + + Save data Speicherdaten - - + + Cheat codes Cheat-Codes - + Enable rewind Rücklauf aktivieren @@ -4011,65 +4011,65 @@ sofern vorhanden Bilineare Filterung - + Rewind history: Rücklauf-Verlauf: - + Idle loops: Leerlaufprozesse: - + Savestate extra data: Zusätzliche Savestate-Daten: - + Load extra data: Lade zusätzliche Daten: - + GB BIOS file: Datei mit GB-BIOS: - + GBA BIOS file: Datei mit GBA-BIOS: - + GBC BIOS file: Datei mit GBC-BIOS: - + Save games Spielstände - - - - + + + + Same directory as the ROM Verzeichnis der ROM-Datei - + Save states Savestates - + Screenshots Screenshots - + Patches Patches From b8c6bba71205aad260a8aec5ec12fe2ad094d215 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 19:14:50 -0700 Subject: [PATCH 138/159] Debugger: Allow segment values for read/write --- src/debugger/cli-debugger.c | 60 +++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 96fc1e1a2..967ed8e90 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -209,7 +209,12 @@ static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { return; } uint32_t address = dv->intValue; - uint8_t value = debugger->d.core->busRead8(debugger->d.core, address); + uint8_t value; + if (dv->segmentValue >= 0) { + value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue); + } else { + value = debugger->d.core->busRead8(debugger->d.core, address); + } debugger->backend->printf(debugger->backend, " 0x%02X\n", value); } @@ -225,7 +230,12 @@ static void _readHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d return; } uint32_t address = dv->intValue; - uint16_t value = debugger->d.core->busRead16(debugger->d.core, address & ~1); + uint16_t value; + if (dv->segmentValue >= 0) { + value = debugger->d.core->rawRead16(debugger->d.core, address & -1, dv->segmentValue); + } else { + value = debugger->d.core->busRead16(debugger->d.core, address & ~1); + } debugger->backend->printf(debugger->backend, " 0x%04X\n", value); } @@ -235,7 +245,12 @@ static void _readWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { return; } uint32_t address = dv->intValue; - uint32_t value = debugger->d.core->busRead32(debugger->d.core, address & ~3); + uint32_t value; + if (dv->segmentValue >= 0) { + value = debugger->d.core->rawRead32(debugger->d.core, address & -3, dv->segmentValue); + } else { + value = debugger->d.core->busRead32(debugger->d.core, address & ~3); + } debugger->backend->printf(debugger->backend, " 0x%08X\n", value); } @@ -254,7 +269,11 @@ static void _writeByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW); return; } - debugger->d.core->busWrite8(debugger->d.core, address, value); + if (dv->segmentValue >= 0) { + debugger->d.core->rawWrite8(debugger->d.core, address, value, dv->segmentValue); + } else { + debugger->d.core->busWrite8(debugger->d.core, address, value); + } } static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -272,7 +291,11 @@ static void _writeHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* debugger->backend->printf(debugger->backend, "%s\n", ERROR_OVERFLOW); return; } - debugger->d.core->busWrite16(debugger->d.core, address, value); + if (dv->segmentValue >= 0) { + debugger->d.core->rawWrite16(debugger->d.core, address, value, dv->segmentValue); + } else { + debugger->d.core->busWrite16(debugger->d.core, address, value); + } } static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -286,7 +309,11 @@ static void _writeWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) } uint32_t address = dv->intValue; uint32_t value = dv->next->intValue; - debugger->d.core->busWrite32(debugger->d.core, address, value); + if (dv->segmentValue >= 0) { + debugger->d.core->rawWrite32(debugger->d.core, address, value, dv->segmentValue); + } else { + debugger->d.core->busWrite32(debugger->d.core, address, value); + } } static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -306,7 +333,12 @@ static void _dumpByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } debugger->backend->printf(debugger->backend, "0x%08X:", address); for (; line > 0; --line, ++address, --words) { - uint32_t value = debugger->d.core->busRead8(debugger->d.core, address); + uint32_t value; + if (dv->segmentValue >= 0) { + value = debugger->d.core->rawRead8(debugger->d.core, address, dv->segmentValue); + } else { + value = debugger->d.core->busRead8(debugger->d.core, address); + } debugger->backend->printf(debugger->backend, " %02X", value); } debugger->backend->printf(debugger->backend, "\n"); @@ -330,7 +362,12 @@ static void _dumpHalfword(struct CLIDebugger* debugger, struct CLIDebugVector* d } debugger->backend->printf(debugger->backend, "0x%08X:", address); for (; line > 0; --line, address += 2, --words) { - uint32_t value = debugger->d.core->busRead16(debugger->d.core, address); + uint32_t value; + if (dv->segmentValue >= 0) { + value = debugger->d.core->rawRead16(debugger->d.core, address, dv->segmentValue); + } else { + value = debugger->d.core->busRead16(debugger->d.core, address); + } debugger->backend->printf(debugger->backend, " %04X", value); } debugger->backend->printf(debugger->backend, "\n"); @@ -354,7 +391,12 @@ static void _dumpWord(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { } debugger->backend->printf(debugger->backend, "0x%08X:", address); for (; line > 0; --line, address += 4, --words) { - uint32_t value = debugger->d.core->busRead32(debugger->d.core, address); + uint32_t value; + if (dv->segmentValue >= 0) { + value = debugger->d.core->rawRead32(debugger->d.core, address, dv->segmentValue); + } else { + value = debugger->d.core->busRead32(debugger->d.core, address); + } debugger->backend->printf(debugger->backend, " %08X", value); } debugger->backend->printf(debugger->backend, "\n"); From fcc8b5c8052ae6bd4b4e7931ceb60461cee69605 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 19:39:27 -0700 Subject: [PATCH 139/159] Debugger: Add segment breakpoints --- include/mgba/internal/debugger/debugger.h | 4 +-- include/mgba/internal/gb/memory.h | 2 ++ include/mgba/internal/lr35902/lr35902.h | 2 ++ src/arm/debugger/debugger.c | 10 ++++--- src/debugger/cli-debugger.c | 4 +-- src/debugger/gdb-stub.c | 4 +-- src/gb/memory.c | 32 +++++++++++++++++++++++ src/lr35902/debugger/cli-debugger.c | 6 ++--- src/lr35902/debugger/debugger.c | 17 +++++++----- 9 files changed, 61 insertions(+), 20 deletions(-) diff --git a/include/mgba/internal/debugger/debugger.h b/include/mgba/internal/debugger/debugger.h index 3cd228dc7..fac608c73 100644 --- a/include/mgba/internal/debugger/debugger.h +++ b/include/mgba/internal/debugger/debugger.h @@ -87,8 +87,8 @@ struct mDebuggerPlatform { void (*entered)(struct mDebuggerPlatform*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*); bool (*hasBreakpoints)(struct mDebuggerPlatform*); - void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address); - void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address); + void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); + void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type); void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address); void (*checkBreakpoints)(struct mDebuggerPlatform*); diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 8e3d118e6..6a4c22d7f 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -161,6 +161,8 @@ void GBMemorySwitchWramBank(struct GBMemory* memory, int bank); uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address); void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value); +int GBCurrentSegment(struct LR35902Core* cpu, uint16_t address); + uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment); void GBMemoryDMA(struct GB* gb, uint16_t base); diff --git a/include/mgba/internal/lr35902/lr35902.h b/include/mgba/internal/lr35902/lr35902.h index 59811cc8b..3e586f0ed 100644 --- a/include/mgba/internal/lr35902/lr35902.h +++ b/include/mgba/internal/lr35902/lr35902.h @@ -54,6 +54,8 @@ struct LR35902Memory { uint8_t (*load8)(struct LR35902Core*, uint16_t address); void (*store8)(struct LR35902Core*, uint16_t address, int8_t value); + int (*currentSegment)(struct LR35902Core*, uint16_t address); + uint8_t* activeRegion; uint16_t activeMask; uint16_t activeRegionEnd; diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 76b474841..47aa0d86e 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -48,8 +48,8 @@ static void ARMDebuggerDeinit(struct mDebuggerPlatform* platform); static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info); -static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address); -static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address); +static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); +static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type); static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address); static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*); @@ -157,14 +157,16 @@ void ARMDebuggerClearSoftwareBreakpoint(struct mDebuggerPlatform* d, uint32_t ad } } -static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) { +static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { + UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMDebugBreakpoint* breakpoint = ARMDebugBreakpointListAppend(&debugger->breakpoints); breakpoint->address = address; breakpoint->isSw = false; } -static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address) { +static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { + UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMDebugBreakpointList* breakpoints = &debugger->breakpoints; size_t i; diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 967ed8e90..c142ed60c 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -409,7 +409,7 @@ static void _setBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* return; } uint32_t address = dv->intValue; - debugger->d.platform->setBreakpoint(debugger->d.platform, address); + debugger->d.platform->setBreakpoint(debugger->d.platform, address, dv->segmentValue); } static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -457,7 +457,7 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector return; } uint32_t address = dv->intValue; - debugger->d.platform->clearBreakpoint(debugger->d.platform, address); + debugger->d.platform->clearBreakpoint(debugger->d.platform, address, dv->segmentValue); if (debugger->d.platform->clearWatchpoint) { debugger->d.platform->clearWatchpoint(debugger->d.platform, address); } diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 7c4111c86..05005648b 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -495,7 +495,7 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) { ARMDebuggerSetSoftwareBreakpoint(stub->d.platform, address, kind == 2 ? MODE_THUMB : MODE_ARM); break; case '1': - stub->d.platform->setBreakpoint(stub->d.platform, address); + stub->d.platform->setBreakpoint(stub->d.platform, address, -1); break; case '2': stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE); @@ -524,7 +524,7 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) { ARMDebuggerClearSoftwareBreakpoint(stub->d.platform, address); break; case '1': - stub->d.platform->clearBreakpoint(stub->d.platform, address); + stub->d.platform->clearBreakpoint(stub->d.platform, address, -1); break; case '2': case '3': diff --git a/src/gb/memory.c b/src/gb/memory.c index fcc01b651..ea84f9632 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -62,6 +62,7 @@ void GBMemoryInit(struct GB* gb) { cpu->memory.cpuLoad8 = GBLoad8; cpu->memory.load8 = GBLoad8; cpu->memory.store8 = GBStore8; + cpu->memory.currentSegment = GBCurrentSegment; cpu->memory.setActiveRegion = GBSetActiveRegion; gb->memory.wram = 0; @@ -271,6 +272,37 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { } } } + +int GBCurrentSegment(struct LR35902Core* cpu, uint16_t address) { + struct GB* gb = (struct GB*) cpu->master; + struct GBMemory* memory = &gb->memory; + switch (address >> 12) { + case GB_REGION_CART_BANK0: + case GB_REGION_CART_BANK0 + 1: + case GB_REGION_CART_BANK0 + 2: + case GB_REGION_CART_BANK0 + 3: + return 0; + case GB_REGION_CART_BANK1: + case GB_REGION_CART_BANK1 + 1: + case GB_REGION_CART_BANK1 + 2: + case GB_REGION_CART_BANK1 + 3: + return memory->currentBank; + case GB_REGION_VRAM: + case GB_REGION_VRAM + 1: + return gb->video.vramCurrentBank; + case GB_REGION_EXTERNAL_RAM: + case GB_REGION_EXTERNAL_RAM + 1: + return memory->sramCurrentBank; + case GB_REGION_WORKING_RAM_BANK0: + case GB_REGION_WORKING_RAM_BANK0 + 2: + return 0; + case GB_REGION_WORKING_RAM_BANK1: + return memory->wramCurrentBank; + default: + return 0; + } +} + uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment) { struct GB* gb = (struct GB*) cpu->master; struct GBMemory* memory = &gb->memory; diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index 953f6d754..6e0740660 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -48,7 +48,7 @@ static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVect size_t i; for (i = 0; i < size; ++i) { - address = _printLine(debugger->p, address, -1); + address = _printLine(debugger->p, address, dv->segmentValue); } } @@ -58,7 +58,7 @@ static inline uint16_t _printLine(struct CLIDebugger* debugger, uint16_t address char disassembly[48]; char* disPtr = disassembly; if (segment >= 0) { - be->printf(be, "%02X: ", segment); + be->printf(be, "%02X:", segment); } be->printf(be, "%04X: ", address); uint8_t instruction; @@ -85,7 +85,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl); be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp); _printFlags(be, cpu->f); - _printLine(debugger->p, cpu->pc, -1); + _printLine(debugger->p, cpu->pc, cpu->memory.currentSegment(cpu, cpu->pc)); } static uint32_t _lookupPlatformIdentifier(struct CLIDebuggerSystem* debugger, const char* name, struct CLIDebugVector* dv) { diff --git a/src/lr35902/debugger/debugger.c b/src/lr35902/debugger/debugger.c index a5f08a394..91de87577 100644 --- a/src/lr35902/debugger/debugger.c +++ b/src/lr35902/debugger/debugger.c @@ -27,7 +27,9 @@ static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform* d) { if (!breakpoint) { return; } - // TODO: Segments + if (breakpoint->segment >= 0 && debugger->cpu->memory.currentSegment(debugger->cpu, breakpoint->address) != breakpoint->segment) { + return; + } struct mDebuggerEntryInfo info = { .address = breakpoint->address }; @@ -39,8 +41,8 @@ static void LR35902DebuggerDeinit(struct mDebuggerPlatform* platform); static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info); -static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address); -static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address); +static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); +static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*); @@ -79,19 +81,20 @@ static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebug cpu->nextEvent = cpu->cycles; } -static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address) { +static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListAppend(&debugger->breakpoints); breakpoint->address = address; - breakpoint->segment = -1; + breakpoint->segment = segment; } -static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address) { +static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; struct LR35902DebugBreakpointList* breakpoints = &debugger->breakpoints; size_t i; for (i = 0; i < LR35902DebugBreakpointListSize(breakpoints); ++i) { - if (LR35902DebugBreakpointListGetPointer(breakpoints, i)->address == address) { + struct LR35902DebugBreakpoint* breakpoint = LR35902DebugBreakpointListGetPointer(breakpoints, i); + if (breakpoint->address == address && breakpoint->segment == segment) { LR35902DebugBreakpointListShift(breakpoints, i, 1); } } From 0b28dad51c16655cd2293e049be03701bfac8ca1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 19:49:48 -0700 Subject: [PATCH 140/159] GB Memory: Simplify DMA memory accesses --- include/mgba/internal/gb/memory.h | 3 - src/gb/memory.c | 112 +++++++++++++----------------- 2 files changed, 50 insertions(+), 65 deletions(-) diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 6a4c22d7f..7a6bd4630 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -168,9 +168,6 @@ uint8_t GBView8(struct LR35902Core* cpu, uint16_t address, int segment); void GBMemoryDMA(struct GB* gb, uint16_t base); void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value); -uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address); -void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value); - void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old, int segment); struct GBSerializedState; diff --git a/src/gb/memory.c b/src/gb/memory.c index ea84f9632..6a2d84f55 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -16,6 +16,33 @@ mLOG_DEFINE_CATEGORY(GB_MEM, "GB Memory", "gb.memory"); +struct OAMBlock { + uint16_t low; + uint16_t high; +}; + +static const struct OAMBlock _oamBlockDMG[] = { + { 0xA000, 0xFE00 }, + { 0xA000, 0xFE00 }, + { 0xA000, 0xFE00 }, + { 0xA000, 0xFE00 }, + { 0x8000, 0xA000 }, + { 0xA000, 0xFE00 }, + { 0xA000, 0xFE00 }, + { 0xA000, 0xFE00 }, +}; + +static const struct OAMBlock _oamBlockCGB[] = { + { 0xA000, 0xC000 }, + { 0xA000, 0xC000 }, + { 0xA000, 0xC000 }, + { 0xA000, 0xC000 }, + { 0x8000, 0xA000 }, + { 0xA000, 0xC000 }, + { 0xC000, 0xFE00 }, + { 0xA000, 0xC000 }, +}; + static void _pristineCow(struct GB* gba); static uint8_t GBFastLoad8(struct LR35902Core* cpu, uint16_t address) { @@ -160,6 +187,16 @@ void GBMemorySwitchWramBank(struct GBMemory* memory, int bank) { uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { struct GB* gb = (struct GB*) cpu->master; struct GBMemory* memory = &gb->memory; + if (gb->memory.dmaRemaining) { + const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB; + block = &block[memory->dmaSource >> 13]; + if (address >= block->low && address < block->high) { + return 0xFF; + } + if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) { + return 0xFF; + } + } switch (address >> 12) { case GB_REGION_CART_BANK0: case GB_REGION_CART_BANK0 + 1: @@ -218,6 +255,16 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { struct GB* gb = (struct GB*) cpu->master; struct GBMemory* memory = &gb->memory; + if (gb->memory.dmaRemaining) { + const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB; + block = &block[memory->dmaSource >> 13]; + if (address >= block->low && address < block->high) { + return; + } + if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) { + return; + } + } switch (address >> 12) { case GB_REGION_CART_BANK0: case GB_REGION_CART_BANK0 + 1: @@ -389,9 +436,6 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) { if (base > 0xF100) { return; } - gb->cpu->memory.store8 = GBDMAStore8; - gb->cpu->memory.load8 = GBDMALoad8; - gb->cpu->memory.cpuLoad8 = GBDMALoad8; mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8); if (gb->cpu->cycles + 8 < gb->cpu->nextEvent) { gb->cpu->nextEvent = gb->cpu->cycles + 8; @@ -425,18 +469,17 @@ void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GB* gb = context; + int dmaRemaining = gb->memory.dmaRemaining; + gb->memory.dmaRemaining = 0; uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource); // TODO: Can DMA write OAM during modes 2-3? gb->video.oam.raw[gb->memory.dmaDest] = b; gb->video.renderer->writeOAM(gb->video.renderer, gb->memory.dmaDest); ++gb->memory.dmaSource; ++gb->memory.dmaDest; - --gb->memory.dmaRemaining; + gb->memory.dmaRemaining = dmaRemaining - 1; if (gb->memory.dmaRemaining) { mTimingSchedule(timing, &gb->memory.dmaEvent, 4 - cyclesLate); - } else { - gb->cpu->memory.store8 = GBStore8; - gb->cpu->memory.load8 = GBLoad8; } } @@ -468,61 +511,6 @@ void _GBMemoryHDMAService(struct mTiming* timing, void* context, uint32_t cycles } } -struct OAMBlock { - uint16_t low; - uint16_t high; -}; - -static const struct OAMBlock _oamBlockDMG[] = { - { 0xA000, 0xFE00 }, - { 0xA000, 0xFE00 }, - { 0xA000, 0xFE00 }, - { 0xA000, 0xFE00 }, - { 0x8000, 0xA000 }, - { 0xA000, 0xFE00 }, - { 0xA000, 0xFE00 }, - { 0xA000, 0xFE00 }, -}; - -static const struct OAMBlock _oamBlockCGB[] = { - { 0xA000, 0xC000 }, - { 0xA000, 0xC000 }, - { 0xA000, 0xC000 }, - { 0xA000, 0xC000 }, - { 0x8000, 0xA000 }, - { 0xA000, 0xC000 }, - { 0xC000, 0xFE00 }, - { 0xA000, 0xC000 }, -}; - -uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) { - struct GB* gb = (struct GB*) cpu->master; - struct GBMemory* memory = &gb->memory; - const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB; - block = &block[memory->dmaSource >> 13]; - if (address >= block->low && address < block->high) { - return 0xFF; - } - if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) { - return 0xFF; - } - return GBLoad8(cpu, address); -} - -void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { - struct GB* gb = (struct GB*) cpu->master; - struct GBMemory* memory = &gb->memory; - const struct OAMBlock* block = gb->model < GB_MODEL_CGB ? _oamBlockDMG : _oamBlockCGB; - block = &block[memory->dmaSource >> 13]; - if (address >= block->low && address < block->high) { - return; - } - if (address >= GB_BASE_OAM && address < GB_BASE_UNUSABLE) { - return; - } - GBStore8(cpu, address, value); -} - void GBPatch8(struct LR35902Core* cpu, uint16_t address, int8_t value, int8_t* old, int segment) { struct GB* gb = (struct GB*) cpu->master; struct GBMemory* memory = &gb->memory; From 404332e68962e0658cf63f1faca1e722174be276 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 21:47:43 -0700 Subject: [PATCH 141/159] Debugger: Add symbol table skeleton, GB support --- CMakeLists.txt | 4 +- include/mgba/core/core.h | 4 ++ include/mgba/internal/debugger/debugger.h | 1 + include/mgba/internal/debugger/symbols.h | 25 ++++++++++ include/mgba/internal/gb/debugger/symbols.h | 19 +++++++ src/debugger/cli-debugger.c | 8 ++- src/debugger/debugger.c | 3 ++ src/debugger/symbols.c | 49 ++++++++++++++++++ src/gb/core.c | 22 ++++++++- src/gb/debugger/symbols.c | 55 +++++++++++++++++++++ src/gba/core.c | 6 +++ 11 files changed, 193 insertions(+), 3 deletions(-) create mode 100644 include/mgba/internal/debugger/symbols.h create mode 100644 include/mgba/internal/gb/debugger/symbols.h create mode 100644 src/debugger/symbols.c create mode 100644 src/gb/debugger/symbols.c diff --git a/CMakeLists.txt b/CMakeLists.txt index cd754fd90..05aa152aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -395,6 +395,7 @@ find_feature(USE_SQLITE3 "sqlite3") set(DEBUGGER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/parser.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/symbols.c ${CMAKE_CURRENT_SOURCE_DIR}/src/debugger/cli-debugger.c) set(FEATURE_SRC) @@ -603,7 +604,8 @@ if(M_CORE_GB) list(APPEND DEBUGGER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/cli-debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/debugger.c - ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c) + ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/symbols.c) list(APPEND TEST_SRC ${LR35902_TEST_SRC} ${GB_TEST_SRC}) diff --git a/include/mgba/core/core.h b/include/mgba/core/core.h index f7238f861..99c57a037 100644 --- a/include/mgba/core/core.h +++ b/include/mgba/core/core.h @@ -39,12 +39,14 @@ enum mCoreChecksumType { struct mCoreConfig; struct mCoreSync; +struct mDebuggerSymbols; struct mStateExtdata; struct mVideoLogContext; struct mCore { void* cpu; void* board; struct mDebugger* debugger; + struct mDebuggerSymbols* symbolTable; #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 struct mDirectorySet dirs; @@ -135,6 +137,8 @@ struct mCore { struct CLIDebuggerSystem* (*cliDebuggerSystem)(struct mCore*); void (*attachDebugger)(struct mCore*, struct mDebugger*); void (*detachDebugger)(struct mCore*); + + void (*loadSymbols)(struct mCore*, struct VFile*); #endif struct mCheatDevice* (*cheatDevice)(struct mCore*); diff --git a/include/mgba/internal/debugger/debugger.h b/include/mgba/internal/debugger/debugger.h index fac608c73..a54c353c8 100644 --- a/include/mgba/internal/debugger/debugger.h +++ b/include/mgba/internal/debugger/debugger.h @@ -94,6 +94,7 @@ struct mDebuggerPlatform { void (*checkBreakpoints)(struct mDebuggerPlatform*); }; +struct mDebuggerSymbols; struct mDebugger { struct mCPUComponent d; struct mDebuggerPlatform* platform; diff --git a/include/mgba/internal/debugger/symbols.h b/include/mgba/internal/debugger/symbols.h new file mode 100644 index 000000000..e8456f8c1 --- /dev/null +++ b/include/mgba/internal/debugger/symbols.h @@ -0,0 +1,25 @@ +/* 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 DEBUGGER_SYMBOLS_H +#define DEBUGGER_SYMBOLS_H + +#include + +CXX_GUARD_START + +struct mDebuggerSymbols; + +struct mDebuggerSymbols* mDebuggerSymbolTableCreate(void); +void mDebuggerSymbolTableDestroy(struct mDebuggerSymbols*); + +bool mDebuggerSymbolLookup(const struct mDebuggerSymbols*, const char* name, int32_t* value, int* segment); + +void mDebuggerSymbolAdd(struct mDebuggerSymbols*, const char* name, int32_t value, int segment); +void mDebuggerSymbolRemove(struct mDebuggerSymbols*, const char* name); + +CXX_GUARD_END + +#endif diff --git a/include/mgba/internal/gb/debugger/symbols.h b/include/mgba/internal/gb/debugger/symbols.h new file mode 100644 index 000000000..ae893f289 --- /dev/null +++ b/include/mgba/internal/gb/debugger/symbols.h @@ -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 GB_SYMBOLS_H +#define GB_SYMBOLS_H + +#include + +CXX_GUARD_START + +struct mDebuggerSymbols; +struct VFile; +void GBLoadSymbols(struct mDebuggerSymbols*, struct VFile* vf); + +CXX_GUARD_END + +#endif diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index c142ed60c..dcf1bd4fe 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -5,6 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include + #include #include #include @@ -497,7 +499,11 @@ static uint32_t _performOperation(enum Operation operation, uint32_t current, ui static void _lookupIdentifier(struct mDebugger* debugger, const char* name, struct CLIDebugVector* dv) { struct CLIDebugger* cliDebugger = (struct CLIDebugger*) debugger; if (cliDebugger->system) { - uint32_t value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv); + uint32_t value; + if (debugger->core->symbolTable && mDebuggerSymbolLookup(debugger->core->symbolTable, name, &dv->intValue, &dv->segmentValue)) { + return; + } + value = cliDebugger->system->lookupPlatformIdentifier(cliDebugger->system, name, dv); if (dv->type != CLIDV_ERROR_TYPE) { dv->intValue = value; return; diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index 49da5fde6..bf7b9d99c 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -62,6 +62,9 @@ void mDebuggerAttach(struct mDebugger* debugger, struct mCore* core) { debugger->d.init = mDebuggerInit; debugger->d.deinit = mDebuggerDeinit; debugger->core = core; + if (!debugger->core->symbolTable) { + debugger->core->loadSymbols(debugger->core, NULL); + } debugger->platform = core->debuggerPlatform(core); debugger->platform->p = debugger; core->attachDebugger(core, debugger); diff --git a/src/debugger/symbols.c b/src/debugger/symbols.c new file mode 100644 index 000000000..a9c874b54 --- /dev/null +++ b/src/debugger/symbols.c @@ -0,0 +1,49 @@ +/* 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 + +#include + +struct mDebuggerSymbol { + int32_t value; + int segment; +}; + +struct mDebuggerSymbols { + struct Table names; +}; + +struct mDebuggerSymbols* mDebuggerSymbolTableCreate(void) { + struct mDebuggerSymbols* st = malloc(sizeof(*st)); + HashTableInit(&st->names, 0, free); + return st; +} + +void mDebuggerSymbolTableDestroy(struct mDebuggerSymbols* st) { + HashTableDeinit(&st->names); + free(st); +} + +bool mDebuggerSymbolLookup(const struct mDebuggerSymbols* st, const char* name, int32_t* value, int* segment) { + struct mDebuggerSymbol* sym = HashTableLookup(&st->names, name); + if (!sym) { + return false; + } + *value = sym->value; + *segment = sym->segment; + return true; +} + +void mDebuggerSymbolAdd(struct mDebuggerSymbols* st, const char* name, int32_t value, int segment) { + struct mDebuggerSymbol* sym = malloc(sizeof(*sym)); + sym->value = value; + sym->segment = segment; + HashTableInsert(&st->names, name, sym); +} + +void mDebuggerSymbolRemove(struct mDebuggerSymbols* st, const char* name) { + HashTableRemove(&st->names, name); +} diff --git a/src/gb/core.c b/src/gb/core.c index 0bcc2aa9d..29e1b0c6f 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include #include #include @@ -90,8 +92,11 @@ static void _GBCoreDeinit(struct mCore* core) { GBDestroy(core->board); mappedMemoryFree(core->cpu, sizeof(struct LR35902Core)); mappedMemoryFree(core->board, sizeof(struct GB)); -#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 +#if defined USE_DEBUGGERS && (!defined(MINIMAL_CORE) || MINIMAL_CORE < 2) mDirectorySetDeinit(&core->dirs); + if (core->symbolTable) { + mDebuggerSymbolTableDestroy(core->symbolTable); + } #endif struct GBCore* gbcore = (struct GBCore*) core; @@ -552,6 +557,19 @@ static void _GBCoreDetachDebugger(struct mCore* core) { cpu->components[CPU_COMPONENT_DEBUGGER] = NULL; core->debugger = NULL; } + +static void _GBCoreLoadSymbols(struct mCore* core, struct VFile* vf) { + core->symbolTable = mDebuggerSymbolTableCreate(); +#if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 + if (!vf) { + vf = mDirectorySetOpenSuffix(&core->dirs, core->dirs.base, ".sym", O_RDONLY); + } +#endif + if (!vf) { + return; + } + GBLoadSymbols(core->symbolTable, vf); +} #endif static struct mCheatDevice* _GBCoreCheatDevice(struct mCore* core) { @@ -671,6 +689,7 @@ struct mCore* GBCoreCreate(void) { core->cpu = NULL; core->board = NULL; core->debugger = NULL; + core->symbolTable = NULL; core->init = _GBCoreInit; core->deinit = _GBCoreDeinit; core->platform = _GBCorePlatform; @@ -728,6 +747,7 @@ struct mCore* GBCoreCreate(void) { core->cliDebuggerSystem = _GBCoreCliDebuggerSystem; core->attachDebugger = _GBCoreAttachDebugger; core->detachDebugger = _GBCoreDetachDebugger; + core->loadSymbols = _GBCoreLoadSymbols; #endif core->cheatDevice = _GBCoreCheatDevice; core->savedataClone = _GBCoreSavedataClone; diff --git a/src/gb/debugger/symbols.c b/src/gb/debugger/symbols.c new file mode 100644 index 000000000..4fc113433 --- /dev/null +++ b/src/gb/debugger/symbols.c @@ -0,0 +1,55 @@ +/* Copyright (c) 2013-2016 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 + +#include +#include +#include + +void GBLoadSymbols(struct mDebuggerSymbols* st, struct VFile* vf) { + char line[512]; + + while (true) { + ssize_t bytesRead = vf->readline(vf, line, sizeof(line)); + if (bytesRead <= 0) { + break; + } + if (line[bytesRead - 1] == '\n') { + line[bytesRead - 1] = '\0'; + } + int segment = -1; + uint32_t address = 0; + + uint8_t byte; + const char* buf = line; + while (buf) { + buf = hex8(buf, &byte); + if (!buf) { + break; + } + address <<= 8; + address += byte; + + if (buf[0] == ':') { + segment = address; + address = 0; + ++buf; + } + if (isspace((int) buf[0])) { + break; + } + } + if (!buf) { + continue; + } + + while (isspace((int) buf[0])) { + ++buf; + } + + mDebuggerSymbolAdd(st, buf, address, segment); + } +} diff --git a/src/gba/core.c b/src/gba/core.c index 6888594ba..6f81e2570 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -72,6 +72,7 @@ static bool _GBACoreInit(struct mCore* core) { core->cpu = cpu; core->board = gba; core->debugger = NULL; + core->symbolTable = NULL; gbacore->overrides = NULL; gbacore->debuggerPlatform = NULL; gbacore->cheatDevice = NULL; @@ -555,6 +556,10 @@ static void _GBACoreDetachDebugger(struct mCore* core) { GBADetachDebugger(core->board); core->debugger = NULL; } + +static void _GBACoreLoadSymbols(struct mCore* core, struct VFile* vf) { + // TODO +} #endif static struct mCheatDevice* _GBACoreCheatDevice(struct mCore* core) { @@ -747,6 +752,7 @@ struct mCore* GBACoreCreate(void) { core->cliDebuggerSystem = _GBACoreCliDebuggerSystem; core->attachDebugger = _GBACoreAttachDebugger; core->detachDebugger = _GBACoreDetachDebugger; + core->loadSymbols = _GBACoreLoadSymbols; #endif core->cheatDevice = _GBACoreCheatDevice; core->savedataClone = _GBACoreSavedataClone; From dae68c2182a8daa5c1770ade38726ed4b7a37193 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 21:57:20 -0700 Subject: [PATCH 142/159] All: Update CHANGES --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 069d8fac6..891977d01 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,8 @@ Features: - Add option to lock video to integer scaling - Video log recording for testing and bug reporting - Library view + - Debugger: Segment/bank support + - GB: Symbol table support Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior From c4175846d88345d203153b64e8eb57559b9301ab Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 22:07:59 -0700 Subject: [PATCH 143/159] Debugger: Print segment numbers --- src/debugger/cli-debugger.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index dcf1bd4fe..74a87c743 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -137,6 +137,10 @@ static void _disassemble(struct CLIDebugger* debugger, struct CLIDebugVector* dv static void _print(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { for (; dv; dv = dv->next) { + if (dv->segmentValue >= 0) { + debugger->backend->printf(debugger->backend, " $%02X:%04X", dv->segmentValue, dv->intValue); + continue; + } debugger->backend->printf(debugger->backend, " %u", dv->intValue); } debugger->backend->printf(debugger->backend, "\n"); From 7de5b33554a9c0b41c963d220dd85ceda1bf3022 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 22 May 2017 22:19:01 -0700 Subject: [PATCH 144/159] LR35902: Fix disassembling segments --- src/lr35902/debugger/cli-debugger.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index 6e0740660..c2bc5fbd2 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -31,11 +31,13 @@ static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVect struct LR35902Core* cpu = debugger->p->d.core->cpu; uint16_t address; + int segment = -1; size_t size; if (!dv || dv->type != CLIDV_INT_TYPE) { address = cpu->pc; } else { address = dv->intValue; + segment = dv->segmentValue; dv = dv->next; } @@ -48,7 +50,7 @@ static void _disassemble(struct CLIDebuggerSystem* debugger, struct CLIDebugVect size_t i; for (i = 0; i < size; ++i) { - address = _printLine(debugger->p, address, dv->segmentValue); + address = _printLine(debugger->p, address, segment); } } From f84af911739274c9e4ea24b37d8b4f784f040f84 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 30 May 2017 22:28:23 -0700 Subject: [PATCH 145/159] GB MBC: Add MBC1-M support with basic heuristic --- CHANGES | 1 + include/mgba/internal/gb/memory.h | 1 + src/gb/gb.c | 2 ++ src/gb/io.c | 2 +- src/gb/mbc.c | 45 +++++++++++++++++++++++++++---- src/gb/memory.c | 8 +++++- 6 files changed, 52 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 891977d01..f9edb5bb6 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,7 @@ Features: - Library view - Debugger: Segment/bank support - GB: Symbol table support + - GB MBC: Add MBC1 multicart support Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 7a6bd4630..11f1e929f 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -85,6 +85,7 @@ enum GBMBC7MachineState { struct GBMBC1State { int mode; + int multicartStride; }; struct GBMBC7State { diff --git a/src/gb/gb.c b/src/gb/gb.c index 8e227a816..4c83984f5 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -385,7 +385,9 @@ bool GBIsBIOS(struct VFile* vf) { void GBReset(struct LR35902Core* cpu) { struct GB* gb = (struct GB*) cpu->master; + gb->memory.romBase = gb->memory.rom; GBDetectModel(gb); + if (gb->biosVf) { if (!GBIsBIOS(gb->biosVf)) { gb->biosVf->close(gb->biosVf); diff --git a/src/gb/io.c b/src/gb/io.c index b7e234ca7..0f386aaae 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -382,7 +382,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { value = gb->video.stat; break; case 0x50: - if (gb->memory.romBase != gb->memory.rom) { + if (gb->memory.romBase < gb->memory.rom && gb->memory.romBase > &gb->memory.rom[gb->memory.romSize - 1]) { free(gb->memory.romBase); gb->memory.romBase = gb->memory.rom; } diff --git a/src/gb/mbc.c b/src/gb/mbc.c index efc294419..ae69defc6 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -46,6 +46,33 @@ void GBMBCSwitchBank(struct GB* gb, int bank) { } } +static void _switchBank0(struct GB* gb, int bank) { + size_t bankStart = bank * GB_SIZE_CART_BANK0 << gb->memory.mbcState.mbc1.multicartStride; + if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { + mLOG(GB_MBC, GAME_ERROR, "Attempting to switch to an invalid ROM bank: %0X", bank); + bankStart &= (gb->memory.romSize - 1); + } + gb->memory.romBase = &gb->memory.rom[bankStart]; + if (gb->cpu->pc < GB_SIZE_CART_BANK0) { + gb->cpu->memory.setActiveRegion(gb->cpu, gb->cpu->pc); + } +} + +static bool _isMulticart(const uint8_t* mem) { + bool success = true; + struct VFile* vf; + + vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x10], 1024); + success = success && GBIsROM(vf); + vf->close(vf); + + vf = VFileFromConstMemory(&mem[GB_SIZE_CART_BANK0 * 0x20], 1024); + success = success && GBIsROM(vf); + vf->close(vf); + + return success; +} + void GBMBCSwitchSramBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_EXTERNAL_RAM; GBResizeSram(gb, (bank + 1) * GB_SIZE_EXTERNAL_RAM); @@ -83,6 +110,11 @@ void GBMBCInit(struct GB* gb) { case 2: case 3: gb->memory.mbcType = GB_MBC1; + if (gb->memory.romSize >= GB_SIZE_CART_BANK0 * 0x31 && _isMulticart(gb->memory.rom)) { + gb->memory.mbcState.mbc1.multicartStride = 4; + } else { + gb->memory.mbcState.mbc1.multicartStride = 5; + } break; case 5: case 6: @@ -233,6 +265,7 @@ static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastL void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; int bank = value & 0x1F; + int stride = 1 << memory->mbcState.mbc1.multicartStride; switch (address >> 13) { case 0x0: switch (value) { @@ -253,21 +286,23 @@ void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { if (!bank) { ++bank; } - GBMBCSwitchBank(gb, bank | (memory->currentBank & 0x60)); + bank &= stride - 1; + GBMBCSwitchBank(gb, bank | (memory->currentBank & (3 * stride))); break; case 0x2: bank &= 3; - if (!memory->mbcState.mbc1.mode) { - GBMBCSwitchBank(gb, (bank << 5) | (memory->currentBank & 0x1F)); - } else { + if (memory->mbcState.mbc1.mode) { + _switchBank0(gb, bank); GBMBCSwitchSramBank(gb, bank); } + GBMBCSwitchBank(gb, (bank << memory->mbcState.mbc1.multicartStride) | (memory->currentBank & (stride - 1))); break; case 0x3: memory->mbcState.mbc1.mode = value & 1; if (memory->mbcState.mbc1.mode) { - GBMBCSwitchBank(gb, memory->currentBank & 0x1F); + _switchBank0(gb, memory->currentBank >> memory->mbcState.mbc1.multicartStride); } else { + _switchBank0(gb, 0); GBMBCSwitchSramBank(gb, 0); } break; diff --git a/src/gb/memory.c b/src/gb/memory.c index 6a2d84f55..46c8dcd78 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -165,7 +165,13 @@ void GBMemoryReset(struct GB* gb) { memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); - memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); + switch (gb->memory.mbcType) { + case GB_MBC1: + gb->memory.mbcState.mbc1.mode = 0; + break; + default: + memset(&gb->memory.mbcState, 0, sizeof(gb->memory.mbcState)); + } GBMBCInit(gb); gb->memory.sramBank = gb->memory.sram; From 6fe1f44351b82f85210260ab2425f8aaecb774a4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 30 May 2017 22:30:11 -0700 Subject: [PATCH 146/159] GB Memory: Mark ROM not pristine when doing copy-on-write --- src/gb/memory.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gb/memory.c b/src/gb/memory.c index 46c8dcd78..3fde79010 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -689,4 +689,5 @@ void _pristineCow(struct GB* gb) { } gb->memory.rom = newRom; GBMBCSwitchBank(gb, gb->memory.currentBank); + gb->isPristine = false; } From 06d89aee48c7b985f95e49185bbf2eef99ace94e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 31 May 2017 17:01:50 -0700 Subject: [PATCH 147/159] Python: Fix uninitialized variable --- src/platform/python/log.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/python/log.c b/src/platform/python/log.c index 6066da4a0..53c8201e2 100644 --- a/src/platform/python/log.c +++ b/src/platform/python/log.c @@ -22,6 +22,7 @@ static void _pyLogShim(struct mLogger* logger, int category, enum mLogLevel leve struct mLogger* mLoggerPythonCreate(void* pyobj) { struct mLoggerPy* logger = malloc(sizeof(*logger)); logger->d.log = _pyLogShim; + logger->d.filter = NULL; logger->pyobj = pyobj; return &logger->d; } From 7b66a702cfe96f34ef2842b1c9c6f6826738be5e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 31 May 2017 17:04:24 -0700 Subject: [PATCH 148/159] Python: Reduce boilerplate --- src/platform/python/mgba/__init__.py | 15 +++++++++++++++ src/platform/python/mgba/log.py | 6 ++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/platform/python/mgba/__init__.py b/src/platform/python/mgba/__init__.py index e69de29bb..7be537a5b 100644 --- a/src/platform/python/mgba/__init__.py +++ b/src/platform/python/mgba/__init__.py @@ -0,0 +1,15 @@ +# 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/. +from ._pylib import ffi, lib + +def createCallback(structName, cbName, funcName=None): + funcName = funcName or "_py{}{}".format(structName, cbName.capitalize()) + fullStruct = "struct {}*".format(structName) + def cb(handle, *args): + h = ffi.cast(fullStruct, handle) + getattr(ffi.from_handle(h.pyobj), cbName)(*args) + + return ffi.def_extern(name=funcName)(cb) diff --git a/src/platform/python/mgba/log.py b/src/platform/python/mgba/log.py index a3412b186..4514e67a9 100644 --- a/src/platform/python/mgba/log.py +++ b/src/platform/python/mgba/log.py @@ -4,11 +4,9 @@ # 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/. from ._pylib import ffi, lib +from . import createCallback -@ffi.def_extern() -def _pyLog(logger, category, level, message): - l = ffi.cast("struct mLoggerPy*", logger) - ffi.from_handle(l.pyobj).log(category, level, ffi.string(message).decode('UTF-8')) +createCallback("mLoggerPy", "log", "_pyLog") def installDefault(logger): lib.mLogSetDefaultLogger(logger._native) From fe2854db79827a6afecad8399fa789210bfcaf74 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 31 May 2017 17:47:38 -0700 Subject: [PATCH 149/159] Python: Reduce boilerplate in builder --- src/platform/python/_builder.h | 3 +++ src/platform/python/_builder.py | 17 ++++------------- src/platform/python/log.h | 2 +- src/platform/python/vfs-py.h | 22 +++++++++------------- 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index 58a214d93..c2ef3fe78 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -29,8 +29,11 @@ void free(void*); #include #include + +#define PYEXPORT extern "Python+C" #include "platform/python/vfs-py.h" #include "platform/python/log.h" +#undef PYEXPORT #ifdef USE_PNG #include diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 94babdb55..3603e991e 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -32,19 +32,10 @@ ffi.set_source("mgba._pylib", """ #include #include -struct VFile* VFileFromPython(void* fileobj); - -struct VFilePy { - struct VFile d; - void* fileobj; -}; - -struct mLogger* mLoggerPythonCreate(void* pyobj); - -struct mLoggerPy { - struct mLogger d; - void* pyobj; -}; +#define PYEXPORT +#include "platform/python/log.h" +#include "platform/python/vfs-py.h" +#undef PYEXPORT """, include_dirs=[incdir, srcdir], extra_compile_args=cppflags, libraries=["mgba"], diff --git a/src/platform/python/log.h b/src/platform/python/log.h index f29dba8bb..26ec70253 100644 --- a/src/platform/python/log.h +++ b/src/platform/python/log.h @@ -12,4 +12,4 @@ struct mLoggerPy { struct mLogger* mLoggerPythonCreate(void* pyobj); -extern "Python+C" void _pyLog(void* logger, int category, enum mLogLevel level, const char* message); +PYEXPORT void _pyLog(void* logger, int category, enum mLogLevel level, const char* message); diff --git a/src/platform/python/vfs-py.h b/src/platform/python/vfs-py.h index 2842d4812..6cc56dc4c 100644 --- a/src/platform/python/vfs-py.h +++ b/src/platform/python/vfs-py.h @@ -13,16 +13,12 @@ struct VFilePy { struct VFile* VFileFromPython(void* fileobj); -extern "Python+C" { - -bool _vfpClose(struct VFile* vf); -off_t _vfpSeek(struct VFile* vf, off_t offset, int whence); -ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size); -ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size); -void* _vfpMap(struct VFile* vf, size_t size, int flags); -void _vfpUnmap(struct VFile* vf, void* memory, size_t size); -void _vfpTruncate(struct VFile* vf, size_t size); -ssize_t _vfpSize(struct VFile* vf); -bool _vfpSync(struct VFile* vf, const void* buffer, size_t size); - -} +PYEXPORT bool _vfpClose(struct VFile* vf); +PYEXPORT off_t _vfpSeek(struct VFile* vf, off_t offset, int whence); +PYEXPORT ssize_t _vfpRead(struct VFile* vf, void* buffer, size_t size); +PYEXPORT ssize_t _vfpWrite(struct VFile* vf, const void* buffer, size_t size); +PYEXPORT void* _vfpMap(struct VFile* vf, size_t size, int flags); +PYEXPORT void _vfpUnmap(struct VFile* vf, void* memory, size_t size); +PYEXPORT void _vfpTruncate(struct VFile* vf, size_t size); +PYEXPORT ssize_t _vfpSize(struct VFile* vf); +PYEXPORT bool _vfpSync(struct VFile* vf, const void* buffer, size_t size); From efd8c4b4665da523591b70309b4f08a3535fe93c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 31 May 2017 17:59:16 -0700 Subject: [PATCH 150/159] Python: Add GBA SIO --- src/platform/python/_builder.h | 3 +- src/platform/python/_builder.py | 3 +- src/platform/python/mgba/__init__.py | 4 +-- src/platform/python/mgba/gba.py | 36 ++++++++++++++++++++ src/platform/python/sio.c | 49 ++++++++++++++++++++++++++++ src/platform/python/sio.h | 23 +++++++++++++ 6 files changed, 114 insertions(+), 4 deletions(-) create mode 100644 src/platform/python/sio.c create mode 100644 src/platform/python/sio.h diff --git a/src/platform/python/_builder.h b/src/platform/python/_builder.h index c2ef3fe78..8820a8c08 100644 --- a/src/platform/python/_builder.h +++ b/src/platform/python/_builder.h @@ -31,8 +31,9 @@ void free(void*); #include #define PYEXPORT extern "Python+C" -#include "platform/python/vfs-py.h" #include "platform/python/log.h" +#include "platform/python/sio.h" +#include "platform/python/vfs-py.h" #undef PYEXPORT #ifdef USE_PNG diff --git a/src/platform/python/_builder.py b/src/platform/python/_builder.py index 3603e991e..ccf35a880 100644 --- a/src/platform/python/_builder.py +++ b/src/platform/python/_builder.py @@ -34,13 +34,14 @@ ffi.set_source("mgba._pylib", """ #define PYEXPORT #include "platform/python/log.h" +#include "platform/python/sio.h" #include "platform/python/vfs-py.h" #undef PYEXPORT """, include_dirs=[incdir, srcdir], extra_compile_args=cppflags, libraries=["mgba"], library_dirs=[bindir], - sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c"]]) + sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "log.c", "sio.c"]]) preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True) diff --git a/src/platform/python/mgba/__init__.py b/src/platform/python/mgba/__init__.py index 7be537a5b..aab19b4d2 100644 --- a/src/platform/python/mgba/__init__.py +++ b/src/platform/python/mgba/__init__.py @@ -6,10 +6,10 @@ from ._pylib import ffi, lib def createCallback(structName, cbName, funcName=None): - funcName = funcName or "_py{}{}".format(structName, cbName.capitalize()) + funcName = funcName or "_py{}{}".format(structName, cbName[0].upper() + cbName[1:]) fullStruct = "struct {}*".format(structName) def cb(handle, *args): h = ffi.cast(fullStruct, handle) - getattr(ffi.from_handle(h.pyobj), cbName)(*args) + return getattr(ffi.from_handle(h.pyobj), cbName)(*args) return ffi.def_extern(name=funcName)(cb) diff --git a/src/platform/python/mgba/gba.py b/src/platform/python/mgba/gba.py index 0b8e12fee..5ba050748 100644 --- a/src/platform/python/mgba/gba.py +++ b/src/platform/python/mgba/gba.py @@ -8,6 +8,7 @@ from .arm import ARMCore from .core import Core, needsReset from .tile import Sprite from .memory import Memory +from . import createCallback class GBA(Core): KEY_A = lib.GBA_KEY_A @@ -21,6 +22,12 @@ class GBA(Core): KEY_L = lib.GBA_KEY_L KEY_R = lib.GBA_KEY_R + SIO_NORMAL_8 = lib.SIO_NORMAL_8 + SIO_NORMAL_32 = lib.SIO_NORMAL_32 + SIO_MULTI = lib.SIO_MULTI + SIO_UART = lib.SIO_UART + SIO_GPIO = lib.SIO_GPIO + def __init__(self, native): super(GBA, self).__init__(native) self._native = ffi.cast("struct GBA*", native.board) @@ -40,6 +47,35 @@ class GBA(Core): super(GBA, self).reset() self.memory = GBAMemory(self._core, self._native.memory.romSize) + def attachSIO(self, link, mode=lib.SIO_MULTI): + lib.GBASIOSetDriver(ffi.addressof(self._native.sio), link._native, mode) + +createCallback("GBASIOPythonDriver", "init") +createCallback("GBASIOPythonDriver", "deinit") +createCallback("GBASIOPythonDriver", "load") +createCallback("GBASIOPythonDriver", "unload") +createCallback("GBASIOPythonDriver", "writeRegister") + +class GBASIODriver(object): + def __init__(self): + self._handle = ffi.new_handle(self) + self._native = ffi.gc(lib.GBASIOPythonDriverCreate(self._handle), lib.free) + + def init(self): + return True + + def deinit(self): + pass + + def load(self): + return True + + def unload(self): + return True + + def writeRegister(self, address, value): + return value + class GBAMemory(Memory): def __init__(self, core, romSize=lib.SIZE_CART0): super(GBAMemory, self).__init__(core, 0x100000000) diff --git a/src/platform/python/sio.c b/src/platform/python/sio.c new file mode 100644 index 000000000..1e5928689 --- /dev/null +++ b/src/platform/python/sio.c @@ -0,0 +1,49 @@ +/* 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 + +#include "flags.h" + +#ifdef M_CORE_GBA + +#define CREATE_SHIM(NAME, RETURN) \ + RETURN _pyGBASIOPythonDriver ## NAME (void* driver); \ + static RETURN _pyGBASIOPythonDriver ## NAME ## Shim(struct GBASIODriver* driver) { \ + struct GBASIODriver* py = (struct GBASIODriver*) driver; \ + return _pyGBASIOPythonDriver ## NAME(py); \ + } + +#define CREATE_SHIM_ARGS(NAME, RETURN, TYPES, ...) \ + RETURN _pyGBASIOPythonDriver ## NAME TYPES; \ + static RETURN _pyGBASIOPythonDriver ## NAME ## Shim TYPES { \ + struct GBASIODriver* py = (struct GBASIODriver*) driver; \ + return _pyGBASIOPythonDriver ## NAME(py, __VA_ARGS__); \ + } + +struct GBASIOPythonDriver { + struct GBASIODriver d; + void* pyobj; +}; + +CREATE_SHIM(Init, bool); +CREATE_SHIM(Deinit, void); +CREATE_SHIM(Load, bool); +CREATE_SHIM(Unload, bool); +CREATE_SHIM_ARGS(WriteRegister, uint16_t, (struct GBASIODriver* driver, uint32_t address, uint16_t value), address, value); + +struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) { + struct GBASIOPythonDriver* driver = malloc(sizeof(*driver)); + driver->d.init = _pyGBASIOPythonDriverInitShim; + driver->d.deinit = _pyGBASIOPythonDriverDeinitShim; + driver->d.load = _pyGBASIOPythonDriverLoadShim; + driver->d.unload = _pyGBASIOPythonDriverUnloadShim; + driver->d.writeRegister = _pyGBASIOPythonDriverWriteRegisterShim; + + driver->pyobj = pyobj; + return &driver->d; +} + +#endif diff --git a/src/platform/python/sio.h b/src/platform/python/sio.h new file mode 100644 index 000000000..919d2f596 --- /dev/null +++ b/src/platform/python/sio.h @@ -0,0 +1,23 @@ +/* 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/. */ +#ifdef M_CORE_GBA + +#include + +struct GBASIOPythonDriver { + struct GBASIODriver d; + void* pyobj; +}; + +struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj); + +PYEXPORT bool _pyGBASIOPythonDriverInit(void* driver); +PYEXPORT void _pyGBASIOPythonDriverDeinit(void* driver); +PYEXPORT bool _pyGBASIOPythonDriverLoad(void* driver); +PYEXPORT bool _pyGBASIOPythonDriverUnload(void* driver); +PYEXPORT uint16_t _pyGBASIOPythonDriverWriteRegister(void* driver, uint32_t address, uint16_t value); + +#endif From 362c572009ac74f34eda03b4b4b7b5f93faa1daf Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 31 May 2017 22:40:03 -0700 Subject: [PATCH 151/159] Python: Add GB SIO --- include/mgba/gb/interface.h | 9 +++++ include/mgba/internal/gb/sio.h | 10 +---- src/platform/python/mgba/gb.py | 45 ++++++++++++++++++++++- src/platform/python/sio.c | 67 ++++++++++++++++++++++++---------- src/platform/python/sio.h | 18 +++++++++ 5 files changed, 119 insertions(+), 30 deletions(-) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 098a5e5dd..03b836865 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -34,6 +34,15 @@ enum GBMemoryBankControllerType { GB_MBC5_RUMBLE = 0x105 }; +struct GBSIODriver { + struct GBSIO* p; + + bool (*init)(struct GBSIODriver* driver); + void (*deinit)(struct GBSIODriver* driver); + void (*writeSB)(struct GBSIODriver* driver, uint8_t value); + uint8_t (*writeSC)(struct GBSIODriver* driver, uint8_t value); +}; + CXX_GUARD_END #endif diff --git a/include/mgba/internal/gb/sio.h b/include/mgba/internal/gb/sio.h index 004cf16e8..50d62868b 100644 --- a/include/mgba/internal/gb/sio.h +++ b/include/mgba/internal/gb/sio.h @@ -12,6 +12,7 @@ CXX_GUARD_START #include #include +#include #define MAX_GBS 2 @@ -34,15 +35,6 @@ struct GBSIO { uint8_t pendingSB; }; -struct GBSIODriver { - struct GBSIO* p; - - bool (*init)(struct GBSIODriver* driver); - void (*deinit)(struct GBSIODriver* driver); - void (*writeSB)(struct GBSIODriver* driver, uint8_t value); - uint8_t (*writeSC)(struct GBSIODriver* driver, uint8_t value); -}; - DECL_BITFIELD(GBRegisterSC, uint8_t); DECL_BIT(GBRegisterSC, ShiftClock, 0); DECL_BIT(GBRegisterSC, ClockSpeed, 1); diff --git a/src/platform/python/mgba/gb.py b/src/platform/python/mgba/gb.py index 0caf7ec99..2b490e008 100644 --- a/src/platform/python/mgba/gb.py +++ b/src/platform/python/mgba/gb.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013-2016 Jeffrey Pfau +# 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 @@ -8,6 +8,7 @@ from .lr35902 import LR35902Core from .core import Core, needsReset from .memory import Memory from .tile import Sprite +from . import createCallback class GB(Core): KEY_A = lib.GBA_KEY_A @@ -34,6 +35,48 @@ class GB(Core): self._native.video.renderer.cache = ffi.NULL lib.mTileCacheDeinit(cache) + def attachSIO(self, link): + lib.GBSIOSetDriver(ffi.addressof(self._native.sio), link._native) + +createCallback("GBSIOPythonDriver", "init") +createCallback("GBSIOPythonDriver", "deinit") +createCallback("GBSIOPythonDriver", "writeSB") +createCallback("GBSIOPythonDriver", "writeSC") + +class GBSIODriver(object): + def __init__(self): + self._handle = ffi.new_handle(self) + self._native = ffi.gc(lib.GBSIOPythonDriverCreate(self._handle), lib.free) + + def init(self): + return True + + def deinit(self): + pass + + def writeSB(self, value): + pass + + def writeSC(self, value): + return value + +class GBSIOSimpleDriver(GBSIODriver): + def __init__(self): + super(GBSIOSimpleDriver, self).__init__() + self.tx = 0xFF + self.rx = 0xFF + + def writeSB(self, value): + self.rx = value + + def schedule(self, period=0x100, when=0): + self._native.p.remainingBits = 8 + self._native.p.period = period + self._native.p.pendingSB = self.tx + self.tx = 0xFF + lib.mTimingDeschedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event)) + lib.mTimingSchedule(ffi.addressof(self._native.p.p.timing), ffi.addressof(self._native.p.event), when) + class GBMemory(Memory): def __init__(self, core): super(GBMemory, self).__init__(core, 0x10000) diff --git a/src/platform/python/sio.c b/src/platform/python/sio.c index 1e5928689..a7cba8973 100644 --- a/src/platform/python/sio.c +++ b/src/platform/python/sio.c @@ -3,36 +3,36 @@ * 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 - #include "flags.h" +#define CREATE_SHIM(PLAT, NAME, RETURN) \ + RETURN _py ## PLAT ## SIOPythonDriver ## NAME (void* driver); \ + static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim(struct PLAT ## SIODriver* driver) { \ + struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \ + return _py ## PLAT ## SIOPythonDriver ## NAME(py); \ + } + +#define CREATE_SHIM_ARGS(PLAT, NAME, RETURN, TYPES, ...) \ + RETURN _py ## PLAT ## SIOPythonDriver ## NAME TYPES; \ + static RETURN _py ## PLAT ## SIOPythonDriver ## NAME ## Shim TYPES { \ + struct PLAT ## SIODriver* py = (struct PLAT ## SIODriver*) driver; \ + return _py ## PLAT ## SIOPythonDriver ## NAME(py, __VA_ARGS__); \ + } + #ifdef M_CORE_GBA -#define CREATE_SHIM(NAME, RETURN) \ - RETURN _pyGBASIOPythonDriver ## NAME (void* driver); \ - static RETURN _pyGBASIOPythonDriver ## NAME ## Shim(struct GBASIODriver* driver) { \ - struct GBASIODriver* py = (struct GBASIODriver*) driver; \ - return _pyGBASIOPythonDriver ## NAME(py); \ - } - -#define CREATE_SHIM_ARGS(NAME, RETURN, TYPES, ...) \ - RETURN _pyGBASIOPythonDriver ## NAME TYPES; \ - static RETURN _pyGBASIOPythonDriver ## NAME ## Shim TYPES { \ - struct GBASIODriver* py = (struct GBASIODriver*) driver; \ - return _pyGBASIOPythonDriver ## NAME(py, __VA_ARGS__); \ - } +#include struct GBASIOPythonDriver { struct GBASIODriver d; void* pyobj; }; -CREATE_SHIM(Init, bool); -CREATE_SHIM(Deinit, void); -CREATE_SHIM(Load, bool); -CREATE_SHIM(Unload, bool); -CREATE_SHIM_ARGS(WriteRegister, uint16_t, (struct GBASIODriver* driver, uint32_t address, uint16_t value), address, value); +CREATE_SHIM(GBA, Init, bool); +CREATE_SHIM(GBA, Deinit, void); +CREATE_SHIM(GBA, Load, bool); +CREATE_SHIM(GBA, Unload, bool); +CREATE_SHIM_ARGS(GBA, WriteRegister, uint16_t, (struct GBASIODriver* driver, uint32_t address, uint16_t value), address, value); struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) { struct GBASIOPythonDriver* driver = malloc(sizeof(*driver)); @@ -47,3 +47,30 @@ struct GBASIODriver* GBASIOPythonDriverCreate(void* pyobj) { } #endif + +#ifdef M_CORE_GB + +#include + +struct GBSIOPythonDriver { + struct GBSIODriver d; + void* pyobj; +}; + +CREATE_SHIM(GB, Init, bool); +CREATE_SHIM(GB, Deinit, void); +CREATE_SHIM_ARGS(GB, WriteSB, void, (struct GBSIODriver* driver, uint8_t value), value); +CREATE_SHIM_ARGS(GB, WriteSC, uint8_t, (struct GBSIODriver* driver, uint8_t value), value); + +struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj) { + struct GBSIOPythonDriver* driver = malloc(sizeof(*driver)); + driver->d.init = _pyGBSIOPythonDriverInitShim; + driver->d.deinit = _pyGBSIOPythonDriverDeinitShim; + driver->d.writeSB = _pyGBSIOPythonDriverWriteSBShim; + driver->d.writeSC = _pyGBSIOPythonDriverWriteSCShim; + + driver->pyobj = pyobj; + return &driver->d; +} + +#endif diff --git a/src/platform/python/sio.h b/src/platform/python/sio.h index 919d2f596..0403fdfd9 100644 --- a/src/platform/python/sio.h +++ b/src/platform/python/sio.h @@ -21,3 +21,21 @@ PYEXPORT bool _pyGBASIOPythonDriverUnload(void* driver); PYEXPORT uint16_t _pyGBASIOPythonDriverWriteRegister(void* driver, uint32_t address, uint16_t value); #endif + +#ifdef M_CORE_GB + +#include + +struct GBSIOPythonDriver { + struct GBSIODriver d; + void* pyobj; +}; + +struct GBSIODriver* GBSIOPythonDriverCreate(void* pyobj); + +PYEXPORT bool _pyGBSIOPythonDriverInit(void* driver); +PYEXPORT void _pyGBSIOPythonDriverDeinit(void* driver); +PYEXPORT void _pyGBSIOPythonDriverWriteSB(void* driver, uint8_t value); +PYEXPORT uint8_t _pyGBSIOPythonDriverWriteSC(void* driver, uint8_t value); + +#endif From 3d77a9d922dbdcf6e92b5f63e577f61fc43776aa Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 11:26:53 -0700 Subject: [PATCH 152/159] GB MBC: Fix initialization and swapping --- src/gb/gb.c | 19 ++++++++++++------- src/gb/mbc.c | 8 ++++++++ src/gb/memory.c | 6 ------ src/gb/overrides.c | 2 ++ 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/gb/gb.c b/src/gb/gb.c index 4c83984f5..a97fe97ef 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -124,7 +124,7 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) { gb->memory.romBase = gb->memory.rom; gb->memory.romSize = gb->pristineRomSize; gb->romCrc32 = doCrc32(gb->memory.rom, gb->memory.romSize); - GBMBCSwitchBank(gb, gb->memory.currentBank); + GBMBCInit(gb); if (gb->cpu) { struct LR35902Core* cpu = gb->cpu; @@ -135,12 +135,6 @@ bool GBLoadROM(struct GB* gb, struct VFile* vf) { return true; } -bool GBLoadSave(struct GB* gb, struct VFile* vf) { - gb->sramVf = vf; - gb->sramRealVf = vf; - return vf; -} - static void GBSramDeinit(struct GB* gb) { if (gb->sramVf) { gb->sramVf->unmap(gb->sramVf, gb->memory.sram, gb->sramSize); @@ -154,6 +148,16 @@ static void GBSramDeinit(struct GB* gb) { gb->memory.sram = 0; } +bool GBLoadSave(struct GB* gb, struct VFile* vf) { + GBSramDeinit(gb); + gb->sramVf = vf; + gb->sramRealVf = vf; + if (gb->sramSize) { + GBResizeSram(gb, gb->sramSize); + } + return vf; +} + void GBResizeSram(struct GB* gb, size_t size) { if (gb->memory.sram && size <= gb->sramSize) { return; @@ -280,6 +284,7 @@ void GBUnloadROM(struct GB* gb) { gb->romVf = NULL; } gb->memory.rom = NULL; + gb->memory.mbcType = GB_MBC_AUTODETECT; gb->isPristine = false; GBSavedataUnmask(gb); diff --git a/src/gb/mbc.c b/src/gb/mbc.c index ae69defc6..0710f10c0 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -204,6 +204,14 @@ void GBMBCInit(struct GB* gb) { break; } + gb->memory.currentBank = 1; + gb->memory.sramCurrentBank = 0; + gb->memory.sramAccess = false; + gb->memory.rtcAccess = false; + gb->memory.activeRtcReg = 0; + gb->memory.rtcLatched = false; + memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); + GBResizeSram(gb, gb->sramSize); if (gb->memory.mbcType == GB_MBC3_RTC) { diff --git a/src/gb/memory.c b/src/gb/memory.c index 3fde79010..8484941b7 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -158,12 +158,6 @@ void GBMemoryReset(struct GB* gb) { gb->memory.hdmaEvent.callback = _GBMemoryHDMAService; gb->memory.hdmaEvent.priority = 0x41; - gb->memory.sramAccess = false; - gb->memory.rtcAccess = false; - gb->memory.activeRtcReg = 0; - gb->memory.rtcLatched = false; - memset(&gb->memory.rtcRegs, 0, sizeof(gb->memory.rtcRegs)); - memset(&gb->memory.hram, 0, sizeof(gb->memory.hram)); switch (gb->memory.mbcType) { case GB_MBC1: diff --git a/src/gb/overrides.c b/src/gb/overrides.c index d86f08f5f..70d11cd22 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -102,6 +103,7 @@ void GBOverrideApply(struct GB* gb, const struct GBCartridgeOverride* override) if (override->mbc != GB_MBC_AUTODETECT) { gb->memory.mbcType = override->mbc; + GBMBCInit(gb); } } From 825805a2c09c9c39c9afb572d6fb73614085ffa2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 11:27:28 -0700 Subject: [PATCH 153/159] GB MBC: Fix swapping carts not detect new MBC --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index f9edb5bb6..a265a0d4e 100644 --- a/CHANGES +++ b/CHANGES @@ -61,6 +61,7 @@ Bugfixes: - Qt: Disable "New multiplayer window" when MAX_GBAS is reached (fixes mgba.io/i/107) - LR35902: Fix decoding LD r, $imm and 0-valued immediates (fixes mgba.io/i/735) - GB: Fix STAT blocking + - GB MBC: Fix swapping carts not detect new MBC Misc: - SDL: Remove scancode key input - GBA Video: Clean up unused timers From 1caa17c00bf58a54956f16711a574e89536a47e4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 15:58:00 -0700 Subject: [PATCH 154/159] GB I/O: Fix bootrom running forever --- src/gb/io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gb/io.c b/src/gb/io.c index 0f386aaae..8b4102c0d 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -382,7 +382,7 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { value = gb->video.stat; break; case 0x50: - 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); gb->memory.romBase = gb->memory.rom; } From 9c23eb8428b2fd81d00c95ea1be56b508ba2b115 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 18:34:24 -0700 Subject: [PATCH 155/159] GBA: Add keypad IRQs (fixes #733) --- CHANGES | 1 + include/mgba/internal/gba/gba.h | 2 ++ src/gba/core.c | 3 +++ src/gba/gba.c | 33 ++++++++++++++++++++++++++++----- src/gba/io.c | 11 ++++++++++- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index a265a0d4e..2767ecde0 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,7 @@ Features: - Debugger: Segment/bank support - GB: Symbol table support - GB MBC: Add MBC1 multicart support + - GBA: Implement keypad interrupts Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 732a4aba6..5710864e0 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -175,6 +175,8 @@ bool GBAIsBIOS(struct VFile* vf); void GBAGetGameCode(const struct GBA* gba, char* out); void GBAGetGameTitle(const struct GBA* gba, char* out); +void GBATestKeypadIRQ(struct GBA* gba); + void GBAFrameStarted(struct GBA* gba); void GBAFrameEnded(struct GBA* gba); diff --git a/src/gba/core.c b/src/gba/core.c index 6f81e2570..32272bb50 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -398,16 +398,19 @@ static bool _GBACoreSaveState(struct mCore* core, void* state) { static void _GBACoreSetKeys(struct mCore* core, uint32_t keys) { struct GBACore* gbacore = (struct GBACore*) core; gbacore->keys = keys; + GBATestKeypadIRQ(core->board); } static void _GBACoreAddKeys(struct mCore* core, uint32_t keys) { struct GBACore* gbacore = (struct GBACore*) core; gbacore->keys |= keys; + GBATestKeypadIRQ(core->board); } static void _GBACoreClearKeys(struct mCore* core, uint32_t keys) { struct GBACore* gbacore = (struct GBACore*) core; gbacore->keys &= ~keys; + GBATestKeypadIRQ(core->board); } static int32_t _GBACoreFrameCounter(const struct mCore* core) { diff --git a/src/gba/gba.c b/src/gba/gba.c index 4a145f5d8..1a1ee4b2d 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -409,10 +409,6 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { } void GBAWriteIE(struct GBA* gba, uint16_t value) { - if (value & (1 << IRQ_KEYPAD)) { - mLOG(GBA, STUB, "Keypad interrupts not implemented"); - } - if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) { ARMRaiseIRQ(gba->cpu); } @@ -637,7 +633,7 @@ void GBABreakpoint(struct ARMCore* cpu, int immediate) { } void GBAFrameStarted(struct GBA* gba) { - UNUSED(gba); + GBATestKeypadIRQ(gba); size_t c; for (c = 0; c < mCoreCallbacksListSize(&gba->coreCallbacks); ++c) { @@ -684,6 +680,33 @@ void GBAFrameEnded(struct GBA* gba) { } } +void GBATestKeypadIRQ(struct GBA* gba) { + uint16_t keycnt = gba->memory.io[REG_KEYCNT >> 1]; + if (!(keycnt & 0x4000)) { + return; + } + int isAnd = keycnt & 0x8000; + uint16_t keyInput; + + if (!gba->keySource) { + // TODO? + return; + } + + keycnt &= 0x3FF; + keyInput = *gba->keySource; + + if (popcount32(keyInput) > 2) { + keycnt = keycnt; + } + + if (isAnd && keycnt == keyInput) { + GBARaiseIRQ(gba, IRQ_KEYPAD); + } else if (!isAnd && keyInput) { + GBARaiseIRQ(gba, IRQ_KEYPAD); + } +} + void GBASetBreakpoint(struct GBA* gba, struct mCPUComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) { size_t immediate; for (immediate = 0; immediate < gba->cpu->numComponents; ++immediate) { diff --git a/src/gba/io.c b/src/gba/io.c index 9574ec9f9..4d36f2800 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -537,6 +537,11 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { break; // Interrupts and misc + case REG_KEYCNT: + value &= 0xC3FF; + gba->memory.io[address >> 1] = value; + GBATestKeypadIRQ(gba); + return; case REG_WAITCNT: value &= 0x5FFF; GBAAdjustWaitstates(gba, value); @@ -689,6 +694,7 @@ bool GBAIOIsReadConstant(uint32_t address) { case REG_TM2CNT_HI: case REG_TM3CNT_HI: case REG_KEYINPUT: + case REG_KEYCNT: case REG_IE: return true; } @@ -721,6 +727,9 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { uint16_t input = 0x3FF; if (gba->keyCallback) { input = gba->keyCallback->readKeys(gba->keyCallback); + if (gba->keySource) { + *gba->keySource = input; + } } else if (gba->keySource) { input = *gba->keySource; } @@ -814,7 +823,6 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { break; case REG_SOUNDBIAS: - case REG_KEYCNT: case REG_POSTFLG: mLOG(GBA_IO, STUB, "Stub I/O register read: %03x", address); break; @@ -863,6 +871,7 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { case REG_TM1CNT_HI: case REG_TM2CNT_HI: case REG_TM3CNT_HI: + case REG_KEYCNT: case REG_SIOMULTI0: case REG_SIOMULTI1: case REG_SIOMULTI2: From 39a73ecb95659ef36ec40519acc16e403adc15a2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 18:46:30 -0700 Subject: [PATCH 156/159] GBA: Oops --- src/gba/gba.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/gba/gba.c b/src/gba/gba.c index 1a1ee4b2d..a6d9467ae 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -696,13 +696,9 @@ void GBATestKeypadIRQ(struct GBA* gba) { keycnt &= 0x3FF; keyInput = *gba->keySource; - if (popcount32(keyInput) > 2) { - keycnt = keycnt; - } - if (isAnd && keycnt == keyInput) { GBARaiseIRQ(gba, IRQ_KEYPAD); - } else if (!isAnd && keyInput) { + } else if (!isAnd && keycnt & keyInput) { GBARaiseIRQ(gba, IRQ_KEYPAD); } } From 9c144266ec57f77eec632c936ec8b1bf5565f140 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 19:15:13 -0700 Subject: [PATCH 157/159] LR35902: Add watchpoints --- CHANGES | 1 + CMakeLists.txt | 1 + include/mgba/internal/debugger/debugger.h | 4 +- .../mgba/internal/lr35902/debugger/debugger.h | 4 ++ src/arm/debugger/debugger.c | 10 +++-- src/debugger/cli-debugger.c | 8 ++-- src/debugger/gdb-stub.c | 8 ++-- src/lr35902/debugger/debugger.c | 37 ++++++++++++++++++- 8 files changed, 57 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 2767ecde0..db620fc67 100644 --- a/CHANGES +++ b/CHANGES @@ -22,6 +22,7 @@ Features: - GB: Symbol table support - GB MBC: Add MBC1 multicart support - GBA: Implement keypad interrupts + - LR35902: Watchpoints Bugfixes: - LR35902: Fix core never exiting with certain event patterns - GB Timer: Improve DIV reset behavior diff --git a/CMakeLists.txt b/CMakeLists.txt index 05aa152aa..e40465c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -604,6 +604,7 @@ if(M_CORE_GB) list(APPEND DEBUGGER_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/cli-debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/debugger.c + ${CMAKE_CURRENT_SOURCE_DIR}/src/lr35902/debugger/memory-debugger.c ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/cli.c ${CMAKE_CURRENT_SOURCE_DIR}/src/gb/debugger/symbols.c) list(APPEND TEST_SRC diff --git a/include/mgba/internal/debugger/debugger.h b/include/mgba/internal/debugger/debugger.h index a54c353c8..d73afd39e 100644 --- a/include/mgba/internal/debugger/debugger.h +++ b/include/mgba/internal/debugger/debugger.h @@ -89,8 +89,8 @@ struct mDebuggerPlatform { bool (*hasBreakpoints)(struct mDebuggerPlatform*); void (*setBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*clearBreakpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); - void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type); - void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address); + void (*setWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); + void (*clearWatchpoint)(struct mDebuggerPlatform*, uint32_t address, int segment); void (*checkBreakpoints)(struct mDebuggerPlatform*); }; diff --git a/include/mgba/internal/lr35902/debugger/debugger.h b/include/mgba/internal/lr35902/debugger/debugger.h index 06801d77b..039ae8e95 100644 --- a/include/mgba/internal/lr35902/debugger/debugger.h +++ b/include/mgba/internal/lr35902/debugger/debugger.h @@ -12,6 +12,9 @@ CXX_GUARD_START #include +#include + + struct LR35902DebugBreakpoint { uint16_t address; int segment; @@ -32,6 +35,7 @@ struct LR35902Debugger { struct LR35902DebugBreakpointList breakpoints; struct LR35902DebugWatchpointList watchpoints; + struct LR35902Memory originalMemory; }; struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void); diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 47aa0d86e..03ae0e90e 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -50,8 +50,8 @@ static void ARMDebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntryRea static void ARMDebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); -static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, enum mWatchpointType type); -static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address); +static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); +static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*); @@ -182,7 +182,8 @@ static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) { return ARMDebugBreakpointListSize(&debugger->breakpoints) || ARMDebugWatchpointListSize(&debugger->watchpoints); } -static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, enum mWatchpointType type) { +static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { + UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; if (!ARMDebugWatchpointListSize(&debugger->watchpoints)) { ARMDebuggerInstallMemoryShim(debugger); @@ -192,7 +193,8 @@ static void ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t addre watchpoint->type = type; } -static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address) { +static void ARMDebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { + UNUSED(segment); struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMDebugWatchpointList* watchpoints = &debugger->watchpoints; size_t i; diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 74a87c743..70cff2a5f 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -428,7 +428,7 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_RW); + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_RW); } static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -441,7 +441,7 @@ static void _setReadWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVect return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_READ); + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_READ); } static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -454,7 +454,7 @@ static void _setWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVec return; } uint32_t address = dv->intValue; - debugger->d.platform->setWatchpoint(debugger->d.platform, address, WATCHPOINT_WRITE); + debugger->d.platform->setWatchpoint(debugger->d.platform, address, dv->segmentValue, WATCHPOINT_WRITE); } static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { @@ -465,7 +465,7 @@ static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector uint32_t address = dv->intValue; debugger->d.platform->clearBreakpoint(debugger->d.platform, address, dv->segmentValue); if (debugger->d.platform->clearWatchpoint) { - debugger->d.platform->clearWatchpoint(debugger->d.platform, address); + debugger->d.platform->clearWatchpoint(debugger->d.platform, address, dv->segmentValue); } } diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index 05005648b..3c149c9fc 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -498,13 +498,13 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) { stub->d.platform->setBreakpoint(stub->d.platform, address, -1); break; case '2': - stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_WRITE); + stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_WRITE); break; case '3': - stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_READ); + stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_READ); break; case '4': - stub->d.platform->setWatchpoint(stub->d.platform, address, WATCHPOINT_RW); + stub->d.platform->setWatchpoint(stub->d.platform, address, -1, WATCHPOINT_RW); break; default: stub->outgoing[0] = '\0'; @@ -529,7 +529,7 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) { case '2': case '3': case '4': - stub->d.platform->clearWatchpoint(stub->d.platform, address); + stub->d.platform->clearWatchpoint(stub->d.platform, address, -1); break; default: break; diff --git a/src/lr35902/debugger/debugger.c b/src/lr35902/debugger/debugger.c index 91de87577..5d3f3a6ce 100644 --- a/src/lr35902/debugger/debugger.c +++ b/src/lr35902/debugger/debugger.c @@ -7,6 +7,7 @@ #include #include +#include DEFINE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint); DEFINE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint); @@ -43,6 +44,8 @@ static void LR35902DebuggerEnter(struct mDebuggerPlatform* d, enum mDebuggerEntr static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerClearBreakpoint(struct mDebuggerPlatform*, uint32_t address, int segment); +static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment, enum mWatchpointType type); +static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform*, uint32_t address, int segment); static void LR35902DebuggerCheckBreakpoints(struct mDebuggerPlatform*); static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform*); @@ -53,8 +56,8 @@ struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void) { platform->deinit = LR35902DebuggerDeinit; platform->setBreakpoint = LR35902DebuggerSetBreakpoint; platform->clearBreakpoint = LR35902DebuggerClearBreakpoint; - platform->setWatchpoint = NULL; - platform->clearWatchpoint = NULL; + platform->setWatchpoint = LR35902DebuggerSetWatchpoint; + platform->clearWatchpoint = LR35902DebuggerClearWatchpoint; platform->checkBreakpoints = LR35902DebuggerCheckBreakpoints; platform->hasBreakpoints = LR35902DebuggerHasBreakpoints; return platform; @@ -79,6 +82,10 @@ static void LR35902DebuggerEnter(struct mDebuggerPlatform* platform, enum mDebug struct LR35902Debugger* debugger = (struct LR35902Debugger*) platform; struct LR35902Core* cpu = debugger->cpu; cpu->nextEvent = cpu->cycles; + + if (debugger->d.p->entered) { + debugger->d.p->entered(debugger->d.p, reason, info); + } } static void LR35902DebuggerSetBreakpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { @@ -104,3 +111,29 @@ static bool LR35902DebuggerHasBreakpoints(struct mDebuggerPlatform* d) { struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; return LR35902DebugBreakpointListSize(&debugger->breakpoints) || LR35902DebugWatchpointListSize(&debugger->watchpoints); } + +static void LR35902DebuggerSetWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment, enum mWatchpointType type) { + struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; + if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) { + LR35902DebuggerInstallMemoryShim(debugger); + } + struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListAppend(&debugger->watchpoints); + watchpoint->address = address; + watchpoint->type = type; + watchpoint->segment = segment; +} + +static void LR35902DebuggerClearWatchpoint(struct mDebuggerPlatform* d, uint32_t address, int segment) { + struct LR35902Debugger* debugger = (struct LR35902Debugger*) d; + struct LR35902DebugWatchpointList* watchpoints = &debugger->watchpoints; + size_t i; + for (i = 0; i < LR35902DebugWatchpointListSize(watchpoints); ++i) { + struct LR35902DebugWatchpoint* watchpoint = LR35902DebugWatchpointListGetPointer(watchpoints, i); + if (watchpoint->address == address && watchpoint->segment == segment) { + LR35902DebugWatchpointListShift(watchpoints, i, 1); + } + } + if (!LR35902DebugWatchpointListSize(&debugger->watchpoints)) { + LR35902DebuggerRemoveMemoryShim(debugger); + } +} From 19ecae8ca14d219fe58a32c625aec71fcb774414 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 19:37:36 -0700 Subject: [PATCH 158/159] Debugger: Show current banks --- .../mgba/internal/lr35902/debugger/debugger.h | 8 +++++++ src/gb/core.c | 23 ++++++++++++++++++- src/lr35902/debugger/cli-debugger.c | 10 ++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/include/mgba/internal/lr35902/debugger/debugger.h b/include/mgba/internal/lr35902/debugger/debugger.h index 039ae8e95..85712ee96 100644 --- a/include/mgba/internal/lr35902/debugger/debugger.h +++ b/include/mgba/internal/lr35902/debugger/debugger.h @@ -26,6 +26,12 @@ struct LR35902DebugWatchpoint { enum mWatchpointType type; }; +struct LR35902Segment { + uint16_t start; + uint16_t end; + const char* name; +}; + DECLARE_VECTOR(LR35902DebugBreakpointList, struct LR35902DebugBreakpoint); DECLARE_VECTOR(LR35902DebugWatchpointList, struct LR35902DebugWatchpoint); @@ -36,6 +42,8 @@ struct LR35902Debugger { struct LR35902DebugBreakpointList breakpoints; struct LR35902DebugWatchpointList watchpoints; struct LR35902Memory originalMemory; + + const struct LR35902Segment* segments; }; struct mDebuggerPlatform* LR35902DebuggerPlatformCreate(void); diff --git a/src/gb/core.c b/src/gb/core.c index 29e1b0c6f..1a5e0475d 100644 --- a/src/gb/core.c +++ b/src/gb/core.c @@ -37,6 +37,20 @@ const static struct mCoreChannelInfo _GBAudioChannels[] = { { 3, "ch3", "Channel 3", "Noise" }, }; +const static struct LR35902Segment _GBSegments[] = { + { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, + { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, + { 0 } +}; + +const static struct LR35902Segment _GBCSegments[] = { + { .name = "ROM", .start = GB_BASE_CART_BANK1, .end = GB_BASE_VRAM }, + { .name = "RAM", .start = GB_BASE_EXTERNAL_RAM, .end = GB_BASE_WORKING_RAM_BANK0 }, + { .name = "WRAM", .start = GB_BASE_WORKING_RAM_BANK1, .end = 0xE000 }, + { .name = "VRAM", .start = GB_BASE_VRAM, .end = GB_BASE_EXTERNAL_RAM }, + { 0 } +}; + struct mVideoLogContext; struct GBCore { struct mCore d; @@ -529,8 +543,15 @@ static bool _GBCoreSupportsDebuggerType(struct mCore* core, enum mDebuggerType t static struct mDebuggerPlatform* _GBCoreDebuggerPlatform(struct mCore* core) { struct GBCore* gbcore = (struct GBCore*) core; + struct GB* gb = core->board; if (!gbcore->debuggerPlatform) { - gbcore->debuggerPlatform = LR35902DebuggerPlatformCreate(); + struct LR35902Debugger* platform = (struct LR35902Debugger*) LR35902DebuggerPlatformCreate(); + if (gb->model >= GB_MODEL_CGB) { + platform->segments = _GBCSegments; + } else { + platform->segments = _GBSegments; + } + gbcore->debuggerPlatform = &platform->d; } return gbcore->debuggerPlatform; } diff --git a/src/lr35902/debugger/cli-debugger.c b/src/lr35902/debugger/cli-debugger.c index c2bc5fbd2..ac01343df 100644 --- a/src/lr35902/debugger/cli-debugger.c +++ b/src/lr35902/debugger/cli-debugger.c @@ -8,6 +8,7 @@ #include #include #include +#include #include static void _printStatus(struct CLIDebuggerSystem*); @@ -86,6 +87,15 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { be->printf(be, "D: %02X E: %02X (DE: %04X)\n", cpu->d, cpu->e, cpu->de); be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl); be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp); + + struct LR35902Debugger* platDebugger = (struct LR35902Debugger*) debugger->p->d.platform; + size_t i; + for (i = 0; platDebugger->segments[i].name; ++i) { + be->printf(be, "%s%s: %02X", i ? " " : "", platDebugger->segments[i].name, cpu->memory.currentSegment(cpu, platDebugger->segments[i].start)); + } + if (i) { + be->printf(be, "\n"); + } _printFlags(be, cpu->f); _printLine(debugger->p, cpu->pc, cpu->memory.currentSegment(cpu, cpu->pc)); } From d240272ac4fa84d9838cfeaa85ddb0b56b8a3637 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 2 Jun 2017 19:43:42 -0700 Subject: [PATCH 159/159] Debugger: Add missing files --- .../lr35902/debugger/memory-debugger.h | 20 ++++++ src/lr35902/debugger/memory-debugger.c | 70 +++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 include/mgba/internal/lr35902/debugger/memory-debugger.h create mode 100644 src/lr35902/debugger/memory-debugger.c diff --git a/include/mgba/internal/lr35902/debugger/memory-debugger.h b/include/mgba/internal/lr35902/debugger/memory-debugger.h new file mode 100644 index 000000000..140e176e4 --- /dev/null +++ b/include/mgba/internal/lr35902/debugger/memory-debugger.h @@ -0,0 +1,20 @@ +/* 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 LR35902_MEMORY_DEBUGGER_H +#define LR35902_MEMORY_DEBUGGER_H + +#include + +CXX_GUARD_START + +struct LR35902Debugger; + +void LR35902DebuggerInstallMemoryShim(struct LR35902Debugger* debugger); +void LR35902DebuggerRemoveMemoryShim(struct LR35902Debugger* debugger); + +CXX_GUARD_END + +#endif diff --git a/src/lr35902/debugger/memory-debugger.c b/src/lr35902/debugger/memory-debugger.c new file mode 100644 index 000000000..3b9f66343 --- /dev/null +++ b/src/lr35902/debugger/memory-debugger.c @@ -0,0 +1,70 @@ +/* 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 + +#include + +#include + +#include + +static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint8_t newValue); + +#define FIND_DEBUGGER(DEBUGGER, CPU) \ + do { \ + DEBUGGER = 0; \ + size_t i; \ + for (i = 0; i < CPU->numComponents; ++i) { \ + if (CPU->components[i]->id == DEBUGGER_ID) { \ + DEBUGGER = (struct LR35902Debugger*) ((struct mDebugger*) cpu->components[i])->platform; \ + goto debuggerFound; \ + } \ + } \ + abort(); \ + debuggerFound: break; \ + } while(0) + +#define CREATE_WATCHPOINT_SHIM(NAME, RW, RETURN, TYPES, ...) \ + static RETURN DebuggerShim_ ## NAME TYPES { \ + struct LR35902Debugger* debugger; \ + FIND_DEBUGGER(debugger, cpu); \ + struct mDebuggerEntryInfo info; \ + if (_checkWatchpoints(debugger, address, &info, WATCHPOINT_ ## RW, 0)) { \ + mDebuggerEnter(debugger->d.p, DEBUGGER_ENTER_WATCHPOINT, &info); \ + } \ + return debugger->originalMemory.NAME(cpu, __VA_ARGS__); \ + } + +CREATE_WATCHPOINT_SHIM(load8, READ, uint8_t, (struct LR35902Core* cpu, uint16_t address), address) +CREATE_WATCHPOINT_SHIM(store8, WRITE, void, (struct LR35902Core* cpu, uint16_t address, int8_t value), address, value) + +static bool _checkWatchpoints(struct LR35902Debugger* debugger, uint16_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint8_t newValue) { + struct LR35902DebugWatchpoint* watchpoint; + size_t i; + for (i = 0; i < LR35902DebugWatchpointListSize(&debugger->watchpoints); ++i) { + watchpoint = LR35902DebugWatchpointListGetPointer(&debugger->watchpoints, i); + if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) { + info->oldValue = debugger->originalMemory.load8(debugger->cpu, address); + info->newValue = newValue; + info->address = address; + info->watchType = watchpoint->type; + info->accessType = type; + return true; + } + } + return false; +} + +void LR35902DebuggerInstallMemoryShim(struct LR35902Debugger* debugger) { + debugger->originalMemory = debugger->cpu->memory; + debugger->cpu->memory.store8 = DebuggerShim_store8; + debugger->cpu->memory.load8 = DebuggerShim_load8; +} + +void LR35902DebuggerRemoveMemoryShim(struct LR35902Debugger* debugger) { + debugger->cpu->memory.store8 = debugger->originalMemory.store8; + debugger->cpu->memory.load8 = debugger->originalMemory.load8; +}