From e1c43b9287556e130d103cc89fff75dc03ba714b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 26 Jun 2022 01:44:09 -0700 Subject: [PATCH 001/159] macOS: Disable OpenGL 1.x on newer macOS --- CMakeLists.txt | 13 ++++++++++++- include/mgba/internal/gba/renderers/gl.h | 6 ++---- src/platform/opengl/gles2.h | 6 ++---- src/platform/qt/CMakeLists.txt | 1 - 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c0b987d1..1c6ee194b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -235,6 +235,7 @@ elseif(UNIX) endif() if(APPLE) + execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) add_definitions(-D_DARWIN_C_SOURCE) list(APPEND OS_LIB "-framework Foundation") if(NOT CMAKE_SYSTEM_VERSION VERSION_LESS "10.0") # Darwin 10.x is Mac OS X 10.6 @@ -413,7 +414,7 @@ endif() if(BUILD_GL) find_package(OpenGL QUIET) - if(NOT OPENGL_FOUND) + if(NOT OPENGL_FOUND OR (APPLE AND MACOSX_SDK VERSION_GREATER 10.14)) set(BUILD_GL OFF CACHE BOOL "OpenGL not found" FORCE) elseif(UNIX AND NOT APPLE AND TARGET OpenGL::GL) set(OPENGL_LIBRARY OpenGL::GL) @@ -430,6 +431,11 @@ if(NOT BUILD_GL AND NOT LIBMGBA_ONLY) endif() if(BUILD_GLES2 AND NOT BUILD_GL) + if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14) + find_package(OpenGL QUIET) + set(OPENGLES2_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + set(OPENGLES2_LIBRARY ${OPENGL_LIBRARY}) + endif() find_path(OPENGLES2_INCLUDE_DIR NAMES GLES2/gl2.h) find_library(OPENGLES2_LIBRARY NAMES GLESv2 GLESv2_CM) if(NOT OPENGLES2_INCLUDE_DIR OR NOT OPENGLES2_LIBRARY) @@ -443,6 +449,11 @@ if(BUILD_GLES2) endif() if(BUILD_GLES3 AND NOT BUILD_GL) + if(APPLE AND MACOSX_SDK VERSION_GREATER 10.14) + find_package(OpenGL QUIET) + set(OPENGLES3_INCLUDE_DIR ${OPENGL_INCLUDE_DIR}) + set(OPENGLES3_LIBRARY ${OPENGL_LIBRARY}) + endif() find_path(OPENGLES3_INCLUDE_DIR NAMES GLES3/gl3.h) find_library(OPENGLES3_LIBRARY NAMES GLESv3 GLESv2) if(NOT OPENGLES3_INCLUDE_DIR OR NOT OPENGLES3_LIBRARY) diff --git a/include/mgba/internal/gba/renderers/gl.h b/include/mgba/internal/gba/renderers/gl.h index ccf682614..14cf11678 100644 --- a/include/mgba/internal/gba/renderers/gl.h +++ b/include/mgba/internal/gba/renderers/gl.h @@ -20,14 +20,12 @@ CXX_GUARD_START #ifdef USE_EPOXY #include -#elif defined(BUILD_GL) -#ifdef __APPLE__ +#elif defined(__APPLE__) #include -#else +#elif defined(BUILD_GL) #define GL_GLEXT_PROTOTYPES #include #include -#endif #else #include #endif diff --git a/src/platform/opengl/gles2.h b/src/platform/opengl/gles2.h index 31a2d3e2a..144a64a13 100644 --- a/src/platform/opengl/gles2.h +++ b/src/platform/opengl/gles2.h @@ -12,14 +12,12 @@ CXX_GUARD_START #ifdef USE_EPOXY #include -#elif defined(BUILD_GL) -#ifdef __APPLE__ +#elif defined(__APPLE__) #include -#else +#elif defined(BUILD_GL) #define GL_GLEXT_PROTOTYPES #include #include -#endif #elif defined(BUILD_GLES3) #include #else diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3db5ebc55..9b1f60288 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -39,7 +39,6 @@ if(NOT ${QT}Widgets_FOUND) endif() if(APPLE) - execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) if(MACOSX_SDK VERSION_GREATER 10.14) list(APPEND QT_DEFINES USE_SHARE_WIDGET) endif() From d5b66cc7e7acdbcd78dc27b230c142105f1ee898 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 02:36:46 -0700 Subject: [PATCH 002/159] Qt: Actually detach video proxy --- src/platform/qt/Window.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8fb271521..20249f4a0 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1121,6 +1121,11 @@ void Window::changeRenderer() { m_config->updateOption("videoScale"); } } else { + std::shared_ptr proxy = m_display->videoProxy(); + if (proxy) { + proxy->detach(m_controller.get()); + m_display->setVideoProxy({}); + } m_controller->setFramebufferHandle(-1); } } From ac8c371219fee52152bcc38a1bbd60ec298e3335 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 02:15:55 -0700 Subject: [PATCH 003/159] Qt: Fix logged error when starting on Mac --- src/platform/qt/DisplayGL.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 16dece1b4..9727285b0 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -83,6 +83,7 @@ void mGLWidget::initializeGL() { m_positionLocation = m_program->attributeLocation("position"); m_vaoDone = false; + m_tex = 0; connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); } @@ -115,6 +116,10 @@ void mGLWidget::paintGL() { if (!m_vaoDone && !finalizeVAO()) { return; } + if (!m_tex) { + m_refresh.start(10); + return; + } QOpenGLFunctions_Baseline* fn = context()->versionFunctions(); m_program->bind(); m_vao->bind(); @@ -520,7 +525,6 @@ void PainterGL::create() { m_finalTexIdx = 0; gl2Backend->finalShader.tex = m_finalTex[m_finalTexIdx]; - m_widget->setTex(m_finalTex[m_finalTexIdx]); } m_shader.preprocessShader = static_cast(&reinterpret_cast(m_backend)->initialShader); } From 903f7927312793295b8cb2b3d3dda4c644498aa0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 19:05:47 -0700 Subject: [PATCH 004/159] Updater: Log to file --- src/feature/updater-main.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/feature/updater-main.c b/src/feature/updater-main.c index 4b1f216ff..f0bdacd5a 100644 --- a/src/feature/updater-main.c +++ b/src/feature/updater-main.c @@ -29,6 +29,8 @@ #define W_OK 02 #endif +FILE* logfile; + bool extractArchive(struct VDir* archive, const char* root, bool prefix) { char path[PATH_MAX] = {0}; struct VDirEntry* vde; @@ -53,7 +55,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) { } switch (vde->type(vde)) { case VFS_DIRECTORY: - printf("mkdir %s\n", fname); + fprintf(logfile, "mkdir %s\n", fname); if (mkdir(path, 0755) < 0 && errno != EEXIST) { return false; } @@ -70,7 +72,7 @@ bool extractArchive(struct VDir* archive, const char* root, bool prefix) { } break; case VFS_FILE: - printf("extract %s\n", fname); + fprintf(logfile, "extract %s\n", fname); vfIn = archive->openFile(archive, vde->name(vde), O_RDONLY); errno = 0; vfOut = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); @@ -111,13 +113,17 @@ int main(int argc, char* argv[]) { const char* root; int ok = 1; + mCoreConfigDirectory(bin, sizeof(bin)); + strncat(bin, "/updater.log", sizeof(bin)); + logfile = fopen(bin, "w"); + mCoreConfigInit(&config, "updater"); if (!mCoreConfigLoad(&config)) { - puts("Failed to load config"); + fputs("Failed to load config", logfile); } else if (!mUpdateGetArchivePath(&config, updateArchive, sizeof(updateArchive)) || !(root = mUpdateGetRoot(&config))) { - puts("No pending update found"); + fputs("No pending update found", logfile); } else if (access(root, W_OK)) { - puts("Cannot write to update path"); + fputs("Cannot write to update path", logfile); } else { #ifdef __APPLE__ char subdir[PATH_MAX]; @@ -160,7 +166,7 @@ int main(int argc, char* argv[]) { } off_t diff = devend - devinfo - 1; memcpy(devpath, &devinfo[1], diff); - puts(devpath); + fputs(devpath, logfile); break; } int retstat; @@ -177,11 +183,11 @@ int main(int argc, char* argv[]) { archive = VDirOpenArchive(updateArchive); } if (archive) { - puts("Extracting update"); + fputs("Extracting update", logfile); if (extractArchive(archive, root, prefix)) { ok = 0; } else { - puts("An error occurred"); + fputs("An error occurred", logfile); } archive->close(archive); unlink(updateArchive); @@ -218,10 +224,10 @@ int main(int argc, char* argv[]) { close(infd); } if (ok == 2) { - puts("Cannot move update over old file"); + fputs("Cannot move update over old file", logfile); } } else { - puts("Cannot move update over old file"); + fputs("Cannot move update over old file", logfile); } } else { ok = 0; @@ -232,10 +238,10 @@ int main(int argc, char* argv[]) { } #endif else { - puts("Cannot open update archive"); + fputs("Cannot open update archive", logfile); } if (ok == 0) { - puts("Complete"); + fputs("Complete", logfile); const char* command = mUpdateGetCommand(&config); strlcpy(bin, command, sizeof(bin)); mUpdateDeregister(&config); @@ -260,6 +266,7 @@ int main(int argc, char* argv[]) { } } mCoreConfigDeinit(&config); + fclose(logfile); if (ok == 0) { #ifdef _WIN32 char qbin[PATH_MAX + 2] = {0}; From ff772fce9d648a4342b3629e4cac918e762643b3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 19:08:50 -0700 Subject: [PATCH 005/159] Qt: Attempt to reduce flickering --- src/platform/qt/DisplayGL.cpp | 10 ++++++++-- src/platform/qt/DisplayGL.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 9727285b0..afae1440d 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -55,6 +55,14 @@ uint qHash(const QSurfaceFormat& format, uint seed) { return qHash(representation, seed); } +mGLWidget::mGLWidget(QWidget* parent) + : QOpenGLWidget(parent) +{ + setUpdateBehavior(QOpenGLWidget::PartialUpdate); + + connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); +} + void mGLWidget::initializeGL() { m_vao = std::make_unique(); m_vao->create(); @@ -84,8 +92,6 @@ void mGLWidget::initializeGL() { m_vaoDone = false; m_tex = 0; - - connect(&m_refresh, &QTimer::timeout, this, static_cast(&QWidget::update)); } bool mGLWidget::finalizeVAO() { diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 913a3ee96..77ea6d195 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -50,6 +50,8 @@ class mGLWidget : public QOpenGLWidget { Q_OBJECT public: + mGLWidget(QWidget* parent = nullptr); + void setTex(GLuint tex) { m_tex = tex; } void setVBO(GLuint vbo) { m_vbo = vbo; } bool finalizeVAO(); From ded409f0da14ccc83dcd6d62d5756e0b75b382e1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 19:52:02 -0700 Subject: [PATCH 006/159] CMake: Include zip/7z deps for updater --- CMakeLists.txt | 15 +++++++++------ src/util/CMakeLists.txt | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c6ee194b..ce8e4d687 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -640,14 +640,15 @@ endif() if(USE_LIBZIP) if(TARGET libzip::zip) - list(APPEND DEPENDENCY_LIB libzip::zip) + set(ZIP_LIBRARIES libzip::zip) elseif(TARGET zip) - list(APPEND DEPENDENCY_LIB zip) + set(ZIP_LIBRARIES zip) else() include_directories(AFTER ${LIBZIP_INCLUDE_DIRS}) link_directories(${LIBZIP_LIBRARY_DIRS}) - list(APPEND DEPENDENCY_LIB ${LIBZIP_LIBRARIES}) + set(ZIP_LIBRARIES ${LIBZIP_LIBRARIES}) endif() + list(APPEND DEPENDENCY_LIB ${ZIP_LIBRARIES}) list(APPEND FEATURES LIBZIP) list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c) string(REGEX MATCH "^[0-9]+" LIBZIP_VERSION_MAJOR "${libzip_VERSION}") @@ -661,6 +662,7 @@ if(USE_LIBZIP) elseif(USE_MINIZIP) include_directories(AFTER ${MINIZIP_INCLUDE_DIRS}) link_directories(${MINIZIP_LIBRARY_DIRS}) + set(ZIP_LIBRARIES ${MINIZIP_LIBRARIES}) list(APPEND DEPENDENCY_LIB ${MINIZIP_LIBRARIES}) list(APPEND FEATURES MINIZIP) list(APPEND VFS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/util/vfs/vfs-zip.c) @@ -789,6 +791,7 @@ add_subdirectory(src/sm83) add_subdirectory(src/util) list(APPEND GUI_SRC ${EXTRA_GUI_SRC}) +list(APPEND UTIL_BASE_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c) list(APPEND UTIL_SRC ${CMAKE_CURRENT_BINARY_DIR}/version.c) set(TEST_SRC ${CORE_TEST_SRC}) @@ -979,12 +982,12 @@ if(BUILD_QT AND (WIN32 OR APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Linux")) endif() if(BUILD_UPDATER) - add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${OS_SRC} ${UTIL_SRC} ${THIRD_PARTY_SRC} + add_executable(updater-stub WIN32 ${CORE_VFS_SRC} ${VFS_SRC} ${OS_SRC} ${UTIL_BASE_SRC} ${THIRD_PARTY_SRC} ${CMAKE_CURRENT_SOURCE_DIR}/src/core/config.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater.c ${CMAKE_CURRENT_SOURCE_DIR}/src/feature/updater-main.c) - target_link_libraries(updater-stub ${OS_LIB} ${PLATFORM_LIBRARY}) - set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};BUILD_STATIC") + target_link_libraries(updater-stub ${ZLIB_LIBRARY} ${ZLIB_LIBRARY} ${ZIP_LIBRARIES} ${OS_LIB} ${PLATFORM_LIBRARY}) + set_target_properties(updater-stub PROPERTIES COMPILE_DEFINITIONS "${OS_DEFINES};${FUNCTION_DEFINES};${FEATURE_DEFINES};BUILD_STATIC") if(MSVC) set_target_properties(updater-stub PROPERTIES LINK_FLAGS /ENTRY:mainCRTStartup) else() diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 8d86f7b60..e4d03d343 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,24 +1,27 @@ include(ExportDirectory) -set(SOURCE_FILES +set(BASE_SOURCE_FILES circle-buffer.c configuration.c - convolve.c crc32.c - elf-read.c - export.c formatting.c gbk-table.c hash.c + string.c + table.c + vfs.c) + +set(SOURCE_FILES + ${BASE_SOURCE_FILES} + convolve.c + elf-read.c + export.c patch.c patch-fast.c patch-ips.c patch-ups.c png-io.c ring-fifo.c - string.c - table.c - text-codec.c - vfs.c) + text-codec.c) set(GUI_FILES gui.c @@ -43,6 +46,7 @@ source_group("Utilities" FILES ${SOURCE_FILES}) source_group("GUI source" FILES ${GUI_FILES}) source_group("Utilities tests" FILES ${TEST_FILES}) +export_directory(UTIL_BASE BASE_SOURCE_FILES) export_directory(UTIL SOURCE_FILES) export_directory(GUI GUI_FILES) export_directory(UTIL_TEST TEST_FILES) From 16bcdd0fc3896a5f36859510182f0ac6d7d8cfdd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 19:52:13 -0700 Subject: [PATCH 007/159] Updater: Cleanup --- src/feature/updater-main.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/feature/updater-main.c b/src/feature/updater-main.c index f0bdacd5a..bc29e5cd4 100644 --- a/src/feature/updater-main.c +++ b/src/feature/updater-main.c @@ -119,11 +119,11 @@ int main(int argc, char* argv[]) { mCoreConfigInit(&config, "updater"); if (!mCoreConfigLoad(&config)) { - fputs("Failed to load config", logfile); + fputs("Failed to load config\n", logfile); } else if (!mUpdateGetArchivePath(&config, updateArchive, sizeof(updateArchive)) || !(root = mUpdateGetRoot(&config))) { - fputs("No pending update found", logfile); + fputs("No pending update found\n", logfile); } else if (access(root, W_OK)) { - fputs("Cannot write to update path", logfile); + fputs("Cannot write to update path\n", logfile); } else { #ifdef __APPLE__ char subdir[PATH_MAX]; @@ -166,7 +166,6 @@ int main(int argc, char* argv[]) { } off_t diff = devend - devinfo - 1; memcpy(devpath, &devinfo[1], diff); - fputs(devpath, logfile); break; } int retstat; @@ -183,11 +182,11 @@ int main(int argc, char* argv[]) { archive = VDirOpenArchive(updateArchive); } if (archive) { - fputs("Extracting update", logfile); + fputs("Extracting update\n", logfile); if (extractArchive(archive, root, prefix)) { ok = 0; } else { - fputs("An error occurred", logfile); + fputs("An error occurred\n", logfile); } archive->close(archive); unlink(updateArchive); @@ -224,10 +223,10 @@ int main(int argc, char* argv[]) { close(infd); } if (ok == 2) { - fputs("Cannot move update over old file", logfile); + fputs("Cannot move update over old file\n", logfile); } } else { - fputs("Cannot move update over old file", logfile); + fputs("Cannot move update over old file\n", logfile); } } else { ok = 0; @@ -238,7 +237,7 @@ int main(int argc, char* argv[]) { } #endif else { - fputs("Cannot open update archive", logfile); + fputs("Cannot open update archive\n", logfile); } if (ok == 0) { fputs("Complete", logfile); From 959e4bf1e600016409e64073a8fb3965c3ad712a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 20:27:49 -0700 Subject: [PATCH 008/159] Qt: Fix flickering when frame-advancing on Windows --- src/platform/qt/DisplayGL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index afae1440d..e4b2af9a0 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -309,7 +309,7 @@ void DisplayGL::pauseDrawing() { if (m_hasStarted) { m_isDrawing = false; QMetaObject::invokeMethod(m_painter.get(), "pause", Qt::BlockingQueuedConnection); - if (QGuiApplication::platformName() != "xcb") { + if (!shouldDisableUpdates()) { setUpdatesEnabled(true); } } From ce6e3fad46999cea5d4ee03af70d365620bb87d0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 20:28:54 -0700 Subject: [PATCH 009/159] Qt: Update translations --- src/platform/qt/ts/mgba-de.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-en.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-es.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-fr.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-hu.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-it.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-ja.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-ko.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-ms.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-nb_NO.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-pl.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-pt_BR.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-ru.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-template.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-tr.ts | 244 ++++++++++++++-------------- src/platform/qt/ts/mgba-zh_CN.ts | 244 ++++++++++++++-------------- 16 files changed, 1952 insertions(+), 1952 deletions(-) diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index 5118fd611..7bc27a274 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -5806,433 +5806,433 @@ Download-Größe: %3 Es konnte kein geeignetes Ausgabegerät erstellt werden, stattdessen wird Software-Rendering als Rückfalloption genutzt. Spiele laufen möglicherweise langsamer, besonders innerhalb großer Fenster. - + 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... - + Save games Spielstände - + Automatically determine Automatisch erkennen - + Use player %0 save game Verwende Spielstand von Spieler %0 - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + Convert e-Reader card image to raw... Lesegerät-Kartenbild in Rohdaten umwandeln … - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate (aktueller Zustand) &laden - + Load state file... Savestate-Datei laden... - + &Save state Savestate (aktueller Zustand) &speichern - + Save state file... Savestate-Datei speichern... - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Zustand - + Undo load state Laden des Savestate rückgängig machen - + Undo save state Speichern des Savestate rückgängig machen - + State &%1 Savestate &%1 - + Load camera image... Lade Kamerabild... - + Convert save game... Spielstand konvertieren... - + Reset needed Zurücksetzen erforderlich - + Some changes will not take effect until the game is reset. Einige Änderungen werden erst dann wirksam, wenn das Spiel zurückgesetzt wird. - + New multiplayer window Neues Multiplayer-Fenster - + Connect to Dolphin... Mit Dolphin verbinden... - + Report bug... Fehler melden... - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Sh&utdown Schli&eßen - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + &Next frame &Nächstes Bild - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + Step backwards Schrittweiser Rücklauf - + Solar sensor Sonnen-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 - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Interframe blending Interframe-Überblendung - + Frame&skip Frame&skip - + Mute Stummschalten - + FPS target Bildwiederholrate - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Scripting... Scripting... - + Game state views Spiel-Zustände ansehen - + Clear Leeren - + Game Boy Printer... Game Boy Printer... - + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + Adjust layer placement... Lage der Bildebenen anpassen... - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole öffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... @@ -6288,182 +6288,182 @@ Download-Größe: %3 Spiel konnte nicht gestartet werden. - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Load alternate save game... Alternativen Spielstand laden... - + Load temporary save game... Temporären Spielstand laden... - + Scan e-Reader dotcodes... e-Reader-Code einlesen... - + Import GameShark Save... GameShare-Speicherstand importieren... - + Export GameShark Save... GameShark-Speicherstand exportieren... - + About... Über... - + %1× %1x - + Bilinear filtering Bilineare Filterung - + Native (59.7275) Nativ (59.7275) - + Record A/V... Audio/Video aufzeichnen... - + Record GIF/WebP/APNG... GIF/WebP/APNG aufzeichnen... - + Game Pak sensors... Spielmodul-Sensoren... - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View &map... &Map betrachten... - + &Frame inspector... &Bildbetrachter... - + View memory... Speicher betrachten... - + Search memory... Speicher durchsuchen... - + View &I/O registers... &I/O-Register betrachten... - + Record debug video log... Video-Protokoll aufzeichnen... - + Stop debug video log Aufzeichnen des Video-Protokolls beenden - + Exit fullscreen Vollbildmodus beenden - + GameShark Button (held) GameShark-Taste (gehalten) - + 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 diff --git a/src/platform/qt/ts/mgba-en.ts b/src/platform/qt/ts/mgba-en.ts index fc1ea72c7..ec361c5dc 100644 --- a/src/platform/qt/ts/mgba-en.ts +++ b/src/platform/qt/ts/mgba-en.ts @@ -5803,62 +5803,62 @@ Download size: %3 - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... @@ -5904,118 +5904,118 @@ Download size: %3 - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - + State &%1 - + Load camera image... - + Convert save game... @@ -6025,437 +6025,437 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index d1f8baf26..8cf07904c 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -5801,137 +5801,137 @@ Tamaño de la descarga: %3 No se pudo crear un dispositivo de pantalla apropiado, recurriendo a software. Los juegos pueden funcionar lentamente, especialmente con ventanas grandes. - + Really make portable? ¿Hacer "portable"? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Esto hará que el emulador cargue su configuración desde el mismo directorio que el ejecutable. ¿Quieres continuar? - + Restart needed Reinicio necesario - + Some changes will not take effect until the emulator is restarted. Algunos cambios no surtirán efecto hasta que se reinicie el emulador. - + - Player %1 of %2 - Jugador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Archivo - + Load &ROM... Cargar &ROM... - + Load ROM in archive... Cargar ROM desde contenedor... - + Add folder to library... Agregar carpeta a la biblioteca... - + Save games Datos de guardado - + Automatically determine Determinar automáticamente - + Use player %0 save game Usar la partida guardada del jugador %0 - + Load &patch... Cargar &parche... - + Boot BIOS Arrancar BIOS - + Replace ROM... Reemplazar ROM... - + ROM &info... &Información de la ROM... - + Recent Recientes - + Make portable Hacer "portable" - + &Load state Ca&rgar estado - + Report bug... Reportar bug... - + About... Acerca de... - + Game Pak sensors... Sensores del cartucho... - + Clear Limpiar - + Load state file... Cargar archivo de estado... @@ -5977,73 +5977,73 @@ Tamaño de la descarga: %3 %1 de %2 tarjetas e-Reader convertidas con éxito. - + Load alternate save game... Elegir juego guardado alterno... - + Load temporary save game... Elegir juego guardado temporal... - + Convert e-Reader card image to raw... Convertir imagen de tarjeta e-Reader a raw... - + &Save state Guardar e&stado - + Save state file... Guardar archivo de estado... - + Quick load Cargado rápido - + Quick save Guardado rápido - + Load recent Cargar reciente - + Save recent Guardar reciente - + Undo load state Deshacer cargar estado - + Undo save state Deshacer guardar estado - + State &%1 Estado &%1 - + Load camera image... Cargar imagen para la cámara... - + Convert save game... Convertir juego guardado... @@ -6053,242 +6053,242 @@ Tamaño de la descarga: %3 Partidas guardadas de GameShark (*.gsv *.sps *.xps) - + Reset needed Reinicio necesario - + Some changes will not take effect until the game is reset. Algunos cambios no tendrán efecto hasta que se reinicie el juego. - + New multiplayer window Nueva ventana multijugador - + Connect to Dolphin... Conectar a Dolphin... - + E&xit Salir (&X) - + &Emulation &Emulación - + &Reset &Reinicializar - + Sh&utdown Apagar (&U) - + Yank game pak Tirar del cartucho - + &Pause &Pausar - + &Next frame Cuadro siguie&nte - + Fast forward (held) Avance rápido (mantener) - + &Fast forward &Avance rápido - + Fast forward speed Velocidad de avance rápido - + Unbounded Sin límite - + %0x %0x - + Rewind (held) Rebobinar (mantener) - + Re&wind Re&bobinar - + Step backwards Paso hacia atrás - + Solar sensor Sensor solar - + Increase solar level Subir nivel - + Decrease solar level Bajar nivel - + Brightest solar level Más claro - + Darkest solar level Más oscuro - + Brightness %1 Brillo %1 - + Audio/&Video Audio/&video - + Frame size Tamaño del cuadro - + Toggle fullscreen Pantalla completa - + Lock aspect ratio Bloquear proporción de aspecto - + Force integer scaling Forzar escala a enteros - + Bilinear filtering Filtro bilineal - + Frame&skip &Salto de cuadros - + Mute Silenciar - + FPS target Objetivo de FPS - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tomar pan&tallazo - + F12 F12 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + %1× %1× - + Interframe blending Mezcla entre cuadros - + Record A/V... Grabar A/V... - + Video layers Capas de video - + Audio channels Canales de audio - + Adjust layer placement... Ajustar ubicación de capas... - + &Tools Herramien&tas - + View &logs... Ver re&gistros... - + Game &overrides... Ajustes específic&os por juego... @@ -6303,167 +6303,167 @@ Tamaño de la descarga: %3 No se pudo iniciar el juego. - + Scan e-Reader dotcodes... Escanear dotcodes del e-Reader... - + Import GameShark Save... Importar desde GameShark... - + Export GameShark Save... Exportar a GameShark... - + Record GIF/WebP/APNG... Grabar GIF/WebP/APNG... - + &Cheats... Tru&cos... - + Settings... Ajustes... - + Open debugger console... Abrir consola de depuración... - + Start &GDB server... Iniciar servidor &GDB... - + Scripting... - + Game state views - + View &palette... Ver &paleta... - + View &sprites... Ver &sprites... - + View &tiles... Ver m&osaicos... - + View &map... Ver &mapa... - + &Frame inspector... Inspec&tor de cuadros... - + View memory... Ver memoria... - + Search memory... Buscar memoria... - + View &I/O registers... Ver registros &I/O... - + Record debug video log... Grabar registro de depuración de video... - + Stop debug video log Detener registro de depuración de video - + Exit fullscreen Salir de pantalla completa - + GameShark Button (held) Botón GameShark (mantener) - + Autofire Disparo automático - + Autofire A Disparo automático A - + Autofire B Disparo automático B - + Autofire L Disparo automático L - + Autofire R Disparo automático R - + Autofire Start Disparo automático Start - + Autofire Select Disparo automático Select - + Autofire Up Disparo automático Arriba - + Autofire Right Disparo automático Derecha - + Autofire Down Disparo automático Abajo - + Autofire Left Disparo automático Izquierda diff --git a/src/platform/qt/ts/mgba-fr.ts b/src/platform/qt/ts/mgba-fr.ts index 474be1297..df40af0a7 100644 --- a/src/platform/qt/ts/mgba-fr.ts +++ b/src/platform/qt/ts/mgba-fr.ts @@ -5840,368 +5840,368 @@ Taille du téléchargement : %3 Échec de la création d'un périphérique d'affichage approprié, retour à l'affichage du logiciel. Les jeux peuvent fonctionner lentement, en particulier avec des fenêtres plus grandes. - + Really make portable? Vraiment rendre portable ? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Cela amènera l'émulateur à charger sa configuration depuis le même répertoire que l'exécutable. Souhaitez vous continuer ? - + Restart needed Un redémarrage est nécessaire - + Some changes will not take effect until the emulator is restarted. Certains changements ne prendront effet qu'après le redémarrage de l'émulateur. - + - Player %1 of %2 - Joueur %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Fichier - + Load &ROM... Charger une &ROM… - + Load ROM in archive... Charger la ROM d'une archive… - + Add folder to library... Ajouter un dossier à la bibliothèque… - + Load &patch... Charger un c&orrectif… - + Boot BIOS Démarrer le BIOS - + Replace ROM... Remplacer la ROM… - + Game state views - + Convert e-Reader card image to raw... - + ROM &info... &Infos sur la ROM… - + Recent Récent - + Make portable Rendre portable - + &Load state &Charger un état - + &Save state &Sauvegarder un état - + Quick load Chargement rapide - + Quick save Sauvegarde rapide - + Load recent Charger un fichier récent - + Save recent Sauvegarder un fichier récent - + Undo load state Annuler le chargement de l'état - + Undo save state Annuler la sauvegarde de l'état - + State &%1 État &%1 - + Load camera image... Charger une image de la caméra… - + Convert save game... - + New multiplayer window Nouvelle fenêtre multijoueur - + Connect to Dolphin... - + Report bug... Signalement de l'erreur… - + E&xit &Quitter - + &Emulation &Émulation - + &Reset &Réinitialiser - + Sh&utdown Extin&ction - + Yank game pak Yank game pak - + &Pause &Pause - + &Next frame &Image suivante - + Fast forward (held) Avance rapide (maintenir) - + &Fast forward A&vance rapide - + Fast forward speed Vitesse de l'avance rapide - + Unbounded Sans limites - + %0x %0x - + Rewind (held) Rembobiner (maintenir) - + Re&wind Rem&bobiner - + Step backwards Retour en arrière - + Solar sensor Capteur solaire - + Increase solar level Augmenter le niveau solaire - + Decrease solar level Diminuer le niveau solaire - + Brightest solar level Tester le niveau solaire - + Darkest solar level Assombrir le niveau solaire - + Brightness %1 Luminosité %1 - + Audio/&Video Audio/&Vidéo - + Frame size Taille de l'image - + Toggle fullscreen Basculer en plein écran - + Lock aspect ratio Bloquer les proportions - + Force integer scaling Forcer la mise à l'échelle par des nombres entiers - + Bilinear filtering Filtrage bilinèaire - + Frame&skip &Saut d'image - + Mute Muet - + FPS target FPS ciblé - + Take &screenshot Prendre une ca&pture d'écran - + F12 F12 - + Game Boy Printer... Imprimante GameBoy… - + Video layers Couches vidéo - + Audio channels Canaux audio - + Adjust layer placement... Ajuster la disposition… - + &Tools Ou&tils - + View &logs... Voir les &journaux… - + Game &overrides... @@ -6242,247 +6242,247 @@ Taille du téléchargement : %3 Impossible de démarrer le jeu. - + Load alternate save game... - + Load temporary save game... - + Scan e-Reader dotcodes... Scanner les dotcodes e-Reader... - + Load state file... Charger le fichier d'état... - + Save state file... Enregistrer le fichier d'état... - + Import GameShark Save... Importer la sauvegarde de GameShark... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Sauvegarder les jeux - + Export GameShark Save... Exporter la sauvegarde de GameShark... - + Automatically determine - + Use player %0 save game - + About... À propos de… - + BattleChip Gate... - + %1× %1× - + Interframe blending Mélange d'images - + Native (59.7275) Natif (59.7275) - + Record A/V... Enregistrer A/V... - + Record GIF/WebP/APNG... Enregistrer GIF/WebP/APNG... - + Game Pak sensors... Capteurs de la Game Pak... - + &Cheats... &Cheats… - + Settings... Paramètres… - + Open debugger console... Ouvrir la console de débug… - + Start &GDB server... Démarrer le serveur &GDB… - + Scripting... - + View &palette... Voir la &palette… - + View &sprites... Voir les &sprites… - + View &tiles... Voir les &tiles… - + View &map... Voir la &map… - + &Frame inspector... Inspecteur de &frame... - + View memory... Voir la mémoire… - + Search memory... Recherche dans la mémoire… - + View &I/O registers... Voir les registres d'&E/S... - + Record debug video log... Enregistrer le journal vidéo de débogage... - + Stop debug video log Arrêter le journal vidéo de débogage - + Exit fullscreen Quitter le plein écran - + GameShark Button (held) Bouton GameShark (maintenir) - + Autofire Tir automatique - + Autofire A Tir automatique A - + Autofire B Tir automatique B - + Autofire L Tir automatique L - + Autofire R Tir automatique R - + Autofire Start Tir automatique Start - + Autofire Select Tir automatique Select - + Autofire Up Tir automatique Up - + Autofire Right Tir automatique Right - + Autofire Down Tir automatique Down - + Autofire Left Tir automatique Gauche - + Clear Vider diff --git a/src/platform/qt/ts/mgba-hu.ts b/src/platform/qt/ts/mgba-hu.ts index 332ea410f..38b8bdd70 100644 --- a/src/platform/qt/ts/mgba-hu.ts +++ b/src/platform/qt/ts/mgba-hu.ts @@ -5802,62 +5802,62 @@ Download size: %3 - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... @@ -5903,118 +5903,118 @@ Download size: %3 - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - + State &%1 - + Load camera image... - + Convert save game... @@ -6024,437 +6024,437 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Napló törlése diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/mgba-it.ts index 3bf759ecf..45092ddac 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -5816,153 +5816,153 @@ Dimensione del download: %3 Impossibile creare un dispositivo di visualizzazione appropriato, tornando alla visualizzazione software. I giochi possono funzionare lentamente, specialmente con finestre più grandi. - + Really make portable? Vuoi davvero rendere portatile l'applicazione? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? In questo modo l'emulatore carica la propria configurazione dalla stessa cartella dell'eseguibile. Vuoi continuare? - + Restart needed È necessario riavviare - + Some changes will not take effect until the emulator is restarted. Alcune modifiche non avranno effetto finché l'emulatore non verrà riavviato. - + - Player %1 of %2 - Giocatore %1 di %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File File - + Load &ROM... Carica ROM... - + Load ROM in archive... Carica la ROM in archivio... - + Load &patch... Carica patch... - + Boot BIOS Avvia BIOS - + Replace ROM... Sostituisci la ROM... - + Scan e-Reader dotcodes... Scansiona e-Reader dotcode... - + Convert e-Reader card image to raw... Converti immagini carte e-Reader in raw... - + ROM &info... Informazioni ROM... - + Recent Recenti - + Make portable Rendi portatile - + &Load state Carica stato - + &Save state Salva stato - + Quick load Caricamento rapido - + Quick save Salvataggio rapido - + Load recent Carica recente - + Save recent Salva recente - + Undo load state Annulla il caricamento dello stato - + Undo save state Annulla salvataggio stato - + State &%1 Stato %1 - + Load camera image... Carica immagine fotocamera... - + Convert save game... Converti salvataggi... @@ -5972,227 +5972,227 @@ Dimensione del download: %3 Salvataggi GameShark (*.gsv *.sps *.xps) - + Reset needed Reset necessario - + Some changes will not take effect until the game is reset. Alcune modifiche non si applicheranno finché il gioco non viene resettato. - + New multiplayer window Nuova finestra multigiocatore - + Connect to Dolphin... Connessione a Dolphin... - + Report bug... Segnala bug... - + E&xit Esci (&X) - + &Emulation Emulazione - + &Reset Reset - + Sh&utdown Spegni (&U) - + Yank game pak Stacca game pak - + &Pause Pausa - + &Next frame Salta il prossimo frame (&N) - + Fast forward (held) Avanzamento rapido (tieni premuto) - + &Fast forward Avanzamento rapido (&F) - + Fast forward speed Velocità di avanzamento rapido - + Unbounded Illimitata - + %0x %0x - + Rewind (held) Riavvolgimento (tieni premuto) - + Re&wind Riavvolgimento (&W) - + Step backwards Torna indietro - + Solar sensor Sensore solare - + Increase solar level Incrementa il livello solare - + Decrease solar level Riduci il livello solare - + Brightest solar level Livello solare massimo - + Darkest solar level Livello solare minimo - + Brightness %1 Luminosità %1 - + Audio/&Video Audio/Video - + Frame size Dimensione frame - + Toggle fullscreen Abilita schermo Intero - + Lock aspect ratio Blocca rapporti aspetto - + Frame&skip Salto frame - + Mute Muto - + FPS target FPS finali - + Take &screenshot Acquisisci schermata - + F12 F12 - + Record GIF/WebP/APNG... Registra GIF / WebP / APNG ... - + Video layers Layers video - + Audio channels Canali audio - + &Tools Strumenti - + View &logs... Visualizza registri (&log)... - + Game &overrides... Valore specifico per il gioco... - + &Cheats... Trucchi... - + Open debugger console... Apri console debugger... - + Start &GDB server... Avvia server GDB... - + Settings... Impostazioni... @@ -6212,47 +6212,47 @@ Dimensione del download: %3 Non è stato possibile avviare il gioco. - + Add folder to library... Aggiungi cartella alla libreria... - + Load state file... Carica stato di salvataggio... - + Save state file... Salva stato di salvataggio... - + Import GameShark Save... Importa Salvataggio GameShark... - + Export GameShark Save... Esporta Salvataggio GameShark... - + About... Informazioni… - + Force integer scaling Forza ridimensionamento a interi - + Bilinear filtering Filtro bilineare - + Game Boy Printer... Stampante Game Boy... @@ -6278,192 +6278,192 @@ Dimensione del download: %3 Seleziona stato di salvataggio - + Save games Salvataggi - + Load alternate save game... Carica stato di salvataggio alternativo... - + Load temporary save game... Carica salvataggio temporaneo... - + Automatically determine Determina automaticamente - + Use player %0 save game Usa il salvataggio del giocatore %0 - + BattleChip Gate... BattleChip Gate... - + %1× %1x - + Interframe blending Miscelazione dei frame - + Native (59.7275) Nativo (59.7) - + Record A/V... Registra A/V... - + Adjust layer placement... Regola posizionamento layer... - + Game Pak sensors... Sensori Game Pak... - + Scripting... Scripting... - + Game state views Viste degli stati del gioco - + View &palette... Mostra palette... - + View &sprites... Mostra sprites... - + View &tiles... Mostra tiles... - + View &map... Mostra mappa... - + &Frame inspector... &Frame inspector... - + View memory... Mostra memoria... - + Search memory... Ricerca memoria... - + View &I/O registers... Mostra registri I/O... - + Record debug video log... Registra video log di debug... - + Stop debug video log Ferma video log di debug - + Exit fullscreen Esci da Schermo Intero - + GameShark Button (held) Pulsante GameShark (tieni premuto) - + Autofire Pulsanti Autofire - + Autofire A Autofire A - + Autofire B Autofire B - + Autofire L Autofire L - + Autofire R Autofire R - + Autofire Start Autofire Start - + Autofire Select Autofire Select - + Autofire Up Autofire Su - + Autofire Right AAutofire Destra - + Autofire Down Autofire Giù - + Autofire Left Autofire Sinistra - + Clear Pulisci diff --git a/src/platform/qt/ts/mgba-ja.ts b/src/platform/qt/ts/mgba-ja.ts index b60334fea..fb0cfb04c 100644 --- a/src/platform/qt/ts/mgba-ja.ts +++ b/src/platform/qt/ts/mgba-ja.ts @@ -5804,132 +5804,132 @@ Download size: %3 適切なディスプレイデバイスの作成に失敗し、ソフトディスプレイにフォールバックしました。特に大きなウィンドウでは、ゲームの実行が遅い場合があります。 - + Really make portable? 本当にポータブルにしますか? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? これによりエミュレータは実行ファイルと同じディレクトリにある設定ファイルをロードします。続けますか? - + Restart needed 再起動が必要 - + Some changes will not take effect until the emulator is restarted. 一部の変更は、エミュレータを再起動するまで有効になりません。 - + - Player %1 of %2 - プレーヤー %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &ファイル (&F) - + Load &ROM... ROMをロード... - + Load ROM in archive... アーカイブにROMをロード... - + Add folder to library... ライブラリーにフォルダを追加... - + Load &patch... パッチをロード... (&P) - + Boot BIOS BIOSを起動 - + Replace ROM... ROMを交換... - + Scan e-Reader dotcodes... カードeをスキャン... - + ROM &info... ROM情報... (&I) - + Recent 最近開いたROM - + Make portable ポータブル化 - + &Load state ステートをロード (&L) - + Report bug... バグ報告 - + About... バージョン情報... - + Record GIF/WebP/APNG... GIF/WebP/APNGを記録 - + Game Pak sensors... カートリッジセンサー... - + Clear 消去 - + Load state file... ステートファイルをロード... @@ -5975,73 +5975,73 @@ Download size: %3 - + Load alternate save game... - + Load temporary save game... - + Convert e-Reader card image to raw... - + &Save state ステートをセーブ (&S) - + Save state file... ステートファイルをセーブ... - + Quick load クイックロード - + Quick save クイックセーブ - + Load recent 最近使ったクイックロットからロード - + Save recent 最近使ったクイックロットにセーブ - + Undo load state クイックロードを元に戻す - + Undo save state クイックセーブを元に戻す - + State &%1 クイックスロット &%1 - + Load camera image... カメラ画像をロード... - + Convert save game... @@ -6051,412 +6051,412 @@ Download size: %3 - + Import GameShark Save... GameSharkスナップショットを読み込む - + Export GameShark Save... GameSharkスナップショットを書き出す - + New multiplayer window 新しいウィンドウ(マルチプレイ) - + Connect to Dolphin... - + E&xit 終了 (&X) - + &Emulation エミュレーション (&E) - + &Reset リセット (&R) - + Sh&utdown 閉じる (&U) - + Yank game pak ゲームパックをヤンク - + &Pause 一時停止 (&P) - + &Next frame 次のフレーム (&N) - + Fast forward (held) 早送り(押し) - + &Fast forward 早送り (&F) - + Fast forward speed 早送り速度 - + Unbounded 制限なし - + %0x %0x - + Rewind (held) 巻戻し(押し) - + Re&wind 巻戻し (&R) - + Step backwards 1フレーム巻き戻す - + Solar sensor 太陽センサー - + Increase solar level 明るさを上げる - + Decrease solar level 明るさを下げる - + Brightest solar level 明るさ最高 - + Darkest solar level 明るさ最低 - + Brightness %1 明るさ %1 - + Audio/&Video オーディオ/ビデオ (&V) - + Frame size 画面サイズ - + Toggle fullscreen 全画面表示 - + Lock aspect ratio 縦横比を固定 - + Force integer scaling 整数スケーリングを強制 - + Bilinear filtering バイリニアフィルタリング - + Frame&skip フレームスキップ (&S) - + Mute ミュート - + FPS target FPS - + Native (59.7275) ネイティブ(59.7275) - + Take &screenshot スクリーンショット (&S) - + F12 F12 - + Game Boy Printer... ポケットプリンタ... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games セーブデータ - + Automatically determine - + Use player %0 save game - + BattleChip Gate... チップゲート... - + %1× %1× - + Interframe blending フレーム間混合 - + Record A/V... ビデオ録画... - + Video layers ビデオレイヤー - + Audio channels オーディオチャンネル - + Adjust layer placement... レイヤーの配置を調整... - + &Tools ツール (&T) - + View &logs... ログビューアー... (&L) - + Game &overrides... ゲーム別設定... (&O) - + &Cheats... チート... (&C) - + Settings... 設定... - + Open debugger console... デバッガコンソールを開く... - + Start &GDB server... GDBサーバを起動... (&G) - + Scripting... - + Game state views - + View &palette... パレットビューアー... (&P) - + View &sprites... スプライトビューアー... (&S) - + View &tiles... タイルビューアー... (&T) - + View &map... マップビューアー... (&M) - + &Frame inspector... フレームインスペクタ... (&F) - + View memory... メモリビューアー... - + Search memory... メモリ検索... - + View &I/O registers... IOビューアー... (&I) - + Record debug video log... デバッグビデオログ... - + Stop debug video log デバッグビデオログを停止 - + Exit fullscreen 全画面表示を終了 - + GameShark Button (held) GameSharkボタン(押し) - + Autofire 連打 - + Autofire A 連打 A - + Autofire B 連打 B - + Autofire L 連打 L - + Autofire R 連打 R - + Autofire Start 連打 Start - + Autofire Select 連打 Select - + Autofire Up 連打 上 - + Autofire Right 連打 右 - + Autofire Down 連打 下 - + Autofire Left 連打 左 diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index 6846a1633..4f47fe85e 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -5814,183 +5814,183 @@ Download size: %3 적절한 디스플레이 장치를 생성하지 못했습니다. 소프트웨어 디스플레이로 돌아갑니다. 특히 큰 창에서는 게임이 느리게 실행될 수 있습니다. - + Really make portable? 정말로 휴대용을 만듭니까? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 이렇게하면 에뮬레이터가 실행 파일과 동일한 디렉토리에서 구성을 로드하게됩니다. 계속 하시겠습니까? - + Restart needed 재시작 필요 - + Some changes will not take effect until the emulator is restarted. 일부 변경 사항은 에뮬레이터가 다시 시작될 때까지 적용되지 않습니다. - + Reset needed 재설정 필요 - + Some changes will not take effect until the game is reset. 일부 변경 사항은 게임이 재설정될 때까지 적용되지 않습니다. - + - Player %1 of %2 - 플레이어 %1 의 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &파일 - + Load &ROM... 로드 &롬... - + Load ROM in archive... 롬을 아카이브에 로드... - + Save games 게임 저장 - + Automatically determine 자동으로 결정 - + Use player %0 save game 플레이어 %0 저장 게임 사용 - + Load &patch... 로드 &패치... - + Boot BIOS BIOS 부팅 - + Replace ROM... 롬 교체... - + Scan e-Reader dotcodes... e-리더 도트코드 스캔... - + Game state views 게임 상태 보기 - + Convert e-Reader card image to raw... e-리더 카드 이미지를 원시 데이터로 변환... - + ROM &info... 롬 &정보... - + Recent 최근 실행 - + Make portable 휴대용 만들기 - + &Load state &로드 파일 상태 - + &Save state &저장 파일 상태 - + Quick load 빠른 로드 - + Quick save 빠른 저장 - + Load recent 최근 실행 로드 - + Save recent 최근 실행 저장 - + Undo load state 로드 파일 상태 복원 - + Undo save state 저장 파일 상태 복원 - + State &%1 상태 &%1 - + Load camera image... 카메라 이미지 로드... - + Convert save game... 저장 게임 변환... @@ -6000,192 +6000,192 @@ Download size: %3 게임샤크 저장 (*.gsv *.sps *.xps) - + New multiplayer window 새로운 멀티 플레이어 창 - + Connect to Dolphin... 돌핀에 연결... - + E&xit 종&료 - + &Emulation &에뮬레이션 - + &Reset &재설정 - + Sh&utdown 종&료 - + Yank game pak 양키 게임 팩 - + &Pause &정지 - + &Next frame &다음 프레임 - + Fast forward (held) 빨리 감기 (누름) - + &Fast forward &빨리 감기 - + Fast forward speed 빨리 감기 속도 - + Unbounded 무제한 - + %0x %0x - + Rewind (held) 되김기 (누름) - + Re&wind 리&와인드 - + Step backwards 돌아가기 - + Brightest solar level 가장 밝은 태양 수준 - + Darkest solar level 가장 어두운 태양 수준 - + Brightness %1 밝기 %1 - + Audio/&Video 오디오/&비디오 - + Frame size 프레임 크기 - + Toggle fullscreen 전체화면 전환 - + Lock aspect ratio 화면비 잠금 - + Frame&skip 프레임&건너뛰기 - + Mute 무음 - + FPS target FPS 대상 - + Take &screenshot 스크린샷 &찍기 - + F12 F12 - + Video layers 비디오 레이어 - + Audio channels 오디오 채널 - + &Tools &도구 - + View &logs... 로그 &보기... - + Game &overrides... 게임 &오버라이드... - + &Cheats... &치트.. - + Open debugger console... 디버거 콘솔 열기... - + Start &GDB server... GDB 서버 &시작... - + Settings... 설정... @@ -6205,67 +6205,67 @@ Download size: %3 게임을 시작할 수 없습니다. - + Add folder to library... 라이브러리에 폴더 추가... - + Load state file... 상태 파일 로드... - + Save state file... 상태 파일 저장... - + Import GameShark Save... 게임샤크 저장 가져오기... - + Export GameShark Save... 게임샤크 저장 내보내기... - + Report bug... 버그를 제보하기... - + About... 정보... - + Solar sensor 태양광 센서 - + Increase solar level 태양광 레벨 증가 - + Decrease solar level 태양광 레벨 감소 - + Force integer scaling 정수 스케일링 강제 수행 - + Bilinear filtering 이중선형 필터링 - + Game Boy Printer... 게임 보이 프린터... @@ -6291,177 +6291,177 @@ Download size: %3 저장 상태 선택 - + Load alternate save game... 대체 저장 게임 로드... - + Load temporary save game... 임시 저장 게임 로드... - + BattleChip Gate... 배틀칩 게이트... - + %1× %1× - + Interframe blending 프레임간 조합 - + Native (59.7275) 실기 (59.7275) - + Record A/V... A/V 녹화... - + Record GIF/WebP/APNG... GIF/WebP/APNG 녹화... - + Adjust layer placement... 레이어 배치 조정... - + Game Pak sensors... 게임 팩 센서... - + Scripting... 스크립팅... - + View &palette... 팔레트 &보기... - + View &sprites... 스프라이트 &보기... - + View &tiles... 타일 &보기... - + View &map... 지도 &보기... - + &Frame inspector... 프레임 검사기 (&F)... - + View memory... 메모리 보기... - + Search memory... 메모리 검색... - + View &I/O registers... I/O 레지스터 &보기... - + Record debug video log... 디버그 비디오 로그 녹화... - + Stop debug video log 디버그 비디오 로그 중지 - + Exit fullscreen 전체화면 종료 - + GameShark Button (held) 게임샤크 버튼 (누름) - + Autofire 연사 - + Autofire A 연사 A - + Autofire B 연사 B - + Autofire L 연사 L - + Autofire R 연사 R - + Autofire Start 연사 시작 - + Autofire Select 연사 선택 - + Clear 지움 - + Autofire Up 연사 위쪽 - + Autofire Right 연사 오른쪽 - + Autofire Down 연사 아래쪽 - + Autofire Left 연사 왼쪽 diff --git a/src/platform/qt/ts/mgba-ms.ts b/src/platform/qt/ts/mgba-ms.ts index 55134457e..54a273a64 100644 --- a/src/platform/qt/ts/mgba-ms.ts +++ b/src/platform/qt/ts/mgba-ms.ts @@ -5803,62 +5803,62 @@ Download size: %3 Gagal mencipta peranti paparan yang sesuai, berbalik ke paparan perisian. Permainan mungkin menjadi lembap, terutamanya dengan tetingkap besar. - + Really make portable? Betulkah mahu buat jadi mudah alih? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Ini akan menetapkan pelagak untuk muat konfigurasi dari direktori yang sama dengan fail bolehlakunya. Teruskan? - + Restart needed Mula semula diperlukan - + Some changes will not take effect until the emulator is restarted. Beberapa perubahan tidak akan dilaksanakan sehingga pelagak dimula semula. - + - Player %1 of %2 - Pemain %1 dari %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &File - + Load &ROM... Muat %ROM... - + Load ROM in archive... Muat ROM daripada arkib... - + Add folder to library... Tambah folder ke perpustakaan... @@ -5904,118 +5904,118 @@ Download size: %3 - + Load alternate save game... Muat simpanan permainan alternatif... - + Load temporary save game... Muat simpanan permainan sementara... - + Load &patch... - + Boot BIOS But BIOS - + Replace ROM... Ganti ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... &Perihal ROM... - + Recent Terkini - + Make portable Buat jadi mudah alih - + &Load state &Muat keadaan - + Load state file... Muat fail keadaan... - + &Save state &Simpan keadaan - + Save state file... Simpan fail keadaan... - + Quick load - + Quick save - + Load recent Muat terkini - + Save recent Simpan terkini - + Undo load state Buat asal keadaan termuat - + Undo save state Buat asal keadaan tersimpan - + State &%1 Keadaan &%1 - + Load camera image... Muat gambar kamera... - + Convert save game... Tukar simpanan permainan... @@ -6025,437 +6025,437 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Simpanan permainan - + Import GameShark Save... Import Simpanan GameShark... - + Export GameShark Save... Eksport Simpanan GameShark... - + Automatically determine - + Use player %0 save game - + New multiplayer window Tetingkap multipemain baru - + Connect to Dolphin... Sambung ke Dolphin... - + Report bug... Laporkan pepijat... - + About... Perihal... - + E&xit &Keluar - + &Emulation Pe&lagak - + &Reset - + Sh&utdown &Matikan - + Yank game pak Alih keluar Game Pak - + &Pause &Jeda - + &Next frame Bingkai se&terusnya - + Fast forward (held) Mundar laju (pegang) - + &Fast forward Mundar &laju - + Fast forward speed Kelajuan mundar laju - + Unbounded Tidak terbatas - + %0x %0x - + Rewind (held) Putar balik (pegang) - + Re&wind Ma&ndir - + Step backwards Langkah belakang - + Solar sensor Pengesan suria - + Increase solar level Meningkatkan aras suria - + Decrease solar level Mengurangkan aras suria - + Brightest solar level Aras suria paling terang - + Darkest solar level Aras suria paling gelap - + Brightness %1 Kecerahan %1 - + Game Boy Printer... Pencetak Game Boy... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Saiz bingkai - + %1× %1× - + Toggle fullscreen Togol skrinpenuh - + Lock aspect ratio Kekalkan nisbah aspek - + Force integer scaling Paksa skala integer - + Interframe blending Persebatian antarabingkai - + Bilinear filtering Penapisan bilinear - + Frame&skip Langkauan &bingkai - + Mute Senyap - + FPS target Sasaran FPS - + Native (59.7275) Asal (59.7275) - + Take &screenshot Ambil &cekupan skrin - + F12 F12 - + Record A/V... Rakam A/V... - + Record GIF/WebP/APNG... Rakam GIF/WebP/APNG... - + Video layers Lapisan video - + Audio channels Saluran audio - + Adjust layer placement... Melaras peletakan lapisan... - + &Tools &Alat - + View &logs... Lihat &log... - + Game &overrides... - + Game Pak sensors... Pengesan Game Pak... - + &Cheats... &Tipu... - + Settings... Tetapan... - + Open debugger console... Buka konsol penyahpepijat... - + Start &GDB server... Mula pelayan &GDB... - + Scripting... - + Game state views - + View &palette... Pelihat &palet... - + View &sprites... - + View &tiles... Pelihat &jubin... - + View &map... Pelihat pe&ta... - + &Frame inspector... Periksa &bingkai... - + View memory... Lihat ingatan... - + Search memory... Cari ingatan... - + View &I/O registers... Lihat daftar &I/O... - + Record debug video log... Rakam log video nyahpepijat... - + Stop debug video log Henti log video nyahpepijat - + Exit fullscreen Keluar skrinpenuh - + GameShark Button (held) Butang GameShark (pegang) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Kosongkan diff --git a/src/platform/qt/ts/mgba-nb_NO.ts b/src/platform/qt/ts/mgba-nb_NO.ts index 2dbaa2a6b..05bea134c 100644 --- a/src/platform/qt/ts/mgba-nb_NO.ts +++ b/src/platform/qt/ts/mgba-nb_NO.ts @@ -5806,62 +5806,62 @@ Nedlastningsstørrelse: %3 - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... @@ -5907,118 +5907,118 @@ Nedlastningsstørrelse: %3 - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - + State &%1 - + Load camera image... - + Convert save game... @@ -6028,437 +6028,437 @@ Nedlastningsstørrelse: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Tøm diff --git a/src/platform/qt/ts/mgba-pl.ts b/src/platform/qt/ts/mgba-pl.ts index da7b91691..b1a95e463 100644 --- a/src/platform/qt/ts/mgba-pl.ts +++ b/src/platform/qt/ts/mgba-pl.ts @@ -5813,62 +5813,62 @@ Rozmiar pobierania: %3 Nie udało się utworzyć odpowiedniego urządzenia wyświetlającego, powracam do wyświetlania programowego. Gry mogą działać wolno, zwłaszcza w przypadku większych okien. - + Really make portable? Naprawdę stworzyć wersję portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? To sprawi, że emulator załaduje swoją konfigurację z tego samego katalogu, co plik wykonywalny. Czy chcesz kontynuować? - + Restart needed Wymagane ponowne uruchomienie - + Some changes will not take effect until the emulator is restarted. Niektóre zmiany nie zaczną obowiązywać, dopóki emulator nie zostanie ponownie uruchomiony. - + - Player %1 of %2 - Gracz %1 z %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 FPS) - %4 - + &File &Plik - + Load &ROM... Załaduj &ROM... - + Load ROM in archive... Załaduj ROM w archiwum... - + Add folder to library... Dodaj folder do biblioteki... @@ -5914,118 +5914,118 @@ Rozmiar pobierania: %3 %1 z %2 kart czytnika e-Reader zostało pomyślnie przekonwertowanych. - + Load alternate save game... Załaduj alternatywny zapis gry... - + Load temporary save game... Załaduj tymczasowy zapis gry... - + Load &patch... Wczytaj &poprawkę... - + Boot BIOS BIOS startowy - + Replace ROM... Wymień ROM... - + Scan e-Reader dotcodes... Skanuj kody kropkowe czytnika e-Reader... - + Convert e-Reader card image to raw... Konwertuj obraz karty czytnika e-Reader na surowy... - + ROM &info... &Informacje o pamięci ROM... - + Recent Ostatnie - + Make portable Stwórz wersję portable - + &Load state &Załaduj stan - + Load state file... Załaduj plik stanu… - + &Save state &Zapisz stan - + Save state file... Zapisz plik stanu... - + Quick load Szybkie załadowanie - + Quick save Szybki zapis - + Load recent Załaduj ostatnie - + Save recent Zapisz ostatnie - + Undo load state Cofnij załadowanie stanu - + Undo save state Cofnij zapisanie stanu - + State &%1 Stan &%1 - + Load camera image... Załaduj obraz do kamery... - + Convert save game... Konwertuj zapisaną grę... @@ -6035,437 +6035,437 @@ Rozmiar pobierania: %3 Zapisy GameShark (*.gsv *.sps *.xps) - + Reset needed Wymagane zresetowanie - + Some changes will not take effect until the game is reset. Niektóre zmiany nie zaczną obowiązywać, dopóki gra nie zostanie zresetowana. - + Save games Zapisy gry - + Import GameShark Save... Importuj Zapis GameShark... - + Export GameShark Save... Eksportuj Zapis GameShark... - + Automatically determine Wykryj automatycznie - + Use player %0 save game Użyj zapis gry gracza %0 - + New multiplayer window Nowe okno dla wielu graczy - + Connect to Dolphin... Połącz z Dolphinem... - + Report bug... Zgłoś błąd... - + About... O Aplikacji... - + E&xit Z&akończ - + &Emulation &Emulacja - + &Reset &Resetuj - + Sh&utdown Za&mknij - + Yank game pak Wyciągnij Game Pak - + &Pause &Pauza - + &Next frame &Następna klatka - + Fast forward (held) Przewijanie (przytrzymaj) - + &Fast forward &Przewijanie do przodu - + Fast forward speed Prędkość przewijania do przodu - + Unbounded Bez ograniczeń - + %0x %0x - + Rewind (held) Przewijanie (przytrzymaj) - + Re&wind Pr&zewijanie - + Step backwards Krok w tył - + Solar sensor Czujnik słoneczny - + Increase solar level Zwiększ poziom energii słonecznej - + Decrease solar level Zmniejsz poziom energii słonecznej - + Brightest solar level Najjaśniejszy poziom energii słonecznej - + Darkest solar level Najciemniejszy poziom energii słonecznej - + Brightness %1 Jasność %1 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Dźwięk/&Wideo - + Frame size Rozmiar klatki - + %1× %1× - + Toggle fullscreen Przełącz tryb pełnoekranowy - + Lock aspect ratio Zablokuj proporcje - + Force integer scaling Wymuś skalowanie całkowite - + Interframe blending Blendowanie międzyklatkowe - + Bilinear filtering Filtrowanie dwuliniowe - + Frame&skip Klatko&wanie - + Mute Wycisz - + FPS target Cel KL./S - + Native (59.7275) Natywny (59.7275) - + Take &screenshot Wykonaj &zrzut ekranu - + F12 F12 - + Record A/V... Nagraj A/W... - + Record GIF/WebP/APNG... Nagraj GIF/WebP/APNG... - + Video layers Warstwy wideo - + Audio channels Kanały audio - + Adjust layer placement... Dostosuj położenie warstw... - + &Tools &Narzędzia - + View &logs... Wyświetl &logi... - + Game &overrides... Nadpisania &ustawień gry... - + Game Pak sensors... Czujniki Game Pak... - + &Cheats... &Kody... - + Settings... Ustawienia... - + Open debugger console... Otwórz konsolę debugera... - + Start &GDB server... Uruchom serwer &GDB... - + Scripting... Skrypty... - + Game state views Widoki stanu gry - + View &palette... Wyświetl &paletę... - + View &sprites... Wyświetl &sprite'y... - + View &tiles... Wyświetl &kafelki... - + View &map... Wyświetl &mapę... - + &Frame inspector... Inspektor &klatek... - + View memory... Wyświetl pamięć... - + Search memory... Przeszukaj pamięć... - + View &I/O registers... Wyświetl rejestry &we/wy... - + Record debug video log... Nagraj dziennik wideo debugowania... - + Stop debug video log Zatrzymaj dziennik wideo debugowania - + Exit fullscreen Wyłączyć tryb pełnoekranowy - + GameShark Button (held) Przycisk GameShark (przytrzymany) - + Autofire Turbo - + Autofire A Turbo A - + Autofire B Turbo B - + Autofire L Turbo L - + Autofire R Turbo R - + Autofire Start Turbo Start - + Autofire Select Turbo Select - + Autofire Up Turbo Góra - + Autofire Right Turbo Prawo - + Autofire Down Turbo Dół - + Autofire Left Turbo Lewo - + Clear Wyczyść diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts index df2eff064..d54c32360 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -5801,137 +5801,137 @@ Tamanho do download: %3 Falhou em criar um dispositivo de exibição apropriado, voltando a exibição por software. Os jogos podem executar lentamente, especialmente com janelas maiores. - + Really make portable? Realmente tornar portátil? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Isto fará o emulador carregar sua configuração do mesmo diretório que o executável. Você quer continuar? - + Restart needed Reiniciar é necessário - + Some changes will not take effect until the emulator is restarted. Algumas mudanças não terão efeito até o emulador ser reiniciado. - + - Player %1 of %2 - Jogador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Arquivo - + Load &ROM... Carregar &ROM... - + Load ROM in archive... Carregar ROM no arquivo compactado... - + Add folder to library... Adicionar pasta a biblioteca... - + Save games Saves dos jogos - + Automatically determine Determinar automaticamente - + Use player %0 save game Usar o save do jogo %0 do jogador - + Load &patch... Carregar &patch... - + Boot BIOS Dar Boot na BIOS - + Replace ROM... Substituir a ROM... - + ROM &info... Informações da &ROM... - + Recent Recentes - + Make portable Tornar portátil - + &Load state &Carregar state - + Report bug... Reportar bug... - + About... Sobre... - + Game Pak sensors... Sensores do Game Pak... - + Clear Limpar - + Load state file... Carregar arquivo do state... @@ -5977,73 +5977,73 @@ Tamanho do download: %3 %1 de %2 cartões do e-Reader convertidos com sucesso. - + Load alternate save game... Carregar save alternativo do jogo... - + Load temporary save game... Carregar save temporário do jogo... - + Convert e-Reader card image to raw... Converter imagem do cartão do e-Reader pro natural... - + &Save state &Salvar o state - + Save state file... Arquivo do save state... - + Quick load Carregamento rápido - + Quick save Salvamento rápido - + Load recent Carregar recentes - + Save recent Salvar recentes - + Undo load state Desfazer o carregamento do state - + Undo save state Desfazer o salvamento do state - + State &%1 State &%1 - + Load camera image... Carregar a imagem da câmera... - + Convert save game... Converter o save do jogo... @@ -6053,242 +6053,242 @@ Tamanho do download: %3 Saves do GameShark (*.gsv *.sps *.xps) - + Reset needed É necessário resetar - + Some changes will not take effect until the game is reset. Algumas mudanças não terão efeito até o jogo ser resetado. - + New multiplayer window Nova janela multi-jogador - + Connect to Dolphin... Conectar ao Dolphin... - + E&xit S&air - + &Emulation &Emulação - + &Reset &Resetar - + Sh&utdown De&sligar - + Yank game pak Arrancar o game pak - + &Pause &Pausar - + &Next frame &Próximo frame - + Fast forward (held) Avanço rápido (segurado) - + &Fast forward &Avanço rápido - + Fast forward speed Velocidade do avanço rápido - + Unbounded Ilimitado - + %0x %0x - + Rewind (held) Retroceder (segurado) - + Re&wind Re&troceder - + Step backwards Voltar um passo - + Solar sensor Sensor solar - + Increase solar level Aumentar nível solar - + Decrease solar level Diminuir nível solar - + Brightest solar level Nível solar mais brilhante - + Darkest solar level Nível solar mais escuro - + Brightness %1 Brilho %1 - + Audio/&Video Áudio/&Vídeo - + Frame size Tamanho do frame - + Toggle fullscreen Alternar tela cheia - + Lock aspect ratio Travar a proporção do aspecto - + Force integer scaling Forçar o dimensionamento do inteiro - + Bilinear filtering Filtragem bilinear - + Frame&skip Frame&skip - + Mute Mudo - + FPS target FPS alvo - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tirar &screenshot - + F12 F12 - + Game Boy Printer... Impressora do Game Boy... - + BattleChip Gate... Portal do BattleChip... - + %1× %1× - + Interframe blending Mistura do interframe - + Record A/V... Gravar A/V... - + Video layers Camadas do vídeo - + Audio channels Canais de áudio - + Adjust layer placement... Ajustar posicionamento da camada... - + &Tools &Ferramentas - + View &logs... Visualizar &registros... - + Game &overrides... Substituições &do jogo... @@ -6303,167 +6303,167 @@ Tamanho do download: %3 Não pôde iniciar o jogo. - + Scan e-Reader dotcodes... Escanear dotcodes do e-Reader... - + Import GameShark Save... Importar Save do GameShark... - + Export GameShark Save... Exportar Save do GameShark... - + Record GIF/WebP/APNG... Gravar GIF/WebP/APNG... - + &Cheats... &Trapaças... - + Settings... Configurações... - + Open debugger console... Abrir console do debugger... - + Start &GDB server... Iniciar servidor do &GDB... - + Scripting... Scripting... - + Game state views Visualizações do estado do jogo - + View &palette... Visualizar &paleta... - + View &sprites... Visualizar &imagens móveis... - + View &tiles... Visualizar &ladrilhos... - + View &map... Visualizar &mapa... - + &Frame inspector... Inspetor dos &frames... - + View memory... Visualizar memória... - + Search memory... Procurar na memória... - + View &I/O registers... Visualizar registros de &E/S... - + Record debug video log... Gravar registro do vídeo de debug... - + Stop debug video log Parar o registro do vídeo de debug - + Exit fullscreen Sair da tela cheia - + GameShark Button (held) Botão do GameShark (segurado) - + Autofire Auto-disparar - + Autofire A Auto-disparar A - + Autofire B Auto-disparar B - + Autofire L Auto-disparar L - + Autofire R Auto-disparar R - + Autofire Start Auto-disparar Start - + Autofire Select Auto-disparar Select - + Autofire Up Auto-disparar Pra Cima - + Autofire Right Auto-disparar Direita - + Autofire Down Auto-disparar Pra Baixo - + Autofire Left Auto-disparar Esquerda diff --git a/src/platform/qt/ts/mgba-ru.ts b/src/platform/qt/ts/mgba-ru.ts index 2c9cd92f1..33874b707 100644 --- a/src/platform/qt/ts/mgba-ru.ts +++ b/src/platform/qt/ts/mgba-ru.ts @@ -5813,62 +5813,62 @@ Download size: %3 Не удалось создать устройство отображения, возврат к программному режиму. Игры могут идти медленнее, особенно в окнах больших размеров. - + Really make portable? Перейти в портативный режим? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? После этого эмулятор будет загружать конфигурацию из папки с исполняемым файлом. Продолжить? - + Restart needed Требуется перезапуск - + Some changes will not take effect until the emulator is restarted. Для применения некоторых изменений требуется перезапустить эмулятор. - + - Player %1 of %2 - Игрок %1 из %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File &Файл - + Load &ROM... Загрузить &ROM... - + Load ROM in archive... Загрузить игру из архива... - + Add folder to library... Добавить папку в библиотеку... @@ -5914,118 +5914,118 @@ Download size: %3 %1 из %2 карточек e-Reader успешно сконвертировано. - + Load alternate save game... Загрузить альтернативное сохранение... - + Load temporary save game... Загрузить временное сохранение... - + Load &patch... Загрузить &патч... - + Boot BIOS Загрузиться в BIOS - + Replace ROM... Заменить ROM... - + Scan e-Reader dotcodes... Сканировать dot-коды e-Reader... - + Convert e-Reader card image to raw... Конвертировать карту e-Reader в raw... - + ROM &info... Информация об &игре... - + Recent Недавние - + Make portable Портативный режим - + &Load state &Загрузить состояние - + Load state file... Загрузить состояние из файла... - + &Save state &Сохранить состояние - + Save state file... Сохранить состояние в файл... - + Quick load Быстрая загрузка - + Quick save Быстрое сохранение - + Load recent Загрузить недавнее - + Save recent Сохранить в недавнее - + Undo load state Отмена загрузки состояния - + Undo save state Отмена сохранения состояния - + State &%1 Слот &%1 - + Load camera image... Загрузить изображение с камеры... - + Convert save game... Конвертировать игровое сохранение... @@ -6035,437 +6035,437 @@ Download size: %3 Сохранения GameShark (*.gsv *.sps *.xps) - + Reset needed Необходима перезагрузка - + Some changes will not take effect until the game is reset. Некоторые изменения не войдут в силу, пока игра не перезагружена. - + Save games Сохранения - + Import GameShark Save... Импорт сохранения GameShark... - + Export GameShark Save... Экспорт сохранения GameShark... - + Automatically determine Определить автоматически - + Use player %0 save game Использовать сохранение игрока %0 - + New multiplayer window Новое окно мультиплеера - + Connect to Dolphin... Соединение с Dolphin... - + Report bug... Сообщить об ошибке... - + About... О программе... - + E&xit &Выход - + &Emulation &Эмуляция - + &Reset Перезагрузить (&R/&К) - + Sh&utdown Выключить (&U/&Г) - + Yank game pak Пнуть картридж - + &Pause Пау&за - + &Next frame Следующий кадр (&N/&Т) - + Fast forward (held) Перемотка (удержание) - + &Fast forward Перемотк&а - + Fast forward speed Скорость перемотки - + Unbounded Неограниченная - + %0x %0x - + Rewind (held) Обратная перемотка (удержание) - + Re&wind Обратная перемотка (&W/&Ц) - + Step backwards Шаг назад - + Solar sensor Датчик солнца - + Increase solar level Усилить солнечный свет - + Decrease solar level Ослабить солнечный свет - + Brightest solar level Ярчайшее солнце - + Darkest solar level Тусклейшее солнце - + Brightness %1 Яркость %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video Аудио/Видео (&V/&М) - + Frame size Размер кадра - + %1× %1× - + Toggle fullscreen Переключить полноэкранный режим - + Lock aspect ratio Зафиксировать соотношение сторон - + Force integer scaling Принудительное целочисленное масштабирование - + Interframe blending Межкадровое смешение - + Bilinear filtering Билинейная фильтрация - + Frame&skip Пропуск кадров (&S/&Ы) - + Mute Выключить звук - + FPS target Целевой FPS - + Native (59.7275) Родной (59.7275) - + Take &screenshot Снять скриншот (&S/&Ы) - + F12 - + Record A/V... Записать аудио/видео... - + Record GIF/WebP/APNG... Записать GIF/WebP/APNG... - + Video layers Видеослои - + Audio channels Аудиоканалы - + Adjust layer placement... Настроить расположение слоёв... - + &Tools Инструм&енты - + View &logs... Посмотреть в журнал... (&L/&Д) - + Game &overrides... Переопределения для этой игры... (&О/&Щ) - + Game Pak sensors... Датчики в Game Pak... - + &Cheats... Читы... (&C/&С) - + Settings... Настройки... - + Open debugger console... Открыть консоль отладки... - + Start &GDB server... Запустить сервер &GDB... - + Scripting... Скрипты... - + Game state views Просмотр состояния игры - + View &palette... Просмотр палитры... (&P/&З) - + View &sprites... Просмотр спрайтов... (&S/&Ы) - + View &tiles... Просмотр тайлов... (&T/&Е) - + View &map... Просмотр карты... (&M/&Ь) - + &Frame inspector... Изучение фрейм&а... - + View memory... Просмотр памяти... - + Search memory... Поиск в памяти... - + View &I/O registers... Просмотр регистров &I/O... - + Record debug video log... Запись отладочного видеожурнала... - + Stop debug video log Закончить запись отладочного видеожурнала - + Exit fullscreen Выйти из полноэкранного режима - + GameShark Button (held) Кнопка GameShark (удерживается) - + Autofire Автострельба - + Autofire A A (автострельба) - + Autofire B B (автострельба) - + Autofire L L (автострельба) - + Autofire R R (автострельба) - + Autofire Start Start (автострельба) - + Autofire Select Select (автострельба) - + Autofire Up Вверх (автострельба) - + Autofire Right Вправо (автострельба) - + Autofire Down Вниз (автострельба) - + Autofire Left Влево (автострельба) - + Clear Очистить diff --git a/src/platform/qt/ts/mgba-template.ts b/src/platform/qt/ts/mgba-template.ts index e4b45de8c..6e39742bf 100644 --- a/src/platform/qt/ts/mgba-template.ts +++ b/src/platform/qt/ts/mgba-template.ts @@ -5801,62 +5801,62 @@ Download size: %3 - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... @@ -5902,118 +5902,118 @@ Download size: %3 - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - + State &%1 - + Load camera image... - + Convert save game... @@ -6023,437 +6023,437 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear diff --git a/src/platform/qt/ts/mgba-tr.ts b/src/platform/qt/ts/mgba-tr.ts index 7d5a86f2e..e5f5c6074 100644 --- a/src/platform/qt/ts/mgba-tr.ts +++ b/src/platform/qt/ts/mgba-tr.ts @@ -5814,183 +5814,183 @@ Download size: %3 Uygun görüntü cihazı oluşturma başarısız, yazılım ekranına dönülüyor. Oyunlar özellikle daha büyük ekranlarda yavaş çalışabilir. - + Really make portable? Taşınabilir yapılsın mı? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Emülatörün yapılandırmasını yürütülebilir dosya ile aynı dizinden yüklemesini sağlar. Devam etmek istiyor musun? - + Restart needed Yeniden başlatma gerekli - + Some changes will not take effect until the emulator is restarted. Bazı değişiklikler emülatör yeniden başlatılıncaya kadar etkili olmaz. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... &ROM yükle... - + Load ROM in archive... ROM'u arşivden yükle ... - + Add folder to library... Kütüphaneye klasör ekle ... - + Save games Oyunları kaydet - + Automatically determine - + Use player %0 save game - + Load &patch... &Patch yükle... - + Boot BIOS BIOS boot et - + Replace ROM... ROM değişti... - + Game state views - + Convert e-Reader card image to raw... e-Okuyucu kart resimlerini rawa dönüştür... - + ROM &info... ROM &info... - + Recent Son kullanılanlar - + Make portable Portatif yap - + &Load state &Kaydedilmiş konum yükle - + Load state file... Kaydedilmiş konum dosyası yükle... - + &Save state &Konumu kaydet - + Save state file... Konum dosyasını kaydet... - + Quick load Hızlı Yükle - + Quick save Hızlı kaydet - + Load recent En son yükle - + Save recent Hızlı kaydet - + Undo load state Kaydedilen konum yüklemeyi geri al - + Undo save state Konum kaydetmeyi geri al - + State &%1 Konum &%1 - + Load camera image... Kamera resmini yükle ... - + Convert save game... Kayıtlı oyun dömnüştürülüyor... @@ -6000,237 +6000,237 @@ Download size: %3 - + Reset needed - + Some changes will not take effect until the game is reset. - + New multiplayer window Yeni çokoyunculu ekranı - + Connect to Dolphin... Dolphin'e Bağlan... - + Report bug... Hata rapor et... - + About... Hakkında... - + E&xit Çıkış - + &Emulation Emülasyon - + &Reset &Reset - + Sh&utdown Kapat - + Yank game pak - + &Pause &Durdur - + &Next frame &Sonraki kare - + Fast forward (held) İleriye sar(basılı tutun) - + &Fast forward &İleriye sar - + Fast forward speed İleriye sarma hızı - + Unbounded - + %0x - + Rewind (held) Geri sar (basılı tutun) - + Re&wind Geri sar - + Step backwards Geriye doğru adım - + Solar sensor - + Increase solar level Solar seviyesini arttır - + Decrease solar level Solar seviyesini düşür - + Brightest solar level En parlak solar seviyesi - + Darkest solar level En karanlık solar seviyesi - + Brightness %1 Parlaklık:%1 - + Game Boy Printer... Game Boy yazıcısı... - + BattleChip Gate... - + Audio/&Video Ses/&Video - + Frame size Çerçeve boyutu - + Toggle fullscreen Tamekranı aç/kapa - + Lock aspect ratio En boy oranını kilitle - + Force integer scaling Tamsayılı ölçeklendirmeyi zorla - + Bilinear filtering Bilinear filtreleme - + Frame&skip Kare atlama - + Mute Sessiz - + FPS target FPS hedefi - + Native (59.7275) - + Take &screenshot Ekran görüntüsü al - + F12 - + Video layers - + Audio channels Ses kanalları - + Adjust layer placement... Katman yerleşimini ayarlayın... - + &Tools &Araçlar - + View &logs... Kayıtları görüntüle... - + Game &overrides... & Oyunun üzerine yazılanlar... @@ -6266,197 +6266,197 @@ Download size: %3 Oyun başlatılamadı. - + Load alternate save game... Alternatif kayıtlı oyun yükle... - + Load temporary save game... Geçici kayıtlı oyunu yükle... - + Scan e-Reader dotcodes... e-Okuyucu noktakodları tara... - + Import GameShark Save... GameShark kaydını içeri aktar... - + Export GameShark Save... GameShark kaydını dışarı aktar... - + %1× %1× - + Interframe blending Kareler-arası Karıştırma - + Record A/V... A/V Kayıt... - + Record GIF/WebP/APNG... GIF/WebP/APNG Kayıt... - + Game Pak sensors... Oyun Kartuş sensörleri... - + &Cheats... &Hileler... - + Settings... Ayarlar... - + Open debugger console... Hata ayıklayıcı konsolunu aç ... - + Start &GDB server... &GDB sunucusunu başlat... - + Scripting... - + View &palette... &Renk Paletini gör... - + View &sprites... &Spriteları gör... - + View &tiles... &Desenleri gör... - + View &map... &Haritayı gör - + &Frame inspector... &Kare denetçisi... - + View memory... Hafıza gör... - + Search memory... Hafızada ara... - + View &I/O registers... &I/O kayıtlarını görüntüle - + Record debug video log... Hata ayıklama video günlüğünü kaydet... - + Stop debug video log Hata ayıklama video günlüğünü durdur - + Exit fullscreen Tam ekrandan çık - + GameShark Button (held) GameShark Butonu (basılı tutun) - + Autofire Otomatik basma - + Autofire A Otomatik basma A - + Autofire B Otomatik basma B - + Autofire L Otomatik basma L - + Autofire R Otomatik basma R - + Autofire Start Otomatik basma Start - + Autofire Select Otomatik basma Select - + Autofire Up Otomatik basma Up - + Autofire Right Otomatik basma Right - + Autofire Down Otomatik basma Down - + Autofire Left Otomatik basma Sol - + Clear Temizle diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 47559f1eb..7c73ad9d8 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -5809,62 +5809,62 @@ Download size: %3 无法创建适合的显示设备,正在回滚到软件显示。游戏的运行速度(特别在大窗口的情况下)可能会变慢。 - + Really make portable? 确定进行程序便携化? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 进行此操作后,模拟器将从其可执行文件所在目录中载入模拟器配置。您想继续吗? - + Restart needed 需要重新启动 - + Some changes will not take effect until the emulator is restarted. 更改将在模拟器下次重新启动时生效。 - + - Player %1 of %2 - 玩家 %1 共 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File 文件(&F) - + Load &ROM... 载入 ROM(&R)... - + Load ROM in archive... 从压缩文件中载入 ROM... - + Add folder to library... 将文件夹添加到库中... @@ -5910,118 +5910,118 @@ Download size: %3 成功转换了 %1 张(共 %2 张)e-Reader 卡片。 - + Load alternate save game... 加载备用保存游戏... - + Load temporary save game... 加载临时保存游戏... - + Load &patch... 载入补丁(&P)... - + Boot BIOS 引导 BIOS - + Replace ROM... 替换 ROM... - + Scan e-Reader dotcodes... 扫描 e-Reader 点码... - + Convert e-Reader card image to raw... 将 e-Reader 卡片图像转换为原始数据... - + ROM &info... ROM 信息(&I)... - + Recent 最近打开 - + Make portable 程序便携化 - + &Load state 载入即时存档(&L) - + Load state file... 载入即时存档文件... - + &Save state 保存即时存档(&S) - + Save state file... 保存即时存档文件... - + Quick load 快速读档 - + Quick save 快速存档 - + Load recent 载入最近存档 - + Save recent 保存最近存档 - + Undo load state 撤消读档 - + Undo save state 撤消存档 - + State &%1 即时存档 (&%1) - + Load camera image... 载入相机图片... - + Convert save game... 转换保存游戏... @@ -6031,437 +6031,437 @@ Download size: %3 GameShark 存档 (*.gsv *.sps *.xps) - + Reset needed 需要重启 - + Some changes will not take effect until the game is reset. 某些改动需要重新启动才会生效。 - + Save games 游戏存档 - + Import GameShark Save... 导入 GameShark 存档... - + Export GameShark Save... 导出 GameShark 存档... - + Automatically determine 自动终止 - + Use player %0 save game 使用玩家 %0 存档 - + New multiplayer window 新建多人游戏窗口 - + Connect to Dolphin... 连接到 Dolphin... - + Report bug... 报告错误... - + About... 关于... - + E&xit 退出(&X) - + &Emulation 模拟(&E) - + &Reset 重置(&R) - + Sh&utdown 关机(&U) - + Yank game pak 快速抽出游戏卡带 - + &Pause 暂停(&P) - + &Next frame 下一帧(&N) - + Fast forward (held) 快进 (长按) - + &Fast forward 快进(&F) - + Fast forward speed 快进速度 - + Unbounded 不限制 - + %0x %0x - + Rewind (held) 倒带 (长按) - + Re&wind 倒带(&W) - + Step backwards 步退 - + Solar sensor 太阳光传感器 - + Increase solar level 增加太阳光等级 - + Decrease solar level 降低太阳光等级 - + Brightest solar level 太阳光等级为最亮 - + Darkest solar level 太阳光等级为最暗 - + Brightness %1 亮度 %1 - + Game Boy Printer... Game Boy 打印机... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video 音频/视频(&V) - + Frame size 画面大小 - + %1× %1× - + Toggle fullscreen 切换全屏 - + Lock aspect ratio 锁定纵横比 - + Force integer scaling 强制整数缩放 - + Interframe blending 帧间混合 - + Bilinear filtering 双线性过滤 - + Frame&skip 跳帧(&S) - + Mute 静音 - + FPS target 目标 FPS - + Native (59.7275) 原生 (59.7275) - + Take &screenshot 截图(&S) - + F12 F12 - + Record A/V... 录制音频/视频... - + Record GIF/WebP/APNG... 录制 GIF/WebP/APNG... - + Video layers 视频图层 - + Audio channels 音频声道 - + Adjust layer placement... 调整图层布局... - + &Tools 工具(&T) - + View &logs... 查看日志(&L)... - + Game &overrides... 覆写游戏(&O)... - + Game Pak sensors... 游戏卡带传感器... - + &Cheats... 作弊码(&C)... - + Settings... 设置... - + Open debugger console... 打开调试器控制台... - + Start &GDB server... 打开 GDB 服务器(&G)... - + Scripting... 脚本... - + Game state views 游戏状态视图 - + View &palette... 查看调色板(&P)... - + View &sprites... 查看精灵图(&S)... - + View &tiles... 查看图块(&T)... - + View &map... 查看映射(&M)... - + &Frame inspector... 帧检查器(&F)... - + View memory... 查看内存... - + Search memory... 搜索内存... - + View &I/O registers... 查看 I/O 寄存器(&I)... - + Record debug video log... 记录调试视频日志... - + Stop debug video log 停止记录调试视频日志 - + Exit fullscreen 退出全屏 - + GameShark Button (held) GameShark 键 (长按) - + Autofire 连发 - + Autofire A 连发 A - + Autofire B 连发 B - + Autofire L 连发 L - + Autofire R 连发 R - + Autofire Start 连发 Start - + Autofire Select 连发 Select - + Autofire Up 连发 上 - + Autofire Right 连发 右 - + Autofire Down 连发 下 - + Autofire Left 连发 左 - + Clear 清除 From 686eee4e20cc555b90810811f2d0d6f8b9b393c5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 20:30:00 -0700 Subject: [PATCH 010/159] Changes: Update for 0.10.0 --- CHANGES | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index f03b446c1..bab536240 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -0.10.0: (Future) +0.10.0: (2022-10-11) Features: - Preliminary Lua scripting support - Presets for Game Boy palettes @@ -25,8 +25,8 @@ Emulation fixes: - GB Audio: Revamp channel rendering - GB Audio: Fix APU re-enable timing glitch - GB I/O: Fix writing to WAVE RAM behavior (fixes mgba.io/i/1334) - - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB MBC: Fix edge case with Pocket Cam register accesses (fixes mgba.io/i/2557) + - GB Memory: Add cursory cartridge open bus emulation (fixes mgba.io/i/2032) - GB Serialize: Fix loading MBC1 states that affect bank 0 (fixes mgba.io/i/2402) - GB SIO: Fix bidirectional transfer starting (fixes mgba.io/i/2290) - GB Video: Draw SGB border pieces that overlap GB graphics (fixes mgba.io/i/1339) From 6fa2f742793a1939c8eae9ef9f1c10203c469c34 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 11 Oct 2022 22:23:40 -0700 Subject: [PATCH 011/159] CMake: Bump verison --- version.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.cmake b/version.cmake index e66142fbd..01509a37b 100644 --- a/version.cmake +++ b/version.cmake @@ -2,9 +2,9 @@ if(NOT PROJECT_NAME) set(PROJECT_NAME "mGBA") endif() set(LIB_VERSION_MAJOR 0) -set(LIB_VERSION_MINOR 10) +set(LIB_VERSION_MINOR 11) set(LIB_VERSION_PATCH 0) -set(LIB_VERSION_ABI 0.10) +set(LIB_VERSION_ABI 0.11) set(LIB_VERSION_STRING ${LIB_VERSION_MAJOR}.${LIB_VERSION_MINOR}.${LIB_VERSION_PATCH}) set(SUMMARY "${PROJECT_NAME} Game Boy Advance Emulator") From 5067eb621c4ed3463e206f2bae05bd799a701c70 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 14 Oct 2022 21:33:38 -0700 Subject: [PATCH 012/159] Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes #2685) --- CHANGES | 4 ++++ res/scripts/pokemon.lua | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/CHANGES b/CHANGES index bab536240..0cdca19c2 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +0.11.0: (Future) +Other fixes: + - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) + 0.10.0: (2022-10-11) Features: - Preliminary Lua scripting support diff --git a/res/scripts/pokemon.lua b/res/scripts/pokemon.lua index 7ebe54bb8..d909396d0 100644 --- a/res/scripts/pokemon.lua +++ b/res/scripts/pokemon.lua @@ -412,6 +412,13 @@ local gameRubyEn = Generation3En:new{ _speciesNameTable=0x1f716c, } +local gameRubyEnR1 = Generation3En:new{ + name="Ruby (USA)", + _party=0x3004360, + _partyCount=0x3004350, + _speciesNameTable=0x1f7184, +} + local gameSapphireEn = Generation3En:new{ name="Sapphire (USA)", _party=0x3004360, @@ -419,6 +426,13 @@ local gameSapphireEn = Generation3En:new{ _speciesNameTable=0x1f70fc, } +local gameSapphireEnR1 = Generation3En:new{ + name="Sapphire (USA)", + _party=0x3004360, + _partyCount=0x3004350, + _speciesNameTable=0x1f7114, +} + local gameEmeraldEn = Generation3En:new{ name="Emerald (USA)", _party=0x20244ec, @@ -471,6 +485,10 @@ gameCrc32 = { [0x7d527d62] = gameYellowEn, [0x84ee4776] = gameFireRedEnR1, [0xdaffecec] = gameLeafGreenEnR1, + [0x61641576] = gameRubyEnR1, -- Rev 1 + [0xaeac73e6] = gameRubyEnR1, -- Rev 2 + [0xbafedae5] = gameSapphireEnR1, -- Rev 1 + [0x9cc4410e] = gameSapphireEnR1, -- Rev 2 } function printPartyStatus(game, buffer) From d5669c2872fc86f2fc1b4605573305db11056d9e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 14 Oct 2022 23:23:37 -0700 Subject: [PATCH 013/159] Qt: Manually split filename to avoid overzealous splitting (fixes #2681) --- CHANGES | 1 + src/platform/qt/ApplicationUpdater.cpp | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 0cdca19c2..59467e569 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,6 @@ 0.11.0: (Future) Other fixes: + - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) 0.10.0: (2022-10-11) diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index ce752ecce..f26e58101 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -137,7 +137,15 @@ QUrl ApplicationUpdater::parseManifest(const QByteArray& manifest) { QString ApplicationUpdater::destination() const { QFileInfo path(updateInfo().url.path()); QDir dir(ConfigController::configDir()); - return dir.filePath(QLatin1String("update.") + path.completeSuffix()); + // QFileInfo::completeSuffix will eat all .'s in the filename...including + // ones in the version string, turning mGBA-1.0.0-win32.7z into + // 0.0-win32.7z instead of the intended .7z + // As a result, so we have to split out the complete suffix manually. + QString suffix(path.suffix()); + if (path.completeBaseName().endsWith(".tar")) { + suffix = "tar." + suffix; + } + return dir.filePath(QLatin1String("update.") + suffix); } const char* ApplicationUpdater::platform() { From 47e704d2578457f6f03f80a59c5736057c7bc79d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 14 Oct 2022 23:35:32 -0700 Subject: [PATCH 014/159] Qt: Expand criteria for tag branch names (fixes #2679) --- CHANGES | 1 + src/platform/qt/ApplicationUpdater.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 59467e569..745789d4d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 0.11.0: (Future) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) + - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) 0.10.0: (2022-10-11) diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index f26e58101..4225bd7ee 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -73,7 +73,7 @@ QStringList ApplicationUpdater::listChannels() { QString ApplicationUpdater::currentChannel() { QLatin1String version(projectVersion); QLatin1String branch(gitBranch); - if (branch == QLatin1String("heads/") + version) { + if (branch == QLatin1String("heads/") + version || branch == version) { return QLatin1String("stable"); } else { return QLatin1String("dev"); From 0bd4ad034ef35f278d2efb1f68a6164b33f6a88f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Oct 2022 04:05:56 -0700 Subject: [PATCH 015/159] GB MBC: Add NT (old 1) support --- CHANGES | 2 + include/mgba/gb/interface.h | 1 + include/mgba/internal/gb/memory.h | 7 ++ include/mgba/internal/gb/serialize.h | 5 ++ src/gb/mbc.c | 112 +++++++++++++++++++++++---- src/gb/memory.c | 11 +++ src/gb/overrides.c | 2 + src/platform/qt/GameBoy.cpp | 2 + 8 files changed, 129 insertions(+), 13 deletions(-) diff --git a/CHANGES b/CHANGES index 745789d4d..240bbcf8e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,6 @@ 0.11.0: (Future) +Features: + - New unlicensed GB mapper: NT (older type 1) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 6b7e1adac..5a0433021 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -41,6 +41,7 @@ enum GBMemoryBankControllerType { GB_MBC5_RUMBLE = 0x105, GB_UNL_WISDOM_TREE = 0x200, GB_UNL_PKJD = 0x203, + GB_UNL_NT_OLD_1 = 0x210, GB_UNL_NT_NEW = 0x212, GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior GB_UNL_HITEK = 0x221, diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 6e02ffa4d..35a833703 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -238,6 +238,12 @@ struct GBPKJDState { uint8_t reg[2]; }; +struct GBNTOld1State { + bool swapped; + uint8_t baseBank; + uint8_t bankCount; +}; + struct GBNTNewState { bool splitMode; }; @@ -263,6 +269,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; + struct GBNTOld1State ntOld1; struct GBNTNewState ntNew; struct GBPKJDState pkjd; struct GBBBDState bbd; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 3087e6b02..c3c781335 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -416,6 +416,11 @@ struct GBSerializedState { uint8_t value; uint8_t mode; } huc3; + struct { + uint8_t swapped; + uint8_t baseBank; + uint8_t bankCount; + } ntOld1; struct { uint8_t dataSwapMode; uint8_t bankSwapMode; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index d6c39035c..62208376f 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -45,6 +45,7 @@ static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); +static void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value); static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); @@ -159,7 +160,7 @@ static struct { {"BBD", GB_UNL_BBD}, {"HITK", GB_UNL_HITEK}, {"SNTX", GB_MBC_AUTODETECT}, // TODO - {"NTO1", GB_MBC_AUTODETECT}, // TODO + {"NTO1", GB_UNL_NT_OLD_1}, {"NTO2", GB_MBC_AUTODETECT}, // TODO {"NTN", GB_UNL_NT_NEW}, {"LICH", GB_MBC_AUTODETECT}, // TODO @@ -486,6 +487,9 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_WISDOM_TREE: gb->memory.mbcWrite = _GBWisdomTree; break; + case GB_UNL_NT_OLD_1: + gb->memory.mbcWrite = _GBNTOld1; + break; case GB_UNL_NT_NEW: gb->memory.mbcWrite = _GBNTNew; break; @@ -1963,6 +1967,100 @@ static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { } } + +static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { + uint8_t newbyte = 0; + int i; + for(i = 0; i < 8; ++i) { + int oldbit = reorder[i]; + int newbit = i; + newbyte += ((input >> oldbit) & 1) << newbit; + } + + return newbyte; +} + +static const uint8_t _ntOld1Reorder[8] = { + 0, 2, 1, 4, 3, 5, 6, 7 +}; + +void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOld1State* mbcState = &memory->mbcState.ntOld1; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + bank &= 0x1F; + if (!bank) { + bank = 1; + } + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld1Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + return; + case 0x5: + switch (address & 3) { + case 0: + mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); + break; + case 1: + value &= 0x3F; + mbcState->baseBank = value * 2; + if (mbcState->baseBank) { + GBMBCSwitchBank0(gb, mbcState->baseBank); + GBMBCSwitchBank(gb, mbcState->baseBank + 1); + } + break; + case 2: + if ((value & 0xF0) == 0xE0) { + gb->sramSize = 0x2000; + GBResizeSram(gb, gb->sramSize); + } + switch (value & 0xF) { + case 0x00: + mbcState->bankCount = 32; + break; + case 0x08: + mbcState->bankCount = 16; + break; + case 0xC: + mbcState->bankCount = 8; + break; + case 0xE: + mbcState->bankCount = 4; + break; + case 0xF: + mbcState->bankCount = 2; + break; + default: + mbcState->bankCount = 32; + break; + } + break; + case 3: + mbcState->swapped = !!(value & 0x10); + + bank = memory->currentBank; + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld1Reorder); + } + GBMBCSwitchBank(gb, bank); + break; + } + return; + } +} + void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; if (address >> 8 == 0x14) { @@ -1986,18 +2084,6 @@ void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { _GBMBC5(gb, address, value); } -static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { - uint8_t newbyte = 0; - int i; - for(i = 0; i < 8; ++i) { - int oldbit = reorder[i]; - int newbit = i; - newbyte += ((input >> oldbit) & 1) << newbit; - } - - return newbyte; -} - static const uint8_t _bbdDataReordering[8][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET diff --git a/src/gb/memory.c b/src/gb/memory.c index 0c11308fe..43ed32550 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -797,6 +797,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.mmm01.locked = memory->mbcState.mmm01.locked; state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; break; + case GB_UNL_NT_OLD_1: + state->memory.ntOld1.swapped = memory->mbcState.ntOld1.swapped; + state->memory.ntOld1.baseBank = memory->mbcState.ntOld1.baseBank; + state->memory.ntOld1.bankCount = memory->mbcState.ntOld1.bankCount; + break; case GB_UNL_BBD: case GB_UNL_HITEK: state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode; @@ -929,6 +934,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { GBMBCSwitchBank0(gb, gb->memory.romSize / GB_SIZE_CART_BANK0 - 2); } break; + case GB_UNL_NT_OLD_1: + memory->mbcState.ntOld1.swapped = state->memory.ntOld1.swapped; + memory->mbcState.ntOld1.baseBank = state->memory.ntOld1.baseBank; + memory->mbcState.ntOld1.bankCount = state->memory.ntOld1.bankCount; + GBMBCSwitchBank0(gb, memory->mbcState.ntOld1.baseBank); + break; case GB_UNL_BBD: case GB_UNL_HITEK: memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index 5bfc4f3cf..a36edf2a3 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -673,6 +673,8 @@ static const struct GBCartridgeOverride _overrides[] = { { 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) { 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) { 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) + { 0xE1147E75, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // Rockman 8 + { 0xEFF88FAA, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // True Color 25 in 1 (NT-9920) { 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001 { 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4 { 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2 diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index e0271d322..2dfa3c9e7 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -35,6 +35,7 @@ static const QList s_mbcList{ GB_HuC3, GB_UNL_WISDOM_TREE, GB_UNL_PKJD, + GB_UNL_NT_OLD_1, GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, @@ -87,6 +88,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_POCKETCAM] = tr("Pocket Cam"); s_mbcNames[GB_TAMA5] = tr("TAMA5"); s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree"); + s_mbcNames[GB_UNL_NT_OLD_1] = tr("NT (old 1)"); s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)"); s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); From 1f3672824481376032b6248fc06965e34a5855cb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Oct 2022 04:42:10 -0700 Subject: [PATCH 016/159] GB Serialize: Add missing savestate support for MBC6 and NT (newer) --- CHANGES | 2 ++ include/mgba/internal/gb/memory.h | 1 - include/mgba/internal/gb/serialize.h | 13 ++++++++++ src/gb/mbc.c | 1 - src/gb/memory.c | 36 ++++++++++++++++++++++++++-- 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 240bbcf8e..43bfb0f4a 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,8 @@ Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) +Misc: + - GB Serialize: Add missing savestate support for MBC6 and NT (newer) 0.10.0: (2022-10-11) Features: diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index 35a833703..cefa138e9 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -191,7 +191,6 @@ struct GBMBC1State { }; struct GBMBC6State { - bool sramAccess; bool flashBank0; bool flashBank1; }; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index c3c781335..97c4f3ba1 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -266,6 +266,10 @@ DECL_BITS(GBSerializedVideoFlags, Mode, 2, 2); DECL_BIT(GBSerializedVideoFlags, NotModeEventScheduled, 4); DECL_BIT(GBSerializedVideoFlags, NotFrameEventScheduled, 5); +DECL_BITFIELD(GBSerializedMBC6Flags, uint8_t); +DECL_BIT(GBSerializedMBC6Flags, FlashBank0, 0); +DECL_BIT(GBSerializedMBC6Flags, FlashBank1, 1); + DECL_BITFIELD(GBSerializedMBC7Flags, uint8_t); DECL_BITS(GBSerializedMBC7Flags, Command, 0, 2); DECL_BIT(GBSerializedMBC7Flags, Writable, 2); @@ -392,6 +396,11 @@ struct GBSerializedState { struct { uint64_t lastLatch; } rtc; + struct { + GBSerializedMBC6Flags flags; + uint8_t bank1; + uint8_t sramBank1; + } mbc6; struct { uint8_t state; GBMBC7Field eeprom; @@ -421,6 +430,10 @@ struct GBSerializedState { uint8_t baseBank; uint8_t bankCount; } ntOld1; + struct { + uint8_t splitMode; + uint8_t bank1; + } ntNew; struct { uint8_t dataSwapMode; uint8_t bankSwapMode; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 62208376f..ea2123844 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -573,7 +573,6 @@ void GBMBCReset(struct GB* gb) { case GB_MBC6: GBMBCSwitchHalfBank(gb, 0, 2); GBMBCSwitchHalfBank(gb, 1, 3); - gb->memory.mbcState.mbc6.sramAccess = false; GBMBCSwitchSramHalfBank(gb, 0, 0); GBMBCSwitchSramHalfBank(gb, 0, 1); break; diff --git a/src/gb/memory.c b/src/gb/memory.c index 43ed32550..5c7441e18 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -755,6 +755,12 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { case GB_MBC3_RTC: STORE_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; + case GB_MBC6: + state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank0(0, memory->mbcState.mbc6.flashBank0); + state->memory.mbc6.flags = GBSerializedMBC6FlagsSetFlashBank1(state->memory.mbc6.flags, memory->mbcState.mbc6.flashBank1); + state->memory.mbc6.bank1 = memory->currentBank1; + state->memory.mbc6.sramBank1 = memory->currentSramBank1; + break; case GB_MBC7: state->memory.mbc7.state = memory->mbcState.mbc7.state; state->memory.mbc7.eeprom = memory->mbcState.mbc7.eeprom; @@ -802,6 +808,10 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.ntOld1.baseBank = memory->mbcState.ntOld1.baseBank; state->memory.ntOld1.bankCount = memory->mbcState.ntOld1.bankCount; break; + case GB_UNL_NT_NEW: + state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode; + state->memory.ntNew.bank1 = memory->currentBank1; + break; case GB_UNL_BBD: case GB_UNL_HITEK: state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode; @@ -828,9 +838,11 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->wramCurrentBank = state->memory.wramCurrentBank; memory->sramCurrentBank = state->memory.sramCurrentBank; - GBMBCSwitchBank(gb, memory->currentBank); GBMemorySwitchWramBank(memory, memory->wramCurrentBank); - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + if (memory->mbcType != GB_MBC6 && memory->mbcType != GB_UNL_NT_NEW) { + GBMBCSwitchBank(gb, memory->currentBank); + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + } LOAD_16LE(memory->dmaSource, 0, &state->memory.dmaSource); LOAD_16LE(memory->dmaDest, 0, &state->memory.dmaDest); @@ -887,6 +899,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { case GB_MBC3_RTC: LOAD_64LE(memory->rtcLastLatch, 0, &state->memory.rtc.lastLatch); break; + case GB_MBC6: + memory->mbcState.mbc6.flashBank0 = GBSerializedMBC6FlagsGetFlashBank0(state->memory.mbc6.flags); + memory->mbcState.mbc6.flashBank1 = GBSerializedMBC6FlagsGetFlashBank1(state->memory.mbc6.flags); + memory->currentBank1 = state->memory.mbc6.bank1; + memory->currentSramBank1 = state->memory.mbc6.sramBank1; + GBMBCSwitchHalfBank(gb, 0, memory->currentBank); + GBMBCSwitchHalfBank(gb, 1, memory->currentBank1); + GBMBCSwitchSramHalfBank(gb, 0, memory->sramCurrentBank); + GBMBCSwitchSramHalfBank(gb, 1, memory->currentSramBank1); + break; case GB_MBC7: memory->mbcState.mbc7.state = state->memory.mbc7.state; memory->mbcState.mbc7.eeprom = state->memory.mbc7.eeprom; @@ -940,6 +962,16 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { memory->mbcState.ntOld1.bankCount = state->memory.ntOld1.bankCount; GBMBCSwitchBank0(gb, memory->mbcState.ntOld1.baseBank); break; + case GB_UNL_NT_NEW: + memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode; + memory->currentBank1 = state->memory.ntNew.bank1; + if (memory->mbcState.ntNew.splitMode) { + GBMBCSwitchHalfBank(gb, 0, memory->currentBank); + GBMBCSwitchHalfBank(gb, 1, memory->currentBank1); + } else { + GBMBCSwitchBank(gb, memory->currentBank); + } + break; case GB_UNL_BBD: case GB_UNL_HITEK: memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7; From 74358521ab08721589263a7b3d0f87713279c5b4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Oct 2022 16:47:49 -0700 Subject: [PATCH 017/159] macOS: Add category to plist (closes #2691) --- CHANGES | 1 + res/info.plist.in | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 43bfb0f4a..2534b21e9 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Other fixes: - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) + - macOS: Add category to plist (closes mgba.io/i/2691) 0.10.0: (2022-10-11) Features: diff --git a/res/info.plist.in b/res/info.plist.in index f37904dee..eb625299c 100644 --- a/res/info.plist.in +++ b/res/info.plist.in @@ -56,5 +56,7 @@ Viewer + LSApplicationCategoryType + public.app-category.games From 506b9c69be0c50996ccd51482e607c5cf2374edd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Oct 2022 23:01:36 -0700 Subject: [PATCH 018/159] GBA Video: Optimize mode 0 16-color tiles a bit more --- src/gba/renderers/software-mode0.c | 34 ++++++++++++------------------ 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/gba/renderers/software-mode0.c b/src/gba/renderers/software-mode0.c index 209032388..1736929c8 100644 --- a/src/gba/renderers/software-mode0.c +++ b/src/gba/renderers/software-mode0.c @@ -160,27 +160,21 @@ pixel += 8; \ continue; \ } \ - LOAD_32(tileData, charBase, vram); \ + if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ + LOAD_32(tileData, charBase, vram); \ + } else { \ + LOAD_32BE(tileData, charBase, vram); \ + tileData = ((tileData & 0xF0F0F0F0) >> 4) | ((tileData & 0x0F0F0F0F) << 4); \ + } \ if (tileData) { \ - if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \ - } else { \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \ - BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \ - } \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 0); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 1); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 2); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 3); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 4); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 5); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 6); \ + BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, 7); \ } \ pixel += 8; \ } From e370f648018c54f2080d20333bf6a6911b214d6d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 15 Oct 2022 23:41:51 -0700 Subject: [PATCH 019/159] GB MBC: Add NT (old 2) support --- CHANGES | 2 +- README.md | 1 + include/mgba/gb/interface.h | 1 + include/mgba/internal/gb/memory.h | 5 +- include/mgba/internal/gb/serialize.h | 8 +- src/gb/mbc.c | 154 ++++++++++++++++++--------- src/gb/memory.c | 18 ++-- src/gb/overrides.c | 10 ++ 8 files changed, 137 insertions(+), 62 deletions(-) diff --git a/CHANGES b/CHANGES index 2534b21e9..f6fa9915b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ 0.11.0: (Future) Features: - - New unlicensed GB mapper: NT (older type 1) + - New unlicensed GB mapper: NT (older types 1 and 2) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/README.md b/README.md index 2d8837c53..179c0d413 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ The following mappers are fully supported: - MBC5+Rumble - MBC7 - Wisdom Tree (unlicensed) +- NT "old type" 1 and 2 (unlicensed multicart) - NT "new type" (unlicensed MBC5-like) - Pokémon Jade/Diamond (unlicensed) - BBD (unlicensed MBC5-like) diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 5a0433021..35170ea82 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -42,6 +42,7 @@ enum GBMemoryBankControllerType { GB_UNL_WISDOM_TREE = 0x200, GB_UNL_PKJD = 0x203, GB_UNL_NT_OLD_1 = 0x210, + GB_UNL_NT_OLD_2 = 0x211, GB_UNL_NT_NEW = 0x212, GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior GB_UNL_HITEK = 0x221, diff --git a/include/mgba/internal/gb/memory.h b/include/mgba/internal/gb/memory.h index cefa138e9..d3a42a229 100644 --- a/include/mgba/internal/gb/memory.h +++ b/include/mgba/internal/gb/memory.h @@ -237,10 +237,11 @@ struct GBPKJDState { uint8_t reg[2]; }; -struct GBNTOld1State { +struct GBNTOldState { bool swapped; uint8_t baseBank; uint8_t bankCount; + bool rumble; }; struct GBNTNewState { @@ -268,7 +269,7 @@ union GBMBCState { struct GBPocketCamState pocketCam; struct GBTAMA5State tama5; struct GBHuC3State huc3; - struct GBNTOld1State ntOld1; + struct GBNTOldState ntOld; struct GBNTNewState ntNew; struct GBPKJDState pkjd; struct GBBBDState bbd; diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index 97c4f3ba1..f4433c495 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -278,6 +278,10 @@ DECL_BITFIELD(GBSerializedSachenFlags, uint8_t); DECL_BITS(GBSerializedSachenFlags, Transition, 0, 6); DECL_BITS(GBSerializedSachenFlags, Locked, 6, 2); +DECL_BITFIELD(GBSerializedNTOldFlags, uint8_t); +DECL_BIT(GBSerializedNTOldFlags, Swapped, 0); +DECL_BIT(GBSerializedNTOldFlags, Rumble, 1); + DECL_BITFIELD(GBSerializedMemoryFlags, uint16_t); DECL_BIT(GBSerializedMemoryFlags, SramAccess, 0); DECL_BIT(GBSerializedMemoryFlags, RtcAccess, 1); @@ -426,10 +430,10 @@ struct GBSerializedState { uint8_t mode; } huc3; struct { - uint8_t swapped; + GBSerializedNTOldFlags flags; uint8_t baseBank; uint8_t bankCount; - } ntOld1; + } ntOld; struct { uint8_t splitMode; uint8_t bank1; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index ea2123844..18ec32ce6 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -46,6 +46,7 @@ static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); static void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value); +static void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value); static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); @@ -161,7 +162,7 @@ static struct { {"HITK", GB_UNL_HITEK}, {"SNTX", GB_MBC_AUTODETECT}, // TODO {"NTO1", GB_UNL_NT_OLD_1}, - {"NTO2", GB_MBC_AUTODETECT}, // TODO + {"NTO2", GB_UNL_NT_OLD_2}, {"NTN", GB_UNL_NT_NEW}, {"LICH", GB_MBC_AUTODETECT}, // TODO {"LBMC", GB_MBC_AUTODETECT}, // TODO @@ -490,6 +491,9 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_NT_OLD_1: gb->memory.mbcWrite = _GBNTOld1; break; + case GB_UNL_NT_OLD_2: + gb->memory.mbcWrite = _GBNTOld2; + break; case GB_UNL_NT_NEW: gb->memory.mbcWrite = _GBNTNew; break; @@ -1983,9 +1987,64 @@ static const uint8_t _ntOld1Reorder[8] = { 0, 2, 1, 4, 3, 5, 6, 7 }; +void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address & 3) { + case 0: + mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); + break; + case 1: + value &= 0x3F; + mbcState->baseBank = value * 2; + if (mbcState->baseBank) { + GBMBCSwitchBank0(gb, mbcState->baseBank); + GBMBCSwitchBank(gb, mbcState->baseBank + 1); + } + break; + case 2: + if ((value & 0xF0) == 0xE0) { + gb->sramSize = 0x2000; + GBResizeSram(gb, gb->sramSize); + } + switch (value & 0xF) { + case 0x00: + mbcState->bankCount = 32; + break; + case 0x08: + mbcState->bankCount = 16; + break; + case 0xC: + mbcState->bankCount = 8; + break; + case 0xE: + mbcState->bankCount = 4; + break; + case 0xF: + mbcState->bankCount = 2; + break; + default: + mbcState->bankCount = 32; + break; + } + break; + case 3: + mbcState->swapped = !!(value & 0x10); + + bank = memory->currentBank; + if (mbcState->swapped) { + bank = _reorderBits(bank, reorder); + } + GBMBCSwitchBank(gb, bank); + break; + } +} + void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { struct GBMemory* memory = &gb->memory; - struct GBNTOld1State* mbcState = &memory->mbcState.ntOld1; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; int bank = value; switch (address >> 12) { @@ -2006,57 +2065,52 @@ void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { bank &= mbcState->bankCount - 1; } GBMBCSwitchBank(gb, bank + mbcState->baseBank); - return; + break; case 0x5: - switch (address & 3) { - case 0: - mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); - break; - case 1: - value &= 0x3F; - mbcState->baseBank = value * 2; - if (mbcState->baseBank) { - GBMBCSwitchBank0(gb, mbcState->baseBank); - GBMBCSwitchBank(gb, mbcState->baseBank + 1); - } - break; - case 2: - if ((value & 0xF0) == 0xE0) { - gb->sramSize = 0x2000; - GBResizeSram(gb, gb->sramSize); - } - switch (value & 0xF) { - case 0x00: - mbcState->bankCount = 32; - break; - case 0x08: - mbcState->bankCount = 16; - break; - case 0xC: - mbcState->bankCount = 8; - break; - case 0xE: - mbcState->bankCount = 4; - break; - case 0xF: - mbcState->bankCount = 2; - break; - default: - mbcState->bankCount = 32; - break; - } - break; - case 3: - mbcState->swapped = !!(value & 0x10); + _ntOldMulticart(gb, address, value, _ntOld1Reorder); + break; + } +} - bank = memory->currentBank; - if (mbcState->swapped) { - bank = _reorderBits(bank, _ntOld1Reorder); - } - GBMBCSwitchBank(gb, bank); - break; +static const uint8_t _ntOld2Reorder[8] = { + 1, 2, 0, 3, 4, 5, 6, 7 +}; + +void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + if (!bank) { + bank = 1; } - return; + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld2Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + break; + case 0x5: + _ntOldMulticart(gb, address, value, _ntOld2Reorder); + // Fall through + case 0x4: + if (address == 0x5001) { + mbcState->rumble = !!(value & 0x80); + } + + if (mbcState->rumble) { + memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02)); + } + break; } } diff --git a/src/gb/memory.c b/src/gb/memory.c index 5c7441e18..8ac15dbdc 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -804,9 +804,11 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { state->memory.mmm01.bank0 = memory->mbcState.mmm01.currentBank0; break; case GB_UNL_NT_OLD_1: - state->memory.ntOld1.swapped = memory->mbcState.ntOld1.swapped; - state->memory.ntOld1.baseBank = memory->mbcState.ntOld1.baseBank; - state->memory.ntOld1.bankCount = memory->mbcState.ntOld1.bankCount; + case GB_UNL_NT_OLD_2: + state->memory.ntOld.flags = GBSerializedNTOldFlagsSetSwapped(0, memory->mbcState.ntOld.swapped); + state->memory.ntOld.flags = GBSerializedNTOldFlagsSetRumble(state->memory.ntOld.flags, memory->mbcState.ntOld.rumble); + state->memory.ntOld.baseBank = memory->mbcState.ntOld.baseBank; + state->memory.ntOld.bankCount = memory->mbcState.ntOld.bankCount; break; case GB_UNL_NT_NEW: state->memory.ntNew.splitMode = memory->mbcState.ntNew.splitMode; @@ -957,10 +959,12 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { } break; case GB_UNL_NT_OLD_1: - memory->mbcState.ntOld1.swapped = state->memory.ntOld1.swapped; - memory->mbcState.ntOld1.baseBank = state->memory.ntOld1.baseBank; - memory->mbcState.ntOld1.bankCount = state->memory.ntOld1.bankCount; - GBMBCSwitchBank0(gb, memory->mbcState.ntOld1.baseBank); + case GB_UNL_NT_OLD_2: + memory->mbcState.ntOld.swapped = GBSerializedNTOldFlagsGetSwapped(state->memory.ntOld.flags); + memory->mbcState.ntOld.rumble = GBSerializedNTOldFlagsGetRumble(state->memory.ntOld.flags); + memory->mbcState.ntOld.baseBank = state->memory.ntOld.baseBank; + memory->mbcState.ntOld.bankCount = state->memory.ntOld.bankCount; + GBMBCSwitchBank0(gb, memory->mbcState.ntOld.baseBank); break; case GB_UNL_NT_NEW: memory->mbcState.ntNew.splitMode = state->memory.ntNew.splitMode; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index a36edf2a3..ca1a8e9bf 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -672,9 +672,19 @@ static const struct GBCartridgeOverride _overrides[] = { { 0x630ED957, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Gold (non-debug) { 0x5AFF0038, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (debug) { 0xA61856BD, GB_MODEL_AUTODETECT, GB_MBC3_RTC, { 0 } }, // Silver (non-debug) + // Unlicensed bootlegs { 0x30F8F86C, GB_MODEL_AUTODETECT, GB_UNL_PKJD, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) { 0xE1147E75, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // Rockman 8 { 0xEFF88FAA, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_1, { 0 } }, // True Color 25 in 1 (NT-9920) + { 0x811925D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 23 in 1 (CR2011) + { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // 29 in 1 (CR2020) + { 0x5758D6D9, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 24 in 1 Diannao Huamian Xuan Game (CY2060) + { 0x62A8016A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Caise Gedou 29 in 1 Diannao Huamian Xuan Game (CY2061) + { 0x80265A64, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Rockman X4 (Megaman X4) + { 0x805459DE, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Sonic Adventure 8 + { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 + { 0x0B1B808A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Donkey Kong 5 (Alt) + { 0x4650EB9A, GB_MODEL_AUTODETECT, GB_UNL_NT_OLD_2, { 0 } }, // Super Mario Special 3 { 0xB289D95A, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Capcom vs SNK - Millennium Fight 2001 { 0x688D6713, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 02 4 { 0x8931A272, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Digimon 2 From ffda3e1c9b1407567caacf63bdac00a67b2ac75b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Oct 2022 01:28:27 -0700 Subject: [PATCH 020/159] GB MBC: Add Li Cheng support --- README.md | 7 ++++--- include/mgba/gb/interface.h | 3 ++- src/gb/mbc.c | 20 +++++++++++++++++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 179c0d413..ce1fa0bb1 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,7 @@ The following mappers are fully supported: - NT "old type" 1 and 2 (unlicensed multicart) - NT "new type" (unlicensed MBC5-like) - Pokémon Jade/Diamond (unlicensed) -- BBD (unlicensed MBC5-like) -- Hitek (unlicensed MBC5-like) -- Sachen MMC1 +- Sachen MMC1 (unlicensed) The following mappers are partially supported: @@ -70,6 +68,9 @@ The following mappers are partially supported: - HuC-1 (missing IR support) - HuC-3 (missing IR support) - Sachen MMC2 (missing alternate wiring support) +- BBD (missing logo switching) +- Hitek (missing logo switching) +- Li Cheng (missing logo switching) ### Planned features diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 35170ea82..7c23bb8dd 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -44,8 +44,9 @@ enum GBMemoryBankControllerType { GB_UNL_NT_OLD_1 = 0x210, GB_UNL_NT_OLD_2 = 0x211, GB_UNL_NT_NEW = 0x212, - GB_UNL_BBD = 0x220, // Also used as a mask for MBCs that need special read behavior + GB_UNL_BBD = 0x220, GB_UNL_HITEK = 0x221, + GB_UNL_LI_CHENG = 0x222, GB_UNL_SACHEN_MMC1 = 0x230, GB_UNL_SACHEN_MMC2 = 0x231, }; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 18ec32ce6..0ce1f4590 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -50,6 +50,7 @@ static void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value); static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); +static void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value); static void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); @@ -164,7 +165,7 @@ static struct { {"NTO1", GB_UNL_NT_OLD_1}, {"NTO2", GB_UNL_NT_OLD_2}, {"NTN", GB_UNL_NT_NEW}, - {"LICH", GB_MBC_AUTODETECT}, // TODO + {"LICH", GB_UNL_LI_CHENG}, {"LBMC", GB_MBC_AUTODETECT}, // TODO {"LIBA", GB_MBC_AUTODETECT}, // TODO {"PKJD", GB_UNL_PKJD}, @@ -257,6 +258,13 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t if (mem[0x7FFF] != 0x01) { // Make sure we're not using a "fixed" version return GB_UNL_BBD; } + break; + case 0x20d092e2: + case 0xd2b57657: + if (cart->type == 0x01) { // Make sure we're not using a "fixed" version + return GB_UNL_LI_CHENG; + } + break; } if (mem[0x104] == 0xCE && mem[0x144] == 0xED && mem[0x114] == 0x66) { @@ -513,6 +521,9 @@ void GBMBCInit(struct GB* gb) { gb->memory.mbcState.bbd.bankSwapMode = 7; gb->memory.mbcReadBank1 = true; break; + case GB_UNL_LI_CHENG: + gb->memory.mbcWrite = _GBLiCheng; + break; case GB_UNL_SACHEN_MMC1: gb->memory.mbcWrite = _GBSachen; gb->memory.mbcRead = _GBSachenMMC1Read; @@ -2242,6 +2253,13 @@ uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { } } +void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { + if (address > 0x2100 && address < 0x3000) { + return; + } + _GBMBC5(gb, address, value); +} + void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { struct GBSachenState* state = &gb->memory.mbcState.sachen; uint8_t bank = value; From 5415cd72e2c32156cc18da88231a53ed94298a9c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Oct 2022 01:30:24 -0700 Subject: [PATCH 021/159] Qt: Update mapper list --- CHANGES | 2 +- src/platform/qt/GameBoy.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index f6fa9915b..3adee4f36 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ 0.11.0: (Future) Features: - - New unlicensed GB mapper: NT (older types 1 and 2) + - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index 2dfa3c9e7..0c1ec8a9b 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -36,10 +36,13 @@ static const QList s_mbcList{ GB_UNL_WISDOM_TREE, GB_UNL_PKJD, GB_UNL_NT_OLD_1, + GB_UNL_NT_OLD_2, GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, + GB_UNL_LI_CHENG, GB_UNL_SACHEN_MMC1, + GB_UNL_SACHEN_MMC2, }; static QMap s_gbModelNames; @@ -89,10 +92,12 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_TAMA5] = tr("TAMA5"); s_mbcNames[GB_UNL_WISDOM_TREE] = tr("Wisdom Tree"); s_mbcNames[GB_UNL_NT_OLD_1] = tr("NT (old 1)"); + s_mbcNames[GB_UNL_NT_OLD_2] = tr("NT (old 2)"); s_mbcNames[GB_UNL_NT_NEW] = tr("NT (new)"); s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); s_mbcNames[GB_UNL_HITEK] = tr("Hitek"); + s_mbcNames[GB_UNL_LI_CHENG] = tr("Li Cheng"); s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)"); s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)"); } From 42f7876731db292292af391732a40fe6bd77b3ea Mon Sep 17 00:00:00 2001 From: bigfarts Date: Wed, 10 Aug 2022 19:59:02 -0700 Subject: [PATCH 022/159] Add range watchpoints. These are accessible via the following new CLI debugger commands: - rw: watchr minAddr maxAddr [cond] - r: watchr/r minAddr maxAddr [cond] - w: watchr/w minAddr maxAddr [cond] - c: watchr/c minAddr maxAddr [cond] This also makes all watchpoints range watchpoints under the hood. Preliminary benchmark results: Time taken to run 10000 frames of Megaman Battle Network 1 (U) with a write watchpoint set at 0x02000000 in milliseconds, 10 runs each: control (no watchpoint): [4184, 4185, 4197, 4207, 4220, 4178, 4304, 4226, 4234, 4292] mean = 4223, stdev = 43.95 old (single address watchpoint): [4743, 4685, 4679, 4670, 4782, 4704, 4698, 4875, 4746, 4718] mean = 4730, stdev = 61.67 new (range watchpoint): [4683, 4691, 4693, 4706, 4782, 4674, 4746, 4768, 4770, 4776] mean = 4728, stdev = 43.36 --- CHANGES | 1 + include/mgba/debugger/debugger.h | 3 +- src/arm/debugger/memory-debugger.c | 7 ++- src/debugger/cli-debugger.c | 85 ++++++++++++++++++++++++++++- src/debugger/gdb-stub.c | 10 ++-- src/sm83/debugger/memory-debugger.c | 2 +- 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/CHANGES b/CHANGES index 3adee4f36..13d50691a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,7 @@ 0.11.0: (Future) Features: - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng + - Debugger: Add range watchpoints Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index fc9ac0f65..b460623e3 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -88,8 +88,9 @@ struct mBreakpoint { struct mWatchpoint { ssize_t id; - uint32_t address; int segment; + uint32_t minAddress; + uint32_t maxAddress; enum mWatchpointType type; struct ParseTree* condition; }; diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c index 68b4b4175..85c9995ae 100644 --- a/src/arm/debugger/memory-debugger.c +++ b/src/arm/debugger/memory-debugger.c @@ -92,12 +92,13 @@ CREATE_MULTIPLE_WATCHPOINT_SHIM(storeMultiple, WATCHPOINT_WRITE) CREATE_SHIM(setActiveRegion, void, (struct ARMCore* cpu, uint32_t address), address) static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, struct mDebuggerEntryInfo* info, enum mWatchpointType type, uint32_t newValue, int width) { - --width; struct mWatchpoint* watchpoint; size_t i; + uint32_t minAddress = address & ~(width - 1); + uint32_t maxAddress = minAddress + width; for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i); - if (!((watchpoint->address ^ address) & ~width) && watchpoint->type & type) { + if (watchpoint->type & type && watchpoint->minAddress < maxAddress && minAddress < watchpoint->maxAddress) { if (watchpoint->condition) { int32_t value; int segment; @@ -107,7 +108,7 @@ static bool _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, st } uint32_t oldValue; - switch (width + 1) { + switch (width) { case 1: oldValue = debugger->originalMemory.load8(debugger->cpu, address, 0); break; diff --git a/src/debugger/cli-debugger.c b/src/debugger/cli-debugger.c index 7c827905a..06ed21d76 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -60,6 +60,10 @@ static void _setReadWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*) static void _setReadWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _setWriteWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _setWriteChangedWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setReadWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setReadRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setWriteRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); +static void _setWriteChangedRangeWatchpoint(struct CLIDebugger*, struct CLIDebugVector*); static void _listWatchpoints(struct CLIDebugger*, struct CLIDebugVector*); static void _trace(struct CLIDebugger*, struct CLIDebugVector*); static void _writeByte(struct CLIDebugger*, struct CLIDebugVector*); @@ -114,6 +118,10 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = { { "watch/c", _setWriteChangedWatchpoint, "Is", "Set a change watchpoint" }, { "watch/r", _setReadWatchpoint, "Is", "Set a read watchpoint" }, { "watch/w", _setWriteWatchpoint, "Is", "Set a write watchpoint" }, + { "watch-range", _setReadWriteRangeWatchpoint, "IIs", "Set a range watchpoint" }, + { "watch-range/c", _setWriteChangedRangeWatchpoint, "IIs", "Set a change range watchpoint" }, + { "watch-range/r", _setReadRangeWatchpoint, "IIs", "Set a read range watchpoint" }, + { "watch-range/w", _setWriteRangeWatchpoint, "IIs", "Set a write range watchpoint" }, { "x/1", _dumpByte, "Ii", "Examine bytes at a specified offset" }, { "x/2", _dumpHalfword, "Ii", "Examine halfwords at a specified offset" }, { "x/4", _dumpWord, "Ii", "Examine words at a specified offset" }, @@ -146,6 +154,10 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = { { "p/x", "print/x" }, { "q", "quit" }, { "w", "watch" }, + { "watchr", "watch-range" }, + { "watchr/c", "watch-range/c" }, + { "watchr/r", "watch-range/r" }, + { "watchr/w", "watch-range/w" }, { ".", "source" }, { 0, 0 } }; @@ -647,8 +659,9 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* return; } struct mWatchpoint watchpoint = { - .address = dv->intValue, .segment = dv->segmentValue, + .minAddress = dv->intValue, + .maxAddress = dv->intValue + 1, .type = type }; if (dv->next && dv->next->type == CLIDV_CHAR_TYPE) { @@ -666,6 +679,48 @@ static void _setWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* } } +static void _setRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv, enum mWatchpointType type) { + if (!dv || dv->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (!dv->next || dv->next->type != CLIDV_INT_TYPE) { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); + return; + } + if (!debugger->d.platform->setWatchpoint) { + debugger->backend->printf(debugger->backend, "Watchpoints are not supported by this platform.\n"); + return; + } + if (dv->intValue >= dv->next->intValue) { + debugger->backend->printf(debugger->backend, "Range watchpoint end is before start. Note that the end of the range is not included.\n"); + return; + } + if (dv->segmentValue != dv->next->segmentValue) { + debugger->backend->printf(debugger->backend, "Range watchpoint does not start and end in the same segment.\n"); + return; + } + struct mWatchpoint watchpoint = { + .segment = dv->segmentValue, + .minAddress = dv->intValue, + .maxAddress = dv->next->intValue, + .type = type + }; + if (dv->next->next && dv->next->next->type == CLIDV_CHAR_TYPE) { + struct ParseTree* tree = _parseTree((const char*[]) { dv->next->next->charValue, NULL }); + if (tree) { + watchpoint.condition = tree; + } else { + debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS); + return; + } + } + ssize_t id = debugger->d.platform->setWatchpoint(debugger->d.platform, &watchpoint); + if (id > 0) { + debugger->backend->printf(debugger->backend, INFO_WATCHPOINT_ADDED, id); + } +} + static void _setReadWriteWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { _setWatchpoint(debugger, dv, WATCHPOINT_RW); } @@ -682,6 +737,22 @@ static void _setWriteChangedWatchpoint(struct CLIDebugger* debugger, struct CLID _setWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE); } +static void _setReadWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_RW); +} + +static void _setReadRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_READ); +} + +static void _setWriteRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE); +} + +static void _setWriteChangedRangeWatchpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { + _setRangeWatchpoint(debugger, dv, WATCHPOINT_WRITE_CHANGE); +} + static void _clearBreakpoint(struct CLIDebugger* debugger, struct CLIDebugVector* dv) { if (!dv || dv->type != CLIDV_INT_TYPE) { debugger->backend->printf(debugger->backend, "%s\n", ERROR_MISSING_ARGS); @@ -717,9 +788,17 @@ static void _listWatchpoints(struct CLIDebugger* debugger, struct CLIDebugVector for (i = 0; i < mWatchpointListSize(&watchpoints); ++i) { struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, i); if (watchpoint->segment >= 0) { - debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->address); + if (watchpoint->maxAddress == watchpoint->minAddress + 1) { + debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress); + } else { + debugger->backend->printf(debugger->backend, "%" PRIz "i: %02X:%X-%X\n", watchpoint->id, watchpoint->segment, watchpoint->minAddress, watchpoint->maxAddress); + } } else { - debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->address); + if (watchpoint->maxAddress == watchpoint->minAddress + 1) { + debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X\n", watchpoint->id, watchpoint->minAddress); + } else { + debugger->backend->printf(debugger->backend, "%" PRIz "i: 0x%X-0x%X\n", watchpoint->id, watchpoint->minAddress, watchpoint->maxAddress); + } } } mWatchpointListDeinit(&watchpoints); diff --git a/src/debugger/gdb-stub.c b/src/debugger/gdb-stub.c index dfc655054..ca8c0d1ec 100644 --- a/src/debugger/gdb-stub.c +++ b/src/debugger/gdb-stub.c @@ -532,7 +532,7 @@ static void _processQReadCommand(struct GDBStub* stub, const char* message) { } else if (!strncmp("Xfer:memory-map:read::", message, 22)) { if (strlen(stub->memoryMapXml) == 0) { _generateMemoryMapXml(stub, stub->memoryMapXml); - } + } _processQXferCommand(stub, message + 22, stub->memoryMapXml); } else if (!strncmp("Supported:", message, 10)) { _processQSupportedCommand(stub, message + 10); @@ -575,7 +575,8 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) { .type = BREAKPOINT_HARDWARE }; struct mWatchpoint watchpoint = { - .address = address + .minAddress = address, + .maxAddress = address + 1 }; switch (message[0]) { @@ -631,10 +632,11 @@ static void _clearBreakpoint(struct GDBStub* stub, const char* message) { mWatchpointListInit(&watchpoints, 0); stub->d.platform->listWatchpoints(stub->d.platform, &watchpoints); for (index = 0; index < mWatchpointListSize(&watchpoints); ++index) { - if (mWatchpointListGetPointer(&watchpoints, index)->address != address) { + struct mWatchpoint* watchpoint = mWatchpointListGetPointer(&watchpoints, index); + if (address >= watchpoint->minAddress && address < watchpoint->maxAddress) { continue; } - stub->d.platform->clearBreakpoint(stub->d.platform, mWatchpointListGetPointer(&watchpoints, index)->id); + stub->d.platform->clearBreakpoint(stub->d.platform, watchpoint->id); } mWatchpointListDeinit(&watchpoints); break; diff --git a/src/sm83/debugger/memory-debugger.c b/src/sm83/debugger/memory-debugger.c index e0a48553f..142ab92c7 100644 --- a/src/sm83/debugger/memory-debugger.c +++ b/src/sm83/debugger/memory-debugger.c @@ -47,7 +47,7 @@ static bool _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, s size_t i; for (i = 0; i < mWatchpointListSize(&debugger->watchpoints); ++i) { watchpoint = mWatchpointListGetPointer(&debugger->watchpoints, i); - if (watchpoint->address == address && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address)) && watchpoint->type & type) { + if (watchpoint->type & type && address >= watchpoint->minAddress && address < watchpoint->maxAddress && (watchpoint->segment < 0 || watchpoint->segment == debugger->originalMemory.currentSegment(debugger->cpu, address))) { if (watchpoint->condition) { int32_t value; int segment; From ca0baae82150c63f85da4a84b529cca908d0becf Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Oct 2022 02:13:09 -0700 Subject: [PATCH 023/159] Debugger: Add more watch-range aliases --- 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 06ed21d76..12cd7af18 100644 --- a/src/debugger/cli-debugger.c +++ b/src/debugger/cli-debugger.c @@ -155,9 +155,13 @@ static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = { { "q", "quit" }, { "w", "watch" }, { "watchr", "watch-range" }, + { "wr", "watch-range" }, { "watchr/c", "watch-range/c" }, + { "wr/c", "watch-range/c" }, { "watchr/r", "watch-range/r" }, + { "wr/r", "watch-range/r" }, { "watchr/w", "watch-range/w" }, + { "wr/w", "watch-range/w" }, { ".", "source" }, { 0, 0 } }; From 1cc32c0785b0838495658229c5143e3e5772330d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Oct 2022 03:20:34 -0700 Subject: [PATCH 024/159] GB MBC: Split out MBC implementations into files --- src/gb/CMakeLists.txt | 6 + src/gb/mbc.c | 1909 +------------------------------------- src/gb/mbc/huc-3.c | 218 +++++ src/gb/mbc/licensed.c | 84 ++ src/gb/mbc/mbc-private.h | 61 ++ src/gb/mbc/mbc.c | 565 +++++++++++ src/gb/mbc/pocket-cam.c | 149 +++ src/gb/mbc/tama5.c | 433 +++++++++ src/gb/mbc/unlicensed.c | 469 ++++++++++ 9 files changed, 1989 insertions(+), 1905 deletions(-) create mode 100644 src/gb/mbc/huc-3.c create mode 100644 src/gb/mbc/licensed.c create mode 100644 src/gb/mbc/mbc-private.h create mode 100644 src/gb/mbc/mbc.c create mode 100644 src/gb/mbc/pocket-cam.c create mode 100644 src/gb/mbc/tama5.c create mode 100644 src/gb/mbc/unlicensed.c diff --git a/src/gb/CMakeLists.txt b/src/gb/CMakeLists.txt index 74e7800de..a02ab370a 100644 --- a/src/gb/CMakeLists.txt +++ b/src/gb/CMakeLists.txt @@ -7,6 +7,12 @@ set(SOURCE_FILES input.c io.c mbc.c + mbc/huc-3.c + mbc/licensed.c + mbc/mbc.c + mbc/pocket-cam.c + mbc/tama5.c + mbc/unlicensed.c memory.c overrides.c serialize.c diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 0ce1f4590..f15cba519 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -3,12 +3,9 @@ * 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 "gb/mbc/mbc-private.h" -#include -#include #include -#include #include #include #include @@ -17,12 +14,6 @@ const uint32_t GB_LOGO_HASH = 0x46195417; mLOG_DEFINE_CATEGORY(GB_MBC, "GB MBC", "gb.mbc"); -static const uint8_t _tama6RTCMask[32] = { - //0 1 2 3 4 5 6 7 8 9 A B C D E F - 0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0, - 0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, -}; - static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { UNUSED(address); UNUSED(value); @@ -32,45 +23,6 @@ static void _GBMBCNone(struct GB* gb, uint16_t address, uint8_t value) { } } -static void _GBMBC1(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC2(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC3(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC5(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC6(struct GB*, uint16_t address, uint8_t value); -static void _GBMBC7(struct GB*, uint16_t address, uint8_t value); -static void _GBMMM01(struct GB*, uint16_t address, uint8_t value); -static void _GBHuC1(struct GB*, uint16_t address, uint8_t value); -static void _GBHuC3(struct GB*, uint16_t address, uint8_t value); -static void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); -static void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); -static void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); -static void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); -static void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value); -static void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value); -static void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); -static void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); -static void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); -static void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value); -static void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); - -static uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); -static uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address); -static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); -static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value); - -static uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address); -static uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); -static uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); -static uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); -static uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); -static uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); -static uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address); - -static uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); -static void _GBPocketCamCapture(struct GBMemory*); - -static void _GBMBC6MapChip(struct GB*, int half, uint8_t value); - void GBMBCSwitchBank(struct GB* gb, int bank) { size_t bankStart = bank * GB_SIZE_CART_BANK0; if (bankStart + GB_SIZE_CART_BANK0 > gb->memory.romSize) { @@ -601,1765 +553,7 @@ void GBMBCReset(struct GB* gb) { gb->memory.sramBank = gb->memory.sram; } -static void _latchRtc(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) { - time_t t; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - time_t currentLatch = t; - t -= *rtcLastLatch; - *rtcLastLatch = currentLatch; - - int64_t diff; - diff = rtcRegs[0] + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - rtcRegs[0] = diff % 60; - t /= 60; - t += diff / 60; - - diff = rtcRegs[1] + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - rtcRegs[1] = diff % 60; - t /= 60; - t += diff / 60; - - diff = rtcRegs[2] + t % 24; - if (diff < 0) { - diff += 24; - t -= 24; - } - rtcRegs[2] = diff % 24; - t /= 24; - t += diff / 24; - - diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF); - rtcRegs[3] = diff; - rtcRegs[4] &= 0xFE; - rtcRegs[4] |= (diff >> 8) & 1; - if (diff & 0x200) { - rtcRegs[4] |= 0x80; - } -} - -static void _GBMBC1Update(struct GB* gb) { - struct GBMBC1State* state = &gb->memory.mbcState.mbc1; - int bank = state->bankLo; - bank &= (1 << state->multicartStride) - 1; - bank |= state->bankHi << state->multicartStride; - if (state->mode) { - GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride); - GBMBCSwitchSramBank(gb, state->bankHi & 3); - } else { - GBMBCSwitchBank0(gb, 0); - GBMBCSwitchSramBank(gb, 0); - } - if (!(state->bankLo & 0x1F)) { - ++state->bankLo; - ++bank; - } - GBMBCSwitchBank(gb, bank); -} - -void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value & 0x1F; - switch (address >> 13) { - case 0x0: - switch (value & 0xF) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); - break; - } - break; - case 0x1: - memory->mbcState.mbc1.bankLo = bank; - _GBMBC1Update(gb); - break; - case 0x2: - bank &= 3; - memory->mbcState.mbc1.bankHi = bank; - _GBMBC1Update(gb); - break; - case 0x3: - memory->mbcState.mbc1.mode = value & 1; - _GBMBC1Update(gb); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int shift = (address & 1) * 4; - int bank = value & 0xF; - switch ((address & 0xC100) >> 8) { - case 0x0: - switch (value & 0x0F) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value); - break; - } - break; - case 0x1: - if (!bank) { - ++bank; - } - GBMBCSwitchBank(gb, bank); - break; - case 0x80: - case 0x81: - case 0x82: - case 0x83: - if (!memory->sramAccess) { - return; - } - address &= 0x1FF; - memory->sramBank[(address >> 1)] &= 0xF0 >> shift; - memory->sramBank[(address >> 1)] |= (value & 0xF) << shift; - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); - break; - } -} - -static uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) { - if (!memory->sramAccess) { - return 0xFF; - } - address &= 0x1FF; - int shift = (address & 1) * 4; - return (memory->sramBank[(address >> 1)] >> shift) | 0xF0; -} - -void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value; - switch (address >> 13) { - case 0x0: - switch (value & 0xF) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value); - break; - } - break; - case 0x1: - if (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) { - bank &= 0x7F; - } - if (!bank) { - ++bank; - } - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - bank &= 0xF; - if (bank < 8) { - GBMBCSwitchSramBank(gb, value); - memory->rtcAccess = false; - } else if (bank <= 0xC) { - memory->activeRtcReg = bank - 8; - memory->rtcAccess = true; - } - break; - case 0x3: - if (memory->rtcLatched && value == 0) { - memory->rtcLatched = false; - } else if (!memory->rtcLatched && value == 1) { - _latchRtc(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch); - memory->rtcLatched = true; - } - break; - } -} - -void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank; - switch (address >> 12) { - case 0x0: - case 0x1: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value); - break; - } - break; - case 0x2: - bank = (memory->currentBank & 0x100) | value; - GBMBCSwitchBank(gb, bank); - break; - case 0x3: - bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); - GBMBCSwitchBank(gb, bank); - break; - case 0x4: - case 0x5: - if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { - memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); - value &= ~8; - } - GBMBCSwitchSramBank(gb, value & 0xF); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value; - switch (address >> 10) { - case 0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value); - break; - } - break; - case 0x1: - GBMBCSwitchSramHalfBank(gb, 0, bank); - break; - case 0x2: - GBMBCSwitchSramHalfBank(gb, 1, bank); - break; - case 0x3: - mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value); - break; - case 0x4: - mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value); - break; - case 0x8: - case 0x9: - GBMBCSwitchHalfBank(gb, 0, bank); - break; - case 0xA: - case 0xB: - _GBMBC6MapChip(gb, 0, value); - break; - case 0xC: - case 0xD: - GBMBCSwitchHalfBank(gb, 1, bank); - break; - case 0xE: - case 0xF: - _GBMBC6MapChip(gb, 1, value); - break; - case 0x28: - case 0x29: - case 0x2A: - case 0x2B: - if (memory->sramAccess) { - memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - } - break; - case 0x2C: - case 0x2D: - case 0x2E: - case 0x2F: - if (memory->sramAccess) { - memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; - } - break; - default: - mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { - if (!memory->sramAccess) { - return 0xFF; - } - switch (address >> 12) { - case 0xA: - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; - case 0xB: - return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; - } - return 0xFF; -} - -static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { - if (!half) { - gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08); - GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); - } else { - gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); - GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1); - } -} - -void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { - int bank = value & 0x7F; - switch (address >> 13) { - case 0x0: - switch (value) { - default: - case 0: - gb->memory.mbcState.mbc7.access = 0; - break; - case 0xA: - gb->memory.mbcState.mbc7.access |= 1; - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - if (value == 0x40) { - gb->memory.mbcState.mbc7.access |= 2; - } else { - gb->memory.mbcState.mbc7.access &= ~2; - } - break; - case 0x5: - _GBMBC7Write(&gb->memory, address, value); - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - if (mbc7->access != 3) { - return 0xFF; - } - switch (address & 0xF0) { - case 0x20: - if (memory->rotation && memory->rotation->readTiltX) { - int32_t x = -memory->rotation->readTiltX(memory->rotation); - x >>= 21; - x += 0x81D0; - return x; - } - return 0xFF; - case 0x30: - if (memory->rotation && memory->rotation->readTiltX) { - int32_t x = -memory->rotation->readTiltX(memory->rotation); - x >>= 21; - x += 0x81D0; - return x >> 8; - } - return 7; - case 0x40: - if (memory->rotation && memory->rotation->readTiltY) { - int32_t y = -memory->rotation->readTiltY(memory->rotation); - y >>= 21; - y += 0x81D0; - return y; - } - return 0xFF; - case 0x50: - if (memory->rotation && memory->rotation->readTiltY) { - int32_t y = -memory->rotation->readTiltY(memory->rotation); - y >>= 21; - y += 0x81D0; - return y >> 8; - } - return 7; - case 0x60: - return 0; - case 0x80: - return mbc7->eeprom; - default: - return 0xFF; - } -} - -static void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { - struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; - if (mbc7->access != 3) { - return; - } - switch (address & 0xF0) { - case 0x00: - mbc7->latch = (value & 0x55) == 0x55; - return; - case 0x10: - mbc7->latch |= (value & 0xAA); - if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) { - memory->rotation->sample(memory->rotation); - } - mbc7->latch = 0; - return; - default: - mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value); - return; - case 0x80: - break; - } - GBMBC7Field old = memory->mbcState.mbc7.eeprom; - value = GBMBC7FieldFillDO(value); // Hi-Z - if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { - mbc7->state = GBMBC7_STATE_IDLE; - } - if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) { - if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) { - mbc7->sr <<= 1; - mbc7->sr |= GBMBC7FieldGetDI(value); - ++mbc7->srBits; - } - switch (mbc7->state) { - case GBMBC7_STATE_IDLE: - if (GBMBC7FieldIsDI(value)) { - mbc7->state = GBMBC7_STATE_READ_COMMAND; - mbc7->srBits = 0; - mbc7->sr = 0; - } - break; - case GBMBC7_STATE_READ_COMMAND: - if (mbc7->srBits == 10) { - mbc7->state = 0x10 | (mbc7->sr >> 6); - if (mbc7->state & 0xC) { - mbc7->state &= ~0x3; - } - mbc7->srBits = 0; - mbc7->address = mbc7->sr & 0x7F; - } - break; - case GBMBC7_STATE_DO: - value = GBMBC7FieldSetDO(value, mbc7->sr >> 15); - mbc7->sr <<= 1; - --mbc7->srBits; - if (!mbc7->srBits) { - mbc7->state = GBMBC7_STATE_IDLE; - } - break; - default: - break; - } - switch (mbc7->state) { - case GBMBC7_STATE_EEPROM_EWEN: - mbc7->writable = true; - mbc7->state = GBMBC7_STATE_IDLE; - break; - case GBMBC7_STATE_EEPROM_EWDS: - mbc7->writable = false; - mbc7->state = GBMBC7_STATE_IDLE; - break; - case GBMBC7_STATE_EEPROM_WRITE: - if (mbc7->srBits == 16) { - if (mbc7->writable) { - memory->sram[mbc7->address * 2] = mbc7->sr >> 8; - memory->sram[mbc7->address * 2 + 1] = mbc7->sr; - } - mbc7->state = GBMBC7_STATE_IDLE; - } - break; - case GBMBC7_STATE_EEPROM_ERASE: - if (mbc7->writable) { - memory->sram[mbc7->address * 2] = 0xFF; - memory->sram[mbc7->address * 2 + 1] = 0xFF; - } - mbc7->state = GBMBC7_STATE_IDLE; - break; - case GBMBC7_STATE_EEPROM_READ: - mbc7->srBits = 16; - mbc7->sr = memory->sram[mbc7->address * 2] << 8; - mbc7->sr |= memory->sram[mbc7->address * 2 + 1]; - mbc7->state = GBMBC7_STATE_DO; - value = GBMBC7FieldClearDO(value); - break; - case GBMBC7_STATE_EEPROM_WRAL: - if (mbc7->srBits == 16) { - if (mbc7->writable) { - int i; - for (i = 0; i < 128; ++i) { - memory->sram[i * 2] = mbc7->sr >> 8; - memory->sram[i * 2 + 1] = mbc7->sr; - } - } - mbc7->state = GBMBC7_STATE_IDLE; - } - break; - case GBMBC7_STATE_EEPROM_ERAL: - if (mbc7->writable) { - int i; - for (i = 0; i < 128; ++i) { - memory->sram[i * 2] = 0xFF; - memory->sram[i * 2 + 1] = 0xFF; - } - } - mbc7->state = GBMBC7_STATE_IDLE; - break; - default: - break; - } - } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) { - value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); - } - mbc7->eeprom = value; -} - -void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - if (!memory->mbcState.mmm01.locked) { - switch (address >> 13) { - case 0x0: - memory->mbcState.mmm01.locked = true; - GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); - break; - case 0x1: - memory->mbcState.mmm01.currentBank0 &= ~0x7F; - memory->mbcState.mmm01.currentBank0 |= value & 0x7F; - break; - case 0x2: - memory->mbcState.mmm01.currentBank0 &= ~0x180; - memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3; - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); - break; - } - return; - } - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - memory->sramAccess = false; - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0); - break; - case 0x2: - GBMBCSwitchSramBank(gb, value); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value & 0x3F; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xE: - memory->sramAccess = false; - break; - default: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - GBMBCSwitchSramBank(gb, value); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value); - break; - } -} - -static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) { - time_t t; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - t -= *rtcLastLatch; - t /= 60; - - if (!t) { - return; - } - *rtcLastLatch += t * 60; - - int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8; - minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4; - minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO]; - minutes += t % 1440; - t /= 1440; - if (minutes >= 1440) { - minutes -= 1440; - ++t; - } else if (minutes < 0) { - minutes += 1440; - --t; - } - huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF; - huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF; - huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF; - - int days = huc3Regs[GBHUC3_RTC_DAYS_LO]; - days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4; - days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8; - - days += t; - - huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF; - huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF; - huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF; -} - -static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) { - size_t c; - switch (state->value & 0x70) { - case 0x10: - if ((state->index & 0xF8) == 0x10) { - _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); - } - state->value &= 0xF0; - state->value |= state->registers[state->index] & 0xF; - mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF); - if (state->value & 0x10) { - ++state->index; - } - break; - case 0x30: - mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF); - state->registers[state->index] = state->value & 0xF; - if (state->value & 0x10) { - ++state->index; - } - break; - case 0x40: - state->index &= 0xF0; - state->index |= (state->value) & 0xF; - mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index); - break; - case 0x50: - state->index &= 0x0F; - state->index |= ((state->value) & 0xF) << 4; - mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index); - break; - case 0x60: - switch (state->value & 0xF) { - case GBHUC3_CMD_LATCH: - _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); - memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6); - mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch"); - break; - case GBHUC3_CMD_SET_RTC: - memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6); - mLOG(GB_MBC, DEBUG, "HuC-3 set RTC"); - break; - case GBHUC3_CMD_RO: - mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode"); - break; - case GBHUC3_CMD_TONE: - if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) { - for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) { - struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c); - if (callbacks->alarm) { - callbacks->alarm(callbacks->context); - } - } - mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3); - } - break; - default: - mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF); - break; - } - state->value = 0xE1; - break; - default: - mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value); - break; - } -} - -void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - struct GBHuC3State* state = &memory->mbcState.huc3; - int bank = value & 0x7F; - if (address & 0x1FFF) { - mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); - } - - switch (address >> 13) { - case 0x0: - switch (value) { - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - memory->sramAccess = false; - break; - } - state->mode = value; - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - GBMBCSwitchSramBank(gb, bank); - break; - case 0x5: - switch (state->mode) { - case GBHUC3_MODE_IN: - state->value = 0x80 | value; - break; - case GBHUC3_MODE_COMMIT: - _huc3Commit(gb, state); - break; - default: - mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value); - } - break; - default: - // TODO - mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) { - struct GBHuC3State* state = &memory->mbcState.huc3; - switch (state->mode) { - case GBHUC3_MODE_SRAM_RO: - case GBHUC3_MODE_SRAM_RW: - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; - case GBHUC3_MODE_IN: - case GBHUC3_MODE_OUT: - return 0x80 | state->value; - default: - return 0xFF; - } -} - -void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - int bank = value & 0x3F; - switch (address >> 13) { - case 0x0: - switch (value) { - case 0: - memory->sramAccess = false; - break; - case 0xA: - memory->sramAccess = true; - GBMBCSwitchSramBank(gb, memory->sramCurrentBank); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value); - break; - } - break; - case 0x1: - GBMBCSwitchBank(gb, bank); - break; - case 0x2: - if (value < 0x10) { - GBMBCSwitchSramBank(gb, value); - memory->mbcState.pocketCam.registersActive = false; - memory->directSramAccess = true; - } else { - memory->mbcState.pocketCam.registersActive = true; - memory->directSramAccess = false; - } - break; - case 0x5: - if (!memory->mbcState.pocketCam.registersActive) { - break; - } - address &= 0x7F; - if (address == 0 && value & 1) { - value &= 6; // TODO: Timing - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - _GBPocketCamCapture(memory); - } - if (address < sizeof(memory->mbcState.pocketCam.registers)) { - memory->mbcState.pocketCam.registers[address] = value; - } - break; - default: - mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value); - break; - } -} - -uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) { - if (memory->mbcState.pocketCam.registersActive) { - if ((address & 0x7F) == 0) { - return memory->mbcState.pocketCam.registers[0]; - } - return 0; - } - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; -} - -void _GBPocketCamCapture(struct GBMemory* memory) { - if (!memory->cam) { - return; - } - const void* image = NULL; - size_t stride; - enum mColorFormat format; - memory->cam->requestImage(memory->cam, &image, &stride, &format); - if (!image) { - return; - } - memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4); - struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam; - size_t x, y; - for (y = 0; y < GBCAM_HEIGHT; ++y) { - for (x = 0; x < GBCAM_WIDTH; ++x) { - uint32_t gray; - uint32_t color; - switch (format) { - case mCOLOR_XBGR8: - case mCOLOR_XRGB8: - case mCOLOR_ARGB8: - case mCOLOR_ABGR8: - color = ((const uint32_t*) image)[y * stride + x]; - gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF); - break; - case mCOLOR_BGRX8: - case mCOLOR_RGBX8: - case mCOLOR_RGBA8: - case mCOLOR_BGRA8: - color = ((const uint32_t*) image)[y * stride + x]; - gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF); - break; - case mCOLOR_BGR5: - case mCOLOR_RGB5: - case mCOLOR_ARGB5: - case mCOLOR_ABGR5: - color = ((const uint16_t*) image)[y * stride + x]; - gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8); - break; - case mCOLOR_BGR565: - case mCOLOR_RGB565: - color = ((const uint16_t*) image)[y * stride + x]; - gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8); - break; - case mCOLOR_BGRA5: - case mCOLOR_RGBA5: - color = ((const uint16_t*) image)[y * stride + x]; - gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8); - break; - default: - mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format); - return; - } - uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]); - gray = (gray + 1) * exposure / 0x300; - // TODO: Additional processing - int matrixEntry = 3 * ((x & 3) + 4 * (y & 3)); - if (gray < pocketCam->registers[matrixEntry + 6]) { - gray = 0x101; - } else if (gray < pocketCam->registers[matrixEntry + 7]) { - gray = 0x100; - } else if (gray < pocketCam->registers[matrixEntry + 8]) { - gray = 0x001; - } else { - gray = 0; - } - int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20; - uint16_t existing; - LOAD_16LE(existing, coord + 0x100, memory->sram); - existing |= gray << (7 - (x & 7)); - STORE_16LE(existing, coord + 0x100, memory->sram); - } - } -} - -static const int _daysToMonth[] = { - [ 1] = 0, - [ 2] = 31, - [ 3] = 31 + 28, - [ 4] = 31 + 28 + 31, - [ 5] = 31 + 28 + 31 + 30, - [ 6] = 31 + 28 + 31 + 30 + 31, - [ 7] = 31 + 28 + 31 + 30 + 31 + 30, - [ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31, - [ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, - [10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, - [11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, - [12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, -}; - -static int _tama6DMYToDayOfYear(int day, int month, int year) { - if (month < 1 || month > 12) { - return -1; - } - day += _daysToMonth[month]; - if (month > 2 && (year & 3) == 0) { - ++day; - } - return day; -} - -static int _tama6DayOfYearToMonth(int day, int year) { - int month; - for (month = 1; month < 12; ++month) { - if (day <= _daysToMonth[month + 1]) { - return month; - } - if (month == 2 && (year & 3) == 0) { - if (day == 60) { - return 2; - } - --day; - } - } - return 12; -} - -static int _tama6DayOfYearToDayOfMonth(int day, int year) { - int month; - for (month = 1; month < 12; ++month) { - if (day <= _daysToMonth[month + 1]) { - return day - _daysToMonth[month]; - } - if (month == 2 && (year & 3) == 0) { - if (day == 60) { - return 29; - } - --day; - } - } - return day - _daysToMonth[12]; -} - -static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) { - time_t t; - if (rtc) { - if (rtc->sample) { - rtc->sample(rtc); - } - t = rtc->unixTime(rtc); - } else { - t = time(0); - } - time_t currentLatch = t; - t -= *rtcLastLatch; - *rtcLastLatch = currentLatch; - if (!t || tama5->disabled) { - return; - } - - uint8_t* timerRegs = tama5->rtcTimerPage; - bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR]; - int64_t diff; - diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10; - timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10; - t /= 60; - t += diff / 60; - - diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60; - if (diff < 0) { - diff += 60; - t -= 60; - } - timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10; - timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10; - t /= 60; - t += diff / 60; - - diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1]; - if (is24hour) { - diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10; - } else { - int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10]; - diff += (hour10 & 1) * 10; - diff += (hour10 & 2) * 12; - } - diff += t % 24; - if (diff < 0) { - diff += 24; - t -= 24; - } - if (is24hour) { - timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10; - timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10; - } else { - timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10; - timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; - } - t /= 24; - t += diff / 24; - - int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10; - int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10; - int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10; - int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR]; - int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK]; - int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear); - diff = dayInYear + t; - while (diff <= 0) { - // Previous year - if (leapYear & 3) { - diff += 365; - } else { - diff += 366; - } - --year; - --leapYear; - } - while (diff > (leapYear & 3 ? 365 : 366)) { - // Future year - if (year % 4) { - diff -= 365; - } else { - diff -= 366; - } - ++year; - ++leapYear; - } - dayOfWeek = (dayOfWeek + diff) % 7; - year %= 100; - leapYear &= 3; - - day = _tama6DayOfYearToDayOfMonth(diff, leapYear); - month = _tama6DayOfYearToMonth(diff, leapYear); - - timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek; - tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear; - - timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10; - timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10; - - timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10; - timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10; - - timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10; - timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10; -} - -void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - struct GBTAMA5State* tama5 = &memory->mbcState.tama5; - switch (address >> 13) { - case 0x5: - if (address & 1) { - tama5->reg = value; - } else { - value &= 0xF; - if (tama5->reg < GBTAMA5_MAX) { - mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value); - tama5->registers[tama5->reg] = value; - uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; - uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO]; - switch (tama5->reg) { - case GBTAMA5_BANK_LO: - case GBTAMA5_BANK_HI: - GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4)); - break; - case GBTAMA5_WRITE_LO: - case GBTAMA5_WRITE_HI: - case GBTAMA5_ADDR_HI: - break; - case GBTAMA5_ADDR_LO: - switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { - case 0x0: // RAM write - memory->sram[address] = out; - gb->sramDirty |= mSAVEDATA_DIRT_NEW; - break; - case 0x1: // RAM read - break; - case 0x2: // Other commands - switch (address) { - case GBTAMA6_DISABLE_TIMER: - tama5->disabled = true; - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7; - break; - case GBTAMA6_ENABLE_TIMER: - tama5->disabled = false; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0; - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8; - break; - case GBTAMA6_MINUTE_WRITE: - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4; - break; - case GBTAMA6_HOUR_WRITE: - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF; - tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4; - break; - case GBTAMA6_DISABLE_ALARM: - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB; - break; - case GBTAMA6_ENABLE_ALARM: - tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4; - tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4; - tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4; - tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4; - break; - } - break; - case 0x4: // RTC access - address = tama5->registers[GBTAMA5_WRITE_LO]; - if (address >= GBTAMA6_RTC_PAGE) { - break; - } - out = tama5->registers[GBTAMA5_WRITE_HI]; - switch (tama5->registers[GBTAMA5_ADDR_LO]) { - case 0: - out &= _tama6RTCMask[address]; - tama5->rtcTimerPage[address] = out; - break; - case 2: - out &= _tama6RTCMask[address | 0x10]; - tama5->rtcAlarmPage[address] = out; - break; - case 4: - tama5->rtcFreePage0[address] = out; - break; - case 6: - tama5->rtcFreePage1[address] = out; - break; - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out); - break; - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value); - break; - } - } else { - mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg); - } - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value); - } -} - -uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) { - struct GBTAMA5State* tama5 = &memory->mbcState.tama5; - if ((address & 0x1FFF) > 1) { - mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address); - } - if (address & 1) { - return 0xFF; - } else { - uint8_t value = 0xF0; - uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; - switch (tama5->reg) { - case GBTAMA5_ACTIVE: - return 0xF1; - case GBTAMA5_READ_LO: - case GBTAMA5_READ_HI: - switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { - case 0x1: - value = memory->sram[address]; - break; - case 0x2: - mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); - _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); - switch (address) { - case GBTAMA6_MINUTE_READ: - value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1]; - break; - case GBTAMA6_HOUR_READ: - value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1]; - break; - default: - value = address; - break; - } - break; - case 0x4: - if (tama5->reg == GBTAMA5_READ_HI) { - mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly"); - break; - } - _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); - address = tama5->registers[GBTAMA5_WRITE_LO]; - if (address > GBTAMA6_RTC_PAGE) { - value = 0; - break; - } - switch (tama5->registers[GBTAMA5_ADDR_LO]) { - case 1: - value = tama5->rtcTimerPage[address]; - break; - case 3: - value = tama5->rtcTimerPage[address]; - break; - case 5: - value = tama5->rtcTimerPage[address]; - break; - case 7: - value = tama5->rtcTimerPage[address]; - break; - } - break; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); - break; - } - if (tama5->reg == GBTAMA5_READ_HI) { - value >>= 4; - } - value |= 0xF0; - return value; - default: - mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg); - return 0xF1; - } - } -} - -void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) { - UNUSED(value); - int bank = address & 0x3F; - switch (address >> 14) { - case 0x0: - GBMBCSwitchBank0(gb, bank * 2); - GBMBCSwitchBank(gb, bank * 2 + 1); - break; - default: - // TODO - mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value); - break; - } -} - -void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - switch (address >> 13) { - case 0x2: - if (value < 8) { - memory->directSramAccess = true; - memory->activeRtcReg = 0; - } else if (value >= 0xD && value <= 0xF) { - memory->directSramAccess = false; - memory->rtcAccess = false; - memory->activeRtcReg = value - 8; - } - break; - case 0x5: - if (!memory->sramAccess) { - return; - } - switch (memory->activeRtcReg) { - case 0: - memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; - break; - case 5: - case 6: - memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value; - break; - case 7: - switch (value) { - case 0x11: - memory->mbcState.pkjd.reg[0]--; - break; - case 0x12: - memory->mbcState.pkjd.reg[1]--; - break; - case 0x41: - memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1]; - break; - case 0x42: - memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0]; - break; - case 0x51: - memory->mbcState.pkjd.reg[0]++; - break; - case 0x52: - memory->mbcState.pkjd.reg[1]--; - break; - } - break; - } - return; - } - _GBMBC3(gb, address, value); -} - -static uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { - if (!memory->sramAccess) { - return 0xFF; - } - switch (memory->activeRtcReg) { - case 0: - return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; - case 5: - case 6: - return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5]; - default: - return 0; - } -} - - -static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { - uint8_t newbyte = 0; - int i; - for(i = 0; i < 8; ++i) { - int oldbit = reorder[i]; - int newbit = i; - newbyte += ((input >> oldbit) & 1) << newbit; - } - - return newbyte; -} - -static const uint8_t _ntOld1Reorder[8] = { - 0, 2, 1, 4, 3, 5, 6, 7 -}; - -void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) { - struct GBMemory* memory = &gb->memory; - struct GBNTOldState* mbcState = &memory->mbcState.ntOld; - int bank = value; - - switch (address & 3) { - case 0: - mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); - break; - case 1: - value &= 0x3F; - mbcState->baseBank = value * 2; - if (mbcState->baseBank) { - GBMBCSwitchBank0(gb, mbcState->baseBank); - GBMBCSwitchBank(gb, mbcState->baseBank + 1); - } - break; - case 2: - if ((value & 0xF0) == 0xE0) { - gb->sramSize = 0x2000; - GBResizeSram(gb, gb->sramSize); - } - switch (value & 0xF) { - case 0x00: - mbcState->bankCount = 32; - break; - case 0x08: - mbcState->bankCount = 16; - break; - case 0xC: - mbcState->bankCount = 8; - break; - case 0xE: - mbcState->bankCount = 4; - break; - case 0xF: - mbcState->bankCount = 2; - break; - default: - mbcState->bankCount = 32; - break; - } - break; - case 3: - mbcState->swapped = !!(value & 0x10); - - bank = memory->currentBank; - if (mbcState->swapped) { - bank = _reorderBits(bank, reorder); - } - GBMBCSwitchBank(gb, bank); - break; - } -} - -void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - struct GBNTOldState* mbcState = &memory->mbcState.ntOld; - int bank = value; - - switch (address >> 12) { - case 0x0: - case 0x1: - _GBMBC3(gb, address, value); - break; - case 0x2: - case 0x3: - bank &= 0x1F; - if (!bank) { - bank = 1; - } - if (mbcState->swapped) { - bank = _reorderBits(bank, _ntOld1Reorder); - } - if (mbcState->bankCount) { - bank &= mbcState->bankCount - 1; - } - GBMBCSwitchBank(gb, bank + mbcState->baseBank); - break; - case 0x5: - _ntOldMulticart(gb, address, value, _ntOld1Reorder); - break; - } -} - -static const uint8_t _ntOld2Reorder[8] = { - 1, 2, 0, 3, 4, 5, 6, 7 -}; - -void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - struct GBNTOldState* mbcState = &memory->mbcState.ntOld; - int bank = value; - - switch (address >> 12) { - case 0x0: - case 0x1: - _GBMBC3(gb, address, value); - break; - case 0x2: - case 0x3: - if (!bank) { - bank = 1; - } - if (mbcState->swapped) { - bank = _reorderBits(bank, _ntOld2Reorder); - } - if (mbcState->bankCount) { - bank &= mbcState->bankCount - 1; - } - GBMBCSwitchBank(gb, bank + mbcState->baseBank); - break; - case 0x5: - _ntOldMulticart(gb, address, value, _ntOld2Reorder); - // Fall through - case 0x4: - if (address == 0x5001) { - mbcState->rumble = !!(value & 0x80); - } - - if (mbcState->rumble) { - memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02)); - } - break; - } -} - -void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - if (address >> 8 == 0x14) { - memory->mbcState.ntNew.splitMode = true; - return; - } - if (memory->mbcState.ntNew.splitMode) { - int bank = value; - if (bank < 2) { - bank = 2; - } - switch (address >> 10) { - case 8: - GBMBCSwitchHalfBank(gb, 0, bank); - return; - case 9: - GBMBCSwitchHalfBank(gb, 1, bank); - return; - } - } - _GBMBC5(gb, address, value); -} - -static const uint8_t _bbdDataReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET - { 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou - { 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET - { 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon -}; - -static const uint8_t _bbdBankReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET - { 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET - { 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET - { 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET -}; - -void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - switch (address & 0xF0FF) { - case 0x2000: - value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]); - break; - case 0x2001: - memory->mbcState.bbd.dataSwapMode = value & 0x07; - if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) { - mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); - } - break; - case 0x2080: - memory->mbcState.bbd.bankSwapMode = value & 0x07; - if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) { - mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); - } - break; - } - _GBMBC5(gb, address, value); -} - -uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) { - switch (address >> 14) { - case 0: - default: - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - case 1: - return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]); - } -} - -static const uint8_t _hitekDataReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 0, 6, 5, 3, 4, 1, 2, 7 }, - { 0, 5, 6, 3, 4, 2, 1, 7 }, - { 0, 6, 2, 3, 4, 5, 1, 7 }, - { 0, 6, 1, 3, 4, 5, 2, 7 }, - { 0, 1, 6, 3, 4, 5, 2, 7 }, - { 0, 2, 6, 3, 4, 1, 5, 7 }, - { 0, 6, 2, 3, 4, 1, 5, 7 }, -}; - -static const uint8_t _hitekBankReordering[8][8] = { - { 0, 1, 2, 3, 4, 5, 6, 7 }, - { 3, 2, 1, 0, 4, 5, 6, 7 }, - { 2, 1, 0, 3, 4, 5, 6, 7 }, - { 1, 0, 3, 2, 4, 5, 6, 7 }, - { 0, 3, 2, 1, 4, 5, 6, 7 }, - { 2, 3, 0, 1, 4, 5, 6, 7 }, - { 3, 0, 1, 2, 4, 5, 6, 7 }, - { 2, 0, 3, 1, 4, 5, 6, 7 }, -}; - -void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) { - struct GBMemory* memory = &gb->memory; - switch (address & 0xF0FF) { - case 0x2000: - value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]); - break; - case 0x2001: - memory->mbcState.bbd.dataSwapMode = value & 0x07; - break; - case 0x2080: - memory->mbcState.bbd.bankSwapMode = value & 0x07; - break; - case 0x300: - // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return - return; - } - _GBMBC5(gb, address, value); -} - -uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { - switch (address >> 14) { - case 0: - default: - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - case 1: - return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]); - } -} - -void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { - if (address > 0x2100 && address < 0x3000) { - return; - } - _GBMBC5(gb, address, value); -} - -void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { - struct GBSachenState* state = &gb->memory.mbcState.sachen; - uint8_t bank = value; - switch (address >> 13) { - case 0: - if ((state->unmaskedBank & 0x30) == 0x30) { - state->baseBank = bank; - GBMBCSwitchBank0(gb, state->baseBank & state->mask); - } - break; - case 1: - if (!bank) { - bank = 1; - } - state->unmaskedBank = bank; - bank = (bank & ~state->mask) | (state->baseBank & state->mask); - GBMBCSwitchBank(gb, bank); - break; - case 2: - if ((state->unmaskedBank & 0x30) == 0x30) { - state->mask = value; - bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask); - GBMBCSwitchBank(gb, bank); - GBMBCSwitchBank0(gb, state->baseBank & state->mask); - } - break; - case 6: - if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) { - state->locked = GB_SACHEN_LOCKED_CGB; - state->transition = 0; - } - break; - } -} - -static uint16_t _unscrambleSachen(uint16_t address) { - uint16_t unscrambled = address & 0xFFAC; - unscrambled |= (address & 0x40) >> 6; - unscrambled |= (address & 0x10) >> 3; - unscrambled |= (address & 0x02) << 3; - unscrambled |= (address & 0x01) << 6; - return unscrambled; -} - -uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) { - struct GBSachenState* state = &memory->mbcState.sachen; - if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) { - ++state->transition; - if (state->transition == 0x31) { - state->locked = GB_SACHEN_UNLOCKED; - } else { - address |= 0x80; - } - } - - if ((address & 0xFF00) == 0x0100) { - address = _unscrambleSachen(address); - } - - if (address < GB_BASE_CART_BANK1) { - return memory->romBase[address]; - } else if (address < GB_BASE_VRAM) { - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - } else { - return 0xFF; - } -} - -uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) { - struct GBSachenState* state = &memory->mbcState.sachen; - if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) { - state->transition = 0; - state->locked = GB_SACHEN_LOCKED_CGB; - } - - if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) { - ++state->transition; - if (state->transition == 0x31) { - ++state->locked; - state->transition = 0; - } - } - - if ((address & 0xFF00) == 0x0100) { - if (state->locked == GB_SACHEN_LOCKED_CGB) { - address |= 0x80; - } - address = _unscrambleSachen(address); - } - - if (address < GB_BASE_CART_BANK1) { - return memory->romBase[address]; - } else if (address < GB_BASE_VRAM) { - return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; - } else { - return 0xFF; - } -} - -static void _appendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { +void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size) { struct VFile* vf = gb->sramVf; if ((size_t) vf->size(vf) < gb->sramSize + size) { // Writing past the end of the file can invalidate the file mapping @@ -2402,7 +596,7 @@ void GBMBCRTCWrite(struct GB* gb) { uint8_t rtcRegs[5]; memcpy(rtcRegs, gb->memory.rtcRegs, sizeof(rtcRegs)); time_t rtcLastLatch = gb->memory.rtcLastLatch; - _latchRtc(gb->memory.rtc, rtcRegs, &rtcLastLatch); + _GBMBCLatchRTC(gb->memory.rtc, rtcRegs, &rtcLastLatch); struct GBMBCRTCSaveBuffer rtcBuffer; STORE_32LE(rtcRegs[0], 0, &rtcBuffer.sec); @@ -2417,100 +611,5 @@ void GBMBCRTCWrite(struct GB* gb) { STORE_32LE(gb->memory.rtcRegs[4], 0, &rtcBuffer.latchedDaysHi); STORE_64LE(gb->memory.rtcLastLatch, 0, &rtcBuffer.unixTime); - _appendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer)); -} - -void GBMBCHuC3Read(struct GB* gb) { - struct GBMBCHuC3SaveBuffer buffer; - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - vf->seek(vf, gb->sramSize, SEEK_SET); - if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { - return; - } - - size_t i; - for (i = 0; i < 0x80; ++i) { - gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF; - gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4; - } - LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); -} - -void GBMBCHuC3Write(struct GB* gb) { - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - - struct GBMBCHuC3SaveBuffer buffer; - size_t i; - for (i = 0; i < 0x80; ++i) { - buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF; - buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4; - } - STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); - - _appendSaveSuffix(gb, &buffer, sizeof(buffer)); -} - -void GBMBCTAMA5Read(struct GB* gb) { - struct GBMBCTAMA5SaveBuffer buffer; - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - vf->seek(vf, gb->sramSize, SEEK_SET); - if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { - gb->memory.mbcState.tama5.disabled = false; - return; - } - - size_t i; - for (i = 0; i < 0x8; ++i) { - gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF; - gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4; - gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF; - gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4; - gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF; - gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4; - gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF; - gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4; - } - LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); - - gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8); - - gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1; - gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2; - gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC; - gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3; -} - -void GBMBCTAMA5Write(struct GB* gb) { - struct VFile* vf = gb->sramVf; - if (!vf) { - return; - } - - struct GBMBCTAMA5SaveBuffer buffer = {0}; - size_t i; - for (i = 0; i < 8; ++i) { - buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF; - buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4; - buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF; - buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4; - buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF; - buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4; - buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF; - buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4; - } - STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); - - _appendSaveSuffix(gb, &buffer, sizeof(buffer)); + _GBMBCAppendSaveSuffix(gb, &rtcBuffer, sizeof(rtcBuffer)); } diff --git a/src/gb/mbc/huc-3.c b/src/gb/mbc/huc-3.c new file mode 100644 index 000000000..522897517 --- /dev/null +++ b/src/gb/mbc/huc-3.c @@ -0,0 +1,218 @@ +/* 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 "gb/mbc/mbc-private.h" + +#include +#include + +static void _latchHuC3Rtc(struct mRTCSource* rtc, uint8_t* huc3Regs, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + t -= *rtcLastLatch; + t /= 60; + + if (!t) { + return; + } + *rtcLastLatch += t * 60; + + int minutes = huc3Regs[GBHUC3_RTC_MINUTES_HI] << 8; + minutes |= huc3Regs[GBHUC3_RTC_MINUTES_MI] << 4; + minutes |= huc3Regs[GBHUC3_RTC_MINUTES_LO]; + minutes += t % 1440; + t /= 1440; + if (minutes >= 1440) { + minutes -= 1440; + ++t; + } else if (minutes < 0) { + minutes += 1440; + --t; + } + huc3Regs[GBHUC3_RTC_MINUTES_LO] = minutes & 0xF; + huc3Regs[GBHUC3_RTC_MINUTES_MI] = (minutes >> 4) & 0xF; + huc3Regs[GBHUC3_RTC_MINUTES_HI] = (minutes >> 8) & 0xF; + + int days = huc3Regs[GBHUC3_RTC_DAYS_LO]; + days |= huc3Regs[GBHUC3_RTC_DAYS_MI] << 4; + days |= huc3Regs[GBHUC3_RTC_DAYS_HI] << 8; + + days += t; + + huc3Regs[GBHUC3_RTC_DAYS_LO] = days & 0xF; + huc3Regs[GBHUC3_RTC_DAYS_MI] = (days >> 4) & 0xF; + huc3Regs[GBHUC3_RTC_DAYS_HI] = (days >> 8) & 0xF; +} + +static void _huc3Commit(struct GB* gb, struct GBHuC3State* state) { + size_t c; + switch (state->value & 0x70) { + case 0x10: + if ((state->index & 0xF8) == 0x10) { + _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); + } + state->value &= 0xF0; + state->value |= state->registers[state->index] & 0xF; + mLOG(GB_MBC, DEBUG, "HuC-3 read: %02X:%X", state->index, state->value & 0xF); + if (state->value & 0x10) { + ++state->index; + } + break; + case 0x30: + mLOG(GB_MBC, DEBUG, "HuC-3 write: %02X:%X", state->index, state->value & 0xF); + state->registers[state->index] = state->value & 0xF; + if (state->value & 0x10) { + ++state->index; + } + break; + case 0x40: + state->index &= 0xF0; + state->index |= (state->value) & 0xF; + mLOG(GB_MBC, DEBUG, "HuC-3 index (low): %02X", state->index); + break; + case 0x50: + state->index &= 0x0F; + state->index |= ((state->value) & 0xF) << 4; + mLOG(GB_MBC, DEBUG, "HuC-3 index (high): %02X", state->index); + break; + case 0x60: + switch (state->value & 0xF) { + case GBHUC3_CMD_LATCH: + _latchHuC3Rtc(gb->memory.rtc, state->registers, &gb->memory.rtcLastLatch); + memcpy(state->registers, &state->registers[GBHUC3_RTC_MINUTES_LO], 6); + mLOG(GB_MBC, DEBUG, "HuC-3 RTC latch"); + break; + case GBHUC3_CMD_SET_RTC: + memcpy(&state->registers[GBHUC3_RTC_MINUTES_LO], state->registers, 6); + mLOG(GB_MBC, DEBUG, "HuC-3 set RTC"); + break; + case GBHUC3_CMD_RO: + mLOG(GB_MBC, STUB, "HuC-3 unimplemented read-only mode"); + break; + case GBHUC3_CMD_TONE: + if (state->registers[GBHUC3_SPEAKER_ENABLE] == 1) { + for (c = 0; c < mCoreCallbacksListSize(&gb->coreCallbacks); ++c) { + struct mCoreCallbacks* callbacks = mCoreCallbacksListGetPointer(&gb->coreCallbacks, c); + if (callbacks->alarm) { + callbacks->alarm(callbacks->context); + } + } + mLOG(GB_MBC, DEBUG, "HuC-3 tone %i", state->registers[GBHUC3_SPEAKER_TONE] & 3); + } + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown command: %X", state->value & 0xF); + break; + } + state->value = 0xE1; + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown mode commit: %02X:%02X", state->index, state->value); + break; + } +} + +void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBHuC3State* state = &memory->mbcState.huc3; + int bank = value & 0x7F; + if (address & 0x1FFF) { + mLOG(GB_MBC, STUB, "HuC-3 unknown value %04X:%02X", address, value); + } + + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + state->mode = value; + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + GBMBCSwitchSramBank(gb, bank); + break; + case 0x5: + switch (state->mode) { + case GBHUC3_MODE_IN: + state->value = 0x80 | value; + break; + case GBHUC3_MODE_COMMIT: + _huc3Commit(gb, state); + break; + default: + mLOG(GB_MBC, STUB, "HuC-3 unknown mode write: %02X:%02X", state->mode, value); + } + break; + default: + // TODO + mLOG(GB_MBC, STUB, "HuC-3 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBHuC3Read(struct GBMemory* memory, uint16_t address) { + struct GBHuC3State* state = &memory->mbcState.huc3; + switch (state->mode) { + case GBHUC3_MODE_SRAM_RO: + case GBHUC3_MODE_SRAM_RW: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + case GBHUC3_MODE_IN: + case GBHUC3_MODE_OUT: + return 0x80 | state->value; + default: + return 0xFF; + } +} + +void GBMBCHuC3Read(struct GB* gb) { + struct GBMBCHuC3SaveBuffer buffer; + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + vf->seek(vf, gb->sramSize, SEEK_SET); + if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { + return; + } + + size_t i; + for (i = 0; i < 0x80; ++i) { + gb->memory.mbcState.huc3.registers[i * 2] = buffer.regs[i] & 0xF; + gb->memory.mbcState.huc3.registers[i * 2 + 1] = buffer.regs[i] >> 4; + } + LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); +} + +void GBMBCHuC3Write(struct GB* gb) { + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + + struct GBMBCHuC3SaveBuffer buffer; + size_t i; + for (i = 0; i < 0x80; ++i) { + buffer.regs[i] = gb->memory.mbcState.huc3.registers[i * 2] & 0xF; + buffer.regs[i] |= gb->memory.mbcState.huc3.registers[i * 2 + 1] << 4; + } + STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + _GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer)); +} diff --git a/src/gb/mbc/licensed.c b/src/gb/mbc/licensed.c new file mode 100644 index 000000000..22f0ae524 --- /dev/null +++ b/src/gb/mbc/licensed.c @@ -0,0 +1,84 @@ +/* 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 "gb/mbc/mbc-private.h" + +#include + +void _GBMMM01(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (!memory->mbcState.mmm01.locked) { + switch (address >> 13) { + case 0x0: + memory->mbcState.mmm01.locked = true; + GBMBCSwitchBank0(gb, memory->mbcState.mmm01.currentBank0); + break; + case 0x1: + memory->mbcState.mmm01.currentBank0 &= ~0x7F; + memory->mbcState.mmm01.currentBank0 |= value & 0x7F; + break; + case 0x2: + memory->mbcState.mmm01.currentBank0 &= ~0x180; + memory->mbcState.mmm01.currentBank0 |= (value & 0x30) << 3; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } + return; + } + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + memory->sramAccess = false; + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, value + memory->mbcState.mmm01.currentBank0); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MMM01 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBHuC1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x3F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0xE: + memory->sramAccess = false; + break; + default: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + GBMBCSwitchSramBank(gb, value); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "HuC-1 unknown address: %04X:%02X", address, value); + break; + } +} diff --git a/src/gb/mbc/mbc-private.h b/src/gb/mbc/mbc-private.h new file mode 100644 index 000000000..b841839f8 --- /dev/null +++ b/src/gb/mbc/mbc-private.h @@ -0,0 +1,61 @@ +/* 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 GB_MBC_PRIVATE_H +#define GB_MBC_PRIVATE_H + +#include + +CXX_GUARD_START + +struct GB; +struct GBMemory; +struct mRTCSource; + +void _GBMBC1(struct GB*, uint16_t address, uint8_t value); +void _GBMBC2(struct GB*, uint16_t address, uint8_t value); +void _GBMBC3(struct GB*, uint16_t address, uint8_t value); +void _GBMBC5(struct GB*, uint16_t address, uint8_t value); +void _GBMBC6(struct GB*, uint16_t address, uint8_t value); +void _GBMBC7(struct GB*, uint16_t address, uint8_t value); + +void _GBMMM01(struct GB*, uint16_t address, uint8_t value); +void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value); +void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value); + +void _GBHuC1(struct GB*, uint16_t address, uint8_t value); +void _GBHuC3(struct GB*, uint16_t address, uint8_t value); + +void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value); +void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value); +void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value); +void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value); +void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); +void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); +void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); +void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value); +void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); + +uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); +uint8_t _GBMBC6Read(struct GBMemory*, uint16_t address); +uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); +void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value); + +uint8_t _GBPocketCamRead(struct GBMemory*, uint16_t address); +uint8_t _GBTAMA5Read(struct GBMemory*, uint16_t address); +uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); + +uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); +uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); +uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); +uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); +uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address); + +void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch); +void _GBMBCAppendSaveSuffix(struct GB* gb, const void* buffer, size_t size); + +CXX_GUARD_END + +#endif diff --git a/src/gb/mbc/mbc.c b/src/gb/mbc/mbc.c new file mode 100644 index 000000000..864b6e9f8 --- /dev/null +++ b/src/gb/mbc/mbc.c @@ -0,0 +1,565 @@ +/* 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 "gb/mbc/mbc-private.h" + +#include +#include +#include + +static void _GBMBC6MapChip(struct GB*, int half, uint8_t value); + +void _GBMBCLatchRTC(struct mRTCSource* rtc, uint8_t* rtcRegs, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + time_t currentLatch = t; + t -= *rtcLastLatch; + *rtcLastLatch = currentLatch; + + int64_t diff; + diff = rtcRegs[0] + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + rtcRegs[0] = diff % 60; + t /= 60; + t += diff / 60; + + diff = rtcRegs[1] + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + rtcRegs[1] = diff % 60; + t /= 60; + t += diff / 60; + + diff = rtcRegs[2] + t % 24; + if (diff < 0) { + diff += 24; + t -= 24; + } + rtcRegs[2] = diff % 24; + t /= 24; + t += diff / 24; + + diff = rtcRegs[3] + ((rtcRegs[4] & 1) << 8) + (t & 0x1FF); + rtcRegs[3] = diff; + rtcRegs[4] &= 0xFE; + rtcRegs[4] |= (diff >> 8) & 1; + if (diff & 0x200) { + rtcRegs[4] |= 0x80; + } +} + +static void _GBMBC1Update(struct GB* gb) { + struct GBMBC1State* state = &gb->memory.mbcState.mbc1; + int bank = state->bankLo; + bank &= (1 << state->multicartStride) - 1; + bank |= state->bankHi << state->multicartStride; + if (state->mode) { + GBMBCSwitchBank0(gb, state->bankHi << state->multicartStride); + GBMBCSwitchSramBank(gb, state->bankHi & 3); + } else { + GBMBCSwitchBank0(gb, 0); + GBMBCSwitchSramBank(gb, 0); + } + if (!(state->bankLo & 0x1F)) { + ++state->bankLo; + ++bank; + } + GBMBCSwitchBank(gb, bank); +} + +void _GBMBC1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x1F; + switch (address >> 13) { + case 0x0: + switch (value & 0xF) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown value %02X", value); + break; + } + break; + case 0x1: + memory->mbcState.mbc1.bankLo = bank; + _GBMBC1Update(gb); + break; + case 0x2: + bank &= 3; + memory->mbcState.mbc1.bankHi = bank; + _GBMBC1Update(gb); + break; + case 0x3: + memory->mbcState.mbc1.mode = value & 1; + _GBMBC1Update(gb); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC1 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBMBC2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int shift = (address & 1) * 4; + int bank = value & 0xF; + switch ((address & 0xC100) >> 8) { + case 0x0: + switch (value & 0x0F) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC2 unknown value %02X", value); + break; + } + break; + case 0x1: + if (!bank) { + ++bank; + } + GBMBCSwitchBank(gb, bank); + break; + case 0x80: + case 0x81: + case 0x82: + case 0x83: + if (!memory->sramAccess) { + return; + } + address &= 0x1FF; + memory->sramBank[(address >> 1)] &= 0xF0 >> shift; + memory->sramBank[(address >> 1)] |= (value & 0xF) << shift; + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC2 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC2Read(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + address &= 0x1FF; + int shift = (address & 1) * 4; + return (memory->sramBank[(address >> 1)] >> shift) | 0xF0; +} + +void _GBMBC3(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value; + switch (address >> 13) { + case 0x0: + switch (value & 0xF) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC3 unknown value %02X", value); + break; + } + break; + case 0x1: + if (gb->memory.romSize < GB_SIZE_CART_BANK0 * 0x80) { + bank &= 0x7F; + } + if (!bank) { + ++bank; + } + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + bank &= 0xF; + if (bank < 8) { + GBMBCSwitchSramBank(gb, value); + memory->rtcAccess = false; + } else if (bank <= 0xC) { + memory->activeRtcReg = bank - 8; + memory->rtcAccess = true; + } + break; + case 0x3: + if (memory->rtcLatched && value == 0) { + memory->rtcLatched = false; + } else if (!memory->rtcLatched && value == 1) { + _GBMBCLatchRTC(gb->memory.rtc, gb->memory.rtcRegs, &gb->memory.rtcLastLatch); + memory->rtcLatched = true; + } + break; + } +} + +void _GBMBC5(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank; + switch (address >> 12) { + case 0x0: + case 0x1: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC5 unknown value %02X", value); + break; + } + break; + case 0x2: + bank = (memory->currentBank & 0x100) | value; + GBMBCSwitchBank(gb, bank); + break; + case 0x3: + bank = (memory->currentBank & 0xFF) | ((value & 1) << 8); + GBMBCSwitchBank(gb, bank); + break; + case 0x4: + case 0x5: + if (memory->mbcType == GB_MBC5_RUMBLE && memory->rumble) { + memory->rumble->setRumble(memory->rumble, (value >> 3) & 1); + value &= ~8; + } + GBMBCSwitchSramBank(gb, value & 0xF); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC5 unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value; + switch (address >> 10) { + case 0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC6 unknown value %02X", value); + break; + } + break; + case 0x1: + GBMBCSwitchSramHalfBank(gb, 0, bank); + break; + case 0x2: + GBMBCSwitchSramHalfBank(gb, 1, bank); + break; + case 0x3: + mLOG(GB_MBC, STUB, "MBC6 unimplemented flash OE write: %04X:%02X", address, value); + break; + case 0x4: + mLOG(GB_MBC, STUB, "MBC6 unimplemented flash WE write: %04X:%02X", address, value); + break; + case 0x8: + case 0x9: + GBMBCSwitchHalfBank(gb, 0, bank); + break; + case 0xA: + case 0xB: + _GBMBC6MapChip(gb, 0, value); + break; + case 0xC: + case 0xD: + GBMBCSwitchHalfBank(gb, 1, bank); + break; + case 0xE: + case 0xF: + _GBMBC6MapChip(gb, 1, value); + break; + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + if (memory->sramAccess) { + memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + } + break; + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + if (memory->sramAccess) { + memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)] = value; + } + break; + default: + mLOG(GB_MBC, STUB, "MBC6 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC6Read(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + switch (address >> 12) { + case 0xA: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + case 0xB: + return memory->sramBank1[address & (GB_SIZE_EXTERNAL_RAM_HALFBANK - 1)]; + } + return 0xFF; +} + +static void _GBMBC6MapChip(struct GB* gb, int half, uint8_t value) { + if (!half) { + gb->memory.mbcState.mbc6.flashBank0 = !!(value & 0x08); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank); + } else { + gb->memory.mbcState.mbc6.flashBank1 = !!(value & 0x08); + GBMBCSwitchHalfBank(gb, half, gb->memory.currentBank1); + } +} + +void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) { + int bank = value & 0x7F; + switch (address >> 13) { + case 0x0: + switch (value) { + default: + case 0: + gb->memory.mbcState.mbc7.access = 0; + break; + case 0xA: + gb->memory.mbcState.mbc7.access |= 1; + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + if (value == 0x40) { + gb->memory.mbcState.mbc7.access |= 2; + } else { + gb->memory.mbcState.mbc7.access &= ~2; + } + break; + case 0x5: + _GBMBC7Write(&gb->memory, address, value); + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + if (mbc7->access != 3) { + return 0xFF; + } + switch (address & 0xF0) { + case 0x20: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 0x81D0; + return x; + } + return 0xFF; + case 0x30: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 0x81D0; + return x >> 8; + } + return 7; + case 0x40: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 0x81D0; + return y; + } + return 0xFF; + case 0x50: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 0x81D0; + return y >> 8; + } + return 7; + case 0x60: + return 0; + case 0x80: + return mbc7->eeprom; + default: + return 0xFF; + } +} + +void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + if (mbc7->access != 3) { + return; + } + switch (address & 0xF0) { + case 0x00: + mbc7->latch = (value & 0x55) == 0x55; + return; + case 0x10: + mbc7->latch |= (value & 0xAA); + if (mbc7->latch == 0xAB && memory->rotation && memory->rotation->sample) { + memory->rotation->sample(memory->rotation); + } + mbc7->latch = 0; + return; + default: + mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value); + return; + case 0x80: + break; + } + GBMBC7Field old = memory->mbcState.mbc7.eeprom; + value = GBMBC7FieldFillDO(value); // Hi-Z + if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { + mbc7->state = GBMBC7_STATE_IDLE; + } + if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) { + if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) { + mbc7->sr <<= 1; + mbc7->sr |= GBMBC7FieldGetDI(value); + ++mbc7->srBits; + } + switch (mbc7->state) { + case GBMBC7_STATE_IDLE: + if (GBMBC7FieldIsDI(value)) { + mbc7->state = GBMBC7_STATE_READ_COMMAND; + mbc7->srBits = 0; + mbc7->sr = 0; + } + break; + case GBMBC7_STATE_READ_COMMAND: + if (mbc7->srBits == 10) { + mbc7->state = 0x10 | (mbc7->sr >> 6); + if (mbc7->state & 0xC) { + mbc7->state &= ~0x3; + } + mbc7->srBits = 0; + mbc7->address = mbc7->sr & 0x7F; + } + break; + case GBMBC7_STATE_DO: + value = GBMBC7FieldSetDO(value, mbc7->sr >> 15); + mbc7->sr <<= 1; + --mbc7->srBits; + if (!mbc7->srBits) { + mbc7->state = GBMBC7_STATE_IDLE; + } + break; + default: + break; + } + switch (mbc7->state) { + case GBMBC7_STATE_EEPROM_EWEN: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_EWDS: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_WRITE: + if (mbc7->srBits == 16) { + if (mbc7->writable) { + memory->sram[mbc7->address * 2] = mbc7->sr >> 8; + memory->sram[mbc7->address * 2 + 1] = mbc7->sr; + } + mbc7->state = GBMBC7_STATE_IDLE; + } + break; + case GBMBC7_STATE_EEPROM_ERASE: + if (mbc7->writable) { + memory->sram[mbc7->address * 2] = 0xFF; + memory->sram[mbc7->address * 2 + 1] = 0xFF; + } + mbc7->state = GBMBC7_STATE_IDLE; + break; + case GBMBC7_STATE_EEPROM_READ: + mbc7->srBits = 16; + mbc7->sr = memory->sram[mbc7->address * 2] << 8; + mbc7->sr |= memory->sram[mbc7->address * 2 + 1]; + mbc7->state = GBMBC7_STATE_DO; + value = GBMBC7FieldClearDO(value); + break; + case GBMBC7_STATE_EEPROM_WRAL: + if (mbc7->srBits == 16) { + if (mbc7->writable) { + int i; + for (i = 0; i < 128; ++i) { + memory->sram[i * 2] = mbc7->sr >> 8; + memory->sram[i * 2 + 1] = mbc7->sr; + } + } + mbc7->state = GBMBC7_STATE_IDLE; + } + break; + case GBMBC7_STATE_EEPROM_ERAL: + if (mbc7->writable) { + int i; + for (i = 0; i < 128; ++i) { + memory->sram[i * 2] = 0xFF; + memory->sram[i * 2 + 1] = 0xFF; + } + } + mbc7->state = GBMBC7_STATE_IDLE; + break; + default: + break; + } + } else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) { + value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old)); + } + mbc7->eeprom = value; +} diff --git a/src/gb/mbc/pocket-cam.c b/src/gb/mbc/pocket-cam.c new file mode 100644 index 000000000..c587dfa6a --- /dev/null +++ b/src/gb/mbc/pocket-cam.c @@ -0,0 +1,149 @@ +/* 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 "gb/mbc/mbc-private.h" + +#include +#include + +static void _GBPocketCamCapture(struct GBMemory*); + +void _GBPocketCam(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + int bank = value & 0x3F; + switch (address >> 13) { + case 0x0: + switch (value) { + case 0: + memory->sramAccess = false; + break; + case 0xA: + memory->sramAccess = true; + GBMBCSwitchSramBank(gb, memory->sramCurrentBank); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "Pocket Cam unknown value %02X", value); + break; + } + break; + case 0x1: + GBMBCSwitchBank(gb, bank); + break; + case 0x2: + if (value < 0x10) { + GBMBCSwitchSramBank(gb, value); + memory->mbcState.pocketCam.registersActive = false; + memory->directSramAccess = true; + } else { + memory->mbcState.pocketCam.registersActive = true; + memory->directSramAccess = false; + } + break; + case 0x5: + if (!memory->mbcState.pocketCam.registersActive) { + break; + } + address &= 0x7F; + if (address == 0 && value & 1) { + value &= 6; // TODO: Timing + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + _GBPocketCamCapture(memory); + } + if (address < sizeof(memory->mbcState.pocketCam.registers)) { + memory->mbcState.pocketCam.registers[address] = value; + } + break; + default: + mLOG(GB_MBC, STUB, "Pocket Cam unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBPocketCamRead(struct GBMemory* memory, uint16_t address) { + if (memory->mbcState.pocketCam.registersActive) { + if ((address & 0x7F) == 0) { + return memory->mbcState.pocketCam.registers[0]; + } + return 0; + } + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; +} + +void _GBPocketCamCapture(struct GBMemory* memory) { + if (!memory->cam) { + return; + } + const void* image = NULL; + size_t stride; + enum mColorFormat format; + memory->cam->requestImage(memory->cam, &image, &stride, &format); + if (!image) { + return; + } + memset(&memory->sram[0x100], 0, GBCAM_HEIGHT * GBCAM_WIDTH / 4); + struct GBPocketCamState* pocketCam = &memory->mbcState.pocketCam; + size_t x, y; + for (y = 0; y < GBCAM_HEIGHT; ++y) { + for (x = 0; x < GBCAM_WIDTH; ++x) { + uint32_t gray; + uint32_t color; + switch (format) { + case mCOLOR_XBGR8: + case mCOLOR_XRGB8: + case mCOLOR_ARGB8: + case mCOLOR_ABGR8: + color = ((const uint32_t*) image)[y * stride + x]; + gray = (color & 0xFF) + ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF); + break; + case mCOLOR_BGRX8: + case mCOLOR_RGBX8: + case mCOLOR_RGBA8: + case mCOLOR_BGRA8: + color = ((const uint32_t*) image)[y * stride + x]; + gray = ((color >> 8) & 0xFF) + ((color >> 16) & 0xFF) + ((color >> 24) & 0xFF); + break; + case mCOLOR_BGR5: + case mCOLOR_RGB5: + case mCOLOR_ARGB5: + case mCOLOR_ABGR5: + color = ((const uint16_t*) image)[y * stride + x]; + gray = ((color << 3) & 0xF8) + ((color >> 2) & 0xF8) + ((color >> 7) & 0xF8); + break; + case mCOLOR_BGR565: + case mCOLOR_RGB565: + color = ((const uint16_t*) image)[y * stride + x]; + gray = ((color << 3) & 0xF8) + ((color >> 3) & 0xFC) + ((color >> 8) & 0xF8); + break; + case mCOLOR_BGRA5: + case mCOLOR_RGBA5: + color = ((const uint16_t*) image)[y * stride + x]; + gray = ((color << 2) & 0xF8) + ((color >> 3) & 0xF8) + ((color >> 8) & 0xF8); + break; + default: + mLOG(GB_MBC, WARN, "Unsupported pixel format: %X", format); + return; + } + uint16_t exposure = (pocketCam->registers[2] << 8) | (pocketCam->registers[3]); + gray = (gray + 1) * exposure / 0x300; + // TODO: Additional processing + int matrixEntry = 3 * ((x & 3) + 4 * (y & 3)); + if (gray < pocketCam->registers[matrixEntry + 6]) { + gray = 0x101; + } else if (gray < pocketCam->registers[matrixEntry + 7]) { + gray = 0x100; + } else if (gray < pocketCam->registers[matrixEntry + 8]) { + gray = 0x001; + } else { + gray = 0; + } + int coord = (((x >> 3) & 0xF) * 8 + (y & 0x7)) * 2 + (y & ~0x7) * 0x20; + uint16_t existing; + LOAD_16LE(existing, coord + 0x100, memory->sram); + existing |= gray << (7 - (x & 7)); + STORE_16LE(existing, coord + 0x100, memory->sram); + } + } +} diff --git a/src/gb/mbc/tama5.c b/src/gb/mbc/tama5.c new file mode 100644 index 000000000..505a35ed8 --- /dev/null +++ b/src/gb/mbc/tama5.c @@ -0,0 +1,433 @@ +/* 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 "gb/mbc/mbc-private.h" + +#include +#include +#include + +static const uint8_t _tama6RTCMask[32] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0xF, 0x7, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0xF, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0, + 0x0, 0x0, 0xF, 0x7, 0xF, 0x3, 0x7, 0xF, 0x3, 0x0, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, +}; + +static const int _daysToMonth[] = { + [ 1] = 0, + [ 2] = 31, + [ 3] = 31 + 28, + [ 4] = 31 + 28 + 31, + [ 5] = 31 + 28 + 31 + 30, + [ 6] = 31 + 28 + 31 + 30 + 31, + [ 7] = 31 + 28 + 31 + 30 + 31 + 30, + [ 8] = 31 + 28 + 31 + 30 + 31 + 30 + 31, + [ 9] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + [10] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + [11] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + [12] = 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, +}; + +static int _tama6DMYToDayOfYear(int day, int month, int year) { + if (month < 1 || month > 12) { + return -1; + } + day += _daysToMonth[month]; + if (month > 2 && (year & 3) == 0) { + ++day; + } + return day; +} + +static int _tama6DayOfYearToMonth(int day, int year) { + int month; + for (month = 1; month < 12; ++month) { + if (day <= _daysToMonth[month + 1]) { + return month; + } + if (month == 2 && (year & 3) == 0) { + if (day == 60) { + return 2; + } + --day; + } + } + return 12; +} + +static int _tama6DayOfYearToDayOfMonth(int day, int year) { + int month; + for (month = 1; month < 12; ++month) { + if (day <= _daysToMonth[month + 1]) { + return day - _daysToMonth[month]; + } + if (month == 2 && (year & 3) == 0) { + if (day == 60) { + return 29; + } + --day; + } + } + return day - _daysToMonth[12]; +} + +static void _latchTAMA6Rtc(struct mRTCSource* rtc, struct GBTAMA5State* tama5, time_t* rtcLastLatch) { + time_t t; + if (rtc) { + if (rtc->sample) { + rtc->sample(rtc); + } + t = rtc->unixTime(rtc); + } else { + t = time(0); + } + time_t currentLatch = t; + t -= *rtcLastLatch; + *rtcLastLatch = currentLatch; + if (!t || tama5->disabled) { + return; + } + + uint8_t* timerRegs = tama5->rtcTimerPage; + bool is24hour = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_24_HOUR]; + int64_t diff; + diff = timerRegs[GBTAMA6_RTC_PA0_SECOND_1] + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] * 10 + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + timerRegs[GBTAMA6_RTC_PA0_SECOND_1] = diff % 10; + timerRegs[GBTAMA6_RTC_PA0_SECOND_10] = (diff % 60) / 10; + t /= 60; + t += diff / 60; + + diff = timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] * 10 + t % 60; + if (diff < 0) { + diff += 60; + t -= 60; + } + timerRegs[GBTAMA6_RTC_PA0_MINUTE_1] = diff % 10; + timerRegs[GBTAMA6_RTC_PA0_MINUTE_10] = (diff % 60) / 10; + t /= 60; + t += diff / 60; + + diff = timerRegs[GBTAMA6_RTC_PA0_HOUR_1]; + if (is24hour) { + diff += timerRegs[GBTAMA6_RTC_PA0_HOUR_10] * 10; + } else { + int hour10 = timerRegs[GBTAMA6_RTC_PA0_HOUR_10]; + diff += (hour10 & 1) * 10; + diff += (hour10 & 2) * 12; + } + diff += t % 24; + if (diff < 0) { + diff += 24; + t -= 24; + } + if (is24hour) { + timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 24) % 10; + timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 24) / 10; + } else { + timerRegs[GBTAMA6_RTC_PA0_HOUR_1] = (diff % 12) % 10; + timerRegs[GBTAMA6_RTC_PA0_HOUR_10] = (diff % 12) / 10 + (diff / 12) * 2; + } + t /= 24; + t += diff / 24; + + int day = timerRegs[GBTAMA6_RTC_PA0_DAY_1] + timerRegs[GBTAMA6_RTC_PA0_DAY_10] * 10; + int month = timerRegs[GBTAMA6_RTC_PA0_MONTH_1] + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] * 10; + int year = timerRegs[GBTAMA6_RTC_PA0_YEAR_1] + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] * 10; + int leapYear = tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR]; + int dayOfWeek = timerRegs[GBTAMA6_RTC_PA0_WEEK]; + int dayInYear = _tama6DMYToDayOfYear(day, month, leapYear); + diff = dayInYear + t; + while (diff <= 0) { + // Previous year + if (leapYear & 3) { + diff += 365; + } else { + diff += 366; + } + --year; + --leapYear; + } + while (diff > (leapYear & 3 ? 365 : 366)) { + // Future year + if (year % 4) { + diff -= 365; + } else { + diff -= 366; + } + ++year; + ++leapYear; + } + dayOfWeek = (dayOfWeek + diff) % 7; + year %= 100; + leapYear &= 3; + + day = _tama6DayOfYearToDayOfMonth(diff, leapYear); + month = _tama6DayOfYearToMonth(diff, leapYear); + + timerRegs[GBTAMA6_RTC_PA0_WEEK] = dayOfWeek; + tama5->rtcAlarmPage[GBTAMA6_RTC_PA1_LEAP_YEAR] = leapYear; + + timerRegs[GBTAMA6_RTC_PA0_DAY_1] = day % 10; + timerRegs[GBTAMA6_RTC_PA0_DAY_10] = day / 10; + + timerRegs[GBTAMA6_RTC_PA0_MONTH_1] = month % 10; + timerRegs[GBTAMA6_RTC_PA0_MONTH_10] = month / 10; + + timerRegs[GBTAMA6_RTC_PA0_YEAR_1] = year % 10; + timerRegs[GBTAMA6_RTC_PA0_YEAR_10] = year / 10; +} + +void _GBTAMA5(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBTAMA5State* tama5 = &memory->mbcState.tama5; + switch (address >> 13) { + case 0x5: + if (address & 1) { + tama5->reg = value; + } else { + value &= 0xF; + if (tama5->reg < GBTAMA5_MAX) { + mLOG(GB_MBC, DEBUG, "TAMA5 write: %02X:%X", tama5->reg, value); + tama5->registers[tama5->reg] = value; + uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; + uint8_t out = (tama5->registers[GBTAMA5_WRITE_HI] << 4) | tama5->registers[GBTAMA5_WRITE_LO]; + switch (tama5->reg) { + case GBTAMA5_BANK_LO: + case GBTAMA5_BANK_HI: + GBMBCSwitchBank(gb, tama5->registers[GBTAMA5_BANK_LO] | (tama5->registers[GBTAMA5_BANK_HI] << 4)); + break; + case GBTAMA5_WRITE_LO: + case GBTAMA5_WRITE_HI: + case GBTAMA5_ADDR_HI: + break; + case GBTAMA5_ADDR_LO: + switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { + case 0x0: // RAM write + memory->sram[address] = out; + gb->sramDirty |= mSAVEDATA_DIRT_NEW; + break; + case 0x1: // RAM read + break; + case 0x2: // Other commands + switch (address) { + case GBTAMA6_DISABLE_TIMER: + tama5->disabled = true; + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0x7; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0x7; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0x7; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0x7; + break; + case GBTAMA6_ENABLE_TIMER: + tama5->disabled = false; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_1] = 0; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_SECOND_10] = 0; + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x8; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x8; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x8; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x8; + break; + case GBTAMA6_MINUTE_WRITE: + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1] = out & 0xF; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] = out >> 4; + break; + case GBTAMA6_HOUR_WRITE: + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1] = out & 0xF; + tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] = out >> 4; + break; + case GBTAMA6_DISABLE_ALARM: + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xB; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xB; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xB; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xB; + break; + case GBTAMA6_ENABLE_ALARM: + tama5->rtcTimerPage[GBTAMA6_RTC_PAGE] |= 0x4; + tama5->rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 0x4; + tama5->rtcFreePage0[GBTAMA6_RTC_PAGE] |= 0x4; + tama5->rtcFreePage1[GBTAMA6_RTC_PAGE] |= 0x4; + break; + } + break; + case 0x4: // RTC access + address = tama5->registers[GBTAMA5_WRITE_LO]; + if (address >= GBTAMA6_RTC_PAGE) { + break; + } + out = tama5->registers[GBTAMA5_WRITE_HI]; + switch (tama5->registers[GBTAMA5_ADDR_LO]) { + case 0: + out &= _tama6RTCMask[address]; + tama5->rtcTimerPage[address] = out; + break; + case 2: + out &= _tama6RTCMask[address | 0x10]; + tama5->rtcAlarmPage[address] = out; + break; + case 4: + tama5->rtcFreePage0[address] = out; + break; + case 6: + tama5->rtcFreePage1[address] = out; + break; + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown address: %02X:%02X", address, out); + break; + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X:%X", tama5->reg, value); + break; + } + } else { + mLOG(GB_MBC, STUB, "TAMA5 unknown write: %02X", tama5->reg); + } + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X:%02X", address, value); + } +} + +uint8_t _GBTAMA5Read(struct GBMemory* memory, uint16_t address) { + struct GBTAMA5State* tama5 = &memory->mbcState.tama5; + if ((address & 0x1FFF) > 1) { + mLOG(GB_MBC, STUB, "TAMA5 unknown address: %04X", address); + } + if (address & 1) { + return 0xFF; + } else { + uint8_t value = 0xF0; + uint8_t address = ((tama5->registers[GBTAMA5_ADDR_HI] << 4) & 0x10) | tama5->registers[GBTAMA5_ADDR_LO]; + switch (tama5->reg) { + case GBTAMA5_ACTIVE: + return 0xF1; + case GBTAMA5_READ_LO: + case GBTAMA5_READ_HI: + switch (tama5->registers[GBTAMA5_ADDR_HI] >> 1) { + case 0x1: + value = memory->sram[address]; + break; + case 0x2: + mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); + _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); + switch (address) { + case GBTAMA6_MINUTE_READ: + value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_MINUTE_1]; + break; + case GBTAMA6_HOUR_READ: + value = (tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_10] << 4) | tama5->rtcTimerPage[GBTAMA6_RTC_PA0_HOUR_1]; + break; + default: + value = address; + break; + } + break; + case 0x4: + if (tama5->reg == GBTAMA5_READ_HI) { + mLOG(GB_MBC, GAME_ERROR, "TAMA5 reading RTC incorrectly"); + break; + } + _latchTAMA6Rtc(memory->rtc, tama5, &memory->rtcLastLatch); + address = tama5->registers[GBTAMA5_WRITE_LO]; + if (address > GBTAMA6_RTC_PAGE) { + value = 0; + break; + } + switch (tama5->registers[GBTAMA5_ADDR_LO]) { + case 1: + value = tama5->rtcTimerPage[address]; + break; + case 3: + value = tama5->rtcTimerPage[address]; + break; + case 5: + value = tama5->rtcTimerPage[address]; + break; + case 7: + value = tama5->rtcTimerPage[address]; + break; + } + break; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown read %s: %02X", tama5->reg == GBTAMA5_READ_HI ? "hi" : "lo", address); + break; + } + if (tama5->reg == GBTAMA5_READ_HI) { + value >>= 4; + } + value |= 0xF0; + return value; + default: + mLOG(GB_MBC, STUB, "TAMA5 unknown read: %02X", tama5->reg); + return 0xF1; + } + } +} + + +void GBMBCTAMA5Read(struct GB* gb) { + struct GBMBCTAMA5SaveBuffer buffer; + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + vf->seek(vf, gb->sramSize, SEEK_SET); + if (vf->read(vf, &buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) { + gb->memory.mbcState.tama5.disabled = false; + return; + } + + size_t i; + for (i = 0; i < 0x8; ++i) { + gb->memory.mbcState.tama5.rtcTimerPage[i * 2] = buffer.rtcTimerPage[i] & 0xF; + gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] = buffer.rtcTimerPage[i] >> 4; + gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] = buffer.rtcAlarmPage[i] & 0xF; + gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] = buffer.rtcAlarmPage[i] >> 4; + gb->memory.mbcState.tama5.rtcFreePage0[i * 2] = buffer.rtcFreePage0[i] & 0xF; + gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] = buffer.rtcFreePage0[i] >> 4; + gb->memory.mbcState.tama5.rtcFreePage1[i * 2] = buffer.rtcFreePage1[i] & 0xF; + gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] = buffer.rtcFreePage1[i] >> 4; + } + LOAD_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + gb->memory.mbcState.tama5.disabled = !(gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] & 0x8); + + gb->memory.mbcState.tama5.rtcTimerPage[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcAlarmPage[GBTAMA6_RTC_PAGE] |= 1; + gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcFreePage0[GBTAMA6_RTC_PAGE] |= 2; + gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] &= 0xC; + gb->memory.mbcState.tama5.rtcFreePage1[GBTAMA6_RTC_PAGE] |= 3; +} + +void GBMBCTAMA5Write(struct GB* gb) { + struct VFile* vf = gb->sramVf; + if (!vf) { + return; + } + + struct GBMBCTAMA5SaveBuffer buffer = {0}; + size_t i; + for (i = 0; i < 8; ++i) { + buffer.rtcTimerPage[i] = gb->memory.mbcState.tama5.rtcTimerPage[i * 2] & 0xF; + buffer.rtcTimerPage[i] |= gb->memory.mbcState.tama5.rtcTimerPage[i * 2 + 1] << 4; + buffer.rtcAlarmPage[i] = gb->memory.mbcState.tama5.rtcAlarmPage[i * 2] & 0xF; + buffer.rtcAlarmPage[i] |= gb->memory.mbcState.tama5.rtcAlarmPage[i * 2 + 1] << 4; + buffer.rtcFreePage0[i] = gb->memory.mbcState.tama5.rtcFreePage0[i * 2] & 0xF; + buffer.rtcFreePage0[i] |= gb->memory.mbcState.tama5.rtcFreePage0[i * 2 + 1] << 4; + buffer.rtcFreePage1[i] = gb->memory.mbcState.tama5.rtcFreePage1[i * 2] & 0xF; + buffer.rtcFreePage1[i] |= gb->memory.mbcState.tama5.rtcFreePage1[i * 2 + 1] << 4; + } + STORE_64LE(gb->memory.rtcLastLatch, 0, &buffer.latchedUnix); + + _GBMBCAppendSaveSuffix(gb, &buffer, sizeof(buffer)); +} diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c new file mode 100644 index 000000000..157dd34e6 --- /dev/null +++ b/src/gb/mbc/unlicensed.c @@ -0,0 +1,469 @@ +/* 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 "gb/mbc/mbc-private.h" + +#include + +void _GBWisdomTree(struct GB* gb, uint16_t address, uint8_t value) { + UNUSED(value); + int bank = address & 0x3F; + switch (address >> 14) { + case 0x0: + GBMBCSwitchBank0(gb, bank * 2); + GBMBCSwitchBank(gb, bank * 2 + 1); + break; + default: + // TODO + mLOG(GB_MBC, STUB, "Wisdom Tree unknown address: %04X:%02X", address, value); + break; + } +} + +void _GBPKJD(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address >> 13) { + case 0x2: + if (value < 8) { + memory->directSramAccess = true; + memory->activeRtcReg = 0; + } else if (value >= 0xD && value <= 0xF) { + memory->directSramAccess = false; + memory->rtcAccess = false; + memory->activeRtcReg = value - 8; + } + break; + case 0x5: + if (!memory->sramAccess) { + return; + } + switch (memory->activeRtcReg) { + case 0: + memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; + break; + case 5: + case 6: + memory->mbcState.pkjd.reg[memory->activeRtcReg - 5] = value; + break; + case 7: + switch (value) { + case 0x11: + memory->mbcState.pkjd.reg[0]--; + break; + case 0x12: + memory->mbcState.pkjd.reg[1]--; + break; + case 0x41: + memory->mbcState.pkjd.reg[0] += memory->mbcState.pkjd.reg[1]; + break; + case 0x42: + memory->mbcState.pkjd.reg[1] += memory->mbcState.pkjd.reg[0]; + break; + case 0x51: + memory->mbcState.pkjd.reg[0]++; + break; + case 0x52: + memory->mbcState.pkjd.reg[1]--; + break; + } + break; + } + return; + } + _GBMBC3(gb, address, value); +} + +uint8_t _GBPKJDRead(struct GBMemory* memory, uint16_t address) { + if (!memory->sramAccess) { + return 0xFF; + } + switch (memory->activeRtcReg) { + case 0: + return memory->sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + case 5: + case 6: + return memory->mbcState.pkjd.reg[memory->activeRtcReg - 5]; + default: + return 0; + } +} + + +static uint8_t _reorderBits(uint8_t input, const uint8_t* reorder) { + uint8_t newbyte = 0; + int i; + for(i = 0; i < 8; ++i) { + int oldbit = reorder[i]; + int newbit = i; + newbyte += ((input >> oldbit) & 1) << newbit; + } + + return newbyte; +} + +static const uint8_t _ntOld1Reorder[8] = { + 0, 2, 1, 4, 3, 5, 6, 7 +}; + +void _ntOldMulticart(struct GB* gb, uint16_t address, uint8_t value, const uint8_t reorder[8]) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address & 3) { + case 0: + mLOG(GB_MBC, STUB, "Unimplemented NT Old 1 address 0"); + break; + case 1: + value &= 0x3F; + mbcState->baseBank = value * 2; + if (mbcState->baseBank) { + GBMBCSwitchBank0(gb, mbcState->baseBank); + GBMBCSwitchBank(gb, mbcState->baseBank + 1); + } + break; + case 2: + if ((value & 0xF0) == 0xE0) { + gb->sramSize = 0x2000; + GBResizeSram(gb, gb->sramSize); + } + switch (value & 0xF) { + case 0x00: + mbcState->bankCount = 32; + break; + case 0x08: + mbcState->bankCount = 16; + break; + case 0xC: + mbcState->bankCount = 8; + break; + case 0xE: + mbcState->bankCount = 4; + break; + case 0xF: + mbcState->bankCount = 2; + break; + default: + mbcState->bankCount = 32; + break; + } + break; + case 3: + mbcState->swapped = !!(value & 0x10); + + bank = memory->currentBank; + if (mbcState->swapped) { + bank = _reorderBits(bank, reorder); + } + GBMBCSwitchBank(gb, bank); + break; + } +} + +void _GBNTOld1(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + bank &= 0x1F; + if (!bank) { + bank = 1; + } + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld1Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + break; + case 0x5: + _ntOldMulticart(gb, address, value, _ntOld1Reorder); + break; + } +} + +static const uint8_t _ntOld2Reorder[8] = { + 1, 2, 0, 3, 4, 5, 6, 7 +}; + +void _GBNTOld2(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + struct GBNTOldState* mbcState = &memory->mbcState.ntOld; + int bank = value; + + switch (address >> 12) { + case 0x0: + case 0x1: + _GBMBC3(gb, address, value); + break; + case 0x2: + case 0x3: + if (!bank) { + bank = 1; + } + if (mbcState->swapped) { + bank = _reorderBits(bank, _ntOld2Reorder); + } + if (mbcState->bankCount) { + bank &= mbcState->bankCount - 1; + } + GBMBCSwitchBank(gb, bank + mbcState->baseBank); + break; + case 0x5: + _ntOldMulticart(gb, address, value, _ntOld2Reorder); + // Fall through + case 0x4: + if (address == 0x5001) { + mbcState->rumble = !!(value & 0x80); + } + + if (mbcState->rumble) { + memory->rumble->setRumble(memory->rumble, !!(mbcState->swapped ? value & 0x08 : value & 0x02)); + } + break; + } +} + +void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + if (address >> 8 == 0x14) { + memory->mbcState.ntNew.splitMode = true; + return; + } + if (memory->mbcState.ntNew.splitMode) { + int bank = value; + if (bank < 2) { + bank = 2; + } + switch (address >> 10) { + case 8: + GBMBCSwitchHalfBank(gb, 0, bank); + return; + case 9: + GBMBCSwitchHalfBank(gb, 1, bank); + return; + } + } + _GBMBC5(gb, address, value); +} + +static const uint8_t _bbdDataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 03 - NOT KNOWN YET + { 0, 5, 1, 3, 4, 2, 6, 7 }, // 04 - Garou + { 0, 4, 2, 3, 1, 5, 6, 7 }, // 05 - Harry + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET + { 0, 1, 5, 3, 4, 2, 6, 7 }, // 07 - Digimon +}; + +static const uint8_t _bbdBankReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 00 - Normal + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 01 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 02 - NOT KNOWN YET + { 3, 4, 2, 0, 1, 5, 6, 7 }, // 03 - 0,1 unconfirmed. Digimon/Garou + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 04 - NOT KNOWN YET + { 1, 2, 3, 4, 0, 5, 6, 7 }, // 05 - 0,1 unconfirmed. Harry + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 06 - NOT KNOWN YET + { 0, 1, 2, 3, 4, 5, 6, 7 }, // 07 - NOT KNOWN YET +}; + +void _GBBBD(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2000: + value = _reorderBits(value, _bbdBankReordering[memory->mbcState.bbd.bankSwapMode]); + break; + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + if (!(memory->mbcState.bbd.dataSwapMode == 0x07 || memory->mbcState.bbd.dataSwapMode == 0x05 || memory->mbcState.bbd.dataSwapMode == 0x04 || memory->mbcState.bbd.dataSwapMode == 0x00)) { + mLOG(GB_MBC, STUB, "Bitswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); + } + break; + case 0x2080: + memory->mbcState.bbd.bankSwapMode = value & 0x07; + if (!(memory->mbcState.bbd.bankSwapMode == 0x03 || memory->mbcState.bbd.bankSwapMode == 0x05 || memory->mbcState.bbd.bankSwapMode == 0x00)) { + mLOG(GB_MBC, STUB, "Bankswap mode unsupported: %X", memory->mbcState.bbd.dataSwapMode); + } + break; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBBBDRead(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _bbdDataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + +static const uint8_t _hitekDataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 6, 5, 3, 4, 1, 2, 7 }, + { 0, 5, 6, 3, 4, 2, 1, 7 }, + { 0, 6, 2, 3, 4, 5, 1, 7 }, + { 0, 6, 1, 3, 4, 5, 2, 7 }, + { 0, 1, 6, 3, 4, 5, 2, 7 }, + { 0, 2, 6, 3, 4, 1, 5, 7 }, + { 0, 6, 2, 3, 4, 1, 5, 7 }, +}; + +static const uint8_t _hitekBankReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 3, 2, 1, 0, 4, 5, 6, 7 }, + { 2, 1, 0, 3, 4, 5, 6, 7 }, + { 1, 0, 3, 2, 4, 5, 6, 7 }, + { 0, 3, 2, 1, 4, 5, 6, 7 }, + { 2, 3, 0, 1, 4, 5, 6, 7 }, + { 3, 0, 1, 2, 4, 5, 6, 7 }, + { 2, 0, 3, 1, 4, 5, 6, 7 }, +}; + +void _GBHitek(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2000: + value = _reorderBits(value, _hitekBankReordering[memory->mbcState.bbd.bankSwapMode]); + break; + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + break; + case 0x2080: + memory->mbcState.bbd.bankSwapMode = value & 0x07; + break; + case 0x300: + // See hhugboy src/memory/mbc/MbcUnlHitek.cpp for commentary on this return + return; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _hitekDataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + +void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { + if (address > 0x2100 && address < 0x3000) { + return; + } + _GBMBC5(gb, address, value); +} + +void _GBSachen(struct GB* gb, uint16_t address, uint8_t value) { + struct GBSachenState* state = &gb->memory.mbcState.sachen; + uint8_t bank = value; + switch (address >> 13) { + case 0: + if ((state->unmaskedBank & 0x30) == 0x30) { + state->baseBank = bank; + GBMBCSwitchBank0(gb, state->baseBank & state->mask); + } + break; + case 1: + if (!bank) { + bank = 1; + } + state->unmaskedBank = bank; + bank = (bank & ~state->mask) | (state->baseBank & state->mask); + GBMBCSwitchBank(gb, bank); + break; + case 2: + if ((state->unmaskedBank & 0x30) == 0x30) { + state->mask = value; + bank = (state->unmaskedBank & ~state->mask) | (state->baseBank & state->mask); + GBMBCSwitchBank(gb, bank); + GBMBCSwitchBank0(gb, state->baseBank & state->mask); + } + break; + case 6: + if (gb->memory.mbcType == GB_UNL_SACHEN_MMC2 && state->locked == GB_SACHEN_LOCKED_DMG) { + state->locked = GB_SACHEN_LOCKED_CGB; + state->transition = 0; + } + break; + } +} + +static uint16_t _unscrambleSachen(uint16_t address) { + uint16_t unscrambled = address & 0xFFAC; + unscrambled |= (address & 0x40) >> 6; + unscrambled |= (address & 0x10) >> 3; + unscrambled |= (address & 0x02) << 3; + unscrambled |= (address & 0x01) << 6; + return unscrambled; +} + +uint8_t _GBSachenMMC1Read(struct GBMemory* memory, uint16_t address) { + struct GBSachenState* state = &memory->mbcState.sachen; + if (state->locked != GB_SACHEN_UNLOCKED && (address & 0xFF00) == 0x100) { + ++state->transition; + if (state->transition == 0x31) { + state->locked = GB_SACHEN_UNLOCKED; + } else { + address |= 0x80; + } + } + + if ((address & 0xFF00) == 0x0100) { + address = _unscrambleSachen(address); + } + + if (address < GB_BASE_CART_BANK1) { + return memory->romBase[address]; + } else if (address < GB_BASE_VRAM) { + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + } else { + return 0xFF; + } +} + +uint8_t _GBSachenMMC2Read(struct GBMemory* memory, uint16_t address) { + struct GBSachenState* state = &memory->mbcState.sachen; + if (address >= 0xC000 && state->locked == GB_SACHEN_LOCKED_DMG) { + state->transition = 0; + state->locked = GB_SACHEN_LOCKED_CGB; + } + + if (state->locked != GB_SACHEN_UNLOCKED && (address & 0x8700) == 0x0100) { + ++state->transition; + if (state->transition == 0x31) { + ++state->locked; + state->transition = 0; + } + } + + if ((address & 0xFF00) == 0x0100) { + if (state->locked == GB_SACHEN_LOCKED_CGB) { + address |= 0x80; + } + address = _unscrambleSachen(address); + } + + if (address < GB_BASE_CART_BANK1) { + return memory->romBase[address]; + } else if (address < GB_BASE_VRAM) { + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + } else { + return 0xFF; + } +} From 2cea9e6d70fecaba150b29d0bea72df5c731e20a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Oct 2022 03:29:36 -0700 Subject: [PATCH 025/159] README: Add MBC30 to the supported mappers list (closes #2686) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ce1fa0bb1..6cf621925 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ The following mappers are fully supported: - MBC2 - MBC3 - MBC3+RTC +- MBC30 - MBC5 - MBC5+Rumble - MBC7 From 153efa253c4f3913ba182e73aeb433145e3de350 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 16 Oct 2022 22:14:46 -0700 Subject: [PATCH 026/159] Qt: Fix e-Reader scanning function reentry (fixes #2693) --- CHANGES | 1 + src/platform/qt/CoreController.cpp | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 13d50691a..3a69d0996 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,7 @@ Features: Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) + - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 61774de71..37fc1b01f 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -914,7 +914,10 @@ void CoreController::scanCard(const QString& path) { if (!file.open(QIODevice::ReadOnly)) { return; } - m_eReaderData = file.read(2912); + QByteArray eReaderData = file.read(2912); + if (eReaderData.isEmpty()) { + return; + } file.seek(0); QStringList lines; @@ -936,6 +939,7 @@ void CoreController::scanCard(const QString& path) { } } scanCards(lines); + m_eReaderData = eReaderData; } else if (image.size() == QSize(989, 44) || image.size() == QSize(639, 44)) { const uchar* bits = image.constBits(); size_t size; From a305882dba28804ba755b634a8f943c26f2d0106 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Oct 2022 00:59:59 -0700 Subject: [PATCH 027/159] GB MBC: Improve Li Cheng heuristic slightly --- src/gb/mbc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gb/mbc.c b/src/gb/mbc.c index f15cba519..441baaa53 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -216,6 +216,9 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t if (cart->type == 0x01) { // Make sure we're not using a "fixed" version return GB_UNL_LI_CHENG; } + if ((0x8000 << cart->romSize) != size) { + return GB_UNL_LI_CHENG; + } break; } From ad2a7a748a6b10331b7d95b293ff56eed26c4254 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 17 Oct 2022 01:31:04 -0700 Subject: [PATCH 028/159] GB MBC: Add GGB-81 support --- CHANGES | 2 +- README.md | 1 + include/mgba/gb/interface.h | 1 + src/gb/mbc.c | 10 +++++++++- src/gb/mbc/mbc-private.h | 2 ++ src/gb/mbc/unlicensed.c | 31 +++++++++++++++++++++++++++++++ src/gb/memory.c | 2 ++ src/gb/overrides.c | 1 + src/platform/qt/GameBoy.cpp | 2 ++ 9 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 3a69d0996..1cca99040 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,6 @@ 0.11.0: (Future) Features: - - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng + - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) diff --git a/README.md b/README.md index 6cf621925..20507604e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ The following mappers are partially supported: - Sachen MMC2 (missing alternate wiring support) - BBD (missing logo switching) - Hitek (missing logo switching) +- GGB-81 (missing logo switching) - Li Cheng (missing logo switching) ### Planned features diff --git a/include/mgba/gb/interface.h b/include/mgba/gb/interface.h index 7c23bb8dd..0ec54559f 100644 --- a/include/mgba/gb/interface.h +++ b/include/mgba/gb/interface.h @@ -47,6 +47,7 @@ enum GBMemoryBankControllerType { GB_UNL_BBD = 0x220, GB_UNL_HITEK = 0x221, GB_UNL_LI_CHENG = 0x222, + GB_UNL_GGB81 = 0x223, GB_UNL_SACHEN_MMC1 = 0x230, GB_UNL_SACHEN_MMC2 = 0x231, }; diff --git a/src/gb/mbc.c b/src/gb/mbc.c index 441baaa53..27ccfe784 100644 --- a/src/gb/mbc.c +++ b/src/gb/mbc.c @@ -126,7 +126,7 @@ static struct { {"SAM2", GB_UNL_SACHEN_MMC2}, {"ROCK", GB_MBC_AUTODETECT}, // TODO {"NGHK", GB_MBC_AUTODETECT}, // TODO - {"GB81", GB_MBC_AUTODETECT}, // TODO + {"GB81", GB_UNL_GGB81}, {"TPP1", GB_MBC_AUTODETECT}, // TODO {NULL, GB_MBC_AUTODETECT}, @@ -211,6 +211,9 @@ static enum GBMemoryBankControllerType _detectUnlMBC(const uint8_t* mem, size_t return GB_UNL_BBD; } break; + case 0x79f34594: // DATA. + case 0x7e8c539b: // TD-SOFT + return GB_UNL_GGB81; case 0x20d092e2: case 0xd2b57657: if (cart->type == 0x01) { // Make sure we're not using a "fixed" version @@ -479,6 +482,11 @@ void GBMBCInit(struct GB* gb) { case GB_UNL_LI_CHENG: gb->memory.mbcWrite = _GBLiCheng; break; + case GB_UNL_GGB81: + gb->memory.mbcWrite = _GBGGB81; + gb->memory.mbcRead = _GBGGB81Read; + gb->memory.mbcReadBank1 = true; + break; case GB_UNL_SACHEN_MMC1: gb->memory.mbcWrite = _GBSachen; gb->memory.mbcRead = _GBSachenMMC1Read; diff --git a/src/gb/mbc/mbc-private.h b/src/gb/mbc/mbc-private.h index b841839f8..4a07ab716 100644 --- a/src/gb/mbc/mbc-private.h +++ b/src/gb/mbc/mbc-private.h @@ -36,6 +36,7 @@ void _GBNTNew(struct GB* gb, uint16_t address, uint8_t value); void _GBBBD(struct GB* gb, uint16_t address, uint8_t value); void _GBHitek(struct GB* gb, uint16_t address, uint8_t value); void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value); +void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value); void _GBSachen(struct GB* gb, uint16_t address, uint8_t value); uint8_t _GBMBC2Read(struct GBMemory*, uint16_t address); @@ -50,6 +51,7 @@ uint8_t _GBHuC3Read(struct GBMemory*, uint16_t address); uint8_t _GBPKJDRead(struct GBMemory*, uint16_t address); uint8_t _GBBBDRead(struct GBMemory*, uint16_t address); uint8_t _GBHitekRead(struct GBMemory*, uint16_t address); +uint8_t _GBGGB81Read(struct GBMemory*, uint16_t address); uint8_t _GBSachenMMC1Read(struct GBMemory*, uint16_t address); uint8_t _GBSachenMMC2Read(struct GBMemory*, uint16_t address); diff --git a/src/gb/mbc/unlicensed.c b/src/gb/mbc/unlicensed.c index 157dd34e6..58725fa98 100644 --- a/src/gb/mbc/unlicensed.c +++ b/src/gb/mbc/unlicensed.c @@ -362,6 +362,37 @@ uint8_t _GBHitekRead(struct GBMemory* memory, uint16_t address) { } } +static const uint8_t _ggb81DataReordering[8][8] = { + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 0, 2, 1, 3, 4, 6, 5, 7 }, + { 0, 6, 5, 3, 4, 2, 1, 7 }, + { 0, 5, 1, 3, 4, 2, 6, 7 }, + { 0, 5, 2, 3, 4, 1, 6, 7 }, + { 0, 2, 6, 3, 4, 5, 1, 7 }, + { 0, 1, 6, 3, 4, 2, 5, 7 }, + { 0, 2, 5, 3, 4, 6, 1, 7 }, +}; + +void _GBGGB81(struct GB* gb, uint16_t address, uint8_t value) { + struct GBMemory* memory = &gb->memory; + switch (address & 0xF0FF) { + case 0x2001: + memory->mbcState.bbd.dataSwapMode = value & 0x07; + break; + } + _GBMBC5(gb, address, value); +} + +uint8_t _GBGGB81Read(struct GBMemory* memory, uint16_t address) { + switch (address >> 14) { + case 0: + default: + return memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)]; + case 1: + return _reorderBits(memory->romBank[address & (GB_SIZE_CART_BANK0 - 1)], _ggb81DataReordering[memory->mbcState.bbd.dataSwapMode]); + } +} + void _GBLiCheng(struct GB* gb, uint16_t address, uint8_t value) { if (address > 0x2100 && address < 0x3000) { return; diff --git a/src/gb/memory.c b/src/gb/memory.c index 8ac15dbdc..50b9b7d6f 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -816,6 +816,7 @@ void GBMemorySerialize(const struct GB* gb, struct GBSerializedState* state) { break; case GB_UNL_BBD: case GB_UNL_HITEK: + case GB_UNL_GGB81: state->memory.bbd.dataSwapMode = memory->mbcState.bbd.dataSwapMode; state->memory.bbd.bankSwapMode = memory->mbcState.bbd.bankSwapMode; break; @@ -978,6 +979,7 @@ void GBMemoryDeserialize(struct GB* gb, const struct GBSerializedState* state) { break; case GB_UNL_BBD: case GB_UNL_HITEK: + case GB_UNL_GGB81: memory->mbcState.bbd.dataSwapMode = state->memory.bbd.dataSwapMode & 0x7; memory->mbcState.bbd.bankSwapMode = state->memory.bbd.bankSwapMode & 0x7; break; diff --git a/src/gb/overrides.c b/src/gb/overrides.c index ca1a8e9bf..20e489b9a 100644 --- a/src/gb/overrides.c +++ b/src/gb/overrides.c @@ -695,6 +695,7 @@ static const struct GBCartridgeOverride _overrides[] = { { 0xBC75D7B8, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Pokemon - Mewtwo Strikes Back { 0xFF0B60CC, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // Shuma Baolong 02 4 { 0x14A992A6, GB_MODEL_AUTODETECT, GB_UNL_NT_NEW, { 0 } }, // /Street Fighter Zero 4 + { 0x3EF5AFB2, GB_MODEL_AUTODETECT, GB_UNL_LI_CHENG, { 0 } }, // Pokemon Jade Version (Telefang Speed bootleg) { 0, 0, 0, { 0 } } }; diff --git a/src/platform/qt/GameBoy.cpp b/src/platform/qt/GameBoy.cpp index 0c1ec8a9b..b07982721 100644 --- a/src/platform/qt/GameBoy.cpp +++ b/src/platform/qt/GameBoy.cpp @@ -40,6 +40,7 @@ static const QList s_mbcList{ GB_UNL_NT_NEW, GB_UNL_BBD, GB_UNL_HITEK, + GB_UNL_GGB81, GB_UNL_LI_CHENG, GB_UNL_SACHEN_MMC1, GB_UNL_SACHEN_MMC2, @@ -97,6 +98,7 @@ QString GameBoy::mbcName(GBMemoryBankControllerType mbc) { s_mbcNames[GB_UNL_PKJD] = tr("Pokémon Jade/Diamond"); s_mbcNames[GB_UNL_BBD] = tr("BBD"); s_mbcNames[GB_UNL_HITEK] = tr("Hitek"); + s_mbcNames[GB_UNL_GGB81] = tr("GGB-81"); s_mbcNames[GB_UNL_LI_CHENG] = tr("Li Cheng"); s_mbcNames[GB_UNL_SACHEN_MMC1] = tr("Sachen (MMC1)"); s_mbcNames[GB_UNL_SACHEN_MMC2] = tr("Sachen (MMC2)"); From 75155738a510c2c4f1a12a74ba6ac777b0862e04 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 18 Oct 2022 01:39:15 -0700 Subject: [PATCH 029/159] GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes #2694) --- CHANGES | 2 ++ src/gb/io.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 1cca99040..1545fbd30 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,8 @@ Features: - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints +Emulation fixes: + - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/src/gb/io.c b/src/gb/io.c index c6ad66c36..06a7927cd 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -754,7 +754,7 @@ void GBIODeserialize(struct GB* gb, const struct GBSerializedState* state) { gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_SCX, state->io[GB_REG_SCX]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WY, state->io[GB_REG_WY]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_WX, state->io[GB_REG_WX]); - if (gb->model & GB_MODEL_SGB) { + if (gb->model == GB_MODEL_SGB) { gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_BGP, state->io[GB_REG_BGP]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP0, state->io[GB_REG_OBP0]); gb->video.renderer->writeVideoRegister(gb->video.renderer, GB_REG_OBP1, state->io[GB_REG_OBP1]); From 879e7561cc42f561623f0116606859a7a6d96c52 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 19 Oct 2022 04:15:41 -0700 Subject: [PATCH 030/159] Qt: Keep track of current pslette preset name (fixes #2680) --- CHANGES | 1 + src/platform/qt/SettingsView.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 1545fbd30..98658856c 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Other fixes: Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - macOS: Add category to plist (closes mgba.io/i/2691) + - Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680) 0.10.0: (2022-10-11) Features: diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index 570b15d6c..cba481dc8 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -296,9 +296,14 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC } const GBColorPreset* colorPresets; + QString usedPreset = m_controller->getQtOption("gb.pal").toString(); size_t nPresets = GBColorPresetList(&colorPresets); for (size_t i = 0; i < nPresets; ++i) { - m_ui.colorPreset->addItem(QString(colorPresets[i].name)); + QString presetName(colorPresets[i].name); + m_ui.colorPreset->addItem(presetName); + if (usedPreset == presetName) { + m_ui.colorPreset->setCurrentIndex(i); + } } connect(m_ui.colorPreset, static_cast(&QComboBox::currentIndexChanged), this, [this, colorPresets](int n) { const GBColorPreset* preset = &colorPresets[n]; @@ -640,6 +645,7 @@ void SettingsView::updateConfig() { m_controller->setOption(color.toUtf8().constData(), m_gbColors[colorId] & ~0xFF000000); } + m_controller->setQtOption("gb.pal", m_ui.colorPreset->currentText()); int gbColors = GB_COLORS_CGB; if (m_ui.gbColor->isChecked()) { From 75da9f0a9444761c891c2c4291587619ccfb3fcd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 19 Oct 2022 04:33:03 -0700 Subject: [PATCH 031/159] Qt: Fix Discord Rich Presence if the game title is excessively, extremely, overly, ridiculously, very very long (fixes #2697) --- src/platform/qt/DiscordCoordinator.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/platform/qt/DiscordCoordinator.cpp b/src/platform/qt/DiscordCoordinator.cpp index b090863cd..72ca4cc1b 100644 --- a/src/platform/qt/DiscordCoordinator.cpp +++ b/src/platform/qt/DiscordCoordinator.cpp @@ -22,7 +22,7 @@ namespace DiscordCoordinator { static bool s_gameRunning = false; static bool s_inited = false; -static QString s_title; +static QByteArray s_title; static void updatePresence() { if (!s_inited) { @@ -30,7 +30,7 @@ static void updatePresence() { } if (s_gameRunning) { DiscordRichPresence discordPresence{}; - discordPresence.details = s_title.toUtf8().constData(); + discordPresence.details = s_title.constData(); discordPresence.instance = 1; discordPresence.largeImageKey = "mgba"; #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)) @@ -74,8 +74,10 @@ void gameStarted(std::shared_ptr controller) { s_title = controller->thread()->core->dirs.baseName; QString dbTitle = controller->dbTitle(); if (!dbTitle.isNull()) { - s_title = dbTitle; + s_title = dbTitle.toUtf8(); } + // Non-const QByteArrays are null-terminated so we don't need to append null even after truncation + s_title.truncate(128); updatePresence(); } From 981d01134b15c1d8214d9a7e5944879852588063 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 20 Oct 2022 20:11:19 -0700 Subject: [PATCH 032/159] macOS: Fix modern build with libepoxy (fixes #2700) --- CHANGES | 1 + CMakeLists.txt | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 98658856c..25e4f5efe 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Other fixes: Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - macOS: Add category to plist (closes mgba.io/i/2691) + - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680) 0.10.0: (2022-10-11) diff --git a/CMakeLists.txt b/CMakeLists.txt index ce8e4d687..a00e93371 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -718,8 +718,12 @@ if (USE_LZMA) endif() if(USE_EPOXY) - list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) - list(APPEND FEATURE_DEFINES BUILD_GL BUILD_GLES2 BUILD_GLES3) + if(NOT APPLE OR NOT MACOSX_SDK VERSION_GREATER 10.14) + list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gl.c) + list(APPEND FEATURE_DEFINES BUILD_GL) + endif() + list(APPEND FEATURE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/opengl/gles2.c) + list(APPEND FEATURE_DEFINES BUILD_GLES2 BUILD_GLES3) list(APPEND FEATURES EPOXY) include_directories(AFTER ${EPOXY_INCLUDE_DIRS}) link_directories(${EPOXY_LIBRARY_DIRS}) From 73afc7d7f703bb6a4d001969627fb212d88f7d99 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Oct 2022 19:33:34 -0700 Subject: [PATCH 033/159] 3DS: Allow loading ROM out of romfs if included --- src/platform/3ds/CMakeLists.txt | 2 +- src/platform/3ds/main.c | 63 +++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/platform/3ds/CMakeLists.txt b/src/platform/3ds/CMakeLists.txt index 7fd3e270f..893af0317 100644 --- a/src/platform/3ds/CMakeLists.txt +++ b/src/platform/3ds/CMakeLists.txt @@ -1,4 +1,4 @@ -set(USE_VFS_3DS ON CACHE BOOL "Use 3DS-specific file support") +set(USE_VFS_3DS OFF CACHE BOOL "Use 3DS-specific file support") mark_as_advanced(USE_VFS_3DS) find_program(3DSLINK 3dslink) diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index 71fcc1aa2..fad2fd96b 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -817,7 +817,43 @@ THREAD_ENTRY _core2Test(void* context) { UNUSED(context); } -int main() { +bool setupRomfs(char* initialPath, size_t outLength, struct mGUIRunner* runner) { + int fd = open("romfs:/filename", O_RDONLY); + strcpy(initialPath, "romfs:/"); + if (fd < 0) { + return false; + } + size_t len = strlen(initialPath); + ssize_t size = read(fd, initialPath + len, outLength - len); + if (size > 0 && initialPath[len + size - 1] == '\n') { + initialPath[len + size - 1] = '\0'; + } + close(fd); + if (size <= 0) { + return false; + } + char basedir[64]; + mCoreConfigDirectory(basedir, sizeof(basedir)); + strlcat(basedir, "/forwarders", sizeof(basedir)); + FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, basedir), 0); + + mCoreConfigSetValue(&runner->config, "savegamePath", basedir); + mCoreConfigSetValue(&runner->config, "savestatePath", basedir); + mCoreConfigSetValue(&runner->config, "screenshotPath", basedir); + mCoreConfigSetValue(&runner->config, "cheatsPath", basedir); + return true; +} + +int main(int argc, char* argv[]) { + char initialPath[PATH_MAX] = { 0 }; + if (argc > 1) { + strncpy(initialPath, argv[1], sizeof(PATH_MAX)); + } else { + u8 hmac[0x20]; + memset(hmac, 0, sizeof(hmac)); + APT_ReceiveDeliverArg(initialPath, sizeof(initialPath), hmac, NULL, NULL); + } + rotation.d.sample = _sampleRotation; rotation.d.readTiltX = _readTiltX; rotation.d.readTiltY = _readTiltY; @@ -1046,9 +1082,32 @@ int main() { _map3DSKey(&runner.params.keyMap, KEY_CSTICK_UP, mGUI_INPUT_INCREASE_BRIGHTNESS); _map3DSKey(&runner.params.keyMap, KEY_CSTICK_DOWN, mGUI_INPUT_DECREASE_BRIGHTNESS); - mGUIRunloop(&runner); + Result res = romfsInit(); + bool useRomfs = false; + if (R_SUCCEEDED(res)) { + useRomfs = setupRomfs(initialPath, sizeof(initialPath), &runner); + if (!useRomfs) { + romfsExit(); + _cleanup(); + return 1; + } + } + + if (initialPath[0] == '/' || useRomfs) { + size_t i; + for (i = 0; runner.keySources[i].id; ++i) { + mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); + } + mGUIRun(&runner, initialPath); + } else { + mGUIRunloop(&runner); + } + mGUIDeinit(&runner); + if (useRomfs) { + romfsExit(); + } _cleanup(); return 0; } From 85e66155f2845fa2a0e720b3ddb6c0415d3bf95a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Oct 2022 23:34:28 -0700 Subject: [PATCH 034/159] Vita: Allow loading ROM out of app0 if included --- src/feature/gui/gui-runner.c | 44 ++++++++++++++++++++++++++++++++++++ src/feature/gui/gui-runner.h | 4 ++++ src/platform/3ds/main.c | 29 +----------------------- src/platform/psp2/main.c | 13 ++++++++++- 4 files changed, 61 insertions(+), 29 deletions(-) diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 64488dcb8..820f1cb07 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -19,6 +19,12 @@ #include #include +#ifdef PSP2 +#include +#elif defined(__3DS__) +#include +#endif + #include mLOG_DECLARE_CATEGORY(GUI_RUNNER); @@ -770,6 +776,44 @@ void mGUIRunloop(struct mGUIRunner* runner) { } } +#if defined(__3DS__) || defined(PSP2) +bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength) { +#ifdef PSP2 + int fd = open("app0:/filename", O_RDONLY); + strcpy(out, "app0:/"); +#elif defined(__3DS__) + int fd = open("romfs:/filename", O_RDONLY); + strcpy(out, "romfs:/"); +#endif + if (fd < 0) { + return false; + } + size_t len = strlen(out); + ssize_t size = read(fd, out + len, outLength - len); + if (size > 0 && out[len + size - 1] == '\n') { + out[len + size - 1] = '\0'; + } + close(fd); + if (size <= 0) { + return false; + } + char basedir[64]; + mCoreConfigDirectory(basedir, sizeof(basedir)); + strlcat(basedir, "/forwarders", sizeof(basedir)); +#ifdef PSP2 + sceIoMkdir(basedir, 0777); +#elif defined(__3DS__) + FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, basedir), 0); +#endif + + mCoreConfigSetValue(&runner->config, "savegamePath", basedir); + mCoreConfigSetValue(&runner->config, "savestatePath", basedir); + mCoreConfigSetValue(&runner->config, "screenshotPath", basedir); + mCoreConfigSetValue(&runner->config, "cheatsPath", basedir); + return true; +} +#endif + #ifndef DISABLE_THREADING THREAD_ENTRY mGUIAutosaveThread(void* context) { struct mGUIAutosaveContext* autosave = context; diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index ea18cc5b8..860b96b99 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -98,6 +98,10 @@ void mGUIDeinit(struct mGUIRunner*); void mGUIRun(struct mGUIRunner*, const char* path); void mGUIRunloop(struct mGUIRunner*); +#if defined(__3DS__) || defined(PSP2) +bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength); +#endif + #ifndef DISABLE_THREADING THREAD_ENTRY mGUIAutosaveThread(void* context); #endif diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index fad2fd96b..b19a0ca2f 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -817,33 +817,6 @@ THREAD_ENTRY _core2Test(void* context) { UNUSED(context); } -bool setupRomfs(char* initialPath, size_t outLength, struct mGUIRunner* runner) { - int fd = open("romfs:/filename", O_RDONLY); - strcpy(initialPath, "romfs:/"); - if (fd < 0) { - return false; - } - size_t len = strlen(initialPath); - ssize_t size = read(fd, initialPath + len, outLength - len); - if (size > 0 && initialPath[len + size - 1] == '\n') { - initialPath[len + size - 1] = '\0'; - } - close(fd); - if (size <= 0) { - return false; - } - char basedir[64]; - mCoreConfigDirectory(basedir, sizeof(basedir)); - strlcat(basedir, "/forwarders", sizeof(basedir)); - FSUSER_CreateDirectory(sdmcArchive, fsMakePath(PATH_ASCII, basedir), 0); - - mCoreConfigSetValue(&runner->config, "savegamePath", basedir); - mCoreConfigSetValue(&runner->config, "savestatePath", basedir); - mCoreConfigSetValue(&runner->config, "screenshotPath", basedir); - mCoreConfigSetValue(&runner->config, "cheatsPath", basedir); - return true; -} - int main(int argc, char* argv[]) { char initialPath[PATH_MAX] = { 0 }; if (argc > 1) { @@ -1085,7 +1058,7 @@ int main(int argc, char* argv[]) { Result res = romfsInit(); bool useRomfs = false; if (R_SUCCEEDED(res)) { - useRomfs = setupRomfs(initialPath, sizeof(initialPath), &runner); + useRomfs = mGUIGetRom(&runner, initialPath, sizeof(initialPath)); if (!useRomfs) { romfsExit(); _cleanup(); diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index c474d9d85..d66ba8494 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -153,6 +153,8 @@ static enum GUIKeyboardStatus _keyboardRun(struct GUIKeyboardParams* keyboard) { } int main() { + char initialPath[PATH_MAX] = { 0 }; + vita2d_init(); struct GUIFont* font = GUIFontCreate(); struct mGUIRunner runner = { @@ -278,7 +280,16 @@ int main() { mPSP2MapKey(&runner.params.keyMap, SCE_CTRL_SQUARE, mGUI_INPUT_SCREEN_MODE); scePowerSetArmClockFrequency(444); - mGUIRunloop(&runner); + + if (mGUIGetRom(&runner, initialPath, sizeof(initialPath))) { + size_t i; + for (i = 0; runner.keySources[i].id; ++i) { + mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); + } + mGUIRun(&runner, initialPath); + } else { + mGUIRunloop(&runner); + } vita2d_fini(); mGUIDeinit(&runner); From fcf764e3c6f62c2c3372da6854bab2493ed817c0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 22 Oct 2022 23:41:24 -0700 Subject: [PATCH 035/159] mGUI: Refactoring out common code --- src/feature/gui/gui-runner.c | 20 ++++++++++---------- src/feature/gui/gui-runner.h | 1 + src/platform/3ds/main.c | 5 +---- src/platform/psp2/main.c | 5 +---- src/platform/switch/main.c | 5 +---- src/platform/wii/main.c | 5 +---- 6 files changed, 15 insertions(+), 26 deletions(-) diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 820f1cb07..2e837891f 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -456,11 +456,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { runner->setup(runner); } if (runner->config.port && runner->keySources) { - mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port); - size_t i; - for (i = 0; runner->keySources[i].id; ++i) { - mInputMapLoad(&runner->core->inputMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); - } + mGUILoadInputMaps(runner); } mLOG(GUI_RUNNER, DEBUG, "Reseting..."); runner->core->reset(runner->core); @@ -751,11 +747,7 @@ void mGUIRun(struct mGUIRunner* runner, const char* path) { void mGUIRunloop(struct mGUIRunner* runner) { if (runner->keySources) { - mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port); - size_t i; - for (i = 0; runner->keySources[i].id; ++i) { - mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); - } + mGUILoadInputMaps(runner); } while (!runner->running || runner->running(runner)) { char path[PATH_MAX]; @@ -776,6 +768,14 @@ void mGUIRunloop(struct mGUIRunner* runner) { } } +void mGUILoadInputMaps(struct mGUIRunner* runner) { + mLOG(GUI_RUNNER, DEBUG, "Loading key sources for %s...", runner->config.port); + size_t i; + for (i = 0; runner->keySources[i].id; ++i) { + mInputMapLoad(&runner->params.keyMap, runner->keySources[i].id, mCoreConfigGetInput(&runner->config)); + } +} + #if defined(__3DS__) || defined(PSP2) bool mGUIGetRom(struct mGUIRunner* runner, char* out, size_t outLength) { #ifdef PSP2 diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index 860b96b99..fc8e85227 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -95,6 +95,7 @@ struct mGUIRunner { void mGUIInit(struct mGUIRunner*, const char* port); void mGUIDeinit(struct mGUIRunner*); +void mGUILoadInputMaps(struct mGUIRunner* runner); void mGUIRun(struct mGUIRunner*, const char* path); void mGUIRunloop(struct mGUIRunner*); diff --git a/src/platform/3ds/main.c b/src/platform/3ds/main.c index b19a0ca2f..5356130ce 100644 --- a/src/platform/3ds/main.c +++ b/src/platform/3ds/main.c @@ -1067,10 +1067,7 @@ int main(int argc, char* argv[]) { } if (initialPath[0] == '/' || useRomfs) { - size_t i; - for (i = 0; runner.keySources[i].id; ++i) { - mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); - } + mGUILoadInputMaps(&runner); mGUIRun(&runner, initialPath); } else { mGUIRunloop(&runner); diff --git a/src/platform/psp2/main.c b/src/platform/psp2/main.c index d66ba8494..747535e25 100644 --- a/src/platform/psp2/main.c +++ b/src/platform/psp2/main.c @@ -282,10 +282,7 @@ int main() { scePowerSetArmClockFrequency(444); if (mGUIGetRom(&runner, initialPath, sizeof(initialPath))) { - size_t i; - for (i = 0; runner.keySources[i].id; ++i) { - mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); - } + mGUILoadInputMaps(&runner); mGUIRun(&runner, initialPath); } else { mGUIRunloop(&runner); diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 25b6f6eca..9f1e84563 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -1061,10 +1061,7 @@ int main(int argc, char* argv[]) { } if (argc > 1) { - size_t i; - for (i = 0; runner.keySources[i].id; ++i) { - mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); - } + mGUILoadInputMaps(&runner); mGUIRun(&runner, argv[1]); } else { mGUIRunloop(&runner); diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 36bb9abfc..312388347 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -653,10 +653,7 @@ int main(int argc, char* argv[]) { } if (argc > 1) { - size_t i; - for (i = 0; runner.keySources[i].id; ++i) { - mInputMapLoad(&runner.params.keyMap, runner.keySources[i].id, mCoreConfigGetInput(&runner.config)); - } + mGUILoadInputMaps(&runner); mGUIRun(&runner, argv[1]); } else { mGUIRunloop(&runner); From 15e8b20537e754a61282a28350b5837375fb4a17 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 27 Oct 2022 02:24:39 -0700 Subject: [PATCH 036/159] Updater: Fix mUpdaterGetUpdateForChannel --- src/feature/updater.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/feature/updater.c b/src/feature/updater.c index 442a71648..e9f47f440 100644 --- a/src/feature/updater.c +++ b/src/feature/updater.c @@ -78,14 +78,7 @@ static void _updateMatch(const char* key, const char* value, void* user) { return; } const char* item = &key[dotLoc + 1]; - - struct Table* out = user; - struct mUpdate* update = HashTableLookup(out, match->channel); - if (!update) { - update = calloc(1, sizeof(*update)); - HashTableInsert(out, match->channel, update); - } - _updateUpdate(update, item, value); + _updateUpdate(match->out, item, value); } bool mUpdaterInit(struct mUpdaterContext* context, const char* manifest) { From c49f09dabc99e18a801fe454bf57d846ffc56bb1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 28 Oct 2022 00:01:21 -0700 Subject: [PATCH 037/159] Util: Add PS Vita SFO generator --- include/mgba-util/sfo.h | 30 ++++++ src/util/CMakeLists.txt | 2 + src/util/sfo.c | 228 ++++++++++++++++++++++++++++++++++++++++ src/util/test/sfo.c | 107 +++++++++++++++++++ 4 files changed, 367 insertions(+) create mode 100644 include/mgba-util/sfo.h create mode 100644 src/util/sfo.c create mode 100644 src/util/test/sfo.c diff --git a/include/mgba-util/sfo.h b/include/mgba-util/sfo.h new file mode 100644 index 000000000..89404ceca --- /dev/null +++ b/include/mgba-util/sfo.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2013-2022 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 SFO_H +#define SFO_H + +#include + +CXX_GUARD_START + +#include + +void SfoInit(struct Table* sfo); + +static inline void SfoDeinit(struct Table* sfo) { + HashTableDeinit(sfo); +} + +struct VFile; +bool SfoWrite(struct Table* sfo, struct VFile* vf); + +bool SfoAddU32Value(struct Table* sfo, const char* name, uint32_t value); +bool SfoAddStrValue(struct Table* sfo, const char* name, const char* value); +bool SfoSetTitle(struct Table* sfo, const char* title); + +CXX_GUARD_END + +#endif diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index e4d03d343..df0d84c5c 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -21,6 +21,7 @@ set(SOURCE_FILES patch-ups.c png-io.c ring-fifo.c + sfo.c text-codec.c) set(GUI_FILES @@ -31,6 +32,7 @@ set(GUI_FILES gui/menu.c) set(TEST_FILES + test/sfo.c test/string-parser.c test/string-utf8.c test/table.c diff --git a/src/util/sfo.c b/src/util/sfo.c new file mode 100644 index 000000000..90142897b --- /dev/null +++ b/src/util/sfo.c @@ -0,0 +1,228 @@ +/* Copyright (c) 2013-2022 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/. */ + +/* This code is loosely based on vita-mksfoex.c from the vitasdk + * Copyright (c) 2015 Sergi Granell + * Copyright (c) 2015 Danielle Church + * Used under the MIT license + * + * Which itself is based on mksfoex.c from the pspsdk + * Copyright (c) 2005 adresd + * Copyright (c) 2005 Marcus R. Brown + * Copyright (c) 2005 James Forshaw + * Copyright (c) 2005 John Kelley + * Copyright (c) 2005 Jesper Svennevid + * Used under the BSD 3-clause license +*/ + +#include +#include +#include + +#define PSF_MAGIC 0x46535000 +#define PSF_VERSION 0x00000101 + +struct SfoHeader { + uint32_t magic; + uint32_t version; + uint32_t keyofs; + uint32_t valofs; + uint32_t count; +}; + +struct SfoEntry { + uint16_t nameofs; + uint8_t alignment; + uint8_t type; + uint32_t valsize; + uint32_t totalsize; + uint32_t dataofs; +}; + +enum PSFType { + PSF_TYPE_BIN = 0, + PSF_TYPE_STR = 2, + PSF_TYPE_U32 = 4, +}; + +struct SfoEntryContainer { + const char* name; + enum PSFType type; + union { + const char* str; + uint32_t u32; + } data; + uint32_t size; +}; + +static struct SfoEntryContainer sfoDefaults[] = { + { "APP_VER", PSF_TYPE_STR, { .str = "00.00" } }, + { "ATTRIBUTE", PSF_TYPE_U32, { .u32 = 0x8000 } }, + { "ATTRIBUTE2", PSF_TYPE_U32, { .u32 = 0 } }, + { "ATTRIBUTE_MINOR", PSF_TYPE_U32, { .u32 = 0x10 } }, + { "BOOT_FILE", PSF_TYPE_STR, { .str = ""}, 0x20 }, + { "CATEGORY", PSF_TYPE_STR, { .str = "gd" } }, + { "CONTENT_ID", PSF_TYPE_STR, { .str = "" }, 0x30 }, + { "EBOOT_APP_MEMSIZE", PSF_TYPE_U32, { .u32 = 0 } }, + { "EBOOT_ATTRIBUTE", PSF_TYPE_U32, { .u32 = 0 } }, + { "EBOOT_PHY_MEMSIZE", PSF_TYPE_U32, { .u32 = 0 } }, + { "LAREA_TYPE", PSF_TYPE_U32, { .u32 = 0 } }, + { "NP_COMMUNICATION_ID", PSF_TYPE_STR, { .str = "" }, 0x10 }, + { "PARENTAL_LEVEL", PSF_TYPE_U32, { .u32 = 0 } }, + { "PSP2_DISP_VER", PSF_TYPE_STR, { .str = "00.000" } }, + { "PSP2_SYSTEM_VER", PSF_TYPE_U32, { .u32 = 0 } }, + { "STITLE", PSF_TYPE_STR, { .str = "Homebrew" }, 52 }, + { "TITLE", PSF_TYPE_STR, { .str = "Homebrew" }, 0x80 }, + { "TITLE_ID", PSF_TYPE_STR, { .str = "ABCD99999" } }, + { "VERSION", PSF_TYPE_STR, { .str = "00.00" } }, +}; + +bool SfoAddStrValue(struct Table* sfo, const char* name, const char* value) { + struct SfoEntryContainer* entry = HashTableLookup(sfo, name); + if (!entry) { + entry = calloc(1, sizeof(*entry)); + if (!entry) { + return false; + } + entry->name = name; + HashTableInsert(sfo, name, entry); + } + entry->type = PSF_TYPE_STR; + entry->data.str = value; + return true; +} + +bool SfoAddU32Value(struct Table* sfo, const char* name, uint32_t value) { + struct SfoEntryContainer* entry = HashTableLookup(sfo, name); + if (!entry) { + entry = calloc(1, sizeof(*entry)); + if (!entry) { + return false; + } + entry->name = name; + HashTableInsert(sfo, name, entry); + } + entry->type = PSF_TYPE_U32; + entry->data.u32 = value; + return true; +} + +bool SfoSetTitle(struct Table* sfo, const char* title) { + return SfoAddStrValue(sfo, "TITLE", title) && SfoAddStrValue(sfo, "STITLE", title); +} + +void SfoInit(struct Table* sfo) { + HashTableInit(sfo, 32, free); + + size_t i; + for (i = 0; i < sizeof(sfoDefaults) / sizeof(sfoDefaults[0]); ++i) { + struct SfoEntryContainer* entry = calloc(1, sizeof(*entry)); + memcpy(entry, &sfoDefaults[i], sizeof(*entry)); + HashTableInsert(sfo, entry->name, entry); + } +} + +#define ALIGN4(X) (((X) + 3) & ~3) + +static int _sfoSort(const void* a, const void* b) { + const struct SfoEntryContainer* ea = a; + const struct SfoEntryContainer* eb = b; + return strcmp(ea->name, eb->name); +} + +bool SfoWrite(struct Table* sfo, struct VFile* vf) { + struct SfoHeader header; + size_t count = HashTableSize(sfo); + STORE_32LE(PSF_MAGIC, 0, &header.magic); + STORE_32LE(PSF_VERSION, 0, &header.version); + STORE_32LE(count, 0, &header.count); + + struct TableIterator iter; + if (!TableIteratorStart(sfo, &iter)) { + return false; + } + + struct SfoEntryContainer* sortedEntries = calloc(count, sizeof(struct SfoEntryContainer)); + + uint32_t keysSize = 0; + uint32_t dataSize = 0; + size_t i = 0; + do { + memcpy(&sortedEntries[i], TableIteratorGetValue(sfo, &iter), sizeof(struct SfoEntryContainer)); + keysSize += strlen(sortedEntries[i].name) + 1; + if (!sortedEntries[i].size) { + switch (sortedEntries[i].type) { + case PSF_TYPE_STR: + sortedEntries[i].size = strlen(sortedEntries[i].data.str) + 1; + break; + case PSF_TYPE_U32: + sortedEntries[i].size = 4; + break; + } + } + dataSize += ALIGN4(sortedEntries[i].size); + ++i; + } while (TableIteratorNext(sfo, &iter)); + + keysSize = ALIGN4(keysSize); + + qsort(sortedEntries, count, sizeof(struct SfoEntryContainer), _sfoSort); + + uint32_t keysOffset = 0; + uint32_t dataOffset = 0; + + char* keys = calloc(1, keysSize); + char* data = calloc(1, dataSize); + + struct SfoEntry* entries = calloc(count, sizeof(struct SfoEntry)); + for (i = 0; i < count; ++i) { + STORE_16LE(keysOffset, 0, &entries[i].nameofs); + STORE_32LE(dataOffset, 0, &entries[i].dataofs); + entries[i].alignment = 4; + entries[i].type = sortedEntries[i].type; + + strcpy(&keys[keysOffset], sortedEntries[i].name); + keysOffset += strlen(sortedEntries[i].name) + 1; + + if (sortedEntries[i].type == PSF_TYPE_U32) { + STORE_32LE(4, 0, &entries[i].valsize); + STORE_32LE(4, 0, &entries[i].totalsize); + STORE_32LE(sortedEntries[i].data.u32, dataOffset, data); + dataOffset += 4; + } else { + STORE_32LE(ALIGN4(sortedEntries[i].size), 0, &entries[i].totalsize); + + memset(&data[dataOffset], 0, ALIGN4(sortedEntries[i].size)); + if (sortedEntries[i].data.str) { + STORE_32LE(strlen(sortedEntries[i].data.str) + 1, 0, &entries[i].valsize); + strncpy(&data[dataOffset], sortedEntries[i].data.str, sortedEntries[i].size); + } else { + STORE_32LE(sortedEntries[i].size, 0, &entries[i].valsize); + } + dataOffset += ALIGN4(sortedEntries[i].size); + } + } + + if (keysSize != ALIGN4(keysOffset) || dataSize != dataOffset) { + abort(); + } + + free(sortedEntries); + + STORE_32LE(count * sizeof(struct SfoEntry) + sizeof(header), 0, &header.keyofs); + STORE_32LE(count * sizeof(struct SfoEntry) + sizeof(header) + keysSize, 0, &header.valofs); + + vf->write(vf, &header, sizeof(header)); + vf->write(vf, entries, sizeof(entries[0]) * count); + vf->write(vf, keys, keysSize); + vf->write(vf, data, dataSize); + + free(entries); + free(keys); + free(data); + + return true; +} diff --git a/src/util/test/sfo.c b/src/util/test/sfo.c new file mode 100644 index 000000000..54f923ae9 --- /dev/null +++ b/src/util/test/sfo.c @@ -0,0 +1,107 @@ +/* Copyright (c) 2013-2022 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 "util/test/suite.h" + +#include +#include + +const char defaultMksfoex[] = { + 0x00, 0x50, 0x53, 0x46, 0x01, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, + 0x30, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x04, 0x04, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x2d, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x37, 0x00, 0x04, 0x02, 0x03, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x02, + 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x4b, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x6d, 0x00, 0x04, 0x04, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x74, 0x00, 0x00, 0x00, 0x8a, 0x00, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x04, 0x04, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0xad, 0x00, 0x04, 0x02, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x00, 0x00, 0xbb, 0x00, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x04, 0x02, + 0x09, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, + 0xd2, 0x00, 0x04, 0x02, 0x09, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x04, 0x02, 0x0a, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x4c, 0x01, 0x00, 0x00, 0xe1, 0x00, 0x04, 0x02, + 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x41, 0x50, 0x50, 0x5f, 0x56, 0x45, 0x52, 0x00, 0x41, 0x54, 0x54, 0x52, + 0x49, 0x42, 0x55, 0x54, 0x45, 0x00, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, + 0x55, 0x54, 0x45, 0x32, 0x00, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, 0x55, + 0x54, 0x45, 0x5f, 0x4d, 0x49, 0x4e, 0x4f, 0x52, 0x00, 0x42, 0x4f, 0x4f, + 0x54, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x00, 0x43, 0x41, 0x54, 0x45, 0x47, + 0x4f, 0x52, 0x59, 0x00, 0x43, 0x4f, 0x4e, 0x54, 0x45, 0x4e, 0x54, 0x5f, + 0x49, 0x44, 0x00, 0x45, 0x42, 0x4f, 0x4f, 0x54, 0x5f, 0x41, 0x50, 0x50, + 0x5f, 0x4d, 0x45, 0x4d, 0x53, 0x49, 0x5a, 0x45, 0x00, 0x45, 0x42, 0x4f, + 0x4f, 0x54, 0x5f, 0x41, 0x54, 0x54, 0x52, 0x49, 0x42, 0x55, 0x54, 0x45, + 0x00, 0x45, 0x42, 0x4f, 0x4f, 0x54, 0x5f, 0x50, 0x48, 0x59, 0x5f, 0x4d, + 0x45, 0x4d, 0x53, 0x49, 0x5a, 0x45, 0x00, 0x4c, 0x41, 0x52, 0x45, 0x41, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x00, 0x4e, 0x50, 0x5f, 0x43, 0x4f, 0x4d, + 0x4d, 0x55, 0x4e, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x49, + 0x44, 0x00, 0x50, 0x41, 0x52, 0x45, 0x4e, 0x54, 0x41, 0x4c, 0x5f, 0x4c, + 0x45, 0x56, 0x45, 0x4c, 0x00, 0x50, 0x53, 0x50, 0x32, 0x5f, 0x44, 0x49, + 0x53, 0x50, 0x5f, 0x56, 0x45, 0x52, 0x00, 0x50, 0x53, 0x50, 0x32, 0x5f, + 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x56, 0x45, 0x52, 0x00, 0x53, + 0x54, 0x49, 0x54, 0x4c, 0x45, 0x00, 0x54, 0x49, 0x54, 0x4c, 0x45, 0x00, + 0x54, 0x49, 0x54, 0x4c, 0x45, 0x5f, 0x49, 0x44, 0x00, 0x56, 0x45, 0x52, + 0x53, 0x49, 0x4f, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x2e, 0x30, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x67, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x6f, 0x6d, 0x65, 0x62, 0x72, 0x65, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x6f, 0x6d, 0x65, + 0x62, 0x72, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x42, 0x43, 0x44, 0x39, 0x39, 0x39, 0x39, + 0x39, 0x00, 0x00, 0x00, 0x30, 0x30, 0x2e, 0x30, 0x30, 0x00, 0x00, 0x00 +}; + +M_TEST_DEFINE(defaultSfo) { + struct VFile* vf = VFileMemChunk(NULL, 0); + struct Table sfo; + SfoInit(&sfo); + SfoWrite(&sfo, vf); + SfoDeinit(&sfo); + + assert_int_equal(vf->size(vf), sizeof(defaultMksfoex)); + void* buffer = vf->map(vf, sizeof(defaultMksfoex), MAP_READ); + assert_memory_equal(defaultMksfoex, buffer, sizeof(defaultMksfoex)); + + vf->unmap(vf, buffer, sizeof(defaultMksfoex)); + vf->close(vf); +} + +M_TEST_SUITE_DEFINE(Sfo, + cmocka_unit_test(defaultSfo), +) From 3b558a950961094bdb6f24aa137dbcbed786ea38 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 29 Oct 2022 01:37:52 -0700 Subject: [PATCH 038/159] Qt: Move ROM filter function to utils --- src/platform/qt/Window.cpp | 51 ++----------------------------------- src/platform/qt/Window.h | 1 - src/platform/qt/utils.cpp | 52 ++++++++++++++++++++++++++++++++++++++ src/platform/qt/utils.h | 2 ++ 4 files changed, 56 insertions(+), 50 deletions(-) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 20249f4a0..41f29dbe5 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -304,53 +304,6 @@ void Window::saveConfig() { m_config->write(); } -QString Window::getFilters() const { - QStringList filters; - QStringList formats; - -#ifdef M_CORE_GBA - QStringList gbaFormats{ - "*.gba", -#if defined(USE_LIBZIP) || defined(USE_MINIZIP) - "*.zip", -#endif -#ifdef USE_LZMA - "*.7z", -#endif -#ifdef USE_ELF - "*.elf", -#endif - "*.agb", - "*.mb", - "*.rom", - "*.bin"}; - formats.append(gbaFormats); - filters.append(tr("Game Boy Advance ROMs (%1)").arg(gbaFormats.join(QChar(' ')))); -#endif - -#ifdef M_CORE_GB - QStringList gbFormats{ - "*.gb", - "*.gbc", - "*.sgb", -#if defined(USE_LIBZIP) || defined(USE_MINIZIP) - "*.zip", -#endif -#ifdef USE_LZMA - "*.7z", -#endif - "*.rom", - "*.bin"}; - formats.append(gbFormats); - filters.append(tr("Game Boy ROMs (%1)").arg(gbFormats.join(QChar(' ')))); -#endif - - formats.removeDuplicates(); - filters.prepend(tr("All ROMs (%1)").arg(formats.join(QChar(' ')))); - filters.append(tr("%1 Video Logs (*.mvl)").arg(projectName)); - return filters.join(";;"); -} - QString Window::getFiltersArchive() const { QStringList filters; @@ -367,7 +320,7 @@ QString Window::getFiltersArchive() const { } void Window::selectROM() { - QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), getFilters()); + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), romFilters(true)); if (!filename.isEmpty()) { setController(m_manager->loadGame(filename), filename); } @@ -410,7 +363,7 @@ void Window::addDirToLibrary() { #endif void Window::replaceROM() { - QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), getFilters()); + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select ROM"), romFilters()); if (!filename.isEmpty()) { m_controller->replaceGame(filename); } diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index 52e0da965..9495cb3ee 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -187,7 +187,6 @@ private: void updateTitle(float fps = -1); - QString getFilters() const; QString getFiltersArchive() const; CoreManager* m_manager; diff --git a/src/platform/qt/utils.cpp b/src/platform/qt/utils.cpp index 8b273cb6d..b77652c6c 100644 --- a/src/platform/qt/utils.cpp +++ b/src/platform/qt/utils.cpp @@ -5,6 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "utils.h" +#include + +#include #include namespace QGBA { @@ -59,4 +62,53 @@ bool convertAddress(const QHostAddress* input, Address* output) { return true; } +QString romFilters(bool includeMvl) { + QStringList filters; + QStringList formats; + +#ifdef M_CORE_GBA + QStringList gbaFormats{ + "*.gba", +#if defined(USE_LIBZIP) || defined(USE_MINIZIP) + "*.zip", +#endif +#ifdef USE_LZMA + "*.7z", +#endif +#ifdef USE_ELF + "*.elf", +#endif + "*.agb", + "*.mb", + "*.rom", + "*.bin"}; + formats.append(gbaFormats); + filters.append(QCoreApplication::translate("QGBA", "Game Boy Advance ROMs (%1)", nullptr).arg(gbaFormats.join(QChar(' ')))); +#endif + +#ifdef M_CORE_GB + QStringList gbFormats{ + "*.gb", + "*.gbc", + "*.sgb", +#if defined(USE_LIBZIP) || defined(USE_MINIZIP) + "*.zip", +#endif +#ifdef USE_LZMA + "*.7z", +#endif + "*.rom", + "*.bin"}; + formats.append(gbFormats); + filters.append(QCoreApplication::translate("QGBA", "Game Boy ROMs (%1)", nullptr).arg(gbFormats.join(QChar(' ')))); +#endif + + formats.removeDuplicates(); + filters.prepend(QCoreApplication::translate("QGBA", "All ROMs (%1)", nullptr).arg(formats.join(QChar(' ')))); + if (includeMvl) { + filters.append(QCoreApplication::translate("QGBA", "%1 Video Logs (*.mvl)", nullptr).arg(projectName)); + } + return filters.join(";;"); +} + } diff --git a/src/platform/qt/utils.h b/src/platform/qt/utils.h index ca9ef027f..9ccc8803f 100644 --- a/src/platform/qt/utils.h +++ b/src/platform/qt/utils.h @@ -67,4 +67,6 @@ constexpr const T& clamp(const T& v, const T& lo, const T& hi) { } #endif +QString romFilters(bool includeMvl = false); + } From dd13ceb42d50c4799ca3968ef22017a586d32fa6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 29 Oct 2022 01:38:34 -0700 Subject: [PATCH 039/159] VFS: Fix minizip write returning 0 on success instead of size --- CHANGES | 1 + src/util/vfs/vfs-zip.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 25e4f5efe..581ec1405 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,7 @@ Other fixes: - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) + - VFS: Fix minizip write returning 0 on success instead of size Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - macOS: Add category to plist (closes mgba.io/i/2691) diff --git a/src/util/vfs/vfs-zip.c b/src/util/vfs/vfs-zip.c index 2d353c566..2f8234b94 100644 --- a/src/util/vfs/vfs-zip.c +++ b/src/util/vfs/vfs-zip.c @@ -593,7 +593,11 @@ ssize_t _vfzRead(struct VFile* vf, void* buffer, size_t size) { ssize_t _vfzWrite(struct VFile* vf, const void* buffer, size_t size) { struct VFileZip* vfz = (struct VFileZip*) vf; - return zipWriteInFileInZip(vfz->z, buffer, size); + int res = zipWriteInFileInZip(vfz->z, buffer, size); + if (res != ZIP_OK) { + return res; + } + return size; } void* _vfzMap(struct VFile* vf, size_t size, int flags) { From fec87062ca6a4125d449d145f38a1c6689ba94d6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 29 Oct 2022 01:39:13 -0700 Subject: [PATCH 040/159] CMake: Add another K&R warning to the -Werror list --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a00e93371..42cff1ebc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ if(NOT MSVC) # mingw32 likes to complain about using the "wrong" format strings despite them actually working set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-format") endif() - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS} -Werror=implicit-function-declaration -Werror=implicit-int") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}") else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267 /Zc:preprocessor-") From 56c9065f70d8037a1fefbe3cf67c26a1dd231796 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 29 Oct 2022 01:40:52 -0700 Subject: [PATCH 041/159] Qt: Add forwarder UI and Vita backend (closes #2267) --- src/platform/qt/CMakeLists.txt | 6 + src/platform/qt/ForwarderController.cpp | 114 ++++++ src/platform/qt/ForwarderController.h | 54 +++ src/platform/qt/ForwarderGenerator.cpp | 74 ++++ src/platform/qt/ForwarderGenerator.h | 57 +++ src/platform/qt/ForwarderGenerator3DS.cpp | 24 ++ src/platform/qt/ForwarderGenerator3DS.h | 25 ++ src/platform/qt/ForwarderGeneratorVita.cpp | 187 +++++++++ src/platform/qt/ForwarderGeneratorVita.h | 35 ++ src/platform/qt/ForwarderView.cpp | 159 ++++++++ src/platform/qt/ForwarderView.h | 42 ++ src/platform/qt/ForwarderView.ui | 426 +++++++++++++++++++++ src/platform/qt/VFileDevice.cpp | 9 + src/platform/qt/VFileDevice.h | 2 + src/platform/qt/Window.cpp | 3 + 15 files changed, 1217 insertions(+) create mode 100644 src/platform/qt/ForwarderController.cpp create mode 100644 src/platform/qt/ForwarderController.h create mode 100644 src/platform/qt/ForwarderGenerator.cpp create mode 100644 src/platform/qt/ForwarderGenerator.h create mode 100644 src/platform/qt/ForwarderGenerator3DS.cpp create mode 100644 src/platform/qt/ForwarderGenerator3DS.h create mode 100644 src/platform/qt/ForwarderGeneratorVita.cpp create mode 100644 src/platform/qt/ForwarderGeneratorVita.h create mode 100644 src/platform/qt/ForwarderView.cpp create mode 100644 src/platform/qt/ForwarderView.h create mode 100644 src/platform/qt/ForwarderView.ui diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 9b1f60288..099bb9d8e 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -94,6 +94,11 @@ set(SOURCE_FILES Display.cpp DisplayGL.cpp DisplayQt.cpp + ForwarderController.cpp + ForwarderGenerator.cpp + ForwarderGenerator3DS.cpp + ForwarderGeneratorVita.cpp + ForwarderView.cpp FrameView.cpp GBAApp.cpp GBAKeyEditor.cpp @@ -151,6 +156,7 @@ set(UI_FILES CheatsView.ui DebuggerConsole.ui DolphinConnector.ui + ForwarderView.ui FrameView.ui GIFView.ui IOViewer.ui diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp new file mode 100644 index 000000000..7130327c2 --- /dev/null +++ b/src/platform/qt/ForwarderController.cpp @@ -0,0 +1,114 @@ +/* Copyright (c) 2013-2022 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 "ForwarderController.h" + +#include +#include +#include + +#include "ConfigController.h" + +#include +#include + +using namespace QGBA; + +ForwarderController::ForwarderController(QObject* parent) + : QObject(parent) + , m_netman(new QNetworkAccessManager(this)) +{ + m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); + connect(this, &ForwarderController::buildFailed, this, [this]() { + m_inProgress = false; + }); + connect(this, &ForwarderController::buildComplete, this, [this]() { + m_inProgress = false; + }); +} + +void ForwarderController::startBuild(const QString& outFilename) { + if (m_inProgress) { + return; + } + m_inProgress = true; + m_outFilename = outFilename; + downloadManifest(); +} + +void ForwarderController::downloadManifest() { + QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl("https://mgba.io/latest.ini"))); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + gotManifest(reply); + }); + connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { + emit buildFailed(); + }); +} + +void ForwarderController::gotManifest(QNetworkReply* reply) { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + emit buildFailed(); + return; + } + + QByteArray manifest = reply->readAll(); + QString platform = m_generator->systemName(); + + mUpdaterContext context; + if (!mUpdaterInit(&context, manifest.constData())) { + emit buildFailed(); + return; + } + QString bucket = QLatin1String(mUpdaterGetBucket(&context)); + + mUpdate update; + mUpdaterGetUpdateForChannel(&context, platform.toUtf8().constData(), m_channel.toUtf8().constData(), &update); + + downloadBuild({bucket + update.path}); + mUpdaterDeinit(&context); +} + +void ForwarderController::downloadBuild(const QUrl& url) { + QString extension(QFileInfo(url.path()).suffix()); + // TODO: cache this + QString configDir(ConfigController::configDir()); + m_sourceFile.setFileName(QString("%1/%2-%3-%4.%5").arg(configDir) + .arg(projectName) + .arg(m_generator->systemName()) + .arg(channel()) + .arg(extension)); + if (!m_sourceFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + emit buildFailed(); + return; + } + QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + gotBuild(reply); + }); + + connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { + QByteArray data = reply->readAll(); + m_sourceFile.write(data); + }); +} + +void ForwarderController::gotBuild(QNetworkReply* reply) { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + emit buildFailed(); + return; + } + + QByteArray data = reply->readAll(); + m_sourceFile.write(data); + m_sourceFile.close(); + if (!m_generator->rebuild(m_sourceFile.fileName(), m_outFilename)) { + emit buildFailed(); + } else { + emit buildComplete(); + } + m_sourceFile.remove(); +} diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h new file mode 100644 index 000000000..47baa3949 --- /dev/null +++ b/src/platform/qt/ForwarderController.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include +#include + +#include "ForwarderGenerator.h" + +#include + +class QNetworkAccessManager; +class QNetworkReply; + +namespace QGBA { + +class ForwarderController : public QObject { +Q_OBJECT + +public: + ForwarderController(QObject* parent = nullptr); + + void setGenerator(std::unique_ptr&& generator) { m_generator = std::move(generator); } + ForwarderGenerator* generator() { return m_generator.get(); } + + QString channel() const { return m_channel; } + +public slots: + void startBuild(const QString& outFilename); + +signals: + void buildComplete(); + void buildFailed(); + +private slots: + void gotManifest(QNetworkReply*); + void gotBuild(QNetworkReply*); + +private: + void downloadManifest(); + void downloadBuild(const QUrl&); + + QString m_channel{"dev"}; + QString m_outFilename; + QNetworkAccessManager* m_netman; + std::unique_ptr m_generator; + QFile m_sourceFile; + bool m_inProgress = false; +}; + +} diff --git a/src/platform/qt/ForwarderGenerator.cpp b/src/platform/qt/ForwarderGenerator.cpp new file mode 100644 index 000000000..1c1c2ef94 --- /dev/null +++ b/src/platform/qt/ForwarderGenerator.cpp @@ -0,0 +1,74 @@ +/* Copyright (c) 2013-2022 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 "ForwarderGenerator.h" + +#include +#include + +#include "ForwarderGenerator3DS.h" +#include "ForwarderGeneratorVita.h" + +using namespace QGBA; + +std::unique_ptr ForwarderGenerator::createForSystem(System system) { + switch (system) { + case System::N3DS: + return std::make_unique(); + case System::VITA: + return std::make_unique(); + } + return nullptr; +} + +ForwarderGenerator::ForwarderGenerator(int imageTypes, QObject* parent) + : QObject(parent) +{ + m_images.resize(imageTypes); +} + +void ForwarderGenerator::setImage(int index, const QImage& image) { + if (index < 0 || index >= m_images.count()) { + return; + } + + m_images[index] = image; +} + +QImage ForwarderGenerator::image(int index) const { + if (index >= m_images.size()) { + return {}; + } + return m_images[index]; +} + +QByteArray ForwarderGenerator::hashRom() const { + if (m_romPath.isEmpty()) { + return {}; + } + + QFile romFile(m_romPath); + if (!romFile.open(QIODevice::ReadOnly)) { + return {}; + } + + QCryptographicHash hash(QCryptographicHash::Sha256); + if (!hash.addData(&romFile)) { + return {}; + } + + return hash.result(); +} + +QString ForwarderGenerator::systemName(ForwarderGenerator::System system) { + switch (system) { + case ForwarderGenerator::System::N3DS: + return QLatin1String("3ds"); + case ForwarderGenerator::System::VITA: + return QLatin1String("vita"); + } + + return {}; +} diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h new file mode 100644 index 000000000..1e8eedce4 --- /dev/null +++ b/src/platform/qt/ForwarderGenerator.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace QGBA { + +class ForwarderGenerator : public QObject { +Q_OBJECT + +public: + enum class System { + N3DS, + VITA, + }; + + static std::unique_ptr createForSystem(System); + + void setTitle(const QString& title) { m_title = title; } + void setRom(const QString& path) { m_romPath = path; } + void setImage(int index, const QImage&); + + QString title() const { return m_title; } + QString rom() const { return m_romPath; } + QImage image(int index) const; + + QByteArray hashRom() const; + + virtual QList> imageTypes() const = 0; + virtual System system() const = 0; + QString systemName() const { return systemName(system()); } + virtual QString extension() const = 0; + + static QString systemName(System); + + virtual bool rebuild(const QString& source, const QString& target) = 0; + +protected: + ForwarderGenerator(int imageTypes, QObject* parent = nullptr); + +private: + QString m_title; + QString m_romPath; + QVector m_images; +}; + +} diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp new file mode 100644 index 000000000..123406a3d --- /dev/null +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -0,0 +1,24 @@ +/* Copyright (c) 2013-2022 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 "ForwarderGenerator3DS.h" + +using namespace QGBA; + +ForwarderGenerator3DS::ForwarderGenerator3DS() + : ForwarderGenerator(2) +{ +} + +QList> ForwarderGenerator3DS::imageTypes() const { + return { + { tr("Icon"), QSize(48, 48) }, + { tr("Banner"), QSize(256, 128) } + }; +} + +bool ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) { + return false; +} diff --git a/src/platform/qt/ForwarderGenerator3DS.h b/src/platform/qt/ForwarderGenerator3DS.h new file mode 100644 index 000000000..ba6a6131a --- /dev/null +++ b/src/platform/qt/ForwarderGenerator3DS.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ForwarderGenerator.h" + +namespace QGBA { + +class ForwarderGenerator3DS final : public ForwarderGenerator { +Q_OBJECT + +public: + ForwarderGenerator3DS(); + + QList> imageTypes() const override; + System system() const override { return System::N3DS; } + QString extension() const override { return QLatin1String("cia"); } + + bool rebuild(const QString& source, const QString& target) override; +}; + +} diff --git a/src/platform/qt/ForwarderGeneratorVita.cpp b/src/platform/qt/ForwarderGeneratorVita.cpp new file mode 100644 index 000000000..1a52817c6 --- /dev/null +++ b/src/platform/qt/ForwarderGeneratorVita.cpp @@ -0,0 +1,187 @@ +/* Copyright (c) 2013-2022 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 "ForwarderGeneratorVita.h" + +#include +#include + +#include "VFileDevice.h" + +#include +#include + +using namespace QGBA; + +ForwarderGeneratorVita::ForwarderGeneratorVita() + : ForwarderGenerator(3) +{ +} + +QList> ForwarderGeneratorVita::imageTypes() const { + return { + { tr("Bubble"), QSize(128, 128) }, + { tr("Background"), QSize(840, 500) }, + { tr("Startup"), QSize(280, 158) } + }; +} + +bool ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) { + QString vpk = dumpVpk(source); + if (vpk.isNull()) { + return false; + } + + QFile vpkFile(vpk); + VDir* outdir = VDirOpenZip(target.toLocal8Bit().constData(), O_WRONLY | O_CREAT | O_TRUNC); + if (outdir && !copyAssets(vpk, outdir)) { + outdir->close(outdir); + outdir = nullptr; + } + vpkFile.remove(); + if (!outdir) { + return false; + } + + VFile* sfo = outdir->openFile(outdir, "sce_sys/param.sfo", O_WRONLY); + writeSfo(sfo); + sfo->close(sfo); + + QFileInfo info(rom()); + QByteArray buffer(info.fileName().toUtf8()); + VFile* filename = outdir->openFile(outdir, "filename", O_WRONLY); + filename->write(filename, buffer.constData(), buffer.size()); + filename->close(filename); + + VFile* romfileOut = outdir->openFile(outdir, buffer.constData(), O_WRONLY); + VFileDevice romfileIn(rom(), QIODevice::ReadOnly); + VFileDevice::copyFile(romfileIn, romfileOut); + romfileIn.close(); + romfileOut->close(romfileOut); + + if (!image(0).isNull()) { + injectImage(outdir, "sce_sys/icon0.png", 0); + } + if (!image(1).isNull()) { + injectImage(outdir, "sce_sys/livearea/contents/bg.png", 1); + } + if (!image(2).isNull()) { + injectImage(outdir, "sce_sys/livearea/contents/startup.png", 2); + } + + outdir->close(outdir); + + return true; +} + +QString ForwarderGeneratorVita::dumpVpk(const QString& archive) { + bool gotFile = false; + + VDir* inArchive = VFileDevice::openArchive(archive); + if (!inArchive) { + return {}; + } + for (VDirEntry* dirent = inArchive->listNext(inArchive); dirent; dirent = inArchive->listNext(inArchive)) { + if (dirent->type(dirent) != VFS_FILE) { + continue; + } + QString filename(dirent->name(dirent)); + if (!filename.endsWith(".vpk")) { + continue; + } + + VFile* outfile = VFileOpen("tmp.vpk", O_WRONLY | O_TRUNC | O_CREAT); + VFile* vpk = inArchive->openFile(inArchive, dirent->name(dirent), O_RDONLY); + VFileDevice::copyFile(vpk, outfile); + vpk->close(vpk); + outfile->close(outfile); + gotFile = true; + break; + } + inArchive->close(inArchive); + + if (gotFile) { + return QLatin1String("tmp.vpk"); + } + return {}; +} + +bool ForwarderGeneratorVita::copyAssets(const QString& vpk, VDir* outdir) { + VDir* indir = VDirOpenZip(vpk.toLocal8Bit().constData(), O_RDONLY); + if (!indir) { + return false; + } + + bool ok = true; + for (VDirEntry* dirent = indir->listNext(indir); dirent; dirent = indir->listNext(indir)) { + if (dirent->name(dirent) == QLatin1String("sce_sys/param.sfo")) { + continue; + } + if (dirent->name(dirent) == QLatin1String("sce_sys/icon0.png") && !image(0).isNull()) { + continue; + } + if (dirent->name(dirent) == QLatin1String("sce_sys/livearea/contents/bg.png") && !image(1).isNull()) { + continue; + } + if (dirent->name(dirent) == QLatin1String("sce_sys/livearea/contents/startup.png") && !image(2).isNull()) { + continue; + } + if (dirent->type(dirent) != VFS_FILE) { + continue; + } + + VFile* infile = indir->openFile(indir, dirent->name(dirent), O_RDONLY); + if (!infile) { + ok = false; + break; + } + + VFile* outfile = outdir->openFile(outdir, dirent->name(dirent), O_WRONLY); + if (!outfile) { + infile->close(infile); + ok = false; + break; + } + + VFileDevice::copyFile(infile, outfile); + + infile->close(infile); + outfile->close(outfile); + } + + indir->close(indir); + return ok; +} + +QString ForwarderGeneratorVita::makeSerial() const { + QByteArray hash = hashRom(); + quint32 hashBits = (hash[0] << 24) | (hash[1] << 16) | (hash[2] << 8) | hash[3]; + + QString serial("MFXXXXXXX"); + for (int i = 0; i < 7; ++i) { + static const char alphabet[37] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + serial[i + 2] = alphabet[hashBits % 36]; + hashBits /= 36; + } + + return serial; +} + +void ForwarderGeneratorVita::writeSfo(VFile* out) { + Table sfo; + QByteArray serial(makeSerial().toLocal8Bit()); + QByteArray titleBytes(title().toUtf8()); + SfoInit(&sfo); + SfoSetTitle(&sfo, titleBytes.constData()); + SfoAddStrValue(&sfo, "TITLE_ID", serial.constData()); + SfoWrite(&sfo, out); + SfoDeinit(&sfo); +} + +void ForwarderGeneratorVita::injectImage(VDir* out, const char* name, int index) { + VFile* outfile = out->openFile(out, name, O_WRONLY); + VFileDevice outdev(outfile); + image(index).convertToFormat(QImage::Format_Indexed8).save(&outdev, "PNG"); +} diff --git a/src/platform/qt/ForwarderGeneratorVita.h b/src/platform/qt/ForwarderGeneratorVita.h new file mode 100644 index 000000000..abb85fea1 --- /dev/null +++ b/src/platform/qt/ForwarderGeneratorVita.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ForwarderGenerator.h" + +struct VDir; +struct VFile; + +namespace QGBA { + +class ForwarderGeneratorVita final : public ForwarderGenerator { +Q_OBJECT + +public: + ForwarderGeneratorVita(); + + QList> imageTypes() const override; + System system() const override { return System::VITA; } + QString extension() const override { return QLatin1String("vpk"); } + + bool rebuild(const QString& source, const QString& target) override; + +private: + QString dumpVpk(const QString& archive); + bool copyAssets(const QString& vpk, VDir* out); + QString makeSerial() const; + void writeSfo(VFile* out); + void injectImage(VDir* out, const char* name, int index); +}; + +} diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp new file mode 100644 index 000000000..bb88c9bc3 --- /dev/null +++ b/src/platform/qt/ForwarderView.cpp @@ -0,0 +1,159 @@ +/* Copyright (c) 2013-2022 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 "ForwarderView.h" + +#include +#include + +#include "ForwarderGenerator.h" +#include "GBAApp.h" +#include "utils.h" + +using namespace QGBA; + +ForwarderView::ForwarderView(QWidget* parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + + connectBrowseButton(m_ui.romBrowse, m_ui.romFilename, tr("Select ROM file"), false, romFilters()); + connectBrowseButton(m_ui.outputBrowse, m_ui.outputFilename, tr("Select output filename"), true); + connectBrowseButton(m_ui.baseBrowse, m_ui.baseFilename, tr("Select base file")); + + connect(m_ui.romFilename, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.outputFilename, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.baseFilename, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.title, &QLineEdit::textChanged, this, &ForwarderView::validate); + connect(m_ui.baseType, qOverload(&QComboBox::currentIndexChanged), this, &ForwarderView::validate); + + connect(m_ui.imageSelect, qOverload(&QComboBox::currentIndexChanged), this, &ForwarderView::setActiveImage); + connect(m_ui.imageBrowse, &QAbstractButton::clicked, this, &ForwarderView::selectImage); + + connect(&m_controller, &ForwarderController::buildComplete, this, &QDialog::accept); + connect(&m_controller, &ForwarderController::buildFailed, this, [this]() { + QMessageBox* error = new QMessageBox(QMessageBox::Critical, tr("Build failed"), + tr("Failed to build forwarder"), + QMessageBox::Ok, this, Qt::Sheet); + error->setAttribute(Qt::WA_DeleteOnClose); + error->show(); + }); + + connect(m_ui.system3DS, &QAbstractButton::clicked, this, [this]() { + setSystem(ForwarderGenerator::System::N3DS); + }); + connect(m_ui.systemVita, &QAbstractButton::clicked, this, [this]() { + setSystem(ForwarderGenerator::System::VITA); + }); + + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, [this]() { + m_controller.generator()->setRom(m_ui.romFilename->text()); + m_controller.startBuild(m_ui.outputFilename->text()); + }); +} + +void ForwarderView::build() { + if (!m_controller.generator()) { + return; + } + m_controller.generator()->setTitle(m_ui.title->text()); + m_controller.generator()->setRom(m_ui.romFilename->text()); + m_controller.startBuild(m_ui.outputFilename->text()); +} + +void ForwarderView::validate() { + bool valid = true; + if (m_ui.romFilename->text().isEmpty()) { + valid = false; + } else if (!QFileInfo(m_ui.romFilename->text()).exists()) { + valid = false; + } + if (m_ui.outputFilename->text().isEmpty()) { + valid = false; + } + if (m_ui.title->text().isEmpty()) { + valid = false; + } + if (!m_ui.system->checkedButton()) { + valid = false; + } + if (m_ui.baseType->currentIndex() != 1) { + valid = false; + } + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); +} + +void ForwarderView::setSystem(ForwarderGenerator::System system) { + m_controller.setGenerator(ForwarderGenerator::createForSystem(system)); + auto types = m_controller.generator()->imageTypes(); + m_images.clear(); + m_images.resize(types.count()); + m_ui.imageSelect->clear(); + for (const auto& pair : types) { + m_ui.imageSelect->addItem(pair.first); + } + m_ui.imageSelect->setEnabled(true); + m_ui.imagePreview->setEnabled(true); + m_ui.imageBrowse->setEnabled(true); + m_ui.imagesLabel->setEnabled(true); + m_ui.preferredLabel->setEnabled(true); + m_ui.preferredWidth->setEnabled(true); + m_ui.preferredX->setEnabled(true); + m_ui.preferredHeight->setEnabled(true); +} + +void ForwarderView::connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save, const QString& filter) { + connect(button, &QAbstractButton::clicked, lineEdit, [this, lineEdit, save, title, filter]() { + QString filename; + if (save) { + filename = GBAApp::app()->getSaveFileName(this, title, filter); + } else { + filename = GBAApp::app()->getOpenFileName(this, title, filter); + } + if (filename.isEmpty()) { + return; + } + lineEdit->setText(filename); + }); +} + +void ForwarderView::selectImage() { + QString filename = GBAApp::app()->getOpenFileName(this, tr("Select an image"), {}); + if (filename.isEmpty()) { + return; + } + + QImage image(filename); + if (image.isNull()) { + return; + } + image = image.scaled(m_activeSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + + m_ui.imagePreview->setPixmap(QPixmap::fromImage(image)); + m_ui.useDefaultImage->setChecked(false); + m_controller.generator()->setImage(m_currentImage, image); +} + +void ForwarderView::setActiveImage(int index) { + if (index < 0) { + m_currentImage = -1; + m_activeSize = QSize(); + return; + } + if (!m_controller.generator()) { + return; + } + auto types = m_controller.generator()->imageTypes(); + if (index >= types.count()) { + return; + } + m_currentImage = index; + m_activeSize = types[index].second; + m_ui.preferredWidth->setText(QString::number(m_activeSize.width())); + m_ui.preferredHeight->setText(QString::number(m_activeSize.height())); + m_ui.imagePreview->setMaximumSize(m_activeSize); + m_ui.imagePreview->setPixmap(QPixmap::fromImage(m_controller.generator()->image(index))); +} diff --git a/src/platform/qt/ForwarderView.h b/src/platform/qt/ForwarderView.h new file mode 100644 index 000000000..cd80d65b5 --- /dev/null +++ b/src/platform/qt/ForwarderView.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +#include "ForwarderController.h" +#include "ForwarderGenerator.h" + +#include +#include + +#include "ui_ForwarderView.h" + +namespace QGBA { + +class ForwarderView : public QDialog { +Q_OBJECT + +public: + ForwarderView(QWidget* parent = nullptr); + +private slots: + void build(); + void validate(); + +private: + void setSystem(ForwarderGenerator::System); + void connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save = false, const QString& filter = {}); + void selectImage(); + void setActiveImage(int); + + ForwarderController m_controller; + QVector m_images; + int m_currentImage; + QSize m_activeSize; + + Ui::ForwarderView m_ui; +}; + +} diff --git a/src/platform/qt/ForwarderView.ui b/src/platform/qt/ForwarderView.ui new file mode 100644 index 000000000..cac27af70 --- /dev/null +++ b/src/platform/qt/ForwarderView.ui @@ -0,0 +1,426 @@ + + + QGBA::ForwarderView + + + + 0 + 0 + 710 + 465 + + + + Create forwarder + + + + + + + 0 + 0 + + + + System + + + + + + 3DS + + + system + + + + + + + Vita + + + system + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Files + + + + + + ROM file: + + + + + + + + + + Browse + + + + + + + Output filename: + + + + + + + + + + Browse + + + + + + + Forwarder base: + + + + + + + + 0 + 0 + + + + + Latest stable verison + + + + + Latest development build + + + + + Specific file + + + + + + + + false + + + Base file: + + + + + + + false + + + + + + + false + + + Browse + + + + + + + Qt::Horizontal + + + + + + + + + + Presentation + + + + + + + + Title: + + + + + + + + + + false + + + Images: + + + + + + + false + + + + 0 + 0 + + + + + + + + Use default image + + + true + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + false + + + + 0 + 0 + + + + + 32 + 32 + + + + + 128 + 128 + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + true + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + + false + + + Preferred size: + + + + + + + false + + + 0 + + + + + + + false + + + + 0 + 0 + + + + × + + + + + + + false + + + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + false + + + + 0 + 0 + + + + Select image file + + + + + + + + + + + + buttonBox + rejected() + QGBA::ForwarderView + deleteLater() + + + 354 + 439 + + + 354 + 232 + + + + + + + + diff --git a/src/platform/qt/VFileDevice.cpp b/src/platform/qt/VFileDevice.cpp index bce0ed830..9ef822cde 100644 --- a/src/platform/qt/VFileDevice.cpp +++ b/src/platform/qt/VFileDevice.cpp @@ -184,6 +184,15 @@ VDir* VFileDevice::openArchive(const QString& path) { return VDirOpenArchive(path.toUtf8().constData()); } +bool VFileDevice::copyFile(VFile* input, VFile* output) { + uint8_t buffer[0x800]; + ssize_t size; + while ((size = input->read(input, buffer, sizeof(buffer))) > 0) { + output->write(output, buffer, size); + } + return size >= 0; +} + VFileAbstractWrapper::VFileAbstractWrapper(QIODevice* iodev) : m_iodev(iodev) { diff --git a/src/platform/qt/VFileDevice.h b/src/platform/qt/VFileDevice.h index 9df1aff93..2f0a94662 100644 --- a/src/platform/qt/VFileDevice.h +++ b/src/platform/qt/VFileDevice.h @@ -42,6 +42,8 @@ public: static VDir* openDir(const QString& path); static VDir* openArchive(const QString& path); + static bool copyFile(VFile* input, VFile* output); + protected: virtual qint64 readData(char* data, qint64 maxSize) override; virtual qint64 writeData(const char* data, qint64 maxSize) override; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 41f29dbe5..02460aeb8 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -30,6 +30,7 @@ #include "Display.h" #include "DolphinConnector.h" #include "CoreController.h" +#include "ForwarderView.h" #include "FrameView.h" #include "GBAApp.h" #include "GDBController.h" @@ -1615,6 +1616,8 @@ void Window::setupMenu(QMenuBar* menubar) { m_actions.addAction(tr("Scripting..."), "scripting", this, &Window::scriptingOpen, "tools"); #endif + m_actions.addAction(tr("Create forwarder..."), "createForwarder", openTView(), "tools"); + m_actions.addSeparator("tools"); m_actions.addAction(tr("Settings..."), "settings", this, &Window::openSettingsWindow, "tools")->setRole(Action::Role::SETTINGS); m_actions.addAction(tr("Make portable"), "makePortable", this, &Window::tryMakePortable, "tools"); From 2a5417e3ce15f8ebf85c59f10c0a6e3c6ffd0c46 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 29 Oct 2022 02:02:30 -0700 Subject: [PATCH 042/159] Qt: Fix build on Qt 5.9 - 5.14 --- src/platform/qt/ForwarderController.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 7130327c2..cfad550e8 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -43,7 +43,11 @@ void ForwarderController::downloadManifest() { connect(reply, &QNetworkReply::finished, this, [this, reply]() { gotManifest(reply); }); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { +#else + connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() { +#endif emit buildFailed(); }); } From 472660a5d3429b1979a10fe45922a4173e08a910 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 30 Oct 2022 02:48:39 -0700 Subject: [PATCH 043/159] Qt: Pay down some VFile technical debt --- src/platform/qt/CoreManager.cpp | 18 ++++++++---------- src/platform/qt/VFileDevice.cpp | 8 ++++++-- src/platform/qt/VFileDevice.h | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index b5fc3e413..cac1c2560 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -31,6 +31,7 @@ void CoreManager::setMultiplayerController(MultiplayerController* multiplayer) { CoreController* CoreManager::loadGame(const QString& path) { QFileInfo info(path); if (!info.isReadable()) { + // Open specific file in archive QString fname = info.fileName(); QString base = info.path(); if (base.endsWith("/") || base.endsWith(QDir::separator())) { @@ -40,13 +41,8 @@ CoreController* CoreManager::loadGame(const QString& path) { if (dir) { VFile* vf = dir->openFile(dir, fname.toUtf8().constData(), O_RDONLY); if (vf) { - struct VFile* vfclone = VFileMemChunk(NULL, vf->size(vf)); - uint8_t buffer[2048]; - ssize_t read; - while ((read = vf->read(vf, buffer, sizeof(buffer))) > 0) { - vfclone->write(vfclone, buffer, read); - } - vf->close(vf); + struct VFile* vfclone = VFileDevice::openMemory(vf->size(vf)); + VFileDevice::copyFile(vf, vfclone); vf = vfclone; } dir->close(dir); @@ -59,15 +55,16 @@ CoreController* CoreManager::loadGame(const QString& path) { VFile* vf = nullptr; VDir* archive = VDirOpenArchive(path.toUtf8().constData()); if (archive) { + // Open first file in archive VFile* vfOriginal = VDirFindFirst(archive, [](VFile* vf) { return mCoreIsCompatible(vf) != mPLATFORM_NONE; }); if (vfOriginal) { ssize_t size = vfOriginal->size(vfOriginal); if (size > 0) { - void* mem = vfOriginal->map(vfOriginal, size, MAP_READ); - vf = VFileMemChunk(mem, size); - vfOriginal->unmap(vfOriginal, mem, size); + struct VFile* vfclone = VFileDevice::openMemory(vfOriginal->size(vfOriginal)); + VFileDevice::copyFile(vfOriginal, vfclone); + vf = vfclone; } vfOriginal->close(vfOriginal); } @@ -75,6 +72,7 @@ CoreController* CoreManager::loadGame(const QString& path) { } QDir dir(info.dir()); if (!vf) { + // Open bare file vf = VFileOpen(info.canonicalFilePath().toUtf8().constData(), O_RDONLY); } return loadGame(vf, info.fileName(), dir.canonicalPath()); diff --git a/src/platform/qt/VFileDevice.cpp b/src/platform/qt/VFileDevice.cpp index 9ef822cde..18231a263 100644 --- a/src/platform/qt/VFileDevice.cpp +++ b/src/platform/qt/VFileDevice.cpp @@ -172,8 +172,8 @@ VFile* VFileDevice::open(const QString& path, int mode) { return VFileOpen(path.toUtf8().constData(), mode); } -VFile* VFileDevice::openMemory() { - return VFileMemChunk(nullptr, 0); +VFile* VFileDevice::openMemory(quint64 size) { + return VFileMemChunk(nullptr, size); } VDir* VFileDevice::openDir(const QString& path) { @@ -186,6 +186,10 @@ VDir* VFileDevice::openArchive(const QString& path) { bool VFileDevice::copyFile(VFile* input, VFile* output) { uint8_t buffer[0x800]; + + input->seek(input, 0, SEEK_SET); + output->seek(output, 0, SEEK_SET); + ssize_t size; while ((size = input->read(input, buffer, sizeof(buffer))) > 0) { output->write(output, buffer, size); diff --git a/src/platform/qt/VFileDevice.h b/src/platform/qt/VFileDevice.h index 2f0a94662..15f79bbcb 100644 --- a/src/platform/qt/VFileDevice.h +++ b/src/platform/qt/VFileDevice.h @@ -38,7 +38,7 @@ public: static VFile* wrap(QBuffer*, QIODevice::OpenMode); static VFile* open(const QString& path, int mode); - static VFile* openMemory(); + static VFile* openMemory(quint64 size = 0); static VDir* openDir(const QString& path); static VDir* openArchive(const QString& path); From 9ed00c95b6373aa2a860a15eeb1260fbc698589b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 31 Oct 2022 19:45:38 -0700 Subject: [PATCH 044/159] Qt: Add cache dir call, for later expansion --- src/platform/qt/ApplicationUpdater.cpp | 2 +- src/platform/qt/ConfigController.cpp | 4 ++++ src/platform/qt/ConfigController.h | 1 + src/platform/qt/ForwarderController.cpp | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index 4225bd7ee..a0efca23d 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -136,7 +136,7 @@ QUrl ApplicationUpdater::parseManifest(const QByteArray& manifest) { QString ApplicationUpdater::destination() const { QFileInfo path(updateInfo().url.path()); - QDir dir(ConfigController::configDir()); + QDir dir(ConfigController::cacheDir()); // QFileInfo::completeSuffix will eat all .'s in the filename...including // ones in the version string, turning mGBA-1.0.0-win32.7z into // 0.0-win32.7z instead of the intended .7z diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index f940e4c78..1116dba79 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -382,3 +382,7 @@ const QString& ConfigController::configDir() { } return s_configDir; } + +const QString& ConfigController::cacheDir() { + return configDir(); +} diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 4b7f859fb..ecd126867 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -105,6 +105,7 @@ public: void usage(const char* arg0) const; static const QString& configDir(); + static const QString& cacheDir(); static bool isPortable(); public slots: diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index cfad550e8..9edbe50b9 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -78,7 +78,7 @@ void ForwarderController::gotManifest(QNetworkReply* reply) { void ForwarderController::downloadBuild(const QUrl& url) { QString extension(QFileInfo(url.path()).suffix()); // TODO: cache this - QString configDir(ConfigController::configDir()); + QString configDir(ConfigController::cacheDir()); m_sourceFile.setFileName(QString("%1/%2-%3-%4.%5").arg(configDir) .arg(projectName) .arg(m_generator->systemName()) From 3f24047abb288111033bd4b7939f19983c9cef01 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 31 Oct 2022 19:46:56 -0700 Subject: [PATCH 045/159] Qt: Split out some helper functions --- src/platform/qt/ForwarderGenerator.cpp | 16 +++++++++++ src/platform/qt/ForwarderGenerator.h | 2 ++ src/platform/qt/ForwarderGeneratorVita.cpp | 32 ++++++---------------- src/platform/qt/utils.cpp | 24 ++++++++++++++-- src/platform/qt/utils.h | 6 ++++ 5 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/platform/qt/ForwarderGenerator.cpp b/src/platform/qt/ForwarderGenerator.cpp index 1c1c2ef94..691c9cf92 100644 --- a/src/platform/qt/ForwarderGenerator.cpp +++ b/src/platform/qt/ForwarderGenerator.cpp @@ -72,3 +72,19 @@ QString ForwarderGenerator::systemName(ForwarderGenerator::System system) { return {}; } + +QString ForwarderGenerator::base36(const QByteArray& bytes, int length) { + static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + QString buffer(length, 'X'); + quint32 running = 0; + for (int i = 0, j = 0; i < length; ++i) { + if (running < 36) { + running <<= 8; + running |= static_cast(bytes[j]); + ++j; + } + buffer[i] = alphabet[running % 36]; + running /= 36; + } + return buffer; +} diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h index 1e8eedce4..0fdf85b2e 100644 --- a/src/platform/qt/ForwarderGenerator.h +++ b/src/platform/qt/ForwarderGenerator.h @@ -48,6 +48,8 @@ public: protected: ForwarderGenerator(int imageTypes, QObject* parent = nullptr); + static QString base36(const QByteArray&, int length); + private: QString m_title; QString m_romPath; diff --git a/src/platform/qt/ForwarderGeneratorVita.cpp b/src/platform/qt/ForwarderGeneratorVita.cpp index 1a52817c6..1f5897825 100644 --- a/src/platform/qt/ForwarderGeneratorVita.cpp +++ b/src/platform/qt/ForwarderGeneratorVita.cpp @@ -8,6 +8,7 @@ #include #include +#include "utils.h" #include "VFileDevice.h" #include @@ -77,29 +78,20 @@ bool ForwarderGeneratorVita::rebuild(const QString& source, const QString& targe } QString ForwarderGeneratorVita::dumpVpk(const QString& archive) { - bool gotFile = false; - VDir* inArchive = VFileDevice::openArchive(archive); if (!inArchive) { return {}; } - for (VDirEntry* dirent = inArchive->listNext(inArchive); dirent; dirent = inArchive->listNext(inArchive)) { + bool gotFile = extractMatchingFile(inArchive, [](VDirEntry* dirent) -> QString { if (dirent->type(dirent) != VFS_FILE) { - continue; + return {}; } QString filename(dirent->name(dirent)); if (!filename.endsWith(".vpk")) { - continue; + return {}; } - - VFile* outfile = VFileOpen("tmp.vpk", O_WRONLY | O_TRUNC | O_CREAT); - VFile* vpk = inArchive->openFile(inArchive, dirent->name(dirent), O_RDONLY); - VFileDevice::copyFile(vpk, outfile); - vpk->close(vpk); - outfile->close(outfile); - gotFile = true; - break; - } + return "tmp.vpk"; + }); inArchive->close(inArchive); if (gotFile) { @@ -156,16 +148,8 @@ bool ForwarderGeneratorVita::copyAssets(const QString& vpk, VDir* outdir) { } QString ForwarderGeneratorVita::makeSerial() const { - QByteArray hash = hashRom(); - quint32 hashBits = (hash[0] << 24) | (hash[1] << 16) | (hash[2] << 8) | hash[3]; - - QString serial("MFXXXXXXX"); - for (int i = 0; i < 7; ++i) { - static const char alphabet[37] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - serial[i + 2] = alphabet[hashBits % 36]; - hashBits /= 36; - } - + QString serial("MF"); + serial += base36(hashRom(), 7); return serial; } diff --git a/src/platform/qt/utils.cpp b/src/platform/qt/utils.cpp index b77652c6c..d6da29781 100644 --- a/src/platform/qt/utils.cpp +++ b/src/platform/qt/utils.cpp @@ -1,15 +1,17 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2022 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 - #include #include +#include "VFileDevice.h" + +#include + namespace QGBA { QString niceSizeFormat(size_t filesize) { @@ -111,4 +113,20 @@ QString romFilters(bool includeMvl) { return filters.join(";;"); } +bool extractMatchingFile(VDir* dir, std::function filter) { + for (VDirEntry* entry = dir->listNext(dir); entry; entry = dir->listNext(dir)) { + QString target = filter(entry); + if (target.isNull()) { + continue; + } + VFile* outfile = VFileOpen(target.toUtf8().constData(), O_WRONLY | O_TRUNC | O_CREAT); + VFile* infile = dir->openFile(dir, entry->name(entry), O_RDONLY); + VFileDevice::copyFile(infile, outfile); + infile->close(infile); + outfile->close(outfile); + return true; + } + return false; +} + } diff --git a/src/platform/qt/utils.h b/src/platform/qt/utils.h index 9ccc8803f..72ce74c30 100644 --- a/src/platform/qt/utils.h +++ b/src/platform/qt/utils.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -14,6 +15,10 @@ #include #include +#include + +struct VDir; +struct VDirEntry; namespace QGBA { @@ -68,5 +73,6 @@ constexpr const T& clamp(const T& v, const T& lo, const T& hi) { #endif QString romFilters(bool includeMvl = false); +bool extractMatchingFile(VDir* dir, std::function filter); } From 2b7f5ba4d067f240d349d80c2f39ed34a2740c10 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 31 Oct 2022 19:48:51 -0700 Subject: [PATCH 046/159] Qt: I wrote a function for this... --- src/platform/qt/ForwarderView.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp index bb88c9bc3..da730cf86 100644 --- a/src/platform/qt/ForwarderView.cpp +++ b/src/platform/qt/ForwarderView.cpp @@ -49,10 +49,7 @@ ForwarderView::ForwarderView(QWidget* parent) }); m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, [this]() { - m_controller.generator()->setRom(m_ui.romFilename->text()); - m_controller.startBuild(m_ui.outputFilename->text()); - }); + connect(m_ui.buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked, this, &ForwarderView::build); } void ForwarderView::build() { From 658f4e1a34db775f37e00edb17b225e05ca40c8e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 31 Oct 2022 19:50:37 -0700 Subject: [PATCH 047/159] Qt: First pass at 3DS forwarder generator --- src/platform/qt/ForwarderController.cpp | 51 ++- src/platform/qt/ForwarderController.h | 5 +- src/platform/qt/ForwarderGenerator.h | 9 +- src/platform/qt/ForwarderGenerator3DS.cpp | 347 ++++++++++++++++++++- src/platform/qt/ForwarderGenerator3DS.h | 32 +- src/platform/qt/ForwarderGeneratorVita.cpp | 10 +- src/platform/qt/ForwarderGeneratorVita.h | 2 +- src/platform/qt/resources.qrc | 1 + 8 files changed, 442 insertions(+), 15 deletions(-) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 9edbe50b9..108169cd4 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ForwarderController.h" +#include #include #include #include @@ -16,6 +17,14 @@ using namespace QGBA; +#ifdef Q_OS_WIN +const QChar LIST_SPLIT{';'}; +const char* SUFFIX = ".exe"; +#else +const QChar LIST_SPLIT{':'}; +const char* SUFFIX = ""; +#endif + ForwarderController::ForwarderController(QObject* parent) : QObject(parent) , m_netman(new QNetworkAccessManager(this)) @@ -29,15 +38,36 @@ ForwarderController::ForwarderController(QObject* parent) }); } +void ForwarderController::setGenerator(std::unique_ptr&& generator) { + m_generator = std::move(generator); + connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::buildFailed); + connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::cleanup); + connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::buildComplete); + connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::cleanup); +} + void ForwarderController::startBuild(const QString& outFilename) { if (m_inProgress) { return; } m_inProgress = true; m_outFilename = outFilename; + + QStringList neededTools = m_generator->externalTools(); + for (const auto& tool : neededTools) { + if (!toolInstalled(tool)) { + downloadForwarderKit(); + return; + } + } downloadManifest(); } +void ForwarderController::downloadForwarderKit() { + // TODO + emit buildFailed(); +} + void ForwarderController::downloadManifest() { QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl("https://mgba.io/latest.ini"))); connect(reply, &QNetworkReply::finished, this, [this, reply]() { @@ -109,10 +139,21 @@ void ForwarderController::gotBuild(QNetworkReply* reply) { QByteArray data = reply->readAll(); m_sourceFile.write(data); m_sourceFile.close(); - if (!m_generator->rebuild(m_sourceFile.fileName(), m_outFilename)) { - emit buildFailed(); - } else { - emit buildComplete(); - } + m_generator->rebuild(m_sourceFile.fileName(), m_outFilename); +} + +void ForwarderController::cleanup() { m_sourceFile.remove(); } + +bool ForwarderController::toolInstalled(const QString& tool) { + QByteArray arr = qgetenv("PATH"); + QStringList path = QString::fromUtf8(arr).split(LIST_SPLIT); + for (QDir dir : path) { + QFileInfo exe(dir, tool + SUFFIX); + if (exe.isExecutable()) { + return true; + } + } + return false; +} diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 47baa3949..391fe0e76 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -23,7 +23,7 @@ Q_OBJECT public: ForwarderController(QObject* parent = nullptr); - void setGenerator(std::unique_ptr&& generator) { m_generator = std::move(generator); } + void setGenerator(std::unique_ptr&& generator); ForwarderGenerator* generator() { return m_generator.get(); } QString channel() const { return m_channel; } @@ -40,8 +40,11 @@ private slots: void gotBuild(QNetworkReply*); private: + void downloadForwarderKit(); void downloadManifest(); void downloadBuild(const QUrl&); + bool toolInstalled(const QString& tool); + void cleanup(); QString m_channel{"dev"}; QString m_outFilename; diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h index 0fdf85b2e..c471910f2 100644 --- a/src/platform/qt/ForwarderGenerator.h +++ b/src/platform/qt/ForwarderGenerator.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -41,9 +42,15 @@ public: QString systemName() const { return systemName(system()); } virtual QString extension() const = 0; + virtual QStringList externalTools() const { return {}; } + static QString systemName(System); - virtual bool rebuild(const QString& source, const QString& target) = 0; + virtual void rebuild(const QString& source, const QString& target) = 0; + +signals: + void buildComplete(); + void buildFailed(); protected: ForwarderGenerator(int imageTypes, QObject* parent = nullptr); diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp index 123406a3d..89fc609e8 100644 --- a/src/platform/qt/ForwarderGenerator3DS.cpp +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -5,11 +5,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ForwarderGenerator3DS.h" +#include "ConfigController.h" +#include "utils.h" +#include "VFileDevice.h" + +#include +#include +#include +#include + +#include +#include + +#include + using namespace QGBA; ForwarderGenerator3DS::ForwarderGenerator3DS() : ForwarderGenerator(2) { + connect(this, &ForwarderGenerator::buildFailed, this, &ForwarderGenerator3DS::cleanup); + connect(this, &ForwarderGenerator::buildComplete, this, &ForwarderGenerator3DS::cleanup); } QList> ForwarderGenerator3DS::imageTypes() const { @@ -19,6 +35,333 @@ QList> ForwarderGenerator3DS::imageTypes() const { }; } -bool ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) { - return false; +void ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) { + m_cia = dumpCia(source); + if (m_cia.isNull()) { + emit buildFailed(); + return; + } + + m_target = target; + extractCia(); +} + +QString ForwarderGenerator3DS::dumpCia(const QString& archive) { + VDir* inArchive = VFileDevice::openArchive(archive); + if (!inArchive) { + return {}; + } + bool gotFile = extractMatchingFile(inArchive, [](VDirEntry* dirent) -> QString { + if (dirent->type(dirent) != VFS_FILE) { + return {}; + } + QString filename(dirent->name(dirent)); + if (!filename.endsWith(".cia")) { + return {}; + } + return "tmp.cia"; + }); + inArchive->close(inArchive); + + if (gotFile) { + return QLatin1String("tmp.cia"); + } + return {}; +} + +void ForwarderGenerator3DS::extractCia() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("ctrtool"); + + QStringList args; + args << QLatin1String("--contents=%0/cxi").arg(ConfigController::cacheDir()); + args << m_cia; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::extractCxi); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::extractCxi() { + QStringList output = QString::fromUtf8(m_currentProc->readAll()).split("\n"); + QString index; + for (const QString& line : output) { + if (!line.contains("|- ContentId:")) { + continue; + } + index = line.trimmed().right(8); + } + m_cxi = ConfigController::cacheDir() + "/cxi.0000." + index; + + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, m_cxi); + args << "--exh" << ConfigController::cacheDir() + "/exheader.bin"; + args << "--header" << ConfigController::cacheDir() + "/header.bin"; + args << "--exefs" << ConfigController::cacheDir() + "/exefs.bin"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::extractExefs); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::extractExefs() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, ConfigController::cacheDir() + "/exefs.bin"); + args << "--header" << ConfigController::cacheDir() + "/exeheader.bin"; + args << "--exefs-dir" << ConfigController::cacheDir() + "/exefs"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::processCxi); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::processCxi() { + QByteArray hash = hashRom(); + QByteArray tid = hash.left(4); + quint32 tidNum; + LOAD_32LE(tidNum, 0, tid.data()); + tidNum &= 0x7FFFFFF; + tidNum += 0x0300000; + STORE_32LE(tidNum, 0, tid.data()); + + QFile header(ConfigController::cacheDir() + "/header.bin"); + if (!header.open(QIODevice::ReadWrite | QIODevice::ExistingOnly)) { + emit buildFailed(); + return; + } + header.seek(0x108); + header.write(tid); + header.seek(0x118); + header.write(tid); + + QByteArray productCode("MGBA-"); + productCode += base36(hash, 11).toLatin1(); + header.seek(0x150); + header.write(productCode); + + header.seek(0x18D); + QByteArray type = header.read(3); + type[0] = type[0] | 1; // Has romfs + type[2] = type[2] & ~2; // Can mount romfs + header.seek(0x18D); + header.write(type); + header.close(); + + QFile exheader(ConfigController::cacheDir() + "/exheader.bin"); + if (!exheader.open(QIODevice::ReadWrite | QIODevice::ExistingOnly)) { + emit buildFailed(); + return; + } + exheader.seek(0x1C8); + exheader.write(tid); + exheader.seek(0x200); + exheader.write(tid); + exheader.seek(0x600); + exheader.write(tid); + exheader.close(); + + prepareRomfs(); +} + +void ForwarderGenerator3DS::prepareRomfs() { + QDir romfsDir(ConfigController::cacheDir()); + + romfsDir.mkdir("romfs"); + romfsDir.cd("romfs"); + + QFileInfo info(rom()); + QByteArray buffer(info.fileName().toUtf8()); + QFile filename(romfsDir.filePath("filename")); + if (!filename.open(QIODevice::Truncate | QIODevice::WriteOnly)) { + emit buildFailed(); + return; + } + if (filename.write(buffer) != filename.size()) { + emit buildFailed(); + return; + } + filename.close(); + + if (!QFile::copy(info.filePath(), romfsDir.filePath(info.fileName()))) { + emit buildFailed(); + return; + } + + buildRomfs(); +} + +void ForwarderGenerator3DS::buildRomfs() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, ConfigController::cacheDir() + "/romfs.bin", "romfs"); + args << "--romfs-dir"; + args << ConfigController::cacheDir() + "/romfs"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildSmdh); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::buildSmdh() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("bannertool"); + + if (image(0).isNull()) { + QFile::copy(":/res/mgba-48.png", ConfigController::cacheDir() + "/smdh.png"); + } else { + image(0).save(ConfigController::cacheDir() + "/smdh.png", "PNG"); + } + + QStringList args; + args << "makesmdh"; + + args << "-s" << title(); + args << "-l" << title(); + args << "-p" << projectName + QString(" Forwarder"); + args << "-i" << ConfigController::cacheDir() + "/smdh.png"; + args << "-o" << ConfigController::cacheDir() + "/exefs/icon.icn"; + m_currentProc->setArguments(args); + + if (image(1).isNull()) { + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildExefs); + } else { + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildBanner); + } + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::buildBanner() { + QFile banner(ConfigController::cacheDir() + "/exefs/banner.bnr"); + if (!banner.open(QIODevice::ReadOnly)) { + emit buildFailed(); + return; + } + + banner.seek(0x84); + QByteArray bcwavOffsetBuffer(banner.read(4)); + qint64 bcwavOffset; + LOAD_64LE(bcwavOffset, 0, bcwavOffsetBuffer.data()); + banner.seek(bcwavOffset); + QByteArray bcwav(banner.readAll()); + QFile bcwavFile(ConfigController::cacheDir() + "/banner.bcwav"); + if (!bcwavFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + emit buildFailed(); + return; + } + bcwavFile.write(bcwav); + banner.close(); + + m_currentProc = std::make_unique(); + m_currentProc->setProgram("bannertool"); + + image(1).save(ConfigController::cacheDir() + "/banner.png", "PNG"); + + QStringList args; + args << "makebanner"; + + args << "-i" << ConfigController::cacheDir() + "/banner.png"; + args << "-ca" << ConfigController::cacheDir() + "/banner.bcwav"; + args << "-o" << ConfigController::cacheDir() + "/exefs/banner.bnr"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildExefs); + m_currentProc->start(QIODevice::ReadOnly); +} + +void ForwarderGenerator3DS::buildExefs() { + QByteArray out = m_currentProc->readAll(); + qDebug() << out; + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QStringList args; + init3dstoolArgs(args, ConfigController::cacheDir() + "/exefs.bin", "exefs"); + args << "--header" << ConfigController::cacheDir() + "/exeheader.bin"; + args << "--exefs-dir" << ConfigController::cacheDir() + "/exefs"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildCxi); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::buildCxi() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("3dstool"); + + QFile cxi(m_cxi); + cxi.remove(); + + QStringList args; + init3dstoolArgs(args, m_cxi, "cxi"); + args << "--exh" << ConfigController::cacheDir() + "/exheader.bin"; + args << "--header" << ConfigController::cacheDir() + "/header.bin"; + args << "--exefs" << ConfigController::cacheDir() + "/exefs.bin"; + args << "--romfs" << ConfigController::cacheDir() + "/romfs.bin"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildCia); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::buildCia() { + m_currentProc = std::make_unique(); + m_currentProc->setProgram("makerom"); + + QStringList args; + args << "-f" << "cia"; + args << "-o" << m_target; + args << "-content" << m_cxi + ":0:0"; + m_currentProc->setArguments(args); + + connect(m_currentProc.get(), qOverload(&QProcess::finished), this, &ForwarderGenerator3DS::buildComplete); + m_currentProc->start(QIODevice::NotOpen); +} + +void ForwarderGenerator3DS::cleanup() { + for (const QString& path : {m_cia, m_cxi}) { + QFile file(path); + if (file.exists()) { + file.remove(); + } + } + + QDir cacheDir(ConfigController::cacheDir()); + QStringList files{ + "romfs.bin", + "exefs.bin", + "exheader.bin", + "exeheader.bin", + "header.bin", + "smdh.png", + "banner.png", + "banner.bcwav", + }; + for (QString path : files) { + QFile file(cacheDir.filePath(path)); + if (file.exists()) { + file.remove(); + } + } + + for (QString path : {"romfs", "exefs"}) { + QDir dir(cacheDir.filePath(path)); + dir.removeRecursively(); + } +} + +void ForwarderGenerator3DS::init3dstoolArgs(QStringList& args, const QString& file, const QString& createType) { + if (createType.isEmpty()) { + args << "-xf" << file; + } else { + args << "-cf" << file; + args << "-t" << createType; + } } diff --git a/src/platform/qt/ForwarderGenerator3DS.h b/src/platform/qt/ForwarderGenerator3DS.h index ba6a6131a..d50d87e42 100644 --- a/src/platform/qt/ForwarderGenerator3DS.h +++ b/src/platform/qt/ForwarderGenerator3DS.h @@ -7,6 +7,10 @@ #include "ForwarderGenerator.h" +#include + +#include + namespace QGBA { class ForwarderGenerator3DS final : public ForwarderGenerator { @@ -19,7 +23,33 @@ public: System system() const override { return System::N3DS; } QString extension() const override { return QLatin1String("cia"); } - bool rebuild(const QString& source, const QString& target) override; + virtual QStringList externalTools() const { return {"bannertool", "3dstool", "ctrtool", "makerom"}; } + + void rebuild(const QString& source, const QString& target) override; + +private slots: + void extractCia(); + void extractCxi(); + void extractExefs(); + void processCxi(); + void prepareRomfs(); + void buildRomfs(); + void buildSmdh(); + void buildBanner(); + void buildExefs(); + void buildCxi(); + void buildCia(); + + void cleanup(); + +private: + QString dumpCia(const QString& archive); + void init3dstoolArgs(QStringList& args, const QString& file, const QString& createType = {}); + + std::unique_ptr m_currentProc; + QString m_cia; + QString m_cxi; + QString m_target; }; } diff --git a/src/platform/qt/ForwarderGeneratorVita.cpp b/src/platform/qt/ForwarderGeneratorVita.cpp index 1f5897825..97b3ef802 100644 --- a/src/platform/qt/ForwarderGeneratorVita.cpp +++ b/src/platform/qt/ForwarderGeneratorVita.cpp @@ -29,10 +29,11 @@ QList> ForwarderGeneratorVita::imageTypes() const { }; } -bool ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) { +void ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) { QString vpk = dumpVpk(source); if (vpk.isNull()) { - return false; + emit buildFailed(); + return; } QFile vpkFile(vpk); @@ -43,7 +44,8 @@ bool ForwarderGeneratorVita::rebuild(const QString& source, const QString& targe } vpkFile.remove(); if (!outdir) { - return false; + emit buildFailed(); + return; } VFile* sfo = outdir->openFile(outdir, "sce_sys/param.sfo", O_WRONLY); @@ -74,7 +76,7 @@ bool ForwarderGeneratorVita::rebuild(const QString& source, const QString& targe outdir->close(outdir); - return true; + emit buildComplete(); } QString ForwarderGeneratorVita::dumpVpk(const QString& archive) { diff --git a/src/platform/qt/ForwarderGeneratorVita.h b/src/platform/qt/ForwarderGeneratorVita.h index abb85fea1..ee2a5b17e 100644 --- a/src/platform/qt/ForwarderGeneratorVita.h +++ b/src/platform/qt/ForwarderGeneratorVita.h @@ -22,7 +22,7 @@ public: System system() const override { return System::VITA; } QString extension() const override { return QLatin1String("vpk"); } - bool rebuild(const QString& source, const QString& target) override; + void rebuild(const QString& source, const QString& target) override; private: QString dumpVpk(const QString& archive); diff --git a/src/platform/qt/resources.qrc b/src/platform/qt/resources.qrc index 737ec55f0..e4b8fab5f 100644 --- a/src/platform/qt/resources.qrc +++ b/src/platform/qt/resources.qrc @@ -3,6 +3,7 @@ ../../../res/mgba-1024.png ../../../res/mgba-256.png + ../../../res/mgba-48.png ../../../res/keymap.qpic ../../../res/patrons.txt ../../../res/no-cam.png From 25bb7a9192b9380604bd51c31566f39cea8d5e79 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 31 Oct 2022 20:41:46 -0700 Subject: [PATCH 048/159] Qt: Fix build --- src/platform/qt/ForwarderGenerator3DS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp index 89fc609e8..7f435b6b9 100644 --- a/src/platform/qt/ForwarderGenerator3DS.cpp +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -74,7 +74,7 @@ void ForwarderGenerator3DS::extractCia() { m_currentProc->setProgram("ctrtool"); QStringList args; - args << QLatin1String("--contents=%0/cxi").arg(ConfigController::cacheDir()); + args << QString("--contents=%0/cxi").arg(ConfigController::cacheDir()); args << m_cia; m_currentProc->setArguments(args); From 6bdb3470e7ce90ad53efd45a38edb5c647a12dd4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 31 Oct 2022 20:45:46 -0700 Subject: [PATCH 049/159] Qt: Fine, whatever, build fixed more --- src/platform/qt/ForwarderGenerator3DS.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp index 7f435b6b9..a9e32f2ce 100644 --- a/src/platform/qt/ForwarderGenerator3DS.cpp +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -131,7 +131,7 @@ void ForwarderGenerator3DS::processCxi() { STORE_32LE(tidNum, 0, tid.data()); QFile header(ConfigController::cacheDir() + "/header.bin"); - if (!header.open(QIODevice::ReadWrite | QIODevice::ExistingOnly)) { + if (!header.open(QIODevice::ReadWrite)) { emit buildFailed(); return; } @@ -154,7 +154,7 @@ void ForwarderGenerator3DS::processCxi() { header.close(); QFile exheader(ConfigController::cacheDir() + "/exheader.bin"); - if (!exheader.open(QIODevice::ReadWrite | QIODevice::ExistingOnly)) { + if (!exheader.open(QIODevice::ReadWrite)) { emit buildFailed(); return; } From 7f30bdc850f8eb8f5abf6fe9b3c7aba9a17d7f2b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 1 Nov 2022 01:59:40 -0700 Subject: [PATCH 050/159] Qt: Initial support for forwarder-kit --- src/platform/qt/ForwarderController.cpp | 105 ++++++++++++++++++++---- src/platform/qt/ForwarderController.h | 4 + 2 files changed, 94 insertions(+), 15 deletions(-) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 108169cd4..ca4676859 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -11,9 +11,11 @@ #include #include "ConfigController.h" +#include "VFileDevice.h" #include #include +#include using namespace QGBA; @@ -28,22 +30,17 @@ const char* SUFFIX = ""; ForwarderController::ForwarderController(QObject* parent) : QObject(parent) , m_netman(new QNetworkAccessManager(this)) + , m_originalPath(qgetenv("PATH")) { m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); - connect(this, &ForwarderController::buildFailed, this, [this]() { - m_inProgress = false; - }); - connect(this, &ForwarderController::buildComplete, this, [this]() { - m_inProgress = false; - }); + connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup); + connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup); } void ForwarderController::setGenerator(std::unique_ptr&& generator) { m_generator = std::move(generator); connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::buildFailed); - connect(m_generator.get(), &ForwarderGenerator::buildFailed, this, &ForwarderController::cleanup); connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::buildComplete); - connect(m_generator.get(), &ForwarderGenerator::buildComplete, this, &ForwarderController::cleanup); } void ForwarderController::startBuild(const QString& outFilename) { @@ -53,6 +50,15 @@ void ForwarderController::startBuild(const QString& outFilename) { m_inProgress = true; m_outFilename = outFilename; +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + // Amend the path for downloaded programs forwarder-kit + QByteArray arr = m_originalPath; + QStringList path = QString::fromUtf8(arr).split(LIST_SPLIT); + path << ConfigController::cacheDir(); + arr = path.join(LIST_SPLIT).toUtf8(); + qputenv("PATH", arr); +#endif + QStringList neededTools = m_generator->externalTools(); for (const auto& tool : neededTools) { if (!toolInstalled(tool)) { @@ -64,8 +70,67 @@ void ForwarderController::startBuild(const QString& outFilename) { } void ForwarderController::downloadForwarderKit() { + QString fkUrl("https://github.com/mgba-emu/forwarder-kit/releases/latest/download/forwarder-kit-%1.zip"); +#ifdef Q_OS_WIN64 + fkUrl = fkUrl.arg("win64"); +#elif defined(Q_OS_WIN32) + fkUrl = fkUrl.arg("win32"); +#elif defined(Q_OS_MAC) && (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + // Modern macOS build + fkUrl = fkUrl.arg("macos"); +#else // TODO emit buildFailed(); + return; +#endif + QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl(fkUrl))); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + gotForwarderKit(reply); + }); + connectErrorFailure(reply); +} + +void ForwarderController::gotForwarderKit(QNetworkReply* reply) { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { + emit buildFailed(); + return; + } + + QFile fkZip(ConfigController::cacheDir() + "/forwarder-kit.zip"); + fkZip.open(QIODevice::WriteOnly | QIODevice::Truncate); + QByteArray arr; + do { + arr = reply->read(0x800); + fkZip.write(arr); + } while (!arr.isEmpty()); + fkZip.close(); + + VDir* fkDir = VFileDevice::openArchive(fkZip.fileName()); + + // This has to be done in multiple passes to avoid seeking breaking the listing + QStringList files; + for (VDirEntry* entry = fkDir->listNext(fkDir); entry; entry = fkDir->listNext(fkDir)) { + if (entry->type(entry) != VFS_FILE) { + continue; + } + files << entry->name(entry); + } + + for (const QString& source : files) { + VFile* sourceVf = fkDir->openFile(fkDir, source.toUtf8().constData(), O_RDONLY); + VFile* targetVf = VFileDevice::open(ConfigController::cacheDir() + "/" + source, O_CREAT | O_TRUNC | O_WRONLY); + VFileDevice::copyFile(sourceVf, targetVf); + sourceVf->close(sourceVf); + targetVf->close(targetVf); + + QFile target(ConfigController::cacheDir() + "/" + source); + target.setPermissions(target.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeUser); + } + + fkDir->close(fkDir); + fkZip.remove(); + + downloadManifest(); } void ForwarderController::downloadManifest() { @@ -73,13 +138,7 @@ void ForwarderController::downloadManifest() { connect(reply, &QNetworkReply::finished, this, [this, reply]() { gotManifest(reply); }); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) - connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { -#else - connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() { -#endif - emit buildFailed(); - }); + connectErrorFailure(reply); } void ForwarderController::gotManifest(QNetworkReply* reply) { @@ -128,6 +187,7 @@ void ForwarderController::downloadBuild(const QUrl& url) { QByteArray data = reply->readAll(); m_sourceFile.write(data); }); + connectErrorFailure(reply); } void ForwarderController::gotBuild(QNetworkReply* reply) { @@ -144,6 +204,11 @@ void ForwarderController::gotBuild(QNetworkReply* reply) { void ForwarderController::cleanup() { m_sourceFile.remove(); + m_inProgress = false; + +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) + qputenv("PATH", m_originalPath); +#endif } bool ForwarderController::toolInstalled(const QString& tool) { @@ -157,3 +222,13 @@ bool ForwarderController::toolInstalled(const QString& tool) { } return false; } + +void ForwarderController::connectErrorFailure(QNetworkReply* reply) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { +#else + connect(reply, qOverload<>(&QNetworkReply::error), this, [this, reply]() { +#endif + emit buildFailed(); + }); +} diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 391fe0e76..09902a397 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -38,6 +38,7 @@ signals: private slots: void gotManifest(QNetworkReply*); void gotBuild(QNetworkReply*); + void gotForwarderKit(QNetworkReply*); private: void downloadForwarderKit(); @@ -46,12 +47,15 @@ private: bool toolInstalled(const QString& tool); void cleanup(); + void connectErrorFailure(QNetworkReply*); + QString m_channel{"dev"}; QString m_outFilename; QNetworkAccessManager* m_netman; std::unique_ptr m_generator; QFile m_sourceFile; bool m_inProgress = false; + QByteArray m_originalPath; }; } From b8c7196dd9da7a56b78b43395a19f78ec57c0c0b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 1 Nov 2022 03:21:15 -0700 Subject: [PATCH 051/159] Qt: Cleanup --- src/platform/qt/ForwarderGenerator3DS.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp index a9e32f2ce..8b52d734a 100644 --- a/src/platform/qt/ForwarderGenerator3DS.cpp +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -17,8 +17,6 @@ #include #include -#include - using namespace QGBA; ForwarderGenerator3DS::ForwarderGenerator3DS() @@ -277,8 +275,6 @@ void ForwarderGenerator3DS::buildBanner() { } void ForwarderGenerator3DS::buildExefs() { - QByteArray out = m_currentProc->readAll(); - qDebug() << out; m_currentProc = std::make_unique(); m_currentProc->setProgram("3dstool"); From 694b80d289cdd1eadf47223bc2d4f2bbef0393e6 Mon Sep 17 00:00:00 2001 From: Luna Mittelbach Date: Sat, 5 Nov 2022 09:44:29 +0100 Subject: [PATCH 052/159] GBA: Verify ELF entrypoint against ROM header (#2714) --- CHANGES | 1 + include/mgba/internal/gba/gba.h | 6 ++++ src/gba/core.c | 4 +-- src/gba/gba.c | 61 +++++++++++++++++++++++++++++++-- 4 files changed, 68 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 581ec1405..e20cf3b5b 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Other fixes: - VFS: Fix minizip write returning 0 on success instead of size Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) + - GBA: Improve detection of valid ELF ROMs - macOS: Add category to plist (closes mgba.io/i/2691) - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680) diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 206aedf70..020dd2ab5 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -154,6 +154,12 @@ void GBAHalt(struct GBA* gba); void GBAStop(struct GBA* gba); void GBADebug(struct GBA* gba, uint16_t value); +#ifdef USE_ELF +struct ELF; + +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target); +#endif + #ifdef USE_DEBUGGERS struct mDebugger; void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger); diff --git a/src/gba/core.c b/src/gba/core.c index 5b134338f..68ea66c79 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -237,7 +237,7 @@ static bool _GBACoreInit(struct mCore* core) { #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mDirectorySetInit(&core->dirs); #endif - + return true; } @@ -512,7 +512,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - if (ELFEntry(elf) == BASE_CART0) { + if (GBAVerifyELFEntry(elf, BASE_CART0)) { GBALoadNull(core->board); } bool success = mCoreLoadELF(core, elf); diff --git a/src/gba/gba.c b/src/gba/gba.c index bd0353d0c..de975855a 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -595,6 +595,63 @@ void GBADebug(struct GBA* gba, uint16_t flags) { gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags); } +#ifdef USE_ELF +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target) { + if (ELFEntry(elf) == target) { + return true; + } + + struct ELFProgramHeaders ph; + ELFProgramHeadersInit(&ph, 0); + ELFGetProgramHeaders(elf, &ph); + size_t i; + for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) { + Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i); + if (!phdr->p_filesz) { + continue; + } + + size_t phdrS = phdr->p_paddr; + size_t phdrE = phdrS + phdr->p_filesz; + + // Does the segment contain our target address? + if (target < phdrS || target + 4 > phdrE) { + continue; + } + + // File offset to what should be the rom entry instruction + size_t off = phdr->p_offset + target - phdrS; + + size_t eSize; + const char* bytes = ELFBytes(elf, &eSize); + + // Bounds and alignment check + if (off >= eSize || off & 3) { + continue; + } + + uint32_t opcode; + LOAD_32(opcode, off, bytes); + struct ARMInstructionInfo info; + ARMDecodeARM(opcode, &info); + + if (info.branchType != ARM_BRANCH && info.branchType != ARM_BRANCH_LINKED) { + continue; + } + + uint32_t bTarget = target + info.op1.immediate + 8; + + if (ELFEntry(elf) == bTarget) { + ELFProgramHeadersDeinit(&ph); + return true; + } + } + + ELFProgramHeadersDeinit(&ph); + return false; +} +#endif + bool GBAIsROM(struct VFile* vf) { if (!vf) { return false; @@ -606,7 +663,7 @@ bool GBAIsROM(struct VFile* vf) { uint32_t entry = ELFEntry(elf); bool isGBA = true; isGBA = isGBA && ELFMachine(elf) == EM_ARM; - isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM + 0xC0); + isGBA = isGBA && (GBAVerifyELFEntry(elf, BASE_CART0) || GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0)); ELFClose(elf); return isGBA; } @@ -662,7 +719,7 @@ bool GBAIsMB(struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - bool isMB = ELFEntry(elf) == BASE_WORKING_RAM + 0xC0; + bool isMB = GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0); ELFClose(elf); return isMB; } From b48c7a18873c7d23cc48dda9822f45b34f3137ab Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Nov 2022 06:25:07 -0700 Subject: [PATCH 053/159] Qt: Add a progress bar for the forwarder builder downloads --- src/platform/qt/ForwarderController.cpp | 29 +++---- src/platform/qt/ForwarderController.h | 12 ++- src/platform/qt/ForwarderView.cpp | 70 +++++++++++++++- src/platform/qt/ForwarderView.h | 5 ++ src/platform/qt/ForwarderView.ui | 103 +++++++++++++----------- 5 files changed, 158 insertions(+), 61 deletions(-) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index ca4676859..b1012eb61 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -84,13 +84,11 @@ void ForwarderController::downloadForwarderKit() { return; #endif QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl(fkUrl))); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - gotForwarderKit(reply); - }); - connectErrorFailure(reply); + connectReply(reply, FORWARDER_KIT, &ForwarderController::gotForwarderKit); } void ForwarderController::gotForwarderKit(QNetworkReply* reply) { + emit downloadComplete(FORWARDER_KIT); if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit buildFailed(); return; @@ -135,13 +133,11 @@ void ForwarderController::gotForwarderKit(QNetworkReply* reply) { void ForwarderController::downloadManifest() { QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl("https://mgba.io/latest.ini"))); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - gotManifest(reply); - }); - connectErrorFailure(reply); + connectReply(reply, MANIFEST, &ForwarderController::gotManifest); } void ForwarderController::gotManifest(QNetworkReply* reply) { + emit downloadComplete(MANIFEST); if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit buildFailed(); return; @@ -179,18 +175,15 @@ void ForwarderController::downloadBuild(const QUrl& url) { } QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); - connect(reply, &QNetworkReply::finished, this, [this, reply]() { - gotBuild(reply); - }); - + connectReply(reply, BASE, &ForwarderController::gotBuild); connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { QByteArray data = reply->readAll(); m_sourceFile.write(data); }); - connectErrorFailure(reply); } void ForwarderController::gotBuild(QNetworkReply* reply) { + emit downloadComplete(BASE); if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit buildFailed(); return; @@ -223,7 +216,7 @@ bool ForwarderController::toolInstalled(const QString& tool) { return false; } -void ForwarderController::connectErrorFailure(QNetworkReply* reply) { +void ForwarderController::connectReply(QNetworkReply* reply, Download download, void (ForwarderController::*next)(QNetworkReply*)) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) connect(reply, &QNetworkReply::errorOccurred, this, [this, reply]() { #else @@ -231,4 +224,12 @@ void ForwarderController::connectErrorFailure(QNetworkReply* reply) { #endif emit buildFailed(); }); + + connect(reply, &QNetworkReply::finished, this, [this, reply, next]() { + (this->*next)(reply); + }); + connect(reply, &QNetworkReply::downloadProgress, this, [this, download](qint64 bytesReceived, qint64 bytesTotal) { + emit downloadProgress(download, bytesReceived, bytesTotal); + }); + emit downloadStarted(download); } diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 09902a397..577308567 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -21,17 +21,27 @@ class ForwarderController : public QObject { Q_OBJECT public: + enum Download : int { + MANIFEST, + BASE, + FORWARDER_KIT + }; ForwarderController(QObject* parent = nullptr); void setGenerator(std::unique_ptr&& generator); ForwarderGenerator* generator() { return m_generator.get(); } QString channel() const { return m_channel; } + bool inProgress() const { return m_inProgress; } public slots: void startBuild(const QString& outFilename); signals: + void buildStarted(bool needsForwarderKit); + void downloadStarted(Download which); + void downloadComplete(Download which); + void downloadProgress(Download which, qint64 bytesGotten, qint64 bytesTotal); void buildComplete(); void buildFailed(); @@ -47,7 +57,7 @@ private: bool toolInstalled(const QString& tool); void cleanup(); - void connectErrorFailure(QNetworkReply*); + void connectReply(QNetworkReply*, Download, void (ForwarderController::*next)(QNetworkReply*)); QString m_channel{"dev"}; QString m_outFilename; diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp index da730cf86..84595ce3e 100644 --- a/src/platform/qt/ForwarderView.cpp +++ b/src/platform/qt/ForwarderView.cpp @@ -32,13 +32,49 @@ ForwarderView::ForwarderView(QWidget* parent) connect(m_ui.imageSelect, qOverload(&QComboBox::currentIndexChanged), this, &ForwarderView::setActiveImage); connect(m_ui.imageBrowse, &QAbstractButton::clicked, this, &ForwarderView::selectImage); - connect(&m_controller, &ForwarderController::buildComplete, this, &QDialog::accept); + connect(&m_controller, &ForwarderController::buildComplete, this, [this]() { + QMessageBox* message = new QMessageBox(QMessageBox::Information, tr("Build finished"), + tr("Forwarder finished building"), + QMessageBox::Ok, parentWidget(), Qt::Sheet); + message->setAttribute(Qt::WA_DeleteOnClose); + message->show(); + accept(); + }); connect(&m_controller, &ForwarderController::buildFailed, this, [this]() { QMessageBox* error = new QMessageBox(QMessageBox::Critical, tr("Build failed"), tr("Failed to build forwarder"), QMessageBox::Ok, this, Qt::Sheet); error->setAttribute(Qt::WA_DeleteOnClose); error->show(); + + m_ui.progressBar->setValue(0); + m_ui.progressBar->setEnabled(false); + validate(); + }); + connect(&m_controller, &ForwarderController::downloadStarted, this, [this](ForwarderController::Download download) { + m_currentDownload = download; + m_downloadProgress = 0; + if (download == ForwarderController::FORWARDER_KIT) { + m_needsForwarderKit = true; + } + updateProgress(); + }); + connect(&m_controller, &ForwarderController::downloadComplete, this, [this](ForwarderController::Download download) { + if (m_currentDownload != download) { + return; + } + m_downloadProgress = 1; + updateProgress(); + }); + connect(&m_controller, &ForwarderController::downloadProgress, this, [this](ForwarderController::Download download, qint64 bytesReceived, qint64 bytesTotal) { + if (m_currentDownload != download) { + return; + } + if (bytesTotal <= 0 || bytesTotal < bytesReceived) { + return; + } + m_downloadProgress = bytesReceived / static_cast(bytesTotal); + updateProgress(); }); connect(m_ui.system3DS, &QAbstractButton::clicked, this, [this]() { @@ -59,6 +95,13 @@ void ForwarderView::build() { m_controller.generator()->setTitle(m_ui.title->text()); m_controller.generator()->setRom(m_ui.romFilename->text()); m_controller.startBuild(m_ui.outputFilename->text()); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui.progressBar->setEnabled(true); + + m_currentDownload = ForwarderController::FORWARDER_KIT; + m_downloadProgress = 0; + m_needsForwarderKit = false; + updateProgress(); } void ForwarderView::validate() { @@ -80,6 +123,9 @@ void ForwarderView::validate() { if (m_ui.baseType->currentIndex() != 1) { valid = false; } + if (m_controller.inProgress()) { + valid = false; + } m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid); } @@ -154,3 +200,25 @@ void ForwarderView::setActiveImage(int index) { m_ui.imagePreview->setMaximumSize(m_activeSize); m_ui.imagePreview->setPixmap(QPixmap::fromImage(m_controller.generator()->image(index))); } + +void ForwarderView::updateProgress() { + switch (m_currentDownload) { + case ForwarderController::FORWARDER_KIT: + m_ui.progressBar->setValue(m_downloadProgress * 450); + break; + case ForwarderController::MANIFEST: + if (m_needsForwarderKit) { + m_ui.progressBar->setValue(450 + m_downloadProgress * 50); + } else { + m_ui.progressBar->setValue(m_downloadProgress * 100); + } + break; + case ForwarderController::BASE: + if (m_needsForwarderKit) { + m_ui.progressBar->setValue(500 + m_downloadProgress * 500); + } else { + m_ui.progressBar->setValue(100 + m_downloadProgress * 900); + } + break; + } +} diff --git a/src/platform/qt/ForwarderView.h b/src/platform/qt/ForwarderView.h index cd80d65b5..51ca5356c 100644 --- a/src/platform/qt/ForwarderView.h +++ b/src/platform/qt/ForwarderView.h @@ -30,12 +30,17 @@ private: void connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save = false, const QString& filter = {}); void selectImage(); void setActiveImage(int); + void updateProgress(); ForwarderController m_controller; QVector m_images; int m_currentImage; QSize m_activeSize; + qreal m_downloadProgress; + ForwarderController::Download m_currentDownload; + bool m_needsForwarderKit; + Ui::ForwarderView m_ui; }; diff --git a/src/platform/qt/ForwarderView.ui b/src/platform/qt/ForwarderView.ui index cac27af70..d5a7de842 100644 --- a/src/platform/qt/ForwarderView.ui +++ b/src/platform/qt/ForwarderView.ui @@ -14,51 +14,6 @@ Create forwarder - - - - - 0 - 0 - - - - System - - - - - - 3DS - - - system - - - - - - - Vita - - - system - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - @@ -168,6 +123,51 @@ + + + + + 0 + 0 + + + + System + + + + + + 3DS + + + system + + + + + + + Vita + + + system + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + @@ -399,6 +399,19 @@ + + + + false + + + 1000 + + + false + + + From 91e62b8e723f4eab7d34623519abe48eade51c90 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 4 Nov 2022 06:38:50 -0700 Subject: [PATCH 054/159] Qt: Use filters for the forwarder file types --- src/platform/qt/ForwarderGenerator.cpp | 11 +++++++++++ src/platform/qt/ForwarderGenerator.h | 2 ++ src/platform/qt/ForwarderView.cpp | 12 ++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/ForwarderGenerator.cpp b/src/platform/qt/ForwarderGenerator.cpp index 691c9cf92..56dda02db 100644 --- a/src/platform/qt/ForwarderGenerator.cpp +++ b/src/platform/qt/ForwarderGenerator.cpp @@ -73,6 +73,17 @@ QString ForwarderGenerator::systemName(ForwarderGenerator::System system) { return {}; } +QString ForwarderGenerator::systemHumanName(ForwarderGenerator::System system) { + switch (system) { + case ForwarderGenerator::System::N3DS: + return tr("3DS"); + case ForwarderGenerator::System::VITA: + return tr("Vita"); + } + + return {}; +} + QString ForwarderGenerator::base36(const QByteArray& bytes, int length) { static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; QString buffer(length, 'X'); diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h index c471910f2..151de9406 100644 --- a/src/platform/qt/ForwarderGenerator.h +++ b/src/platform/qt/ForwarderGenerator.h @@ -40,11 +40,13 @@ public: virtual QList> imageTypes() const = 0; virtual System system() const = 0; QString systemName() const { return systemName(system()); } + QString systemHumanName() const { return systemHumanName(system()); } virtual QString extension() const = 0; virtual QStringList externalTools() const { return {}; } static QString systemName(System); + static QString systemHumanName(System); virtual void rebuild(const QString& source, const QString& target) = 0; diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp index 84595ce3e..2e8929f1d 100644 --- a/src/platform/qt/ForwarderView.cpp +++ b/src/platform/qt/ForwarderView.cpp @@ -151,10 +151,18 @@ void ForwarderView::setSystem(ForwarderGenerator::System system) { void ForwarderView::connectBrowseButton(QAbstractButton* button, QLineEdit* lineEdit, const QString& title, bool save, const QString& filter) { connect(button, &QAbstractButton::clicked, lineEdit, [this, lineEdit, save, title, filter]() { QString filename; + QString usedFilter = filter; + if (filter.isEmpty()) { + // Use the forwarder type, if selected + ForwarderGenerator* generator = m_controller.generator(); + if (generator) { + usedFilter = tr("%1 installable package (*.%2)").arg(generator->systemHumanName()).arg(generator->extension()); + } + } if (save) { - filename = GBAApp::app()->getSaveFileName(this, title, filter); + filename = GBAApp::app()->getSaveFileName(this, title, usedFilter); } else { - filename = GBAApp::app()->getOpenFileName(this, title, filter); + filename = GBAApp::app()->getOpenFileName(this, title, usedFilter); } if (filename.isEmpty()) { return; From f847502f4a131dfc7c87c462716e2a399cd2e6dd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 5 Nov 2022 03:11:33 -0700 Subject: [PATCH 055/159] Qt: Use a UA string for all HTTP requests --- src/platform/qt/AbstractUpdater.cpp | 11 ++++++----- src/platform/qt/AbstractUpdater.h | 2 -- src/platform/qt/ForwarderController.cpp | 9 ++++----- src/platform/qt/ForwarderController.h | 2 -- src/platform/qt/GBAApp.cpp | 16 ++++++++++++++++ src/platform/qt/GBAApp.h | 8 +++++++- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/platform/qt/AbstractUpdater.cpp b/src/platform/qt/AbstractUpdater.cpp index b878f8a2f..ae6df137b 100644 --- a/src/platform/qt/AbstractUpdater.cpp +++ b/src/platform/qt/AbstractUpdater.cpp @@ -8,16 +8,17 @@ #include #include +#include "GBAApp.h" + using namespace QGBA; AbstractUpdater::AbstractUpdater(QObject* parent) : QObject(parent) - , m_netman(new QNetworkAccessManager(this)) { } void AbstractUpdater::checkUpdate() { - QNetworkReply* reply = m_netman->get(QNetworkRequest(manifestLocation())); + QNetworkReply* reply = GBAApp::app()->httpGet(manifestLocation()); chaseRedirects(reply, &AbstractUpdater::manifestDownloaded); } @@ -36,7 +37,7 @@ void AbstractUpdater::downloadUpdate() { return; } m_isUpdating = true; - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); chaseRedirects(reply, &AbstractUpdater::updateDownloaded); } @@ -54,7 +55,7 @@ void AbstractUpdater::chaseRedirects(QNetworkReply* reply, void (AbstractUpdater connect(reply, &QNetworkReply::finished, this, [this, reply, cb]() { // TODO: check domains, etc if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) { - QNetworkReply* newReply = m_netman->get(QNetworkRequest(reply->header(QNetworkRequest::LocationHeader).toString())); + QNetworkReply* newReply = GBAApp::app()->httpGet(reply->header(QNetworkRequest::LocationHeader).toString()); chaseRedirects(newReply, cb); } else { (this->*cb)(reply); @@ -69,7 +70,7 @@ void AbstractUpdater::manifestDownloaded(QNetworkReply* reply) { if (!url.isValid()) { emit updateDone(false); } else { - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); chaseRedirects(reply, &AbstractUpdater::updateDownloaded); } } else { diff --git a/src/platform/qt/AbstractUpdater.h b/src/platform/qt/AbstractUpdater.h index 5fa385175..e12d3eabe 100644 --- a/src/platform/qt/AbstractUpdater.h +++ b/src/platform/qt/AbstractUpdater.h @@ -9,7 +9,6 @@ #include #include -class QNetworkAccessManager; class QNetworkReply; namespace QGBA { @@ -44,7 +43,6 @@ private: void updateDownloaded(QNetworkReply*); bool m_isUpdating = false; - QNetworkAccessManager* m_netman; QByteArray m_manifest; }; diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index b1012eb61..bb60b7c62 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -11,6 +11,7 @@ #include #include "ConfigController.h" +#include "GBAApp.h" #include "VFileDevice.h" #include @@ -29,10 +30,8 @@ const char* SUFFIX = ""; ForwarderController::ForwarderController(QObject* parent) : QObject(parent) - , m_netman(new QNetworkAccessManager(this)) , m_originalPath(qgetenv("PATH")) { - m_netman->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); connect(this, &ForwarderController::buildFailed, this, &ForwarderController::cleanup); connect(this, &ForwarderController::buildComplete, this, &ForwarderController::cleanup); } @@ -83,7 +82,7 @@ void ForwarderController::downloadForwarderKit() { emit buildFailed(); return; #endif - QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl(fkUrl))); + QNetworkReply* reply = GBAApp::app()->httpGet(QUrl(fkUrl)); connectReply(reply, FORWARDER_KIT, &ForwarderController::gotForwarderKit); } @@ -132,7 +131,7 @@ void ForwarderController::gotForwarderKit(QNetworkReply* reply) { } void ForwarderController::downloadManifest() { - QNetworkReply* reply = m_netman->get(QNetworkRequest(QUrl("https://mgba.io/latest.ini"))); + QNetworkReply* reply = GBAApp::app()->httpGet(QUrl("https://mgba.io/latest.ini")); connectReply(reply, MANIFEST, &ForwarderController::gotManifest); } @@ -173,7 +172,7 @@ void ForwarderController::downloadBuild(const QUrl& url) { emit buildFailed(); return; } - QNetworkReply* reply = m_netman->get(QNetworkRequest(url)); + QNetworkReply* reply = GBAApp::app()->httpGet(url); connectReply(reply, BASE, &ForwarderController::gotBuild); connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 577308567..7129b63d0 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -12,7 +12,6 @@ #include -class QNetworkAccessManager; class QNetworkReply; namespace QGBA { @@ -61,7 +60,6 @@ private: QString m_channel{"dev"}; QString m_outFilename; - QNetworkAccessManager* m_netman; std::unique_ptr m_generator; QFile m_sourceFile; bool m_inProgress = false; diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index d53985313..1e3c6a894 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -81,6 +82,8 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) m_configController->updateOption("useDiscordPresence"); #endif + m_netman.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); + cleanupAfterUpdate(); connect(this, &GBAApp::aboutToQuit, this, &GBAApp::cleanup); @@ -240,6 +243,19 @@ bool GBAApp::reloadGameDB() { } #endif +QNetworkAccessManager* GBAApp::netman() { + return &m_netman; +} + +QNetworkReply* GBAApp::httpGet(const QUrl& url) { + QNetworkRequest req(url); + req.setHeader(QNetworkRequest::UserAgentHeader, + QString("%1/%2 (+https://mgba.io) is definitely not Mozilla/5.0") + .arg(projectName) + .arg(projectVersion)); + return m_netman.get(req); +} + qint64 GBAApp::submitWorkerJob(std::function job, std::function callback) { return submitWorkerJob(job, nullptr, callback); } diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index d39f3a4a8..6245dba3f 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -11,7 +11,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -70,6 +71,9 @@ public: const NoIntroDB* gameDB() const { return m_db; } bool reloadGameDB(); + QNetworkAccessManager* netman(); + QNetworkReply* httpGet(const QUrl&); + qint64 submitWorkerJob(std::function job, std::function callback = {}); qint64 submitWorkerJob(std::function job, QObject* context, std::function callback); bool removeWorkerJob(qint64 jobId); @@ -128,6 +132,8 @@ private: QFont m_monospace; NoIntroDB* m_db = nullptr; + + QNetworkAccessManager m_netman; }; } From 877020087476f6605c561345c7ab927377b6cda6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 5 Nov 2022 03:23:05 -0700 Subject: [PATCH 056/159] Qt: Refactor out common generator code --- src/platform/qt/ForwarderController.cpp | 8 ++++- src/platform/qt/ForwarderGenerator.cpp | 27 +++++++++++++++++ src/platform/qt/ForwarderGenerator.h | 1 + src/platform/qt/ForwarderGenerator3DS.cpp | 32 +------------------- src/platform/qt/ForwarderGenerator3DS.h | 1 - src/platform/qt/ForwarderGeneratorVita.cpp | 34 ++-------------------- src/platform/qt/ForwarderGeneratorVita.h | 1 - 7 files changed, 38 insertions(+), 66 deletions(-) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index bb60b7c62..6083a1d5e 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -191,7 +191,13 @@ void ForwarderController::gotBuild(QNetworkReply* reply) { QByteArray data = reply->readAll(); m_sourceFile.write(data); m_sourceFile.close(); - m_generator->rebuild(m_sourceFile.fileName(), m_outFilename); + + QString extracted = m_generator->extract(m_sourceFile.fileName()); + if (extracted.isNull()) { + emit buildFailed(); + return; + } + m_generator->rebuild(extracted, m_outFilename); } void ForwarderController::cleanup() { diff --git a/src/platform/qt/ForwarderGenerator.cpp b/src/platform/qt/ForwarderGenerator.cpp index 56dda02db..7230be1e0 100644 --- a/src/platform/qt/ForwarderGenerator.cpp +++ b/src/platform/qt/ForwarderGenerator.cpp @@ -10,6 +10,10 @@ #include "ForwarderGenerator3DS.h" #include "ForwarderGeneratorVita.h" +#include "utils.h" +#include "VFileDevice.h" + +#include using namespace QGBA; @@ -84,6 +88,29 @@ QString ForwarderGenerator::systemHumanName(ForwarderGenerator::System system) { return {}; } +QString ForwarderGenerator::extract(const QString& archive) { + VDir* inArchive = VFileDevice::openArchive(archive); + if (!inArchive) { + return {}; + } + bool gotFile = extractMatchingFile(inArchive, [this](VDirEntry* dirent) -> QString { + if (dirent->type(dirent) != VFS_FILE) { + return {}; + } + QString filename(dirent->name(dirent)); + if (!filename.endsWith("." + extension())) { + return {}; + } + return "tmp." + extension(); + }); + inArchive->close(inArchive); + + if (gotFile) { + return QLatin1String("tmp.") + extension(); + } + return {}; +} + QString ForwarderGenerator::base36(const QByteArray& bytes, int length) { static const char* alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; QString buffer(length, 'X'); diff --git a/src/platform/qt/ForwarderGenerator.h b/src/platform/qt/ForwarderGenerator.h index 151de9406..0db97f148 100644 --- a/src/platform/qt/ForwarderGenerator.h +++ b/src/platform/qt/ForwarderGenerator.h @@ -48,6 +48,7 @@ public: static QString systemName(System); static QString systemHumanName(System); + virtual QString extract(const QString& archive); virtual void rebuild(const QString& source, const QString& target) = 0; signals: diff --git a/src/platform/qt/ForwarderGenerator3DS.cpp b/src/platform/qt/ForwarderGenerator3DS.cpp index 8b52d734a..51edc6aa7 100644 --- a/src/platform/qt/ForwarderGenerator3DS.cpp +++ b/src/platform/qt/ForwarderGenerator3DS.cpp @@ -6,8 +6,6 @@ #include "ForwarderGenerator3DS.h" #include "ConfigController.h" -#include "utils.h" -#include "VFileDevice.h" #include #include @@ -34,39 +32,11 @@ QList> ForwarderGenerator3DS::imageTypes() const { } void ForwarderGenerator3DS::rebuild(const QString& source, const QString& target) { - m_cia = dumpCia(source); - if (m_cia.isNull()) { - emit buildFailed(); - return; - } - + m_cia = source; m_target = target; extractCia(); } -QString ForwarderGenerator3DS::dumpCia(const QString& archive) { - VDir* inArchive = VFileDevice::openArchive(archive); - if (!inArchive) { - return {}; - } - bool gotFile = extractMatchingFile(inArchive, [](VDirEntry* dirent) -> QString { - if (dirent->type(dirent) != VFS_FILE) { - return {}; - } - QString filename(dirent->name(dirent)); - if (!filename.endsWith(".cia")) { - return {}; - } - return "tmp.cia"; - }); - inArchive->close(inArchive); - - if (gotFile) { - return QLatin1String("tmp.cia"); - } - return {}; -} - void ForwarderGenerator3DS::extractCia() { m_currentProc = std::make_unique(); m_currentProc->setProgram("ctrtool"); diff --git a/src/platform/qt/ForwarderGenerator3DS.h b/src/platform/qt/ForwarderGenerator3DS.h index d50d87e42..b22585063 100644 --- a/src/platform/qt/ForwarderGenerator3DS.h +++ b/src/platform/qt/ForwarderGenerator3DS.h @@ -43,7 +43,6 @@ private slots: void cleanup(); private: - QString dumpCia(const QString& archive); void init3dstoolArgs(QStringList& args, const QString& file, const QString& createType = {}); std::unique_ptr m_currentProc; diff --git a/src/platform/qt/ForwarderGeneratorVita.cpp b/src/platform/qt/ForwarderGeneratorVita.cpp index 97b3ef802..f6e97e5d3 100644 --- a/src/platform/qt/ForwarderGeneratorVita.cpp +++ b/src/platform/qt/ForwarderGeneratorVita.cpp @@ -8,7 +8,6 @@ #include #include -#include "utils.h" #include "VFileDevice.h" #include @@ -30,15 +29,9 @@ QList> ForwarderGeneratorVita::imageTypes() const { } void ForwarderGeneratorVita::rebuild(const QString& source, const QString& target) { - QString vpk = dumpVpk(source); - if (vpk.isNull()) { - emit buildFailed(); - return; - } - - QFile vpkFile(vpk); + QFile vpkFile(source); VDir* outdir = VDirOpenZip(target.toLocal8Bit().constData(), O_WRONLY | O_CREAT | O_TRUNC); - if (outdir && !copyAssets(vpk, outdir)) { + if (outdir && !copyAssets(source, outdir)) { outdir->close(outdir); outdir = nullptr; } @@ -79,29 +72,6 @@ void ForwarderGeneratorVita::rebuild(const QString& source, const QString& targe emit buildComplete(); } -QString ForwarderGeneratorVita::dumpVpk(const QString& archive) { - VDir* inArchive = VFileDevice::openArchive(archive); - if (!inArchive) { - return {}; - } - bool gotFile = extractMatchingFile(inArchive, [](VDirEntry* dirent) -> QString { - if (dirent->type(dirent) != VFS_FILE) { - return {}; - } - QString filename(dirent->name(dirent)); - if (!filename.endsWith(".vpk")) { - return {}; - } - return "tmp.vpk"; - }); - inArchive->close(inArchive); - - if (gotFile) { - return QLatin1String("tmp.vpk"); - } - return {}; -} - bool ForwarderGeneratorVita::copyAssets(const QString& vpk, VDir* outdir) { VDir* indir = VDirOpenZip(vpk.toLocal8Bit().constData(), O_RDONLY); if (!indir) { diff --git a/src/platform/qt/ForwarderGeneratorVita.h b/src/platform/qt/ForwarderGeneratorVita.h index ee2a5b17e..e3a083e6e 100644 --- a/src/platform/qt/ForwarderGeneratorVita.h +++ b/src/platform/qt/ForwarderGeneratorVita.h @@ -25,7 +25,6 @@ public: void rebuild(const QString& source, const QString& target) override; private: - QString dumpVpk(const QString& archive); bool copyAssets(const QString& vpk, VDir* out); QString makeSerial() const; void writeSfo(VFile* out); From c4384d1d1ef52f5873f6009018f9990e8e5929a1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 6 Nov 2022 22:26:41 -0800 Subject: [PATCH 057/159] Qt: Add forwarder building from a specific base file --- src/platform/qt/ForwarderController.cpp | 10 ++++++++-- src/platform/qt/ForwarderController.h | 5 +++++ src/platform/qt/ForwarderView.cpp | 19 ++++++++++++++++++- src/platform/qt/ForwarderView.ui | 2 +- 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index 6083a1d5e..fe0530a73 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -65,7 +65,11 @@ void ForwarderController::startBuild(const QString& outFilename) { return; } } - downloadManifest(); + if (m_baseFilename.isEmpty()) { + downloadManifest(); + } else { + m_generator->rebuild(m_baseFilename, m_outFilename); + } } void ForwarderController::downloadForwarderKit() { @@ -201,7 +205,9 @@ void ForwarderController::gotBuild(QNetworkReply* reply) { } void ForwarderController::cleanup() { - m_sourceFile.remove(); + if (m_sourceFile.exists()) { + m_sourceFile.remove(); + } m_inProgress = false; #if defined(Q_OS_WIN) || defined(Q_OS_MAC) diff --git a/src/platform/qt/ForwarderController.h b/src/platform/qt/ForwarderController.h index 7129b63d0..e53006bc2 100644 --- a/src/platform/qt/ForwarderController.h +++ b/src/platform/qt/ForwarderController.h @@ -30,6 +30,10 @@ public: void setGenerator(std::unique_ptr&& generator); ForwarderGenerator* generator() { return m_generator.get(); } + void setBaseFilename(const QString& path) { m_baseFilename = path; } + void clearBaseFilename() { m_baseFilename = QString(); } + QString baseFilename() const { return m_baseFilename; } + QString channel() const { return m_channel; } bool inProgress() const { return m_inProgress; } @@ -62,6 +66,7 @@ private: QString m_outFilename; std::unique_ptr m_generator; QFile m_sourceFile; + QString m_baseFilename; bool m_inProgress = false; QByteArray m_originalPath; }; diff --git a/src/platform/qt/ForwarderView.cpp b/src/platform/qt/ForwarderView.cpp index 2e8929f1d..0546e7180 100644 --- a/src/platform/qt/ForwarderView.cpp +++ b/src/platform/qt/ForwarderView.cpp @@ -94,6 +94,11 @@ void ForwarderView::build() { } m_controller.generator()->setTitle(m_ui.title->text()); m_controller.generator()->setRom(m_ui.romFilename->text()); + if (m_ui.baseType->currentIndex() == 2) { + m_controller.setBaseFilename(m_ui.baseFilename->text()); + } else { + m_controller.clearBaseFilename(); + } m_controller.startBuild(m_ui.outputFilename->text()); m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); m_ui.progressBar->setEnabled(true); @@ -120,9 +125,21 @@ void ForwarderView::validate() { if (!m_ui.system->checkedButton()) { valid = false; } - if (m_ui.baseType->currentIndex() != 1) { + if (m_ui.baseType->currentIndex() < 1) { valid = false; } + if (m_ui.baseType->currentIndex() == 2) { + m_ui.baseFilename->setEnabled(true); + m_ui.baseLabel->setEnabled(true); + m_ui.baseBrowse->setEnabled(true); + if (m_ui.baseFilename->text().isEmpty()) { + valid = false; + } + } else { + m_ui.baseFilename->setEnabled(true); + m_ui.baseLabel->setEnabled(true); + m_ui.baseBrowse->setEnabled(true); + } if (m_controller.inProgress()) { valid = false; } diff --git a/src/platform/qt/ForwarderView.ui b/src/platform/qt/ForwarderView.ui index d5a7de842..1c72e3caa 100644 --- a/src/platform/qt/ForwarderView.ui +++ b/src/platform/qt/ForwarderView.ui @@ -87,7 +87,7 @@ - + false From 2ea0114ae2ba234f0b1b21353c88dde8335c58ac Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 7 Nov 2022 20:43:22 -0800 Subject: [PATCH 058/159] GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes #2489) --- CHANGES | 1 + .../baseline_0000.png | Bin 0 -> 2190 bytes .../blend/disabled-bg-semitrans-blend/config.ini | 3 +++ .../blend/disabled-bg-semitrans-blend/test.gba | Bin 0 -> 5288 bytes src/gba/renderers/software-obj.c | 8 ++++---- 5 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 cinema/gba/blend/disabled-bg-semitrans-blend/baseline_0000.png create mode 100644 cinema/gba/blend/disabled-bg-semitrans-blend/config.ini create mode 100644 cinema/gba/blend/disabled-bg-semitrans-blend/test.gba diff --git a/CHANGES b/CHANGES index e20cf3b5b..76b3b451f 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Features: - Debugger: Add range watchpoints Emulation fixes: - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) + - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/cinema/gba/blend/disabled-bg-semitrans-blend/baseline_0000.png b/cinema/gba/blend/disabled-bg-semitrans-blend/baseline_0000.png new file mode 100644 index 0000000000000000000000000000000000000000..f90f448c4f4370b0457759cc9891539d6d667ded GIT binary patch literal 2190 zcmcIm>pv3=8^)vOokLEMLyy)oq?V=wR*0853<;G~TB2r(%(NWBR+w{obui{^j$y?J z6XL0yGg~&&sHb8WV1M@(JbeSdW=N*6%8_MohcIAEq;bvp3!1oDBJ zil?_iz(v)etHg#&Z?#@t8!>x%e3V{s98y>ICUJ*rS641lf;nG(N`hOfVoo%b565@% zMeY}H?Zi0w`c0-kP>ybrm~v2Y=Q)5um4?OXKSS~y=|>2qfyxelY5y}~Yseq??~N+K zi&nd2po%8U0T!j>gPY@ZbKjZHS2yY8CU~IItq8@pfO5OMy>RD}c7g#1N|D z8vth^{^Y8Cw+tODIOghCmR7jVr~#^$bAjp}3BNWLdyct5-cL&R_KVg5qL$gY+LETI z_3tVP8Qe!=!nG z=dpg(VV|a#u-oyFIdp=mp@0A17BdH4oYdAI+;p)+Nf;wC;NeTQMaZpD{B~<|u;#p} zWPDwBKJ!M?y^YFlvl-vH6hSL9p8O5X$hl-Vp+T4zu1 z)TPz?bTZ691K_#|m*~h2y#)f0WSN9LltsV&_8kG0BdjOxuPYk6?7snAWm_wIEuqQy z{P@#k+={WhOs2>=6WD|aX}jy$!A$KK)kT?61f0)G?Uq%4tp7TGkb0MUfmo;erFpgn z$Su6MT@XCEFGJYFFj?q@+xw?}+w;}d0IdDWhztyi@)RSsj_nh|f0`i{FN72#@|`=c%aN8321+ zxb)Omlub83u?ZG6_V(_5?$tKjA(7{SxNOE#j<`E@-a<_w!MlJ(BD$6TwKYv@=H}{{ zUMa^N$q{*w_6?U!fr~D0S_bqrQU=>mGL7Y;fSSg=)hw;WuliM~RO?8_wLS(VhdZnZ z=0+%138~ddjbVe?R^%q0+IMzG$6Z*=M|B}Nb=izujCl0e>AmTe5_ICW-bYw=r*YMYVy%Li@u~<-hbHh0Mx3ZN>Zu1O(38+R^mNF(*6v?AnV?b$w(K&qt8?xfu05~cIc97hOv(-UeAt)<%=(V5|*%7Eu&by{uj<}udc zbqpTP5^{d-wAasuFQz4>oMtq2pdJ8}l zBZ>vL(s-za_p_7MPe&v?tB0&BmKp4QTmv%E`$cJB z0&*kk#=?iTu7l@m`QJ8^K-_y|Vg#<8SqIT`m0WwZTQlE&wz{egjgF`Zj&?KP!FM{G3vB07PIh;MEhx&n(U` zL)!9j!9n~eS{8jhcrn7(%e25;;{SB+Fa&8!9DqAHIgWjO{ht=KIcwoG#0rLMd+6&V z^bZSPwtu$zL7I<(_5e8NrGa4$7x*Wr+M4>K?+_VJ(WQqX$1~A^xO!646l`Alqo6bN zMzU1@)O0l{^6oBKx^f_)F*@!6(+T}f-;?^;UJr(;>Du@icORNzo-9Jku>FtI;lhJy z7eJta&m9UC=B=4!HYw=MJES)N1kA&ra7f^M<4KEMrJ*i}iE1Uc^fPb8b#k5UcTMU) zQvv>9pf}dfNwtVgcsWBU_NPloBIWNO<$I%(qE5S*)zHnDlH8OZkI0m`Z3Za%Xmiw+ zqst4eF(NNJ%-C<-*EbWT^4LAitoi^*RV*`A>yN@r_<;9wj5w)LTwOlp_iIffC$JEB z-^!D&0}z$=u3R}~O(U7(Zu=Ev_vqxmvd((N+cEOa&G+-l=Oad(`e&brTKKxSl=x14wuYn(n$Iad~*$vn{U+Fa6d!YaT1ZcfwPd z7{eengfr?rr%PQ-QA;^#v4Sw-f?Y?mx(WNKw>7X2je_6LC{UMD@?hH^^I*TtSpKuJ ul=W`Vir?7@aflu1dh-9LkYyeM{4VK9o!yCfak4){&dvFZ6UFgb;(r0GGD403 literal 0 HcmV?d00001 diff --git a/cinema/gba/blend/disabled-bg-semitrans-blend/config.ini b/cinema/gba/blend/disabled-bg-semitrans-blend/config.ini new file mode 100644 index 000000000..f26f6d8b1 --- /dev/null +++ b/cinema/gba/blend/disabled-bg-semitrans-blend/config.ini @@ -0,0 +1,3 @@ +[testinfo] +skip=10 +frames=1 diff --git a/cinema/gba/blend/disabled-bg-semitrans-blend/test.gba b/cinema/gba/blend/disabled-bg-semitrans-blend/test.gba new file mode 100644 index 0000000000000000000000000000000000000000..91cb67e5f614b4a906973c9741a331d7a7bae870 GIT binary patch literal 5288 zcmeHLZERa-6+YM3zH!pla<_qkySDL1w{FoOW%ox11BKojC$X2xq`p=(@o}fd7ol}q zv`VAWka{Vpa5^NqQMNRjsMR92L}RqlG9l5e7k+eu*-;9p;=?vc+CYaI%SfQPG+>@{ zZKq|6lm0+xop`0^J?EbDzUQ9jyzk9v{c)ndgetFm{;Olh{ht{=&@z0n`A6$RPmNmN z*zZ5l|Hj9@+dS3g4M!s7JGWgPc=OV|2QEyVIrz6f9+`?Z|LjQOkag?hFD@P%?%R9f zmtzkNJa^*GMRD|r;Knx}5}mIk&TJp1&KEuZtn~j&eC&H2e|_$%bx=QVZW-VGhA8aZ zK6mA~T>0r|MRgw?emk}PQ%924*LQ!bbpMafZ@Ku?_Li3)_y4BcMDH0L+n&80U(-$!qMny7Stxud(&-z>f*P^rBf>1pWw4kl|%YoitB=~@BgBF73FGKe3 z@;Ro15h`sc2W$3^KnwM_^-z>bo-(zOKLD+{ECxnepH{?a3OFt2eT(OYx?8(X_{6pi zt>1)I_=WIu=clj2YK7DLNhAi58*mI0i9Y~XXB@tb-2D71uogOOBCkn9XRw`ReSI3w zL{7&eBDo)Nkh?r-h0CYir@tQ_>h?}HJrLYS?M>&TD78DLT|G2IXM|vI$a`8iP2C(9 zambB$uBdmC+Qn&SZ*^65g%xIh3hQ`{8Qc>2gL7!or;CAL)Mo~xp@`26*|Qu7!BW*v zL4SsPCe>E8e9~v8g8}UInf_=XVzv29GxThrEowPi^|s!&=raO1m-@;9+SI$bXLBEw zddl7a>r_(8Gr>)~SAW6W81uleUUF(+-lX0>pTX7ezKdbc^0$$t*?1#JI zxJmq85LgzCc-QR>iNDWbWr#9)vhqdBT_$VcQObG9AS;u}I19WwO^h zt`(pF&rA&v_7<$o`r2vI1GLMHi%A<0f8k~FG-iUYI&WA|3MeL)7AmW#glHpsFSWE-$WNumMNjCl#p2}MnaVhgGkd1QWZ^-RW+$7 zYFw4&xS}e%WLb@)c!+_B{2CT@zyJ>==Ch>#2|RVa57&b$!C3y@*X?zljU+fqRp^ij zMl$CK1Bt<$2mFkL25R7>%Alv{s(P<3D=Kv6C~HQSvP%Q$ctTT^q#>)xK}AUps_~?u zYDQR18b&gqX$ehHG*wlwO;!~}=~87i(WR(dUX+JqO_H!j$6i7VQsPO9!$t)1I*O3E znAIUAeztl4clqPoby}d9o+}ca#W${>&6#dwhQwp20S?(nP|<@AbfK8vlgsYWU`-9g zHDgdSB&l{yXdMb)z;wBg&m&Ax2X|~WVqk)!B}tN|U9sr}{K1$iA`sqyBHJ!wrbtpD zU(~WjzG!G?a|crzu3MF-gtJdlf#Suigl&}FBaxw*+^=us`!{j^j1(|>9n92-TN@oa zYK~8&+~Xr|nm|(?LsZ5Hf*<5JU@tHVaTH+lOEnwLE~B|)nt`n`*p^01P17=lW@s+c zMJD$D$4FP7%mID{2R#a96gUK(Z9Ft{H03@zK4PXO#t>o*6ef_vcom!zd9l_;3KDUa z!1fyNW&0oZwk0z4gzdLhrw>T)V~!|Ex?Yl{3^_mM2*a{iE&ruJ|5t9H0w37^e*?L% zxWt0@X72whl3Ue@=693#|5YijwBSR~|5u{7s;|!fChq^Ma>N3&2DC`KXL3(rd>Hz_ z{cims)q<_mu}9e)6#w_P+kL!jt@q22qpcm^QcEWeMxBRpSxPA94a-veo3&tFz2RITiul2%C=4T?X13U4et~0 zc}M6-&kCJD4A6lgzyuDbXa6Bq<1KPLu^P{TSp3Yf?&d_&B=zFAsExqj!=LMCKK};0 C4c~nL literal 0 HcmV?d00001 diff --git a/src/gba/renderers/software-obj.c b/src/gba/renderers/software-obj.c index 8d67199ec..3b44d86a5 100644 --- a/src/gba/renderers/software-obj.c +++ b/src/gba/renderers/software-obj.c @@ -169,10 +169,10 @@ int GBAVideoSoftwareRendererPreprocessSprite(struct GBAVideoSoftwareRenderer* re (renderer->blendEffect == BLEND_BRIGHTEN || renderer->blendEffect == BLEND_DARKEN); if (GBAObjAttributesAGetMode(sprite->a) == OBJ_MODE_SEMITRANSPARENT || (renderer->target1Obj && renderer->blendEffect == BLEND_ALPHA) || objwinSlowPath) { int target2 = renderer->target2Bd; - target2 |= renderer->bg[0].target2; - target2 |= renderer->bg[1].target2; - target2 |= renderer->bg[2].target2; - target2 |= renderer->bg[3].target2; + target2 |= renderer->bg[0].target2 && renderer->bg[0].enabled; + target2 |= renderer->bg[1].target2 && renderer->bg[1].enabled; + target2 |= renderer->bg[2].target2 && renderer->bg[2].enabled; + target2 |= renderer->bg[3].target2 && renderer->bg[3].enabled; if (target2) { renderer->forceTarget1 = true; flags |= FLAG_REBLEND; From 085ce4890da2f89165f56ce126d628866295dcde Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 12 Nov 2022 00:35:34 -0800 Subject: [PATCH 059/159] GBA: Fix resetting key IRQ state (fixes #2716) --- CHANGES | 1 + src/gba/gba.c | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 76b3b451f..5d17491d3 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Features: - Debugger: Add range watchpoints Emulation fixes: - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) + - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) diff --git a/src/gba/gba.c b/src/gba/gba.c index de975855a..f40e85ace 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -213,6 +213,7 @@ void GBAReset(struct ARMCore* cpu) { gba->earlyExit = false; gba->dmaPC = 0; gba->biosStall = 0; + gba->keysLast = 0x400; if (gba->yankedRomSize) { gba->memory.romSize = gba->yankedRomSize; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; From 0271f122809f8c009549543f3084f4a9c8762a7e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 12 Nov 2022 00:45:25 -0800 Subject: [PATCH 060/159] CHANGES: Spill chicken --- CHANGES | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 5d17491d3..0d09f8dc0 100644 --- a/CHANGES +++ b/CHANGES @@ -17,7 +17,7 @@ Misc: - GBA: Improve detection of valid ELF ROMs - macOS: Add category to plist (closes mgba.io/i/2691) - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - - Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680) + - Qt: Keep track of current palette preset name (fixes mgba.io/i/2680) 0.10.0: (2022-10-11) Features: @@ -27,7 +27,7 @@ Features: - Tool for converting scanned pictures of e-Reader cards to raw dotcode data - Options for muting when inactive, minimized, or for different players in multiplayer - Cheat code support in homebrew ports - - Acclerometer and gyro support for controllers on PC + - Accelerometer and gyro support for controllers on PC - Support for combo "Super Game Boy Color" SGB + GBC ROM hacks - Improved support for HuC-3 mapper, including RTC - Support for 64 kiB SRAM saves used in some bootlegs @@ -41,7 +41,7 @@ Emulation fixes: - ARM7: Fix unsigned multiply timing - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) - GB: Fix HALT breaking M-cycle alignment (fixes mgba.io/i/250) - - GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925) + - GB Audio: Fix channel 1/2 resetting edge cases (fixes mgba.io/i/1925) - GB Audio: Properly apply per-model audio differences - GB Audio: Revamp channel rendering - GB Audio: Fix APU re-enable timing glitch @@ -151,7 +151,7 @@ Emulation fixes: Other fixes: - ARM Decoder: Fix decoding of lsl r0 (fixes mgba.io/i/2349) - FFmpeg: Don't attempt to use YUV 4:2:0 for lossless videos (fixes mgba.io/i/2084) - - GB Video: Fix memory leak when reseting SGB games + - GB Video: Fix memory leak when resetting SGB games - GBA: Fix out of bounds ROM accesses on patched ROMs smaller than 32 MiB - GBA: Fix maximum tile ID in caching for 256-color modes - GBA Video: Fix cache updating with proxy and GL renderers @@ -262,7 +262,7 @@ Emulation fixes: - GBA BIOS: Implement dummy sound driver calls - GBA BIOS: Improve HLE BIOS timing - GBA BIOS: Fix reloading video registers after reset (fixes mgba.io/i/1808) - - GBA BIOS: Make HLE BIOS calls interruptable (fixes mgba.io/i/1711 and mgba.io/i/1823) + - GBA BIOS: Make HLE BIOS calls interruptible (fixes mgba.io/i/1711 and mgba.io/i/1823) - GBA BIOS: Fix invalid decompression bounds checking - GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320) - GBA DMA: Fix ordering and timing of overlapping DMAs @@ -278,7 +278,7 @@ Emulation fixes: - GBA Serialize: Fix alignment check when loading states - GBA SIO: Fix copying Normal mode transfer values - GBA SIO: Fix Normal mode being totally broken (fixes mgba.io/i/1800) - - GBA SIO: Fix deseralizing SIO registers + - GBA SIO: Fix deserializing SIO registers - GBA SIO: Fix hanging on starting a second multiplayer window (fixes mgba.io/i/854) - GBA SIO: Fix Normal mode transfer start timing (fixes mgba.io/i/425) - GBA Timers: Fix toggling timer cascading while timer is active (fixes mgba.io/i/2043) From 6aa558c4a0525c72ed69bf86f5c387779751012c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 20 Nov 2022 00:39:00 -0800 Subject: [PATCH 061/159] Qt: Move OpenGL proxy onto its own thread (fixes #2493) --- CHANGES | 1 + src/platform/qt/DisplayGL.cpp | 150 +++++++++++++++++++++++++++------- src/platform/qt/DisplayGL.h | 23 ++++++ 3 files changed, 144 insertions(+), 30 deletions(-) diff --git a/CHANGES b/CHANGES index 0d09f8dc0..f564da133 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Misc: - macOS: Add category to plist (closes mgba.io/i/2691) - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - Qt: Keep track of current palette preset name (fixes mgba.io/i/2680) + - Qt: Move OpenGL proxy onto its own thread (fixes mgba.io/i/2493) 0.10.0: (2022-10-11) Features: diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index e4b2af9a0..b8c0f8cd9 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -175,6 +175,11 @@ DisplayGL::DisplayGL(const QSurfaceFormat& format, QWidget* parent) m_drawThread.setObjectName("Painter Thread"); m_painter->setThread(&m_drawThread); + m_proxyThread.setObjectName("OpenGL Proxy Thread"); + m_proxyContext = std::make_unique(); + m_proxyContext->setFormat(format); + connect(m_painter.get(), &PainterGL::created, this, &DisplayGL::setupProxyThread); + connect(&m_drawThread, &QThread::started, m_painter.get(), &PainterGL::create); connect(m_painter.get(), &PainterGL::started, this, [this] { m_hasStarted = true; @@ -189,6 +194,11 @@ DisplayGL::~DisplayGL() { QMetaObject::invokeMethod(m_painter.get(), "destroy", Qt::BlockingQueuedConnection); m_drawThread.exit(); m_drawThread.wait(); + + if (m_proxyThread.isRunning()) { + m_proxyThread.exit(); + m_proxyThread.wait(); + } } bool DisplayGL::supportsShaders() const { @@ -209,9 +219,6 @@ void DisplayGL::startDrawing(std::shared_ptr controller) { m_painter->setContext(controller); m_painter->setMessagePainter(messagePainter()); m_context = controller; - if (videoProxy()) { - videoProxy()->moveToThread(&m_drawThread); - } lockAspectRatio(isAspectRatioLocked()); lockIntegerScaling(isIntegerScalingLocked()); @@ -411,11 +418,34 @@ bool DisplayGL::shouldDisableUpdates() { void DisplayGL::setVideoProxy(std::shared_ptr proxy) { Display::setVideoProxy(proxy); if (proxy) { - proxy->moveToThread(&m_drawThread); + proxy->moveToThread(&m_proxyThread); } m_painter->setVideoProxy(proxy); } +void DisplayGL::setupProxyThread() { + m_proxyContext->moveToThread(&m_proxyThread); + connect(&m_proxyThread, &QThread::started, m_proxyContext.get(), [this]() { + m_proxyContext->setShareContext(m_painter->shareContext()); + m_proxyContext->create(); + m_proxySurface.create(); + m_proxyContext->makeCurrent(&m_proxySurface); +#if defined(_WIN32) && defined(USE_EPOXY) + epoxy_handle_external_wglMakeCurrent(); +#endif + }); + connect(m_painter.get(), &PainterGL::texSwapped, m_proxyContext.get(), [this]() { + if (!m_context->hardwareAccelerated()) { + return; + } + if (videoProxy()) { + videoProxy()->processData(); + } + m_painter->updateFramebufferHandle(); + }, Qt::BlockingQueuedConnection); + m_proxyThread.start(); +} + int DisplayGL::framebufferHandle() { return m_painter->glTex(); } @@ -481,9 +511,15 @@ void PainterGL::create() { #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders) { + QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); gl2Backend = static_cast(malloc(sizeof(mGLES2Context))); mGLES2ContextCreate(gl2Backend); m_backend = &gl2Backend->d; + fn->glGenTextures(m_bridgeTexes.size(), m_bridgeTexes.data()); + for (auto tex : m_bridgeTexes) { + m_freeTex.enqueue(tex); + } + m_bridgeTexIn = m_freeTex.dequeue(); } #endif @@ -503,10 +539,10 @@ void PainterGL::create() { painter->makeCurrent(); #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + mGLES2Context* gl2Backend = reinterpret_cast(painter->m_backend); if (painter->m_widget && painter->supportsShaders()) { QOpenGLFunctions_Baseline* fn = painter->m_gl->versionFunctions(); fn->glFinish(); - mGLES2Context* gl2Backend = reinterpret_cast(painter->m_backend); painter->m_widget->setTex(painter->m_finalTex[painter->m_finalTexIdx]); painter->m_finalTexIdx ^= 1; gl2Backend->finalShader.tex = painter->m_finalTex[painter->m_finalTexIdx]; @@ -540,6 +576,8 @@ void PainterGL::create() { m_backend->filter = false; m_backend->lockAspectRatio = false; m_backend->interframeBlending = false; + + emit created(); } void PainterGL::destroy() { @@ -548,9 +586,11 @@ void PainterGL::destroy() { } makeCurrent(); #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); if (m_shader.passes) { mGLES2ShaderFree(&m_shader); } + fn->glDeleteTextures(m_bridgeTexes.size(), m_bridgeTexes.data()); #endif m_backend->deinit(m_backend); m_gl->doneCurrent(); @@ -636,6 +676,7 @@ void PainterGL::start() { } #endif resizeContext(); + m_context->addFrameAction(std::bind(&PainterGL::swapTex, this)); m_buffer = nullptr; m_active = true; @@ -644,7 +685,7 @@ void PainterGL::start() { } void PainterGL::draw() { - if (!m_started || m_queue.isEmpty()) { + if (!m_started || (m_queue.isEmpty() && m_queueTex.isEmpty())) { return; } @@ -671,7 +712,7 @@ void PainterGL::draw() { return; } dequeue(); - bool forceRedraw = !m_videoProxy; + bool forceRedraw = true; if (!m_delayTimer.isValid()) { m_delayTimer.start(); } else { @@ -725,11 +766,6 @@ void PainterGL::doStop() { m_videoProxy->processData(); } } - if (m_videoProxy) { - m_videoProxy->reset(); - m_videoProxy->moveToThread(m_window->thread()); - m_videoProxy.reset(); - } m_backend->clear(m_backend); m_backend->swap(m_backend); } @@ -759,38 +795,60 @@ void PainterGL::performDraw() { } void PainterGL::enqueue(const uint32_t* backing) { + if (!backing) { + return; + } QMutexLocker locker(&m_mutex); uint32_t* buffer = nullptr; - if (backing) { - if (m_free.isEmpty()) { - buffer = m_queue.dequeue(); - } else { - buffer = m_free.takeLast(); - } - if (buffer) { - QSize size = m_context->screenDimensions(); - memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); - } + if (m_free.isEmpty()) { + buffer = m_queue.dequeue(); + } else { + buffer = m_free.takeLast(); + } + if (buffer) { + QSize size = m_context->screenDimensions(); + memcpy(buffer, backing, size.width() * size.height() * BYTES_PER_PIXEL); } m_queue.enqueue(buffer); } +void PainterGL::enqueue(GLuint tex) { + QMutexLocker locker(&m_mutex); + if (m_freeTex.isEmpty()) { + m_bridgeTexIn = m_queueTex.dequeue(); + } else { + m_bridgeTexIn = m_freeTex.takeLast(); + } + m_queueTex.enqueue(tex); +} + void PainterGL::dequeue() { QMutexLocker locker(&m_mutex); - if (m_queue.isEmpty()) { - return; + if (!m_queue.isEmpty()) { + uint32_t* buffer = m_queue.dequeue(); + if (m_buffer) { + m_free.append(m_buffer); + } + m_buffer = buffer; } - uint32_t* buffer = m_queue.dequeue(); - if (m_buffer) { - m_free.append(m_buffer); - m_buffer = nullptr; + + if (!m_queueTex.isEmpty()) { + if (m_bridgeTexOut != std::numeric_limits::max()) { + m_freeTex.enqueue(m_bridgeTexOut); + } + m_bridgeTexOut = m_queueTex.dequeue(); +#if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (supportsShaders()) { + mGLES2Context* gl2Backend = reinterpret_cast(m_backend); + gl2Backend->tex = m_bridgeTexOut; + } +#endif } - m_buffer = buffer; } void PainterGL::dequeueAll(bool keep) { QMutexLocker locker(&m_mutex); - uint32_t* buffer = 0; + uint32_t* buffer = nullptr; while (!m_queue.isEmpty()) { buffer = m_queue.dequeue(); if (keep) { @@ -802,6 +860,13 @@ void PainterGL::dequeueAll(bool keep) { m_free.append(buffer); } } + m_queueTex.clear(); + m_freeTex.clear(); + for (auto tex : m_bridgeTexes) { + m_freeTex.enqueue(tex); + } + m_bridgeTexIn = m_freeTex.dequeue(); + m_bridgeTexOut = std::numeric_limits::max(); if (m_buffer && !keep) { m_free.append(m_buffer); m_buffer = nullptr; @@ -861,4 +926,29 @@ int PainterGL::glTex() { #endif } +QOpenGLContext* PainterGL::shareContext() { + if (m_widget) { + return m_widget->context(); + } else { + return m_gl.get(); + } +} + +void PainterGL::updateFramebufferHandle() { + QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + fn->glFinish(); + enqueue(m_bridgeTexIn); + m_context->setFramebufferHandle(m_bridgeTexIn); +} + +void PainterGL::swapTex() { + if (!m_started) { + return; + } + + CoreController::Interrupter interrupter(m_context); + emit texSwapped(); + m_context->addFrameAction(std::bind(&PainterGL::swapTex, this)); +} + #endif diff --git a/src/platform/qt/DisplayGL.h b/src/platform/qt/DisplayGL.h index 77ea6d195..3ed31a9d8 100644 --- a/src/platform/qt/DisplayGL.h +++ b/src/platform/qt/DisplayGL.h @@ -112,6 +112,9 @@ protected: virtual void paintEvent(QPaintEvent*) override { forceDraw(); } virtual void resizeEvent(QResizeEvent*) override; +private slots: + void setupProxyThread(); + private: void resizePainter(); bool shouldDisableUpdates(); @@ -122,8 +125,11 @@ private: bool m_hasStarted = false; std::unique_ptr m_painter; QThread m_drawThread; + QThread m_proxyThread; std::shared_ptr m_context; mGLWidget* m_gl; + QOffscreenSurface m_proxySurface; + std::unique_ptr m_proxyContext; }; class PainterGL : public QObject { @@ -137,15 +143,21 @@ public: void setContext(std::shared_ptr); void setMessagePainter(MessagePainter*); void enqueue(const uint32_t* backing); + void enqueue(GLuint tex); void stop(); bool supportsShaders() const { return m_supportsShaders; } int glTex(); + QOpenGLContext* shareContext(); + void setVideoProxy(std::shared_ptr); void interrupt(); + // Run on main thread + void swapTex(); + public slots: void create(); void destroy(); @@ -163,13 +175,16 @@ public slots: void showFrameCounter(bool enable); void filter(bool filter); void resizeContext(); + void updateFramebufferHandle(); void setShaders(struct VDir*); void clearShaders(); VideoShader* shaders(); signals: + void created(); void started(); + void texSwapped(); private slots: void doStop(); @@ -184,6 +199,14 @@ private: QList m_free; QQueue m_queue; uint32_t* m_buffer = nullptr; + + std::array m_bridgeTexes; + QQueue m_freeTex; + QQueue m_queueTex; + + GLuint m_bridgeTexIn = std::numeric_limits::max(); + GLuint m_bridgeTexOut = std::numeric_limits::max(); + QPainter m_painter; QMutex m_mutex; QWindow* m_window; From c511d53d595bbdb204c62725f285a4cc72542818 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 25 Nov 2022 21:37:12 -0800 Subject: [PATCH 062/159] GBA Video: Disable BG target 1 blending when OBJ blending (fixes #2722) --- CHANGES | 1 + cinema/gba/blend/gbg-blend/baseline_0000.png | Bin 0 -> 6569 bytes cinema/gba/blend/gbg-blend/baseline_0001.png | Bin 0 -> 6526 bytes cinema/gba/blend/gbg-blend/baseline_0002.png | Bin 0 -> 6433 bytes cinema/gba/blend/gbg-blend/baseline_0003.png | Bin 0 -> 6380 bytes cinema/gba/blend/gbg-blend/baseline_0004.png | Bin 0 -> 6301 bytes cinema/gba/blend/gbg-blend/baseline_0005.png | Bin 0 -> 6108 bytes cinema/gba/blend/gbg-blend/test.mvl | Bin 0 -> 24871 bytes src/gba/renderers/software-mode0.c | 14 +++----- src/gba/renderers/software-private.h | 35 ++++++++++++++++--- 10 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 cinema/gba/blend/gbg-blend/baseline_0000.png create mode 100644 cinema/gba/blend/gbg-blend/baseline_0001.png create mode 100644 cinema/gba/blend/gbg-blend/baseline_0002.png create mode 100644 cinema/gba/blend/gbg-blend/baseline_0003.png create mode 100644 cinema/gba/blend/gbg-blend/baseline_0004.png create mode 100644 cinema/gba/blend/gbg-blend/baseline_0005.png create mode 100644 cinema/gba/blend/gbg-blend/test.mvl diff --git a/CHANGES b/CHANGES index f564da133..03dc9a7aa 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ Emulation fixes: - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) + - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) diff --git a/cinema/gba/blend/gbg-blend/baseline_0000.png b/cinema/gba/blend/gbg-blend/baseline_0000.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbf58114d923954cade85d2003befb0056d9b74 GIT binary patch literal 6569 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 kcY}UHq)$07*qoM6N<$g7|yf<^TWy literal 0 HcmV?d00001 diff --git a/cinema/gba/blend/gbg-blend/baseline_0002.png b/cinema/gba/blend/gbg-blend/baseline_0002.png new file mode 100644 index 0000000000000000000000000000000000000000..1d38b9113ba0a33c33cda6710a836ffd8b9b9295 GIT binary patch literal 6433 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 literal 0 HcmV?d00001 diff --git a/cinema/gba/blend/gbg-blend/baseline_0003.png b/cinema/gba/blend/gbg-blend/baseline_0003.png new file mode 100644 index 0000000000000000000000000000000000000000..368572a3129bdf171e8cee7686327b8e2062b9a0 GIT binary patch literal 6380 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 + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ @@ -36,7 +35,6 @@ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ LOAD_32(tileData, charBase, vram); \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ pixel = &renderer->row[outX]; \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ if (outX < renderer->start) { \ @@ -93,7 +91,6 @@ carryData = 0; \ } else { \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= 4 * baseX; \ @@ -123,7 +120,6 @@ carryData = 0; \ } else { \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ LOAD_32(tileData, charBase, vram); \ if (!GBA_TEXT_MAP_HFLIP(mapData)) { \ tileData >>= x * 4; \ @@ -154,7 +150,6 @@ localY = 7 - localY; \ } \ paletteData = GBA_TEXT_MAP_PALETTE(mapData) << 4; \ - palette = &mainPalette[paletteData]; \ charBase = (background->charBase + (GBA_TEXT_MAP_TILE(mapData) << 5)) + (localY << 2); \ if (UNLIKELY(charBase >= 0x10000)) { \ pixel += 8; \ @@ -517,17 +512,16 @@ void GBAVideoSoftwareRendererDrawBackgroundMode0(struct GBAVideoSoftwareRenderer uint32_t screenBase; uint32_t charBase; - color_t* mainPalette = renderer->normalPalette; + color_t* palette = renderer->normalPalette; if (renderer->d.highlightAmount && background->highlight) { - mainPalette = renderer->highlightPalette; + palette = renderer->highlightPalette; } if (variant) { - mainPalette = renderer->variantPalette; + palette = renderer->variantPalette; if (renderer->d.highlightAmount && background->highlight) { - mainPalette = renderer->highlightVariantPalette; + palette = renderer->highlightVariantPalette; } } - color_t* palette = mainPalette; PREPARE_OBJWIN; int outX = renderer->start; diff --git a/src/gba/renderers/software-private.h b/src/gba/renderers/software-private.h index 2ace684f1..ec8ef4175 100644 --- a/src/gba/renderers/software-private.h +++ b/src/gba/renderers/software-private.h @@ -86,28 +86,55 @@ static inline void _compositeNoBlendNoObjwin(struct GBAVideoSoftwareRenderer* re #define COMPOSITE_16_OBJWIN(BLEND, IDX) \ if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \ - unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[paletteData | pixelData] : palette[pixelData]; \ + unsigned color; \ unsigned mergedFlags = flags; \ if (current & FLAG_OBJWIN) { \ mergedFlags = objwinFlags; \ + color = objwinPalette[paletteData | pixelData]; \ + } else if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[paletteData | pixelData]; \ + } else { \ + color = palette[paletteData | pixelData]; \ } \ _composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \ } #define COMPOSITE_16_NO_OBJWIN(BLEND, IDX) \ - _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], palette[pixelData] | flags, current); + { \ + unsigned color; \ + if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[paletteData | pixelData]; \ + } else { \ + color = palette[paletteData | pixelData]; \ + } \ + _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], color | flags, current); \ + } #define COMPOSITE_256_OBJWIN(BLEND, IDX) \ if (objwinForceEnable || (!(current & FLAG_OBJWIN)) == objwinOnly) { \ - unsigned color = (current & FLAG_OBJWIN) ? objwinPalette[pixelData] : palette[pixelData]; \ + unsigned color; \ unsigned mergedFlags = flags; \ if (current & FLAG_OBJWIN) { \ mergedFlags = objwinFlags; \ + color = objwinPalette[pixelData]; \ + } else if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[pixelData]; \ + } else { \ + color = palette[pixelData]; \ } \ _composite ## BLEND ## Objwin(renderer, &pixel[IDX], color | mergedFlags, current); \ } -#define COMPOSITE_256_NO_OBJWIN COMPOSITE_16_NO_OBJWIN +#define COMPOSITE_256_NO_OBJWIN(BLEND, IDX) \ + { \ + unsigned color; \ + if ((current & (FLAG_IS_BACKGROUND | FLAG_REBLEND)) == FLAG_REBLEND) { \ + color = renderer->normalPalette[pixelData]; \ + } else { \ + color = palette[pixelData]; \ + } \ + _composite ## BLEND ## NoObjwin(renderer, &pixel[IDX], color | flags, current); \ + } #define BACKGROUND_DRAW_PIXEL_16(BLEND, OBJWIN, IDX) \ pixelData = tileData & 0xF; \ From 9b2d4bc68e2198e15579e629cb1e3b49e8056fe9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 27 Nov 2022 20:08:20 -0800 Subject: [PATCH 063/159] Qt: Don't re-enable sync if GBA link modes aren't the same (fixes #2044) --- CHANGES | 1 + src/platform/qt/MultiplayerController.cpp | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 03dc9a7aa..a0ce4f5b6 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,7 @@ Other fixes: - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) + - Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) - VFS: Fix minizip write returning 0 on success instead of size Misc: diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 7f29100bd..417f9cb50 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -75,12 +75,6 @@ MultiplayerController::MultiplayerController() { if (!id) { for (int i = 1; i < controller->m_players.count(); ++i) { Player* player = &controller->m_players[i]; -#ifdef M_CORE_GBA - if (player->controller->platform() == mPLATFORM_GBA && player->gbaNode->d.p->mode != controller->m_players[0].gbaNode->d.p->mode) { - player->controller->setSync(true); - continue; - } -#endif player->controller->setSync(false); player->cyclesPosted += cycles; if (player->awake < 1) { From 6f08b740f9b480ad0631bcfd894ae5282ce38f35 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 28 Nov 2022 00:59:36 -0800 Subject: [PATCH 064/159] GB SIO: Further fix bidirectional transfer starting --- CHANGES | 1 + src/gb/sio/lockstep.c | 34 +++++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index a0ce4f5b6..65f53ebb3 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Features: - Debugger: Add range watchpoints Emulation fixes: - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) + - GB SIO: Further fix bidirectional transfer starting - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) diff --git a/src/gb/sio/lockstep.c b/src/gb/sio/lockstep.c index 4d1f1f549..b8dccf891 100644 --- a/src/gb/sio/lockstep.c +++ b/src/gb/sio/lockstep.c @@ -157,7 +157,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { } } // Tell the other GBs they can continue up to where we were - node->p->d.addCycles(&node->p->d, node->id, node->eventDiff); + node->p->d.addCycles(&node->p->d, 0, node->eventDiff); #ifndef NDEBUG node->phase = node->p->d.transferActive; #endif @@ -169,26 +169,28 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) { static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) { enum mLockstepPhase transferActive; + int id; ATOMIC_LOAD(transferActive, node->p->d.transferActive); + ATOMIC_LOAD(id, node->id); bool signal = false; switch (transferActive) { case TRANSFER_IDLE: - node->p->d.addCycles(&node->p->d, node->id, LOCKSTEP_INCREMENT); + node->p->d.addCycles(&node->p->d, id, LOCKSTEP_INCREMENT); break; case TRANSFER_STARTING: case TRANSFER_FINISHING: break; case TRANSFER_STARTED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) { break; } node->transferFinished = false; signal = true; break; case TRANSFER_FINISHED: - if (node->p->d.unusedCycles(&node->p->d, node->id) > node->eventDiff) { + if (node->p->d.unusedCycles(&node->p->d, id) > node->eventDiff) { break; } _finishTransfer(node); @@ -199,7 +201,7 @@ static uint32_t _slaveUpdate(struct GBSIOLockstepNode* node) { node->phase = node->p->d.transferActive; #endif if (signal) { - node->p->d.signal(&node->p->d, 1 << node->id); + node->p->d.signal(&node->p->d, 1 << id); } return 0; } @@ -215,11 +217,13 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, int32_t cycles = 0; node->nextEvent -= cyclesLate; if (node->nextEvent <= 0) { - if (!node->id) { + int id; + ATOMIC_LOAD(id, node->id); + if (!id) { cycles = _masterUpdate(node); } else { cycles = _slaveUpdate(node); - cycles += node->p->d.useCycles(&node->p->d, node->id, node->eventDiff); + cycles += node->p->d.useCycles(&node->p->d, id, node->eventDiff); } node->eventDiff = 0; } else { @@ -240,7 +244,9 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user, static void GBSIOLockstepNodeWriteSB(struct GBSIODriver* driver, uint8_t value) { struct GBSIOLockstepNode* node = (struct GBSIOLockstepNode*) driver; - node->p->pendingSB[node->id] = value; + int id; + ATOMIC_LOAD(id, node->id); + node->p->pendingSB[id] = value; } static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t value) { @@ -252,11 +258,17 @@ static uint8_t GBSIOLockstepNodeWriteSC(struct GBSIODriver* driver, uint8_t valu mLockstepLock(&node->p->d); bool claimed = false; if (ATOMIC_CMPXCHG(node->p->masterClaimed, claimed, true)) { - if (node->id != 0) { + int id; + ATOMIC_LOAD(id, node->id); + if (id != 0) { + unsigned sb; node->p->players[0]->id = 1; - node->p->players[1] = node->p->players[0]; - node->p->players[0] = node->p->players[1]; node->id = 0; + node->p->players[1] = node->p->players[0]; + node->p->players[0] = node; + sb = node->p->pendingSB[0]; + node->p->pendingSB[0] = node->p->pendingSB[1]; + node->p->pendingSB[1] = sb; } ATOMIC_STORE(node->p->d.transferActive, TRANSFER_STARTING); ATOMIC_STORE(node->p->d.transferCycles, GBSIOCyclesPerTransfer[(value >> 1) & 1]); From 1b684ae2e32b499e8b2c725d521968f612c63fbc Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 28 Nov 2022 01:00:59 -0800 Subject: [PATCH 065/159] Qt: Improve handling of multiplayer syncing (fixes #2720) --- CHANGES | 1 + src/platform/qt/MultiplayerController.cpp | 128 ++++++++++++++++------ src/platform/qt/MultiplayerController.h | 17 ++- 3 files changed, 109 insertions(+), 37 deletions(-) diff --git a/CHANGES b/CHANGES index 65f53ebb3..daa24e880 100644 --- a/CHANGES +++ b/CHANGES @@ -13,6 +13,7 @@ Other fixes: - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) - Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044) + - Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) - VFS: Fix minizip write returning 0 on success instead of size Misc: diff --git a/src/platform/qt/MultiplayerController.cpp b/src/platform/qt/MultiplayerController.cpp index 417f9cb50..f44323603 100644 --- a/src/platform/qt/MultiplayerController.cpp +++ b/src/platform/qt/MultiplayerController.cpp @@ -14,24 +14,46 @@ #include #endif +#include + using namespace QGBA; #ifdef M_CORE_GB -MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* node) +MultiplayerController::Player::Player(CoreController* coreController, GBSIOLockstepNode* gbNode) : controller(coreController) - , gbNode(node) { + node.gb = gbNode; } #endif #ifdef M_CORE_GBA -MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* node) +MultiplayerController::Player::Player(CoreController* coreController, GBASIOLockstepNode* gbaNode) : controller(coreController) - , gbaNode(node) { + node.gba = gbaNode; } #endif +int MultiplayerController::Player::id() const { + switch (controller->platform()) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + return node.gba->id; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + return node.gb->id; +#endif + case mPLATFORM_NONE: + break; + } + return -1; +} + +bool MultiplayerController::Player::operator<(const MultiplayerController::Player& other) const { + return id() < other.id(); +} + MultiplayerController::MultiplayerController() { mLockstepInit(&m_lockstep); m_lockstep.context = this; @@ -65,6 +87,7 @@ MultiplayerController::MultiplayerController() { player->awake = 0; slept = true; } + player->controller->setSync(true); return slept; }; m_lockstep.addCycles = [](mLockstep* lockstep, int id, int32_t cycles) { @@ -72,38 +95,51 @@ MultiplayerController::MultiplayerController() { abort(); } MultiplayerController* controller = static_cast(lockstep->context); - if (!id) { - for (int i = 1; i < controller->m_players.count(); ++i) { - Player* player = &controller->m_players[i]; - player->controller->setSync(false); - player->cyclesPosted += cycles; - if (player->awake < 1) { - switch (player->controller->platform()) { + Player* player = controller->player(id); + switch (player->controller->platform()) { #ifdef M_CORE_GBA - case mPLATFORM_GBA: - player->gbaNode->nextEvent += player->cyclesPosted; - break; -#endif -#ifdef M_CORE_GB - case mPLATFORM_GB: - player->gbNode->nextEvent += player->cyclesPosted; - break; -#endif - default: - break; + case mPLATFORM_GBA: + if (!id) { + for (int i = 1; i < controller->m_players.count(); ++i) { + player = controller->player(i); + player->controller->setSync(false); + player->cyclesPosted += cycles; + if (player->awake < 1) { + player->node.gba->nextEvent += player->cyclesPosted; } mCoreThreadStopWaiting(player->controller->thread()); player->awake = 1; } + } else { + player->controller->setSync(true); + player->cyclesPosted += cycles; } - } else { - controller->m_players[id].controller->setSync(true); - controller->m_players[id].cyclesPosted += cycles; + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + if (!id) { + player = controller->player(1); + player->controller->setSync(false); + player->cyclesPosted += cycles; + if (player->awake < 1) { + player->node.gb->nextEvent += player->cyclesPosted; + } + mCoreThreadStopWaiting(player->controller->thread()); + player->awake = 1; + } else { + player->controller->setSync(true); + player->cyclesPosted += cycles; + } + break; +#endif + default: + break; } }; m_lockstep.useCycles = [](mLockstep* lockstep, int id, int32_t cycles) { MultiplayerController* controller = static_cast(lockstep->context); - Player* player = &controller->m_players[id]; + Player* player = controller->player(id); player->cyclesPosted -= cycles; if (player->cyclesPosted <= 0) { mCoreThreadWaitFromThread(player->controller->thread()); @@ -112,21 +148,21 @@ MultiplayerController::MultiplayerController() { cycles = player->cyclesPosted; return cycles; }; - m_lockstep.unusedCycles= [](mLockstep* lockstep, int id) { + m_lockstep.unusedCycles = [](mLockstep* lockstep, int id) { MultiplayerController* controller = static_cast(lockstep->context); - Player* player = &controller->m_players[id]; + Player* player = controller->player(id); auto cycles = player->cyclesPosted; return cycles; }; m_lockstep.unload = [](mLockstep* lockstep, int id) { MultiplayerController* controller = static_cast(lockstep->context); if (id) { - Player* player = &controller->m_players[id]; + Player* player = controller->player(id); player->controller->setSync(true); player->cyclesPosted = 0; // release master GBA if it is waiting for this GBA - player = &controller->m_players[0]; + player = controller->player(0); player->waitMask &= ~(1 << id); if (!player->waitMask && player->awake < 1) { mCoreThreadStopWaiting(player->controller->thread()); @@ -134,7 +170,7 @@ MultiplayerController::MultiplayerController() { } } else { for (int i = 1; i < controller->m_players.count(); ++i) { - Player* player = &controller->m_players[i]; + Player* player = controller->player(i); player->controller->setSync(true); switch (player->controller->platform()) { #ifdef M_CORE_GBA @@ -154,12 +190,12 @@ MultiplayerController::MultiplayerController() { switch (player->controller->platform()) { #ifdef M_CORE_GBA case mPLATFORM_GBA: - player->gbaNode->nextEvent += player->cyclesPosted; + player->node.gba->nextEvent += player->cyclesPosted; break; #endif #ifdef M_CORE_GB case mPLATFORM_GB: - player->gbNode->nextEvent += player->cyclesPosted; + player->node.gb->nextEvent += player->cyclesPosted; break; #endif default: @@ -309,3 +345,29 @@ int MultiplayerController::attached() { num = m_lockstep.attached; return num; } + +MultiplayerController::Player* MultiplayerController::player(int id) { + Player* player = &m_players[id]; + switch (player->controller->platform()) { +#ifdef M_CORE_GBA + case mPLATFORM_GBA: + if (player->node.gba->id != id) { + std::sort(m_players.begin(), m_players.end()); + player = &m_players[id]; + } + break; +#endif +#ifdef M_CORE_GB + case mPLATFORM_GB: + if (player->node.gb->id != id) { + std::swap(m_players[0], m_players[1]); + player = &m_players[id]; + } + break; +#endif + case mPLATFORM_NONE: + break; + } + + return player; +} diff --git a/src/platform/qt/MultiplayerController.h b/src/platform/qt/MultiplayerController.h index 03aad86d8..2eb9ab20a 100644 --- a/src/platform/qt/MultiplayerController.h +++ b/src/platform/qt/MultiplayerController.h @@ -6,8 +6,8 @@ #pragma once #include -#include #include +#include #include #ifdef M_CORE_GBA @@ -44,6 +44,10 @@ signals: void gameDetached(); private: + union Node { + GBSIOLockstepNode* gb; + GBASIOLockstepNode* gba; + }; struct Player { #ifdef M_CORE_GB Player(CoreController* controller, GBSIOLockstepNode* node); @@ -52,13 +56,18 @@ private: Player(CoreController* controller, GBASIOLockstepNode* node); #endif + int id() const; + bool operator<(const Player&) const; + CoreController* controller; - GBSIOLockstepNode* gbNode = nullptr; - GBASIOLockstepNode* gbaNode = nullptr; + Node node = {nullptr}; int awake = 1; int32_t cyclesPosted = 0; unsigned waitMask = 0; }; + + Player* player(int id); + union { mLockstep m_lockstep; #ifdef M_CORE_GB @@ -68,7 +77,7 @@ private: GBASIOLockstep m_gbaLockstep; #endif }; - QList m_players; + QVector m_players; QMutex m_lock; }; From 4fefa0c51a43d198693277e14e80c9b0056dcbe8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 28 Nov 2022 14:25:04 -0800 Subject: [PATCH 066/159] Qt: Fix build on older Qt --- src/platform/qt/MultiplayerController.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/MultiplayerController.h b/src/platform/qt/MultiplayerController.h index 2eb9ab20a..5ad6124db 100644 --- a/src/platform/qt/MultiplayerController.h +++ b/src/platform/qt/MultiplayerController.h @@ -5,9 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once +#include #include #include -#include #include #ifdef M_CORE_GBA @@ -77,7 +77,7 @@ private: GBASIOLockstep m_gbaLockstep; #endif }; - QVector m_players; + QList m_players; QMutex m_lock; }; From 083585b56525e5985291ff7ee895857ae618a5ea Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 28 Nov 2022 22:37:31 -0800 Subject: [PATCH 067/159] GB Audio: Fix channels 1/2 not playing when resetting volume (fixes #2614) --- CHANGES | 1 + src/gb/audio.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index daa24e880..73b36504c 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ Features: - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints Emulation fixes: + - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) diff --git a/src/gb/audio.c b/src/gb/audio.c index 0b6a03ecb..26675a983 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -480,7 +480,7 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { GBAudioSample(audio, timestamp); } - if (audio->playingCh1 && (channels & 0x1)) { + if (audio->playingCh1 && (channels & 0x1) && audio->ch1.envelope.dead != 2) { int period = 4 * (2048 - audio->ch1.control.frequency) * audio->timingFactor; int32_t diff = timestamp - audio->ch1.lastUpdate; if (diff >= period) { @@ -490,7 +490,7 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { _updateSquareSample(&audio->ch1); } } - if (audio->playingCh2 && (channels & 0x2)) { + if (audio->playingCh2 && (channels & 0x2) && audio->ch2.envelope.dead != 2) { int period = 4 * (2048 - audio->ch2.control.frequency) * audio->timingFactor; int32_t diff = timestamp - audio->ch2.lastUpdate; if (diff >= period) { @@ -863,7 +863,7 @@ bool _writeEnvelope(struct GBAudioEnvelope* envelope, uint8_t value, enum GBAudi envelope->currentVolume &= 0xF; } _updateEnvelopeDead(envelope); - return (envelope->initialVolume || envelope->direction) && envelope->dead != 2; + return envelope->initialVolume || envelope->direction; } static void _updateSquareSample(struct GBAudioSquareChannel* ch) { From 2ba42761b8d96346ed9b5b3f5da6f88e301ade64 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 29 Nov 2022 02:20:02 -0800 Subject: [PATCH 068/159] Libretro: Add back missing audio overkill (fixes #2734) --- src/platform/libretro/libretro.c | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 9bbd55ae6..2c0184d99 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -617,6 +617,39 @@ void retro_run(void) { core->desiredVideoDimensions(core, &width, &height); videoCallback(outputBuffer, width, height, BYTES_PER_PIXEL * 256); +#ifdef M_CORE_GBA + if (core->platform(core) == mPLATFORM_GBA) { + blip_t *audioChannelLeft = core->getAudioChannel(core, 0); + blip_t *audioChannelRight = core->getAudioChannel(core, 1); + int samplesAvail = blip_samples_avail(audioChannelLeft); + if (samplesAvail > 0) { + /* Update 'running average' of number of + * samples per frame. + * Note that this is not a true running + * average, but just a leaky-integrator/ + * exponential moving average, used because + * it is simple and fast (i.e. requires no + * window of samples). */ + audioSamplesPerFrameAvg = (SAMPLES_PER_FRAME_MOVING_AVG_ALPHA * (float)samplesAvail) + + ((1.0f - SAMPLES_PER_FRAME_MOVING_AVG_ALPHA) * audioSamplesPerFrameAvg); + size_t samplesToRead = (size_t)(audioSamplesPerFrameAvg); + /* Resize audio output buffer, if required */ + if (audioSampleBufferSize < (samplesToRead * 2)) { + audioSampleBufferSize = (samplesToRead * 2); + audioSampleBuffer = realloc(audioSampleBuffer, audioSampleBufferSize * sizeof(int16_t)); + } + int produced = blip_read_samples(audioChannelLeft, audioSampleBuffer, samplesToRead, true); + blip_read_samples(audioChannelRight, audioSampleBuffer + 1, samplesToRead, true); + if (produced > 0) { + if (audioLowPassEnabled) { + _audioLowPassFilter(audioSampleBuffer, produced); + } + audioCallback(audioSampleBuffer, (size_t)produced); + } + } + } +#endif + if (rumbleCallback) { if (rumbleUp) { rumbleCallback(0, RETRO_RUMBLE_STRONG, rumbleUp * 0xFFFF / (rumbleUp + rumbleDown)); From 3385f02b5154b22154db6be8aa7856a47751ec9c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 30 Nov 2022 17:44:53 -0800 Subject: [PATCH 069/159] GBA DMA: Minor optimization of warm function --- src/gba/dma.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/gba/dma.c b/src/gba/dma.c index 14b9fb777..60a8eb056 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -244,7 +244,6 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { struct GBAMemory* memory = &gba->memory; struct ARMCore* cpu = gba->cpu; uint32_t width = 2 << GBADMARegisterGetWidth(info->reg); - int32_t wordsRemaining = info->nextCount; uint32_t source = info->nextSource; uint32_t dest = info->nextDest; uint32_t sourceRegion = source >> BASE_OFFSET; @@ -252,6 +251,8 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { int32_t cycles = 2; gba->cpuBlocked = true; + gba->performingDMA = 1 | (number << 1); + if (info->count == info->nextCount) { if (width == 4) { cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; @@ -267,12 +268,10 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } info->when += cycles; - gba->performingDMA = 1 | (number << 1); if (width == 4) { if (source) { memory->dmaTransferRegister = cpu->memory.load32(cpu, source, 0); } - gba->bus = memory->dmaTransferRegister; cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0); } else { if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { @@ -288,14 +287,13 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { GBASavedataInitEEPROM(&memory->savedata); } if (memory->savedata.type == SAVEDATA_EEPROM512 || memory->savedata.type == SAVEDATA_EEPROM) { - GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, wordsRemaining); + GBASavedataWriteEEPROM(&memory->savedata, memory->dmaTransferRegister, info->nextCount); } } else { cpu->memory.store16(cpu, dest, memory->dmaTransferRegister, 0); - } - gba->bus = memory->dmaTransferRegister; } + gba->bus = memory->dmaTransferRegister; int sourceOffset; if (info->nextSource >= BASE_CART0 && info->nextSource < BASE_CART_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) { @@ -305,15 +303,12 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width; if (source) { - source += sourceOffset; + info->nextSource += sourceOffset; } - dest += destOffset; - --wordsRemaining; - gba->performingDMA = 0; + info->nextDest += destOffset; + --info->nextCount; - info->nextCount = wordsRemaining; - info->nextSource = source; - info->nextDest = dest; + gba->performingDMA = 0; int i; for (i = 0; i < 4; ++i) { @@ -324,7 +319,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } } - if (!wordsRemaining) { + if (!info->nextCount) { info->nextCount |= 0x80000000; if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) { info->when += 2; From e9ec009836d9501909a519fc94b52d09c2ad6ddd Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 7 Dec 2022 01:21:21 -0800 Subject: [PATCH 070/159] GB Audio: Fix channel 3 volume being changed between samples (fixes #1896) --- CHANGES | 1 + src/gb/audio.c | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/CHANGES b/CHANGES index 73b36504c..07d262ca1 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ Features: - Debugger: Add range watchpoints Emulation fixes: - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) + - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) diff --git a/src/gb/audio.c b/src/gb/audio.c index 26675a983..516c6c663 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -269,6 +269,29 @@ void GBAudioWriteNR31(struct GBAudio* audio, uint8_t value) { void GBAudioWriteNR32(struct GBAudio* audio, uint8_t value) { GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x4); audio->ch3.volume = GBAudioRegisterBankVolumeGetVolumeGB(value); + + audio->ch3.sample = audio->ch3.wavedata8[audio->ch3.window >> 1]; + if (!(audio->ch3.window & 1)) { + audio->ch3.sample >>= 4; + } + audio->ch3.sample &= 0xF; + int volume; + switch (audio->ch3.volume) { + case 0: + volume = 4; + break; + case 1: + volume = 0; + break; + case 2: + volume = 1; + break; + default: + case 3: + volume = 2; + break; + } + audio->ch3.sample >>= volume; } void GBAudioWriteNR33(struct GBAudio* audio, uint8_t value) { From b220c7a68b56d7572fc976be2a2200cb6d03f575 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 7 Dec 2022 02:35:08 -0800 Subject: [PATCH 071/159] Qt: Attempt to fix 5.8 build --- src/platform/qt/ForwarderController.cpp | 12 ++++++++++-- src/platform/qt/GBAApp.cpp | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/ForwarderController.cpp b/src/platform/qt/ForwarderController.cpp index fe0530a73..56e202019 100644 --- a/src/platform/qt/ForwarderController.cpp +++ b/src/platform/qt/ForwarderController.cpp @@ -180,6 +180,9 @@ void ForwarderController::downloadBuild(const QUrl& url) { connectReply(reply, BASE, &ForwarderController::gotBuild); connect(reply, &QNetworkReply::readyRead, this, [this, reply]() { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 != 2) { + return; + } QByteArray data = reply->readAll(); m_sourceFile.write(data); }); @@ -236,8 +239,13 @@ void ForwarderController::connectReply(QNetworkReply* reply, Download download, emit buildFailed(); }); - connect(reply, &QNetworkReply::finished, this, [this, reply, next]() { - (this->*next)(reply); + connect(reply, &QNetworkReply::finished, this, [this, reply, download, next]() { + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() / 100 == 3) { + QNetworkReply* newReply = GBAApp::app()->httpGet(reply->header(QNetworkRequest::LocationHeader).toString()); + connectReply(newReply, download, next); + } else { + (this->*next)(reply); + } }); connect(reply, &QNetworkReply::downloadProgress, this, [this, download](qint64 bytesReceived, qint64 bytesTotal) { emit downloadProgress(download, bytesReceived, bytesTotal); diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 1e3c6a894..30287262a 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -82,7 +82,9 @@ GBAApp::GBAApp(int& argc, char* argv[], ConfigController* config) m_configController->updateOption("useDiscordPresence"); #endif +#if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)) m_netman.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); +#endif cleanupAfterUpdate(); From dc5f6e37e133c6cd509a969aaf493362363b6118 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 7 Dec 2022 20:56:16 -0800 Subject: [PATCH 072/159] GB Audio: Initialize ch3 window value --- src/gb/audio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gb/audio.c b/src/gb/audio.c index 516c6c663..a1af17247 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -116,6 +116,7 @@ void GBAudioReset(struct GBAudio* audio) { audio->ch3.wavedata8[13] = 0xFF; audio->ch3.wavedata8[14] = 0x00; audio->ch3.wavedata8[15] = 0xFF; + audio->ch3.window = 0; audio->ch4 = (struct GBAudioNoiseChannel) { .envelope = { .dead = 2 } }; audio->frame = 0; audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES; From 4f70b313fcf82b043bee232dd5af231a7755e1d8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 8 Dec 2022 01:20:51 -0800 Subject: [PATCH 073/159] GB Memory: Actually, HDMAs should start when LCD is off (fixes #2662) --- CHANGES | 1 + src/gb/memory.c | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 07d262ca1..ec39e3bce 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,7 @@ Features: Emulation fixes: - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) + - GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) diff --git a/src/gb/memory.c b/src/gb/memory.c index 50b9b7d6f..5c4d97876 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -558,7 +558,7 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { gb->memory.hdmaDest |= 0x8000; bool wasHdma = gb->memory.isHdma; gb->memory.isHdma = value & 0x80; - if ((!wasHdma && !gb->memory.isHdma) || (GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC]) && gb->video.mode == 0)) { + if ((!wasHdma && !gb->memory.isHdma) || gb->video.mode == 0) { if (gb->memory.isHdma) { gb->memory.hdmaRemaining = 0x10; } else { @@ -566,8 +566,6 @@ uint8_t GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { } gb->cpuBlocked = true; mTimingSchedule(&gb->timing, &gb->memory.hdmaEvent, 0); - } else if (gb->memory.isHdma && !GBRegisterLCDCIsEnable(gb->memory.io[GB_REG_LCDC])) { - return 0x80 | ((value + 1) & 0x7F); } return value & 0x7F; } From 50fd46506c01b6028f1c27c097e9e4ecbf4caf54 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 11 Dec 2022 19:08:53 -0800 Subject: [PATCH 074/159] GB Audio: Fix up boot sequence --- CHANGES | 1 + src/gb/audio.c | 1 - src/gb/gb.c | 21 ++++++++++++++++++++- src/gb/io.c | 31 +------------------------------ 4 files changed, 22 insertions(+), 32 deletions(-) diff --git a/CHANGES b/CHANGES index ec39e3bce..f88219117 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,7 @@ Features: Emulation fixes: - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) + - GB Audio: Fix up boot sequence - GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting diff --git a/src/gb/audio.c b/src/gb/audio.c index a1af17247..516c6c663 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -116,7 +116,6 @@ void GBAudioReset(struct GBAudio* audio) { audio->ch3.wavedata8[13] = 0xFF; audio->ch3.wavedata8[14] = 0x00; audio->ch3.wavedata8[15] = 0xFF; - audio->ch3.window = 0; audio->ch4 = (struct GBAudioNoiseChannel) { .envelope = { .dead = 2 } }; audio->frame = 0; audio->sampleInterval = SAMPLE_INTERVAL * GB_MAX_SAMPLES; diff --git a/src/gb/gb.c b/src/gb/gb.c index dc3a37191..a3a870f28 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -596,13 +596,13 @@ void GBReset(struct SM83Core* cpu) { GBVideoReset(&gb->video); GBTimerReset(&gb->timer); GBIOReset(gb); + GBAudioReset(&gb->audio); if (!gb->biosVf && gb->memory.rom) { GBSkipBIOS(gb); } else { mTimingSchedule(&gb->timing, &gb->timer.event, 0); } - GBAudioReset(&gb->audio); GBSIOReset(&gb->sio); cpu->memory.setActiveRegion(cpu, cpu->pc); @@ -744,6 +744,25 @@ void GBSkipBIOS(struct GB* gb) { GBUnmapBIOS(gb); } + GBIOWrite(gb, GB_REG_NR52, 0xF1); + GBIOWrite(gb, GB_REG_NR14, 0x3F); + GBIOWrite(gb, GB_REG_NR10, 0x80); + GBIOWrite(gb, GB_REG_NR11, 0xBF); + GBIOWrite(gb, GB_REG_NR12, 0xF3); + GBIOWrite(gb, GB_REG_NR13, 0xF3); + GBIOWrite(gb, GB_REG_NR24, 0x3F); + GBIOWrite(gb, GB_REG_NR21, 0x3F); + GBIOWrite(gb, GB_REG_NR22, 0x00); + GBIOWrite(gb, GB_REG_NR34, 0x3F); + GBIOWrite(gb, GB_REG_NR30, 0x7F); + GBIOWrite(gb, GB_REG_NR31, 0xFF); + GBIOWrite(gb, GB_REG_NR32, 0x9F); + GBIOWrite(gb, GB_REG_NR44, 0x3F); + GBIOWrite(gb, GB_REG_NR41, 0xFF); + GBIOWrite(gb, GB_REG_NR42, 0x00); + GBIOWrite(gb, GB_REG_NR43, 0x00); + GBIOWrite(gb, GB_REG_NR50, 0x77); + GBIOWrite(gb, GB_REG_NR51, 0xF3); GBIOWrite(gb, GB_REG_LCDC, 0x91); gb->memory.io[GB_REG_BANK] = 0x1; GBVideoSkipBIOS(&gb->video); diff --git a/src/gb/io.c b/src/gb/io.c index 06a7927cd..511989d0a 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -162,36 +162,7 @@ void GBIOReset(struct GB* gb) { GBIOWrite(gb, GB_REG_TMA, 0); GBIOWrite(gb, GB_REG_TAC, 0); GBIOWrite(gb, GB_REG_IF, 1); - gb->audio.playingCh1 = false; - gb->audio.playingCh2 = false; - gb->audio.playingCh3 = false; - gb->audio.playingCh4 = false; - GBIOWrite(gb, GB_REG_NR52, 0xF1); - GBIOWrite(gb, GB_REG_NR14, 0x3F); - GBIOWrite(gb, GB_REG_NR10, 0x80); - GBIOWrite(gb, GB_REG_NR11, 0xBF); - GBIOWrite(gb, GB_REG_NR12, 0xF3); - GBIOWrite(gb, GB_REG_NR13, 0xF3); - GBIOWrite(gb, GB_REG_NR24, 0x3F); - GBIOWrite(gb, GB_REG_NR21, 0x3F); - GBIOWrite(gb, GB_REG_NR22, 0x00); - GBIOWrite(gb, GB_REG_NR34, 0x3F); - GBIOWrite(gb, GB_REG_NR30, 0x7F); - GBIOWrite(gb, GB_REG_NR31, 0xFF); - GBIOWrite(gb, GB_REG_NR32, 0x9F); - GBIOWrite(gb, GB_REG_NR44, 0x3F); - GBIOWrite(gb, GB_REG_NR41, 0xFF); - GBIOWrite(gb, GB_REG_NR42, 0x00); - GBIOWrite(gb, GB_REG_NR43, 0x00); - GBIOWrite(gb, GB_REG_NR50, 0x77); - GBIOWrite(gb, GB_REG_NR51, 0xF3); - if (!gb->biosVf) { - GBIOWrite(gb, GB_REG_LCDC, 0x91); - gb->memory.io[GB_REG_BANK] = 1; - } else { - GBIOWrite(gb, GB_REG_LCDC, 0x00); - gb->memory.io[GB_REG_BANK] = 0xFF; - } + GBIOWrite(gb, GB_REG_LCDC, 0x00); GBIOWrite(gb, GB_REG_SCY, 0x00); GBIOWrite(gb, GB_REG_SCX, 0x00); GBIOWrite(gb, GB_REG_LYC, 0x00); From 27076733d4ef556634efcd23d4bf575e6b05b1d8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 11 Dec 2022 19:09:21 -0800 Subject: [PATCH 075/159] GB Audio: Fix updating channels other than 2 when writing NR5x --- CHANGES | 1 + src/gb/audio.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index f88219117..d06564d26 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ Emulation fixes: - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) - GB Audio: Fix up boot sequence + - GB Audio: Fix updating channels other than 2 when writing NR5x - GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting diff --git a/src/gb/audio.c b/src/gb/audio.c index 516c6c663..8bce46e07 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -400,13 +400,13 @@ void GBAudioWriteNR44(struct GBAudio* audio, uint8_t value) { } void GBAudioWriteNR50(struct GBAudio* audio, uint8_t value) { - GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); audio->volumeRight = GBRegisterNR50GetVolumeRight(value); audio->volumeLeft = GBRegisterNR50GetVolumeLeft(value); } void GBAudioWriteNR51(struct GBAudio* audio, uint8_t value) { - GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0x2); + GBAudioRun(audio, mTimingCurrentTime(audio->timing), 0xF); audio->ch1Right = GBRegisterNR51GetCh1Right(value); audio->ch2Right = GBRegisterNR51GetCh2Right(value); audio->ch3Right = GBRegisterNR51GetCh3Right(value); From 1127cf8b849106dee61781269aa1fd907327f6a8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 12 Dec 2022 03:12:06 -0800 Subject: [PATCH 076/159] Qt: Fix proxy context creation on Nvidia (fixes #2746) --- src/platform/qt/DisplayGL.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index b8c0f8cd9..9f5729a67 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -425,10 +425,10 @@ void DisplayGL::setVideoProxy(std::shared_ptr proxy) { void DisplayGL::setupProxyThread() { m_proxyContext->moveToThread(&m_proxyThread); + m_proxySurface.create(); connect(&m_proxyThread, &QThread::started, m_proxyContext.get(), [this]() { m_proxyContext->setShareContext(m_painter->shareContext()); m_proxyContext->create(); - m_proxySurface.create(); m_proxyContext->makeCurrent(&m_proxySurface); #if defined(_WIN32) && defined(USE_EPOXY) epoxy_handle_external_wglMakeCurrent(); @@ -576,6 +576,7 @@ void PainterGL::create() { m_backend->filter = false; m_backend->lockAspectRatio = false; m_backend->interframeBlending = false; + m_gl->doneCurrent(); emit created(); } From 4626cac9e8c532775359daba7ce5fcb1e7dd4028 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 12 Dec 2022 03:58:12 -0800 Subject: [PATCH 077/159] Qt: Tentative fix for renderer switching crash --- src/platform/qt/DisplayGL.cpp | 5 +++++ src/platform/qt/Window.cpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 9f5729a67..3816a658b 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -938,6 +938,11 @@ QOpenGLContext* PainterGL::shareContext() { void PainterGL::updateFramebufferHandle() { QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); fn->glFinish(); + + CoreController::Interrupter interrupter(m_context); + if (!m_context->hardwareAccelerated()) { + return; + } enqueue(m_bridgeTexIn); m_context->setFramebufferHandle(m_bridgeTexIn); } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 02460aeb8..cdfb5b9c1 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -1061,6 +1061,8 @@ void Window::changeRenderer() { if (!m_controller) { return; } + + CoreController::Interrupter interrupter(m_controller); if (m_config->getOption("hwaccelVideo").toInt() && m_display->supportsShaders() && m_controller->supportsFeature(CoreController::Feature::OPENGL)) { std::shared_ptr proxy = m_display->videoProxy(); if (!proxy) { From b3f9d9b18621258a5ee64293745177404dbf2be0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 12 Dec 2022 14:31:41 -0800 Subject: [PATCH 078/159] Qt: Fix loading/unloading shaders --- src/platform/qt/DisplayGL.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 3816a658b..ec496e7ad 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -887,12 +887,20 @@ void PainterGL::setShaders(struct VDir* dir) { return; } #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (!m_started) { + makeCurrent(); + } + if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } mGLES2ShaderLoad(&m_shader, dir); mGLES2ShaderAttach(reinterpret_cast(m_backend), static_cast(m_shader.passes), m_shader.nPasses); + + if (!m_started) { + m_gl->doneCurrent(); + } #endif } @@ -901,10 +909,18 @@ void PainterGL::clearShaders() { return; } #if defined(BUILD_GLES2) || defined(BUILD_GLES3) + if (!m_started) { + makeCurrent(); + } + if (m_shader.passes) { mGLES2ShaderDetach(reinterpret_cast(m_backend)); mGLES2ShaderFree(&m_shader); } + + if (!m_started) { + m_gl->doneCurrent(); + } #endif } From ab82b2c856f44049604019452ff01c6f201ed78f Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 13 Dec 2022 15:18:21 -0800 Subject: [PATCH 079/159] GB IO: Fix BANK not getting initialized after recent change --- src/gb/io.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gb/io.c b/src/gb/io.c index 511989d0a..ba4473550 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -174,6 +174,7 @@ void GBIOReset(struct GB* gb) { } GBIOWrite(gb, GB_REG_WY, 0x00); GBIOWrite(gb, GB_REG_WX, 0x00); + gb->memory.io[GB_REG_BANK] = 0xFF; if (gb->model & GB_MODEL_CGB) { GBIOWrite(gb, GB_REG_KEY0, 0); GBIOWrite(gb, GB_REG_JOYP, 0xFF); From 0ac446bedad95bf1d422691a16f5670a6758ff28 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Fri, 16 Dec 2022 22:12:24 -0800 Subject: [PATCH 080/159] Check gba->mbVf for force skipping the BIOS (#2754) --- CHANGES | 1 + src/gba/core.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index d06564d26..04ab3c850 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,7 @@ Emulation fixes: - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: + - GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753) - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) diff --git a/src/gba/core.c b/src/gba/core.c index 68ea66c79..b44c8aa5c 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -700,7 +700,7 @@ static void _GBACoreReset(struct mCore* core) { #endif ARMReset(core->cpu); - bool forceSkip = gba->romVf && GBAIsMB(gba->romVf); + bool forceSkip = gba->mbVf; if (!(forceSkip || core->opts.skipBios) && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) { uint32_t crc = doCrc32(&gba->memory.rom[1], 0x9C); if (crc != LOGO_CRC32) { From 83578d915bece29d7497412ee531b0d5c9612e04 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 17 Dec 2022 00:21:07 -0800 Subject: [PATCH 081/159] Qt: Refactor and fix dequeueAll so keep mode keeps last draw tex too --- src/platform/qt/DisplayGL.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index ec496e7ad..86e115517 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -861,17 +861,23 @@ void PainterGL::dequeueAll(bool keep) { m_free.append(buffer); } } - m_queueTex.clear(); - m_freeTex.clear(); - for (auto tex : m_bridgeTexes) { - m_freeTex.enqueue(tex); - } - m_bridgeTexIn = m_freeTex.dequeue(); - m_bridgeTexOut = std::numeric_limits::max(); if (m_buffer && !keep) { m_free.append(m_buffer); m_buffer = nullptr; } + + m_queueTex.clear(); + m_freeTex.clear(); + for (auto tex : m_bridgeTexes) { + if (keep && tex == m_bridgeTexIn) { + continue; + } + m_freeTex.enqueue(tex); + } + if (!keep) { + m_bridgeTexIn = m_freeTex.dequeue(); + m_bridgeTexOut = std::numeric_limits::max(); + } } void PainterGL::setVideoProxy(std::shared_ptr proxy) { From d2949383d584cdf9a00f1e80d986eda0f8fb9dfb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 17 Dec 2022 00:21:07 -0800 Subject: [PATCH 082/159] GBA: Clean up BIOS skip detection; add second multiboot entry --- src/gba/core.c | 6 +++--- src/gba/gba.c | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/gba/core.c b/src/gba/core.c index b44c8aa5c..a50c11175 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -700,8 +700,8 @@ static void _GBACoreReset(struct mCore* core) { #endif ARMReset(core->cpu); - bool forceSkip = gba->mbVf; - if (!(forceSkip || core->opts.skipBios) && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) { + bool forceSkip = gba->mbVf || core->opts.skipBios; + if (!forceSkip && (gba->romVf || gba->memory.rom) && gba->pristineRomSize >= 0xA0 && gba->biosVf) { uint32_t crc = doCrc32(&gba->memory.rom[1], 0x9C); if (crc != LOGO_CRC32) { mLOG(STATUS, WARN, "Invalid logo, skipping BIOS"); @@ -709,7 +709,7 @@ static void _GBACoreReset(struct mCore* core) { } } - if (forceSkip || (core->opts.skipBios && (gba->romVf || gba->memory.rom))) { + if (forceSkip) { GBASkipBIOS(core->board); } diff --git a/src/gba/gba.c b/src/gba/gba.c index f40e85ace..b26ec85af 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -275,8 +275,10 @@ void GBASkipBIOS(struct GBA* gba) { if (cpu->gprs[ARM_PC] == BASE_RESET + WORD_SIZE_ARM) { if (gba->memory.rom) { cpu->gprs[ARM_PC] = BASE_CART0; - } else { + } else if (gba->memory.wram[0x30]) { cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0; + } else { + cpu->gprs[ARM_PC] = BASE_WORKING_RAM; } gba->video.vcount = 0x7E; gba->memory.io[REG_VCOUNT >> 1] = 0x7E; From 90893dff603f14d4bf45d5ae1e5028db8480d0b4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 18 Dec 2022 17:01:01 -0800 Subject: [PATCH 083/159] GBA Video: Fix #2489 in OpenGL renderer --- src/gba/renderers/gl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/gba/renderers/gl.c b/src/gba/renderers/gl.c index 9d7ccafa7..b6ffa2aac 100644 --- a/src/gba/renderers/gl.c +++ b/src/gba/renderers/gl.c @@ -664,10 +664,12 @@ static const char* const _finalize = " if (((topFlags.y & 13) == 5 || topFlags.w > 0) && (bottomFlags.y & 2) == 2) {\n" " topPixel.rgb *= float(topFlags.z) / 16.;\n" " topPixel.rgb += bottomPixel.rgb * float(windowFlags.y) / 16.;\n" - " } else if ((topFlags.y & 13) == 9) {\n" - " topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n" - " } else if ((topFlags.y & 13) == 13) {\n" - " topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n" + " } else if (topFlags.w == 0) { \n" + " if ((topFlags.y & 13) == 9) {\n" + " topPixel.rgb += (1. - topPixel.rgb) * float(windowFlags.z) / 16.;\n" + " } else if ((topFlags.y & 13) == 13) {\n" + " topPixel.rgb -= topPixel.rgb * float(windowFlags.z) / 16.;\n" + " }\n" " }\n" " color = topPixel;\n" "}"; From 455e34edcfbb64fb3021a6219ded317a8af907b7 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 18 Dec 2022 21:48:08 -0800 Subject: [PATCH 084/159] Qt: glFlush is (thankfully) sufficient here --- src/platform/qt/DisplayGL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 86e115517..4b0fcdce3 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -959,7 +959,7 @@ QOpenGLContext* PainterGL::shareContext() { void PainterGL::updateFramebufferHandle() { QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); - fn->glFinish(); + fn->glFlush(); CoreController::Interrupter interrupter(m_context); if (!m_context->hardwareAccelerated()) { From 86bcbf171651804df0f77e5a2a104f36ae938c2a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 21 Dec 2022 22:13:23 -0800 Subject: [PATCH 085/159] Qt: Start OpenGL bug list with glFlush cross-thread on Windows (fixes #2761) --- src/platform/qt/CMakeLists.txt | 1 + src/platform/qt/DisplayGL.cpp | 9 ++++++++- src/platform/qt/OpenGLBug.cpp | 34 ++++++++++++++++++++++++++++++++++ src/platform/qt/OpenGLBug.h | 17 +++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/platform/qt/OpenGLBug.cpp create mode 100644 src/platform/qt/OpenGLBug.h diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 099bb9d8e..1bb8758c7 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -123,6 +123,7 @@ set(SOURCE_FILES MessagePainter.cpp MultiplayerController.cpp ObjView.cpp + OpenGLBug.cpp OverrideView.cpp PaletteView.cpp PlacementControl.cpp diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 4b0fcdce3..0528922fb 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -44,6 +44,8 @@ using QOpenGLFunctions_Baseline = QOpenGLFunctions_3_2_Core; #define OVERHEAD_NSEC 300000 #endif +#include "OpenGLBug.h" + using namespace QGBA; QHash DisplayGL::s_supports; @@ -959,7 +961,12 @@ QOpenGLContext* PainterGL::shareContext() { void PainterGL::updateFramebufferHandle() { QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); - fn->glFlush(); + // TODO: Figure out why glFlush doesn't work here on Intel/Windows + if (glContextHasBug(OpenGLBug::CROSS_THREAD_FLUSH)) { + fn->glFinish(); + } else { + fn->glFlush(); + } CoreController::Interrupter interrupter(m_context); if (!m_context->hardwareAccelerated()) { diff --git a/src/platform/qt/OpenGLBug.cpp b/src/platform/qt/OpenGLBug.cpp new file mode 100644 index 000000000..e00310c56 --- /dev/null +++ b/src/platform/qt/OpenGLBug.cpp @@ -0,0 +1,34 @@ +/* Copyright (c) 2013-2022 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 "OpenGLBug.h" + +#include +#include + +namespace QGBA { + +bool glContextHasBug(OpenGLBug bug) { + QOpenGLContext* context = QOpenGLContext::currentContext(); + if (!context) { + abort(); + } + QOpenGLFunctions* fn = context->functions(); + QString vendor(reinterpret_cast(fn->glGetString(GL_VENDOR))); + QString renderer(reinterpret_cast(fn->glGetString(GL_RENDERER))); + + switch (bug) { + case OpenGLBug::CROSS_THREAD_FLUSH: +#ifndef Q_OS_WIN + return false; +#else + return vendor == "Intel"; +#endif + default: + return false; + } +} + +} diff --git a/src/platform/qt/OpenGLBug.h b/src/platform/qt/OpenGLBug.h new file mode 100644 index 000000000..fda0ed555 --- /dev/null +++ b/src/platform/qt/OpenGLBug.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013-2022 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/. */ +#pragma once + +namespace QGBA { + +enum class OpenGLBug { + // mgba.io/i/2761 + CROSS_THREAD_FLUSH +}; + +bool glContextHasBug(OpenGLBug); + +} From 1b9671bf21c6332a08b49679174950adcb9e1c24 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 22 Dec 2022 16:02:12 -0800 Subject: [PATCH 086/159] GBA Cheats: Fix issues detecting unencrypted cheats (fixes #2724) --- CHANGES | 1 + src/gba/cheats.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 04ab3c850..00e8d7587 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,7 @@ Emulation fixes: - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: - GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753) + - GBA Cheats: Fix issues detecting unencrypted cheats (fixes mgba.io/i/2724) - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) diff --git a/src/gba/cheats.c b/src/gba/cheats.c index ec92bc3c2..08848077b 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -126,13 +126,13 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_ GBACheatSetGameSharkVersion(set, GBA_GS_PARV3); } - rgsaP = GBACheatGameSharkProbability(op1, op1); + rgsaP = GBACheatGameSharkProbability(op1, op2); if (rgsaP > maxProbability) { maxProbability = rgsaP; GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW); } - rparP = GBACheatProActionReplayProbability(op1, op1); + rparP = GBACheatProActionReplayProbability(op1, op2); if (rparP > maxProbability) { maxProbability = rparP; GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW); From f3b6593ab65bafecfcd8c3418047010496450303 Mon Sep 17 00:00:00 2001 From: momocaoo <121313710+momocaoo@users.noreply.github.com> Date: Mon, 26 Dec 2022 03:13:37 +0100 Subject: [PATCH 087/159] Qt: Fix typo in Forwarder ui (#2764) --- src/platform/qt/ForwarderView.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ForwarderView.ui b/src/platform/qt/ForwarderView.ui index 1c72e3caa..f83facf1c 100644 --- a/src/platform/qt/ForwarderView.ui +++ b/src/platform/qt/ForwarderView.ui @@ -71,7 +71,7 @@ - Latest stable verison + Latest stable version From 61950a52dc976598424f69be3e0b6c5af5bcc9ea Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 27 Dec 2022 01:36:42 -0800 Subject: [PATCH 088/159] GBA Cheats: Clean up redundant variables --- src/gba/cheats.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/gba/cheats.c b/src/gba/cheats.c index 08848077b..04082d48a 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -105,36 +105,36 @@ static bool GBACheatAddAutodetect(struct GBACheatSet* set, uint32_t op1, uint32_ char line[18] = "XXXXXXXX XXXXXXXX"; snprintf(line, sizeof(line), "%08X %08X", op1, op2); - int gsaP, rgsaP, parP, rparP; + int nextProbability; int maxProbability = INT_MIN; switch (set->gsaVersion) { case 0: // Try to detect GameShark version GBACheatDecryptGameShark(&o1, &o2, GBACheatGameSharkSeeds); - gsaP = GBACheatGameSharkProbability(o1, o2); + nextProbability = GBACheatGameSharkProbability(o1, o2); o1 = op1; o2 = op2; - if (gsaP > maxProbability) { - maxProbability = gsaP; + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1); } GBACheatDecryptGameShark(&o1, &o2, GBACheatProActionReplaySeeds); - parP = GBACheatProActionReplayProbability(o1, o2); - if (parP > maxProbability) { - maxProbability = parP; + nextProbability = GBACheatProActionReplayProbability(o1, o2); + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_PARV3); } - rgsaP = GBACheatGameSharkProbability(op1, op2); - if (rgsaP > maxProbability) { - maxProbability = rgsaP; + nextProbability = GBACheatGameSharkProbability(op1, op2); + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_GSAV1_RAW); } - rparP = GBACheatProActionReplayProbability(op1, op2); - if (rparP > maxProbability) { - maxProbability = rparP; + nextProbability = GBACheatProActionReplayProbability(op1, op2); + if (nextProbability > maxProbability) { + maxProbability = nextProbability; GBACheatSetGameSharkVersion(set, GBA_GS_PARV3_RAW); } From 520609d12a2619adc10434179679e8f6c70fcb43 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 27 Dec 2022 03:22:08 -0800 Subject: [PATCH 089/159] Qt: Fix indentation --- src/platform/qt/OpenGLBug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/OpenGLBug.h b/src/platform/qt/OpenGLBug.h index fda0ed555..11b0bba43 100644 --- a/src/platform/qt/OpenGLBug.h +++ b/src/platform/qt/OpenGLBug.h @@ -8,8 +8,8 @@ namespace QGBA { enum class OpenGLBug { - // mgba.io/i/2761 - CROSS_THREAD_FLUSH + // mgba.io/i/2761 + CROSS_THREAD_FLUSH }; bool glContextHasBug(OpenGLBug); From 0a4cafcd57637b17c8efba9b607f81d5e4511584 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 27 Dec 2022 20:39:19 -0800 Subject: [PATCH 090/159] GB BIOS: Include timing in degenerate ArcTan2 cases (fixes #2763) --- CHANGES | 1 + src/gba/bios.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 00e8d7587..06b1a5235 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Emulation fixes: - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) - GB Audio: Fix up boot sequence - GB Audio: Fix updating channels other than 2 when writing NR5x + - GB BIOS: Include timing in degenerate ArcTan2 cases (fixes mgba.io/i/2763) - GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting diff --git a/src/gba/bios.c b/src/gba/bios.c index d824830aa..1d0c0dd10 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -336,12 +336,14 @@ static int16_t _ArcTan(int32_t i, int32_t* r1, int32_t* r3, uint32_t* cycles) { static int16_t _ArcTan2(int32_t x, int32_t y, int32_t* r1, uint32_t* cycles) { if (!y) { + *cycles = 11; if (x >= 0) { return 0; } return 0x8000; } if (!x) { + *cycles = 11; if (y >= 0) { return 0x4000; } From 1c370f6ebebeb5b8af85099d59d614c166aada4b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 31 Dec 2022 01:19:58 -0800 Subject: [PATCH 091/159] Scripting: Refactor out some testing code --- src/core/test/scripting.c | 18 ++---------------- src/script/test.h | 29 +++++++++++++++++++++++++++++ src/script/test/lua.c | 13 ++----------- src/script/test/stdlib.c | 22 ++-------------------- 4 files changed, 35 insertions(+), 47 deletions(-) create mode 100644 src/script/test.h diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index ddcb448c5..2e26b096f 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -12,6 +12,8 @@ #include #include +#include "script/test.h" + #ifdef M_CORE_GBA #include #define TEST_PLATFORM mPLATFORM_GBA @@ -66,22 +68,6 @@ static const uint8_t _fakeGBROM[0x4000] = { mCoreConfigDeinit(&core->config); \ core->deinit(core) -#define LOAD_PROGRAM(PROG) \ - do { \ - struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, NULL, vf)); \ - vf->close(vf); \ - } while(0) - -#define TEST_VALUE(TYPE, NAME, VALUE) \ - do { \ - struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ - struct mScriptValue* global = lua->getGlobal(lua, NAME); \ - assert_non_null(global); \ - assert_true(global->type->equal(global, &val)); \ - mScriptValueDeref(global); \ - } while(0) - static void _mScriptTestLog(struct mLogger* log, int category, enum mLogLevel level, const char* format, va_list args) { UNUSED(category); struct mScriptTestLogger* logger = (struct mScriptTestLogger*) log; diff --git a/src/script/test.h b/src/script/test.h new file mode 100644 index 000000000..f526bce30 --- /dev/null +++ b/src/script/test.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013-2022 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 M_SCRIPT_TEST_H +#define M_SCRIPT_TEST_H + +#define LOAD_PROGRAM(PROG) \ + do { \ + struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ + assert_true(lua->load(lua, NULL, vf)); \ + vf->close(vf); \ + } while(0) + +#define TEST_VALUE(TYPE, NAME, VALUE) \ + do { \ + struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ + struct mScriptValue* global = lua->getGlobal(lua, NAME); \ + assert_non_null(global); \ + assert_true(global->type->equal(global, &val)); \ + mScriptValueDeref(global); \ + } while(0) + +#define TEST_PROGRAM(PROG) \ + LOAD_PROGRAM(PROG); \ + assert_true(lua->run(lua)); \ + +#endif diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 2cc7fcbff..73954e91e 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -8,22 +8,13 @@ #include #include +#include "script/test.h" + #define SETUP_LUA \ struct mScriptContext context; \ mScriptContextInit(&context); \ struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA) -#define LOAD_PROGRAM(PROG) \ - do { \ - struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, NULL, vf)); \ - vf->close(vf); \ - } while(0) - -#define TEST_PROGRAM(PROG) \ - LOAD_PROGRAM(PROG); \ - assert_true(lua->run(lua)); \ - struct Test { int32_t i; int32_t (*ifn0)(struct Test*); diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c index d4c57605b..63ddda4fc 100644 --- a/src/script/test/stdlib.c +++ b/src/script/test/stdlib.c @@ -10,32 +10,14 @@ #include #include +#include "script/test.h" + #define SETUP_LUA \ struct mScriptContext context; \ mScriptContextInit(&context); \ struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ mScriptContextAttachStdlib(&context) -#define LOAD_PROGRAM(PROG) \ - do { \ - struct VFile* vf = VFileFromConstMemory(PROG, strlen(PROG)); \ - assert_true(lua->load(lua, NULL, vf)); \ - vf->close(vf); \ - } while(0) - -#define TEST_PROGRAM(PROG) \ - LOAD_PROGRAM(PROG); \ - assert_true(lua->run(lua)); \ - -#define TEST_VALUE(TYPE, NAME, VALUE) \ - do { \ - struct mScriptValue val = mSCRIPT_MAKE(TYPE, VALUE); \ - struct mScriptValue* global = lua->getGlobal(lua, NAME); \ - assert_non_null(global); \ - assert_true(global->type->equal(global, &val)); \ - mScriptValueDeref(global); \ - } while(0) - M_TEST_SUITE_SETUP(mScriptStdlib) { if (mSCRIPT_ENGINE_LUA->init) { mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); From bf3a2071b7f73e4dbfd2947e0e9a3c0521887ce4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 31 Dec 2022 01:21:03 -0800 Subject: [PATCH 092/159] All: Add more build products to the gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 20c19fc41..0e5d801be 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,12 @@ *.a *.dylib *.dll +*.lib *.exe *.o *.so CMakeCache.txt CMakeFiles CMakeSettings.json +cmake_install.cmake version.c From 9f8679ffa66a047ba387067e72b28e1cd90d6e9a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 31 Dec 2022 17:54:29 -0800 Subject: [PATCH 093/159] Qt: Fix initializing update revision info --- CHANGES | 1 + src/platform/qt/ApplicationUpdater.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 06b1a5235..703038081 100644 --- a/CHANGES +++ b/CHANGES @@ -22,6 +22,7 @@ Other fixes: - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) - Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044) - Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720) + - Qt: Fix initializing update revision info - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) - VFS: Fix minizip write returning 0 on success instead of size Misc: diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index a0efca23d..e2d1e5e4c 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -174,7 +174,8 @@ const char* ApplicationUpdater::platform() { } ApplicationUpdater::UpdateInfo::UpdateInfo(const QString& prefix, const mUpdate* update) - : size(update->size) + : rev(-1) + , size(update->size) , url(prefix + update->path) { if (update->rev > 0) { From 819d19dddcba8f389bc266c2f39b2a173dd2bd6b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 31 Dec 2022 18:15:47 -0800 Subject: [PATCH 094/159] Qt: Redo stable branch detection heuristic --- CHANGES | 2 +- src/platform/qt/ApplicationUpdater.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 703038081..8fe044973 100644 --- a/CHANGES +++ b/CHANGES @@ -18,11 +18,11 @@ Other fixes: - GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753) - GBA Cheats: Fix issues detecting unencrypted cheats (fixes mgba.io/i/2724) - Qt: Manually split filename to avoid overzealous splitting (fixes mgba.io/i/2681) - - Qt: Expand criteria for tag branch names (fixes mgba.io/i/2679) - Qt: Fix scanning specific e-Reader dotcodes (fixes mgba.io/i/2693) - Qt: Don't re-enable sync if GBA link modes aren't the same (fixes mgba.io/i/2044) - Qt: Improve handling of multiplayer syncing (fixes mgba.io/i/2720) - Qt: Fix initializing update revision info + - Qt: Redo stable branch detection heuristic (fixes mgba.io/i/2679) - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) - VFS: Fix minizip write returning 0 on success instead of size Misc: diff --git a/src/platform/qt/ApplicationUpdater.cpp b/src/platform/qt/ApplicationUpdater.cpp index e2d1e5e4c..21def050c 100644 --- a/src/platform/qt/ApplicationUpdater.cpp +++ b/src/platform/qt/ApplicationUpdater.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "ApplicationUpdatePrompt.h" #include "ConfigController.h" @@ -71,9 +72,10 @@ QStringList ApplicationUpdater::listChannels() { } QString ApplicationUpdater::currentChannel() { - QLatin1String version(projectVersion); - QLatin1String branch(gitBranch); - if (branch == QLatin1String("heads/") + version || branch == version) { + QString version(projectVersion); + QString branch(gitBranch); + QRegularExpression stable("^(?:(?:refs/)?(?:tags|heads)/)?[0-9]+\\.[0-9]+\\.[0-9]+$"); + if (branch.contains(stable) || (branch == "(unknown)" && version.contains(stable))) { return QLatin1String("stable"); } else { return QLatin1String("dev"); From 068e1fb612f1bb9f8f4baec6d3177fb8a637571d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 6 Jan 2023 14:26:14 -0800 Subject: [PATCH 095/159] GB Audio: Fix regression in channel updating with NR5x (fixes #2775) --- src/gb/audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gb/audio.c b/src/gb/audio.c index 8bce46e07..d2d6a9260 100644 --- a/src/gb/audio.c +++ b/src/gb/audio.c @@ -499,7 +499,7 @@ void GBAudioRun(struct GBAudio* audio, int32_t timestamp, int channels) { if (!audio->enable) { return; } - if (audio->p && channels != 0xF && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) { + if (audio->p && channels != 0x1F && timestamp - audio->lastSample > (int) (SAMPLE_INTERVAL * audio->timingFactor)) { GBAudioSample(audio, timestamp); } @@ -779,7 +779,7 @@ void GBAudioSample(struct GBAudio* audio, int32_t timestamp) { for (sample = audio->sampleIndex; timestamp >= interval && sample < GB_MAX_SAMPLES; ++sample, timestamp -= interval) { int16_t sampleLeft = 0; int16_t sampleRight = 0; - GBAudioRun(audio, sample * interval + audio->lastSample, 0xF); + GBAudioRun(audio, sample * interval + audio->lastSample, 0x1F); GBAudioSamplePSG(audio, &sampleLeft, &sampleRight); sampleLeft = (sampleLeft * audio->masterVolume * 6) >> 7; sampleRight = (sampleRight * audio->masterVolume * 6) >> 7; From fa3b0d087588713d3fa7240b2ceffc36b70bfbb3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 Jan 2023 15:49:23 -0800 Subject: [PATCH 096/159] Scripting: Fix internal socket header --- include/mgba/internal/script/socket.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/mgba/internal/script/socket.h b/include/mgba/internal/script/socket.h index 014a765bb..98953fb80 100644 --- a/include/mgba/internal/script/socket.h +++ b/include/mgba/internal/script/socket.h @@ -6,6 +6,10 @@ #ifndef M_SCRIPT_SOCKET_H #define M_SCRIPT_SOCKET_H +#include + +CXX_GUARD_START + enum mSocketErrorCode { mSCRIPT_SOCKERR_UNKNOWN_ERROR = -1, mSCRIPT_SOCKERR_OK = 0, @@ -22,4 +26,6 @@ enum mSocketErrorCode { mSCRIPT_SOCKERR_UNSUPPORTED, }; +CXX_GUARD_END + #endif From 70e6470e8bc792d31920733fb18753c38948441d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 Jan 2023 23:59:36 -0800 Subject: [PATCH 097/159] Qt: Fix minor leak --- src/platform/qt/ReportView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index 61a7a2cd1..494f45a64 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -308,6 +308,7 @@ void ReportView::generateReport() { deferredBinaries.append(qMakePair(QString("Save %1").arg(winId), save)); } mStateExtdataDeinit(&extdata); + vf->close(vf); } } } else { From 9df06383b575c0fad5fc4a2846bcb65cfcdd99c9 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 9 Jan 2023 00:09:25 -0800 Subject: [PATCH 098/159] Qt: Work around Mesa issue 8035 --- src/platform/qt/DisplayGL.cpp | 12 ++++++++++++ src/platform/qt/OpenGLBug.cpp | 5 +++++ src/platform/qt/OpenGLBug.h | 4 ++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 0528922fb..896f3acaf 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -670,8 +670,20 @@ void PainterGL::filter(bool filter) { } } +#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#endif + void PainterGL::start() { makeCurrent(); +#if defined(BUILD_GLES3) && !defined(Q_OS_MAC) + if (glContextHasBug(OpenGLBug::GLTHREAD_BLOCKS_SWAP)) { + // Suggested on Discord as a way to strongly hint that glthread should be disabled + // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/8035 + QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + fn->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + } +#endif #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders && m_shader.passes) { diff --git a/src/platform/qt/OpenGLBug.cpp b/src/platform/qt/OpenGLBug.cpp index e00310c56..df007c1cb 100644 --- a/src/platform/qt/OpenGLBug.cpp +++ b/src/platform/qt/OpenGLBug.cpp @@ -18,6 +18,7 @@ bool glContextHasBug(OpenGLBug bug) { QOpenGLFunctions* fn = context->functions(); QString vendor(reinterpret_cast(fn->glGetString(GL_VENDOR))); QString renderer(reinterpret_cast(fn->glGetString(GL_RENDERER))); + QString version(reinterpret_cast(fn->glGetString(GL_VERSION))); switch (bug) { case OpenGLBug::CROSS_THREAD_FLUSH: @@ -26,6 +27,10 @@ bool glContextHasBug(OpenGLBug bug) { #else return vendor == "Intel"; #endif + + case OpenGLBug::GLTHREAD_BLOCKS_SWAP: + return version.contains(" Mesa "); + default: return false; } diff --git a/src/platform/qt/OpenGLBug.h b/src/platform/qt/OpenGLBug.h index 11b0bba43..5b5bc7736 100644 --- a/src/platform/qt/OpenGLBug.h +++ b/src/platform/qt/OpenGLBug.h @@ -8,8 +8,8 @@ namespace QGBA { enum class OpenGLBug { - // mgba.io/i/2761 - CROSS_THREAD_FLUSH + CROSS_THREAD_FLUSH, // mgba.io/i/2761 + GLTHREAD_BLOCKS_SWAP, // mgba.io/i/2767 }; bool glContextHasBug(OpenGLBug); From 5bb12f9238d5806ad539cd70db3f0ae37e4c6c18 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 Jan 2023 19:56:08 -0800 Subject: [PATCH 099/159] CHANGES: Update for 0.10.1 --- CHANGES | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 8fe044973..574e11008 100644 --- a/CHANGES +++ b/CHANGES @@ -2,18 +2,24 @@ Features: - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints +Emulation fixes: + - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) +Misc: + - GB Serialize: Add missing savestate support for MBC6 and NT (newer) + - GBA: Improve detection of valid ELF ROMs + +0.10.1: (2022-01-10) Emulation fixes: - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) - GB Audio: Fix up boot sequence - GB Audio: Fix updating channels other than 2 when writing NR5x - - GB BIOS: Include timing in degenerate ArcTan2 cases (fixes mgba.io/i/2763) - GB Memory: Actually, HDMAs should start when LCD is off (fixes mgba.io/i/2662) - GB Serialize: Don't write BGP/OBP when loading SCGB state (fixes mgba.io/i/2694) - GB SIO: Further fix bidirectional transfer starting - GBA: Fix resetting key IRQ state (fixes mgba.io/i/2716) + - GBA BIOS: Include timing in degenerate ArcTan2 cases (fixes mgba.io/i/2763) - GBA Video: Ignore disabled backgrounds as OBJ blend target (fixes mgba.io/i/2489) - - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: - GBA: Fix forceskip BIOS logic for multiboot ROMs (fixes mgba.io/i/2753) - GBA Cheats: Fix issues detecting unencrypted cheats (fixes mgba.io/i/2724) @@ -26,8 +32,6 @@ Other fixes: - Res: Fix species name location in Ruby/Sapphire revs 1/2 (fixes mgba.io/i/2685) - VFS: Fix minizip write returning 0 on success instead of size Misc: - - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - - GBA: Improve detection of valid ELF ROMs - macOS: Add category to plist (closes mgba.io/i/2691) - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - Qt: Keep track of current palette preset name (fixes mgba.io/i/2680) From fff9d1264ef9b82457cd8b2bc4785c6d105b8568 Mon Sep 17 00:00:00 2001 From: TheMechasaur <88063194+TheMechasaur@users.noreply.github.com> Date: Thu, 12 Jan 2023 00:59:17 -0500 Subject: [PATCH 100/159] Correct year of release date of 0.10.1 to 2023 (#2783) --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 574e11008..3829d23fa 100644 --- a/CHANGES +++ b/CHANGES @@ -8,7 +8,7 @@ Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs -0.10.1: (2022-01-10) +0.10.1: (2023-01-10) Emulation fixes: - GB Audio: Fix channels 1/2 not playing when resetting volume (fixes mgba.io/i/2614) - GB Audio: Fix channel 3 volume being changed between samples (fixes mgba.io/i/1896) From 941ad5072387f4679832c9a76f4e96c08fdef35e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 14 Jan 2023 15:51:32 -0800 Subject: [PATCH 101/159] Qt: Attempt to shorten Game Boy settings pane --- src/platform/qt/SettingsView.ui | 76 +++++++++++++++------------------ 1 file changed, 34 insertions(+), 42 deletions(-) diff --git a/src/platform/qt/SettingsView.ui b/src/platform/qt/SettingsView.ui index f0869aca5..95f71565b 100644 --- a/src/platform/qt/SettingsView.ui +++ b/src/platform/qt/SettingsView.ui @@ -6,8 +6,8 @@ 0 0 - 885 - 797 + 880 + 700 @@ -95,7 +95,7 @@ - 9 + 1 @@ -2091,45 +2091,37 @@ - - - - Default color palette only - - - gbColors - - - - - - - SGB color palette if available - - - gbColors - - - - - - - GBC color palette if available - - - gbColors - - - - - - - SGB (preferred) or GBC color palette if available - - - gbColors - - + + + + + + SGB color palette if available + + + + + + + Default color palette only + + + + + + + GBC color palette if available + + + + + + + SGB (preferred) or GBC color palette if available + + + + From 0d0e92ce59047fd6f8e58c0c2ca1f28c2b877db8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 Jan 2023 00:09:35 -0800 Subject: [PATCH 102/159] GBA Memory: Make VRAM access stalls only apply to BG RAM --- CHANGES | 1 + src/gba/memory.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 3829d23fa..637fdf28f 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ Features: - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 - Debugger: Add range watchpoints Emulation fixes: + - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) diff --git a/src/gba/memory.c b/src/gba/memory.c index c9a1f59dd..ba862afb9 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -397,7 +397,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { LOAD_32(value, address & 0x0001FFFC, gba->video.vram); \ } \ ++wait; \ - if (gba->video.shouldStall) { \ + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { \ wait += GBAMemoryStallVRAM(gba, wait, 1); \ } @@ -557,7 +557,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } else { LOAD_16(value, address & 0x0001FFFE, gba->video.vram); } - if (gba->video.shouldStall) { + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; @@ -777,7 +777,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } \ } \ ++wait; \ - if (gba->video.shouldStall) { \ + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { \ wait += GBAMemoryStallVRAM(gba, wait, 1); \ } @@ -904,7 +904,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); } } - if (gba->video.shouldStall) { + if (gba->video.shouldStall && (address & 0x0001FFFF) < ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; From 8c5940e5406831a3dd73006707325d92b092b4be Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 Jan 2023 17:43:09 -0800 Subject: [PATCH 103/159] Qt: Add ig4icd32 crash to OpenGL bug database (see #2136) --- src/platform/qt/DisplayGL.cpp | 2 +- src/platform/qt/OpenGLBug.cpp | 11 +++++++++++ src/platform/qt/OpenGLBug.h | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 896f3acaf..7a4f73858 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -802,7 +802,7 @@ void PainterGL::performDraw() { m_backend->postFrame(m_backend, m_buffer); } m_backend->drawFrame(m_backend); - if (m_showOSD && m_messagePainter) { + if (m_showOSD && m_messagePainter && !glContextHasBug(OpenGLBug::IG4ICD_CRASH)) { m_painter.begin(m_paintDev.get()); m_messagePainter->paint(&m_painter); m_painter.end(); diff --git a/src/platform/qt/OpenGLBug.cpp b/src/platform/qt/OpenGLBug.cpp index df007c1cb..089d7d754 100644 --- a/src/platform/qt/OpenGLBug.cpp +++ b/src/platform/qt/OpenGLBug.cpp @@ -31,6 +31,17 @@ bool glContextHasBug(OpenGLBug bug) { case OpenGLBug::GLTHREAD_BLOCKS_SWAP: return version.contains(" Mesa "); + case OpenGLBug::IG4ICD_CRASH: +#ifdef Q_OS_WIN + if (vendor != "Intel") { + return false; + } + if (renderer == "Intel Pineview Platform") { + return true; + } +#endif + return false; + default: return false; } diff --git a/src/platform/qt/OpenGLBug.h b/src/platform/qt/OpenGLBug.h index 5b5bc7736..f63fe282e 100644 --- a/src/platform/qt/OpenGLBug.h +++ b/src/platform/qt/OpenGLBug.h @@ -10,6 +10,7 @@ namespace QGBA { enum class OpenGLBug { CROSS_THREAD_FLUSH, // mgba.io/i/2761 GLTHREAD_BLOCKS_SWAP, // mgba.io/i/2767 + IG4ICD_CRASH, // mgba.io/i/2136 }; bool glContextHasBug(OpenGLBug); From 1584023f340498ffbeb532cdc2758cc861cfa81b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 Jan 2023 22:58:17 -0800 Subject: [PATCH 104/159] Core: Round out input API pre-revamp --- include/mgba/core/input.h | 2 ++ src/core/input.c | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/mgba/core/input.h b/include/mgba/core/input.h index f7ef212a5..f5d8002ed 100644 --- a/include/mgba/core/input.h +++ b/include/mgba/core/input.h @@ -55,6 +55,7 @@ int mInputMapKeyBits(const struct mInputMap* map, uint32_t type, uint32_t bits, void mInputBindKey(struct mInputMap*, uint32_t type, int key, int input); int mInputQueryBinding(const struct mInputMap*, uint32_t type, int input); void mInputUnbindKey(struct mInputMap*, uint32_t type, int input); +void mInputUnbindAllKeys(struct mInputMap*, uint32_t type); int mInputMapAxis(const struct mInputMap*, uint32_t type, int axis, int value); int mInputClearAxis(const struct mInputMap*, uint32_t type, int axis, int keys); @@ -69,6 +70,7 @@ void mInputBindHat(struct mInputMap*, uint32_t type, int id, const struct mInput bool mInputQueryHat(const struct mInputMap*, uint32_t type, int id, struct mInputHatBindings* bindings); void mInputUnbindHat(struct mInputMap*, uint32_t type, int id); void mInputUnbindAllHats(struct mInputMap*, uint32_t type); +void mInputEnumerateHats(const struct mInputMap*, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user); bool mInputMapLoad(struct mInputMap*, uint32_t type, const struct Configuration*); void mInputMapSave(const struct mInputMap*, uint32_t type, struct Configuration*); diff --git a/src/core/input.c b/src/core/input.c index 76296a373..787e31f86 100644 --- a/src/core/input.c +++ b/src/core/input.c @@ -38,6 +38,11 @@ struct mInputAxisEnumerate { void* user; }; +struct mInputHatEnumerate { + void (*handler)(int axis, const struct mInputHatBindings* bindings, void* user); + void* user; +}; + static void _makeSectionName(const char* platform, char* sectionName, size_t len, uint32_t type) { snprintf(sectionName, len, "%s.input.%c%c%c%c", platform, type >> 24, type >> 16, type >> 8, type); sectionName[len - 1] = '\0'; @@ -304,6 +309,12 @@ void _unbindAxis(uint32_t axis, void* dp, void* user) { } } +void _enumerateHat(uint32_t axis, void* dp, void* ep) { + struct mInputHatEnumerate* enumUser = ep; + const struct mInputHatBindings* description = dp; + enumUser->handler(axis, description, enumUser->user); +} + static bool _loadAll(struct mInputMap* map, uint32_t type, const char* sectionName, const struct Configuration* config) { if (!ConfigurationHasSection(config, sectionName)) { return false; @@ -415,6 +426,16 @@ void mInputUnbindKey(struct mInputMap* map, uint32_t type, int input) { } } +void mInputUnbindAllKeys(struct mInputMap* map, uint32_t type) { + struct mInputMapImpl* impl = _lookupMap(map, type); + if (impl) { + size_t i; + for (i = 0; i < map->info->nKeys; ++i) { + impl->map[i] = -1; + } + } +} + int mInputQueryBinding(const struct mInputMap* map, uint32_t type, int input) { if (input < 0 || (size_t) input >= map->info->nKeys) { return -1; @@ -578,6 +599,18 @@ void mInputUnbindAllHats(struct mInputMap* map, uint32_t type) { } } +void mInputEnumerateHats(const struct mInputMap* map, uint32_t type, void (handler(int hat, const struct mInputHatBindings* bindings, void* user)), void* user) { + const struct mInputMapImpl* impl = _lookupMapConst(map, type); + if (!impl) { + return; + } + struct mInputHatEnumerate enumUser = { + handler, + user + }; + TableEnumerate(&impl->axes, _enumerateHat, &enumUser); +} + bool mInputMapLoad(struct mInputMap* map, uint32_t type, const struct Configuration* config) { char sectionName[SECTION_NAME_MAX]; _makeSectionName(map->info->platformName, sectionName, SECTION_NAME_MAX, type); From be3022156fcc4abafdbdf8f4a789b2868f433252 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 16 Jan 2023 23:55:52 -0800 Subject: [PATCH 105/159] Qt: Start splitting up InputController --- src/platform/qt/CMakeLists.txt | 1 + src/platform/qt/GBAKeyEditor.cpp | 13 ++- src/platform/qt/InputController.cpp | 64 +----------- src/platform/qt/InputController.h | 11 +-- src/platform/qt/InputProfile.cpp | 10 +- src/platform/qt/input/InputMapper.cpp | 137 ++++++++++++++++++++++++++ src/platform/qt/input/InputMapper.h | 51 ++++++++++ 7 files changed, 210 insertions(+), 77 deletions(-) create mode 100644 src/platform/qt/input/InputMapper.cpp create mode 100644 src/platform/qt/input/InputMapper.h diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 1bb8758c7..3d17014a1 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -107,6 +107,7 @@ set(SOURCE_FILES GamepadButtonEvent.cpp GamepadHatEvent.cpp IOViewer.cpp + input/InputMapper.cpp InputController.cpp InputProfile.cpp KeyEditor.cpp diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 7890812e7..3d7a40b2c 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -13,6 +13,7 @@ #include #include +#include "input/InputMapper.h" #include "InputController.h" #include "KeyEditor.h" @@ -214,8 +215,9 @@ void GBAKeyEditor::setNext() { void GBAKeyEditor::save() { #ifdef BUILD_SDL - m_controller->unbindAllAxes(m_type); - m_controller->unbindAllHats(m_type); + InputMapper mapper = m_controller->mapper(m_type); + mapper.unbindAllAxes(); + mapper.unbindAllHats(); #endif bindKey(m_keyDU, GBA_KEY_UP); @@ -324,15 +326,16 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { #endif void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) { + InputMapper mapper = m_controller->mapper(m_type); #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) { - m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key); + mapper.bindAxis(keyEditor->axis(), keyEditor->direction(), key); } if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) { - m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key); + mapper.bindHat(keyEditor->hat(), keyEditor->hatDirection(), key); } #endif - m_controller->bindKey(m_type, keyEditor->value(), key); + mapper.bindKey(keyEditor->value(), key); } bool GBAKeyEditor::findFocus(KeyEditor* needle) { diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index a0fb91397..c616e4314 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -301,6 +301,10 @@ void InputController::setPreferredGamepad(uint32_t type, int index) { #endif } +InputMapper InputController::mapper(uint32_t type) { + return InputMapper(&m_inputMap, type); +} + mRumble* InputController::rumble() { #ifdef BUILD_SDL #if SDL_VERSION_ATLEAST(2, 0, 0) @@ -384,10 +388,6 @@ GBAKey InputController::mapKeyboard(int key) const { return static_cast(mInputMapKey(&m_inputMap, KEYBOARD, key)); } -void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) { - return mInputBindKey(&m_inputMap, type, key, gbaKey); -} - void InputController::updateJoysticks() { #ifdef BUILD_SDL QString profile = profileForType(SDL_BINDING_BUTTON); @@ -501,36 +501,6 @@ QSet> InputController::activeGamepadAxes return activeAxes; } -void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) { - const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis); - mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD }; - if (old) { - description = *old; - } - int deadzone = 0; - if (axis > 0 && m_deadzones.size() > axis) { - deadzone = m_deadzones[axis]; - } - switch (direction) { - case GamepadAxisEvent::NEGATIVE: - description.lowDirection = key; - - description.deadLow = deadzone - AXIS_THRESHOLD; - break; - case GamepadAxisEvent::POSITIVE: - description.highDirection = key; - description.deadHigh = deadzone + AXIS_THRESHOLD; - break; - default: - return; - } - mInputBindAxis(&m_inputMap, type, axis, &description); -} - -void InputController::unbindAllAxes(uint32_t type) { - mInputUnbindAllAxes(&m_inputMap, type); -} - QSet> InputController::activeGamepadHats(int type) { QSet> activeHats; #ifdef BUILD_SDL @@ -563,32 +533,6 @@ QSet> InputController::activeGamepadHats( return activeHats; } -void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) { - mInputHatBindings bindings{ -1, -1, -1, -1 }; - mInputQueryHat(&m_inputMap, type, hat, &bindings); - switch (direction) { - case GamepadHatEvent::UP: - bindings.up = gbaKey; - break; - case GamepadHatEvent::RIGHT: - bindings.right = gbaKey; - break; - case GamepadHatEvent::DOWN: - bindings.down = gbaKey; - break; - case GamepadHatEvent::LEFT: - bindings.left = gbaKey; - break; - default: - return; - } - mInputBindHat(&m_inputMap, type, hat, &bindings); -} - -void InputController::unbindAllHats(uint32_t type) { - mInputUnbindAllHats(&m_inputMap, type); -} - void InputController::testGamepad(int type) { QWriteLocker l(&m_eventsLock); auto activeAxes = activeGamepadAxes(type); diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index a370ba80e..ef33a2ade 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -7,6 +7,7 @@ #include "GamepadAxisEvent.h" #include "GamepadHatEvent.h" +#include "input/InputMapper.h" #include #include @@ -64,8 +65,6 @@ public: GBAKey mapKeyboard(int key) const; - void bindKey(uint32_t type, int key, GBAKey); - const mInputMap* map() const { return &m_inputMap; } int pollEvents(); @@ -76,17 +75,13 @@ public: QSet> activeGamepadHats(int type); void recalibrateAxes(); - void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey); - void unbindAllAxes(uint32_t type); - - void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey); - void unbindAllHats(uint32_t type); - QStringList connectedGamepads(uint32_t type) const; int gamepad(uint32_t type) const; void setGamepad(uint32_t type, int index); void setPreferredGamepad(uint32_t type, int index); + InputMapper mapper(uint32_t type); + void registerTiltAxisX(int axis); void registerTiltAxisY(int axis); void registerGyroAxisX(int axis); diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index aedc47f6e..82e5090af 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "InputProfile.h" +#include "input/InputMapper.h" #include "InputController.h" #include @@ -210,12 +211,13 @@ const InputProfile* InputProfile::findProfile(const QString& name) { } void InputProfile::apply(InputController* controller) const { - for (size_t i = 0; i < GBA_KEY_MAX; ++i) { #ifdef BUILD_SDL - controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast(i)); - controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast(i)); -#endif + InputMapper mapper = controller->mapper(SDL_BINDING_BUTTON); + for (size_t i = 0; i < GBA_KEY_MAX; ++i) { + mapper.bindKey(m_keys[i], static_cast(i)); + mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, static_cast(i)); } +#endif controller->registerTiltAxisX(m_tiltAxis.x); controller->registerTiltAxisY(m_tiltAxis.y); controller->registerGyroAxisX(m_gyroAxis.x); diff --git a/src/platform/qt/input/InputMapper.cpp b/src/platform/qt/input/InputMapper.cpp new file mode 100644 index 000000000..a5166e55a --- /dev/null +++ b/src/platform/qt/input/InputMapper.cpp @@ -0,0 +1,137 @@ +/* Copyright (c) 2013-2023 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 "input/InputMapper.h" + +#include + +using namespace QGBA; + +InputMapper::InputMapper(mInputMap* map, uint32_t type) + : m_map(map) + , m_type(type) +{ +} + +int InputMapper::mapKey(int key) { + return mInputMapKey(m_map, m_type, key); +} + +int InputMapper::mapAxis(int axis, int16_t value) { + return mInputMapAxis(m_map, m_type, axis, value); +} + +int InputMapper::mapHat(int hat, GamepadHatEvent::Direction direction) { + return mInputMapHat(m_map, m_type, hat, direction); +} + +void InputMapper::bindKey(int key, int platformKey) { + mInputBindKey(m_map, m_type, key, platformKey); +} + +void InputMapper::bindAxis(int axis, GamepadAxisEvent::Direction direction, int platformKey) { + const mInputAxis* old = mInputQueryAxis(m_map, m_type, axis); + mInputAxis description = { -1, -1, -axisThreshold(axis), axisThreshold(axis) }; + if (old) { + description = *old; + } + switch (direction) { + case GamepadAxisEvent::NEGATIVE: + description.lowDirection = platformKey; + description.deadLow = axisCenter(axis) - axisThreshold(axis); + break; + case GamepadAxisEvent::POSITIVE: + description.highDirection = platformKey; + description.deadHigh = axisCenter(axis) + axisThreshold(axis); + break; + default: + return; + } + mInputBindAxis(m_map, m_type, axis, &description); +} + +void InputMapper::bindHat(int hat, GamepadHatEvent::Direction direction, int platformKey) { + mInputHatBindings bindings{ -1, -1, -1, -1 }; + mInputQueryHat(m_map, m_type, hat, &bindings); + switch (direction) { + case GamepadHatEvent::UP: + bindings.up = platformKey; + break; + case GamepadHatEvent::RIGHT: + bindings.right = platformKey; + break; + case GamepadHatEvent::DOWN: + bindings.down = platformKey; + break; + case GamepadHatEvent::LEFT: + bindings.left = platformKey; + break; + default: + return; + } + mInputBindHat(m_map, m_type, hat, &bindings); +} + +QSet InputMapper::queryKeyBindings(int platformKey) const { + return {mInputQueryBinding(m_map, m_type, platformKey)}; +} + +QSet> InputMapper::queryAxisBindings(int platformKey) const { + QPair>> userdata; + userdata.first = platformKey; + + mInputEnumerateAxes(m_map, m_type, [](int axis, const struct mInputAxis* description, void* user) { + auto userdata = static_cast>>*>(user); + int platformKey = userdata->first; + auto& bindings = userdata->second; + + if (description->lowDirection == platformKey) { + bindings.insert({axis, GamepadAxisEvent::NEGATIVE}); + } + if (description->highDirection == platformKey) { + bindings.insert({axis, GamepadAxisEvent::POSITIVE}); + } + }, &userdata); + + return userdata.second; +} + +QSet> InputMapper::queryHatBindings(int platformKey) const { + QPair>> userdata; + userdata.first = platformKey; + + mInputEnumerateHats(m_map, m_type, [](int hat, const struct mInputHatBindings* description, void* user) { + auto userdata = static_cast>>*>(user); + int platformKey = userdata->first; + auto& bindings = userdata->second; + + if (description->up == platformKey) { + bindings.insert({hat, GamepadHatEvent::UP}); + } + if (description->right == platformKey) { + bindings.insert({hat, GamepadHatEvent::RIGHT}); + } + if (description->down == platformKey) { + bindings.insert({hat, GamepadHatEvent::DOWN}); + } + if (description->left == platformKey) { + bindings.insert({hat, GamepadHatEvent::LEFT}); + } + }, &userdata); + + return userdata.second; +} + +void InputMapper::unbindAllKeys() { + mInputUnbindAllKeys(m_map, m_type); +} + +void InputMapper::unbindAllAxes() { + mInputUnbindAllAxes(m_map, m_type); +} + +void InputMapper::unbindAllHats() { + mInputUnbindAllHats(m_map, m_type); +} diff --git a/src/platform/qt/input/InputMapper.h b/src/platform/qt/input/InputMapper.h new file mode 100644 index 000000000..69912ff35 --- /dev/null +++ b/src/platform/qt/input/InputMapper.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include +#include + +#include "GamepadAxisEvent.h" +#include "GamepadHatEvent.h" + +struct mInputMap; + +namespace QGBA { + +class InputMapper final { +public: + static const int32_t DEFAULT_AXIS_THRESHOLD = 0x3000; + + InputMapper(mInputMap* map, uint32_t type); + + mInputMap* inputMap() const { return m_map; } + uint32_t type() const { return m_type; } + + int mapKey(int key); + int mapAxis(int axis, int16_t value); + int mapHat(int hat, GamepadHatEvent::Direction); + + void bindKey(int key, int platformKey); + void bindAxis(int axis, GamepadAxisEvent::Direction, int platformKey); + void bindHat(int hat, GamepadHatEvent::Direction, int platformKey); + + QSet queryKeyBindings(int platformKey) const; + QSet> queryAxisBindings(int platformKey) const; + QSet> queryHatBindings(int platformKey) const; + + int16_t axisThreshold(int) const { return DEFAULT_AXIS_THRESHOLD; } + int16_t axisCenter(int) const { return 0; } + + void unbindAllKeys(); + void unbindAllAxes(); + void unbindAllHats(); + +private: + mInputMap* m_map; + uint32_t m_type; +}; + +} From 4580e8d2e91ddee399c7a488fcc1b16e232c2df4 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 18 Jan 2023 00:35:26 -0800 Subject: [PATCH 106/159] Qt: Start adding better input abstractions --- src/platform/qt/CMakeLists.txt | 5 + src/platform/qt/InputController.h | 1 + src/platform/qt/input/Gamepad.cpp | 13 ++ src/platform/qt/input/Gamepad.h | 30 +++ src/platform/qt/input/InputDriver.cpp | 37 ++++ src/platform/qt/input/InputDriver.h | 46 +++++ src/platform/qt/input/InputSource.cpp | 14 ++ src/platform/qt/input/InputSource.h | 31 +++ src/platform/qt/input/KeySource.cpp | 13 ++ src/platform/qt/input/KeySource.h | 23 +++ src/platform/qt/input/SDLInputDriver.cpp | 247 +++++++++++++++++++++++ src/platform/qt/input/SDLInputDriver.h | 84 ++++++++ 12 files changed, 544 insertions(+) create mode 100644 src/platform/qt/input/Gamepad.cpp create mode 100644 src/platform/qt/input/Gamepad.h create mode 100644 src/platform/qt/input/InputDriver.cpp create mode 100644 src/platform/qt/input/InputDriver.h create mode 100644 src/platform/qt/input/InputSource.cpp create mode 100644 src/platform/qt/input/InputSource.h create mode 100644 src/platform/qt/input/KeySource.cpp create mode 100644 src/platform/qt/input/KeySource.h create mode 100644 src/platform/qt/input/SDLInputDriver.cpp create mode 100644 src/platform/qt/input/SDLInputDriver.h diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 3d17014a1..d20adb05e 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -107,7 +107,11 @@ set(SOURCE_FILES GamepadButtonEvent.cpp GamepadHatEvent.cpp IOViewer.cpp + input/Gamepad.cpp + input/InputDriver.cpp + input/InputSource.cpp input/InputMapper.cpp + input/KeySource.cpp InputController.cpp InputProfile.cpp KeyEditor.cpp @@ -199,6 +203,7 @@ set(CPACK_DEBIAN_PACKAGE_DEPENDS "${CPACK_DEBIAN_PACKAGE_DEPENDS},libqt5widgets5 set(AUDIO_SRC) if(BUILD_SDL) + list(APPEND SOURCE_FILES input/SDLInputDriver.cpp) list(APPEND AUDIO_SRC AudioProcessorSDL.cpp) endif() diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index ef33a2ade..746806577 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -65,6 +65,7 @@ public: GBAKey mapKeyboard(int key) const; + mInputMap* map() { return &m_inputMap; } const mInputMap* map() const { return &m_inputMap; } int pollEvents(); diff --git a/src/platform/qt/input/Gamepad.cpp b/src/platform/qt/input/Gamepad.cpp new file mode 100644 index 000000000..183c9c0ab --- /dev/null +++ b/src/platform/qt/input/Gamepad.cpp @@ -0,0 +1,13 @@ +/* Copyright (c) 2013-2023 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 "input/Gamepad.h" + +using namespace QGBA; + +Gamepad::Gamepad(InputDriver* driver, QObject* parent) + : InputSource(driver, parent) +{ +} diff --git a/src/platform/qt/input/Gamepad.h b/src/platform/qt/input/Gamepad.h new file mode 100644 index 000000000..1781885cd --- /dev/null +++ b/src/platform/qt/input/Gamepad.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include "GamepadHatEvent.h" +#include "input/InputSource.h" + +namespace QGBA { + +class InputDriver; + +class Gamepad : public InputSource { +Q_OBJECT + +public: + Gamepad(InputDriver* driver, QObject* parent = nullptr); + + virtual QList currentButtons() = 0; + virtual QList currentAxes() = 0; + virtual QList currentHats() = 0; + + virtual int buttonCount() const = 0; + virtual int axisCount() const = 0; + virtual int hatCount() const = 0; +}; + +} diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp new file mode 100644 index 000000000..549fa13bb --- /dev/null +++ b/src/platform/qt/input/InputDriver.cpp @@ -0,0 +1,37 @@ +/* Copyright (c) 2013-2023 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 "input/InputDriver.h" + +using namespace QGBA; + +InputDriver::InputDriver(QObject* parent) + : QObject(parent) +{} + +void InputDriver::loadConfiguration(ConfigController*) { +} + +void InputDriver::saveConfiguration(ConfigController*) { +} + +void InputDriver::bindDefaults(InputController*) { +} + +QList InputDriver::connectedKeySources() const { + return {}; +} + +QList InputDriver::connectedGamepads() const { + return {}; +} + +mRumble* InputDriver::rumble() { + return nullptr; +} + +mRotationSource* InputDriver::rotationSource() { + return nullptr; +} diff --git a/src/platform/qt/input/InputDriver.h b/src/platform/qt/input/InputDriver.h new file mode 100644 index 000000000..139f1a500 --- /dev/null +++ b/src/platform/qt/input/InputDriver.h @@ -0,0 +1,46 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include +#include +#include + +struct mRotationSource; +struct mRumble; + +namespace QGBA { + +class ConfigController; +class Gamepad; +class InputController; +class KeySource; + +class InputDriver : public QObject { +Q_OBJECT + +public: + InputDriver(QObject* parent = nullptr); + virtual ~InputDriver() = default; + + virtual uint32_t type() const = 0; + virtual QString visibleName() const = 0; + + virtual void loadConfiguration(ConfigController*); + virtual void saveConfiguration(ConfigController*); + + virtual void bindDefaults(InputController*); + + virtual bool update() = 0; + + virtual QList connectedKeySources() const; + virtual QList connectedGamepads() const; + + virtual mRumble* rumble(); + virtual mRotationSource* rotationSource(); +}; + +} diff --git a/src/platform/qt/input/InputSource.cpp b/src/platform/qt/input/InputSource.cpp new file mode 100644 index 000000000..ec1dc8bc5 --- /dev/null +++ b/src/platform/qt/input/InputSource.cpp @@ -0,0 +1,14 @@ +/* Copyright (c) 2013-2023 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 "input/InputSource.h" + +using namespace QGBA; + +InputSource::InputSource(InputDriver* driver, QObject* parent) + : QObject(parent) + , m_driver(driver) +{ +} diff --git a/src/platform/qt/input/InputSource.h b/src/platform/qt/input/InputSource.h new file mode 100644 index 000000000..9ba18acf5 --- /dev/null +++ b/src/platform/qt/input/InputSource.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include +#include + +namespace QGBA { + +class InputDriver; + +class InputSource : public QObject { +Q_OBJECT + +public: + InputSource(InputDriver* driver, QObject* parent = nullptr); + virtual ~InputSource() = default; + + InputDriver* driver() { return m_driver; } + + virtual QString name() const = 0; + virtual QString visibleName() const = 0; + +protected: + InputDriver* const m_driver; +}; + +} diff --git a/src/platform/qt/input/KeySource.cpp b/src/platform/qt/input/KeySource.cpp new file mode 100644 index 000000000..32f0ca740 --- /dev/null +++ b/src/platform/qt/input/KeySource.cpp @@ -0,0 +1,13 @@ +/* Copyright (c) 2013-2023 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 "input/KeySource.h" + +using namespace QGBA; + +KeySource::KeySource(InputDriver* driver, QObject* parent) + : InputSource(driver, parent) +{ +} diff --git a/src/platform/qt/input/KeySource.h b/src/platform/qt/input/KeySource.h new file mode 100644 index 000000000..e3df912a6 --- /dev/null +++ b/src/platform/qt/input/KeySource.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include "input/InputSource.h" + +namespace QGBA { + +class InputDriver; + +class KeySource : public InputSource { +Q_OBJECT + +public: + KeySource(InputDriver* driver, QObject* parent = nullptr); + + virtual QSet currentKeys() = 0; +}; + +} diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp new file mode 100644 index 000000000..efd8e3f36 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -0,0 +1,247 @@ +/* Copyright (c) 2013-2023 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 "input/SDLInputDriver.h" + +#include "ConfigController.h" +#include "InputController.h" + +#include + +using namespace QGBA; + +int s_sdlInited = 0; +mSDLEvents s_sdlEvents; + +SDLInputDriver::SDLInputDriver(InputController* controller, QObject* parent) + : InputDriver(parent) + , m_controller(controller) +{ + if (s_sdlInited == 0) { + mSDLInitEvents(&s_sdlEvents); + } + ++s_sdlInited; + m_sdlPlayer.bindings = m_controller->map(); + + for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + m_gamepads.append(std::make_shared(this, i)); + } +} + +SDLInputDriver::~SDLInputDriver() { + if (m_playerAttached) { + mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); + } + + --s_sdlInited; + if (s_sdlInited == 0) { + mSDLDeinitEvents(&s_sdlEvents); + } +} + +void SDLInputDriver::loadConfiguration(ConfigController* config) { + mSDLEventsLoadConfig(&s_sdlEvents, config->input()); + if (!m_playerAttached) { + m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + } + if (m_playerAttached) { + mSDLPlayerLoadConfig(&m_sdlPlayer, config->input()); + } +} + +void SDLInputDriver::saveConfiguration(ConfigController* config) { + if (m_playerAttached) { + mSDLPlayerSaveConfig(&m_sdlPlayer, config->input()); + } +} + +void SDLInputDriver::bindDefaults(InputController* controller) { + mSDLInitBindingsGBA(controller->map()); +} + +mRumble* SDLInputDriver::rumble() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (m_playerAttached) { + return &m_sdlPlayer.rumble.d; + } +#endif + return nullptr; +} + +mRotationSource* SDLInputDriver::rotationSource() { + if (m_playerAttached) { + return &m_sdlPlayer.rotation.d; + } + return nullptr; +} + + +bool SDLInputDriver::update() { + if (!m_playerAttached || !m_sdlPlayer.joystick) { + return false; + } + + SDL_JoystickUpdate(); +#if SDL_VERSION_ATLEAST(2, 0, 0) + updateGamepads(); +#endif + + return true; +} + +QList SDLInputDriver::connectedGamepads() const { + QList pads; + for (auto& pad : m_gamepads) { + pads.append(pad.get()); + } + return pads; +} + +#if SDL_VERSION_ATLEAST(2, 0, 0) +void SDLInputDriver::updateGamepads() { + for (int i = 0; i < m_gamepads.size(); ++i) { + if (m_gamepads.at(i)->updateIndex()) { + continue; + } + m_gamepads.removeAt(i); + --i; + } + std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) { + return a->m_index < b->m_index; + }); + + for (size_t i = 0, j = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + std::shared_ptr gamepad = m_gamepads.at(j); + if (gamepad->m_index == i) { + ++j; + continue; + } + m_gamepads.append(std::make_shared(this, i)); + } + std::sort(m_gamepads.begin(), m_gamepads.end(), [](const auto& a, const auto b) { + return a->m_index < b->m_index; + }); +} +#endif + +SDLGamepad::SDLGamepad(SDLInputDriver* driver, int index, QObject* parent) + : Gamepad(driver, parent) + , m_index(index) +{ +#if SDL_VERSION_ATLEAST(2, 0, 0) + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), m_guid, sizeof(m_guid)); +#endif +} + +QList SDLGamepad::currentButtons() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList buttons; + + int numButtons = SDL_JoystickNumButtons(joystick); + for (int i = 0; i < numButtons; ++i) { + buttons.append(SDL_JoystickGetButton(joystick, i)); + } + + return buttons; +} + +QList SDLGamepad::currentAxes() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList axes; + + int numAxes = SDL_JoystickNumAxes(joystick); + for (int i = 0; i < numAxes; ++i) { + axes.append(SDL_JoystickGetAxis(joystick, i)); + } + + return axes; +} + +QList SDLGamepad::currentHats() { + if (!verify()) { + return {}; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + QList hats; + + int numHats = SDL_JoystickNumHats(joystick); + for (int i = 0; i < numHats; ++i) { + hats.append(static_cast(SDL_JoystickGetHat(joystick, i))); + } + + return hats; +} + +int SDLGamepad::buttonCount() const { + if (!verify()) { + return -1; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + return SDL_JoystickNumButtons(joystick); +} + +int SDLGamepad::axisCount() const { + if (!verify()) { + return -1; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + return SDL_JoystickNumAxes(joystick); +} + +int SDLGamepad::hatCount() const { + if (!verify()) { + return -1; + } + + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick; + return SDL_JoystickNumHats(joystick); +} + +QString SDLGamepad::name() const { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return m_guid; +#else + return visibleName(); +#endif +} + +QString SDLGamepad::visibleName() const { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick); +#else + return SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, m_index)->joystick)); +#endif +} + +#if SDL_VERSION_ATLEAST(2, 0, 0) +bool SDLGamepad::updateIndex() { + char guid[34]; + for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { + SDL_Joystick* joystick = SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick; + SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), guid, sizeof(guid)); + if (memcmp(guid, m_guid, 33) == 0) { + m_index = i; + return true; + } + } + return false; +} +#endif + +bool SDLGamepad::verify() const { + return m_index < SDL_JoystickListSize(&s_sdlEvents.joysticks); +} diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h new file mode 100644 index 000000000..752ab7a91 --- /dev/null +++ b/src/platform/qt/input/SDLInputDriver.h @@ -0,0 +1,84 @@ +/* Copyright (c) 2013-2023 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/. */ +#pragma once + +#include "input/Gamepad.h" +#include "input/InputDriver.h" + +#include "platform/sdl/sdl-events.h" + +#include + +namespace QGBA { + +class SDLGamepad; + +class SDLInputDriver : public InputDriver { +Q_OBJECT + +public: + SDLInputDriver(InputController*, QObject* parent = nullptr); + ~SDLInputDriver(); + + uint32_t type() const override { return SDL_BINDING_BUTTON; } + QString visibleName() const override { return QLatin1String("SDL"); } + + void loadConfiguration(ConfigController* config) override; + void saveConfiguration(ConfigController* config) override; + + void bindDefaults(InputController*) override; + + bool update() override; + + QList connectedGamepads() const override; + + mRumble* rumble() override; + mRotationSource* rotationSource() override; + +private: + InputController* m_controller; + mSDLPlayer m_sdlPlayer{}; + bool m_playerAttached = false; + QList> m_gamepads; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + void updateGamepads(); +#endif +}; + +class SDLGamepad : public Gamepad { +Q_OBJECT + +public: + SDLGamepad(SDLInputDriver*, int index, QObject* parent = nullptr); + + QList currentButtons() override; + QList currentAxes() override; + QList currentHats() override; + + int buttonCount() const override; + int axisCount() const override; + int hatCount() const override; + + QString name() const override; + QString visibleName() const override; + +#if SDL_VERSION_ATLEAST(2, 0, 0) + bool updateIndex(); +#endif + +private: + friend class SDLInputDriver; + + size_t m_index; +#if SDL_VERSION_ATLEAST(2, 0, 0) + char m_guid[34]{}; +#endif + + bool verify() const; +}; + +} From 8ec856e10cd8f760439a2b64c54c3723e5338578 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 18 Jan 2023 22:35:34 -0800 Subject: [PATCH 107/159] Qt: Move Gamepad events into input/ --- src/platform/qt/CMakeLists.txt | 6 +++--- src/platform/qt/InputController.cpp | 5 ++--- src/platform/qt/InputController.h | 4 ++-- src/platform/qt/InputProfile.h | 2 +- src/platform/qt/KeyEditor.cpp | 4 ++-- src/platform/qt/KeyEditor.h | 4 ++-- src/platform/qt/LoadSaveState.cpp | 4 ++-- src/platform/qt/SensorView.cpp | 2 +- src/platform/qt/ShortcutController.cpp | 2 +- src/platform/qt/ShortcutController.h | 2 +- src/platform/qt/ShortcutView.cpp | 2 +- src/platform/qt/ShortcutView.h | 2 +- src/platform/qt/{ => input}/GamepadAxisEvent.cpp | 4 ++-- src/platform/qt/{ => input}/GamepadAxisEvent.h | 0 src/platform/qt/{ => input}/GamepadButtonEvent.cpp | 4 ++-- src/platform/qt/{ => input}/GamepadButtonEvent.h | 0 src/platform/qt/{ => input}/GamepadHatEvent.cpp | 4 ++-- src/platform/qt/{ => input}/GamepadHatEvent.h | 0 18 files changed, 25 insertions(+), 26 deletions(-) rename src/platform/qt/{ => input}/GamepadAxisEvent.cpp (91%) rename src/platform/qt/{ => input}/GamepadAxisEvent.h (100%) rename src/platform/qt/{ => input}/GamepadButtonEvent.cpp (92%) rename src/platform/qt/{ => input}/GamepadButtonEvent.h (100%) rename src/platform/qt/{ => input}/GamepadHatEvent.cpp (93%) rename src/platform/qt/{ => input}/GamepadHatEvent.h (100%) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index d20adb05e..327309acf 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -103,11 +103,11 @@ set(SOURCE_FILES GBAApp.cpp GBAKeyEditor.cpp GIFView.cpp - GamepadAxisEvent.cpp - GamepadButtonEvent.cpp - GamepadHatEvent.cpp IOViewer.cpp input/Gamepad.cpp + input/GamepadAxisEvent.cpp + input/GamepadButtonEvent.cpp + input/GamepadHatEvent.cpp input/InputDriver.cpp input/InputSource.cpp input/InputMapper.cpp diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index c616e4314..8067bb466 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 @@ -6,8 +6,7 @@ #include "InputController.h" #include "ConfigController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" #include "InputProfile.h" #include "LogController.h" #include "utils.h" diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 746806577..e9d909684 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadHatEvent.h" #include "input/InputMapper.h" #include diff --git a/src/platform/qt/InputProfile.h b/src/platform/qt/InputProfile.h index 4e43d16a7..96fb8b4c3 100644 --- a/src/platform/qt/InputProfile.h +++ b/src/platform/qt/InputProfile.h @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include diff --git a/src/platform/qt/KeyEditor.cpp b/src/platform/qt/KeyEditor.cpp index 95b83a9e8..e8da70970 100644 --- a/src/platform/qt/KeyEditor.cpp +++ b/src/platform/qt/KeyEditor.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "KeyEditor.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadButtonEvent.h" #include "ShortcutController.h" #include diff --git a/src/platform/qt/KeyEditor.h b/src/platform/qt/KeyEditor.h index e74df7f27..c1bb56fae 100644 --- a/src/platform/qt/KeyEditor.h +++ b/src/platform/qt/KeyEditor.h @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" -#include "GamepadHatEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadHatEvent.h" #include #include diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 3198a4c90..11ae9e587 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -6,8 +6,8 @@ #include "LoadSaveState.h" #include "CoreController.h" -#include "GamepadAxisEvent.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadAxisEvent.h" +#include "input/GamepadButtonEvent.h" #include "VFileDevice.h" #include "utils.h" diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index 22c2f5f3f..a40d0a4a5 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -6,7 +6,7 @@ #include "SensorView.h" #include "CoreController.h" -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include "InputController.h" #include "utils.h" diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index d401fd1ba..e81c01b26 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -6,7 +6,7 @@ #include "ShortcutController.h" #include "ConfigController.h" -#include "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" #include "InputProfile.h" #include diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index eb24cdc96..c4db4241e 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -6,7 +6,7 @@ #pragma once #include "ActionMapper.h" -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include #include diff --git a/src/platform/qt/ShortcutView.cpp b/src/platform/qt/ShortcutView.cpp index e82c2dd9b..5efbc4d62 100644 --- a/src/platform/qt/ShortcutView.cpp +++ b/src/platform/qt/ShortcutView.cpp @@ -5,8 +5,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ShortcutView.h" -#include "GamepadButtonEvent.h" #include "InputController.h" +#include "input/GamepadButtonEvent.h" #include "ShortcutController.h" #include "ShortcutModel.h" diff --git a/src/platform/qt/ShortcutView.h b/src/platform/qt/ShortcutView.h index 903d365c8..d2e307419 100644 --- a/src/platform/qt/ShortcutView.h +++ b/src/platform/qt/ShortcutView.h @@ -5,7 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #pragma once -#include "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include diff --git a/src/platform/qt/GamepadAxisEvent.cpp b/src/platform/qt/input/GamepadAxisEvent.cpp similarity index 91% rename from src/platform/qt/GamepadAxisEvent.cpp rename to src/platform/qt/input/GamepadAxisEvent.cpp index 937e055ed..a009af367 100644 --- a/src/platform/qt/GamepadAxisEvent.cpp +++ b/src/platform/qt/input/GamepadAxisEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 "GamepadAxisEvent.h" +#include "input/GamepadAxisEvent.h" #include "InputController.h" diff --git a/src/platform/qt/GamepadAxisEvent.h b/src/platform/qt/input/GamepadAxisEvent.h similarity index 100% rename from src/platform/qt/GamepadAxisEvent.h rename to src/platform/qt/input/GamepadAxisEvent.h diff --git a/src/platform/qt/GamepadButtonEvent.cpp b/src/platform/qt/input/GamepadButtonEvent.cpp similarity index 92% rename from src/platform/qt/GamepadButtonEvent.cpp rename to src/platform/qt/input/GamepadButtonEvent.cpp index 6657a2c69..b267147e2 100644 --- a/src/platform/qt/GamepadButtonEvent.cpp +++ b/src/platform/qt/input/GamepadButtonEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 "GamepadButtonEvent.h" +#include "input/GamepadButtonEvent.h" #include "InputController.h" diff --git a/src/platform/qt/GamepadButtonEvent.h b/src/platform/qt/input/GamepadButtonEvent.h similarity index 100% rename from src/platform/qt/GamepadButtonEvent.h rename to src/platform/qt/input/GamepadButtonEvent.h diff --git a/src/platform/qt/GamepadHatEvent.cpp b/src/platform/qt/input/GamepadHatEvent.cpp similarity index 93% rename from src/platform/qt/GamepadHatEvent.cpp rename to src/platform/qt/input/GamepadHatEvent.cpp index 052783e8e..ee61e931e 100644 --- a/src/platform/qt/GamepadHatEvent.cpp +++ b/src/platform/qt/input/GamepadHatEvent.cpp @@ -1,9 +1,9 @@ -/* Copyright (c) 2013-2017 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 "GamepadHatEvent.h" +#include "input/GamepadHatEvent.h" #include "InputController.h" diff --git a/src/platform/qt/GamepadHatEvent.h b/src/platform/qt/input/GamepadHatEvent.h similarity index 100% rename from src/platform/qt/GamepadHatEvent.h rename to src/platform/qt/input/GamepadHatEvent.h From 547c9269fa6f8925a60fd5b906324b39993f7374 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 18 Jan 2023 22:53:11 -0800 Subject: [PATCH 108/159] Qt: Start shaking out GBAKey --- src/platform/qt/GBAKeyEditor.cpp | 22 ++++++------ src/platform/qt/GBAKeyEditor.h | 6 ++-- src/platform/qt/InputController.cpp | 36 ++++++++++---------- src/platform/qt/InputController.h | 14 ++++---- src/platform/qt/InputProfile.h | 1 + src/platform/qt/LoadSaveState.cpp | 7 ++-- src/platform/qt/Window.cpp | 9 ++--- src/platform/qt/input/GamepadAxisEvent.cpp | 4 +-- src/platform/qt/input/GamepadAxisEvent.h | 6 ++-- src/platform/qt/input/GamepadButtonEvent.cpp | 4 +-- src/platform/qt/input/GamepadButtonEvent.h | 8 ++--- src/platform/qt/input/GamepadHatEvent.cpp | 4 +-- src/platform/qt/input/GamepadHatEvent.h | 6 ++-- 13 files changed, 62 insertions(+), 65 deletions(-) diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 3d7a40b2c..6cca18d56 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -262,7 +262,7 @@ void GBAKeyEditor::refresh() { #endif } -void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) { +void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, int key) { #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON) { int value = mInputQueryBinding(map, m_type, key); @@ -277,14 +277,14 @@ void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBA void GBAKeyEditor::lookupAxes(const mInputMap* map) { mInputEnumerateAxes(map, m_type, [](int axis, const mInputAxis* description, void* user) { GBAKeyEditor* self = static_cast(user); - if (description->highDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(description->highDirection)); + if (description->highDirection != -1) { + KeyEditor* key = self->keyById(description->highDirection); if (key) { key->setValueAxis(axis, GamepadAxisEvent::POSITIVE); } } - if (description->lowDirection != GBA_KEY_NONE) { - KeyEditor* key = self->keyById(static_cast(description->lowDirection)); + if (description->lowDirection != -1) { + KeyEditor* key = self->keyById(description->lowDirection); if (key) { key->setValueAxis(axis, GamepadAxisEvent::NEGATIVE); } @@ -297,25 +297,25 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { int i = 0; while (mInputQueryHat(map, m_type, i, &bindings)) { if (bindings.up >= 0) { - KeyEditor* key = keyById(static_cast(bindings.up)); + KeyEditor* key = keyById(bindings.up); if (key) { key->setValueHat(i, GamepadHatEvent::UP); } } if (bindings.right >= 0) { - KeyEditor* key = keyById(static_cast(bindings.right)); + KeyEditor* key = keyById(bindings.right); if (key) { key->setValueHat(i, GamepadHatEvent::RIGHT); } } if (bindings.down >= 0) { - KeyEditor* key = keyById(static_cast(bindings.down)); + KeyEditor* key = keyById(bindings.down); if (key) { key->setValueHat(i, GamepadHatEvent::DOWN); } } if (bindings.left >= 0) { - KeyEditor* key = keyById(static_cast(bindings.left)); + KeyEditor* key = keyById(bindings.left); if (key) { key->setValueHat(i, GamepadHatEvent::LEFT); } @@ -325,7 +325,7 @@ void GBAKeyEditor::lookupHats(const mInputMap* map) { } #endif -void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) { +void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, int key) { InputMapper mapper = m_controller->mapper(m_type); #ifdef BUILD_SDL if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) { @@ -361,7 +361,7 @@ void GBAKeyEditor::selectGamepad(int index) { } #endif -KeyEditor* GBAKeyEditor::keyById(GBAKey key) { +KeyEditor* GBAKeyEditor::keyById(int key) { switch (key) { case GBA_KEY_UP: return m_keyDU; diff --git a/src/platform/qt/GBAKeyEditor.h b/src/platform/qt/GBAKeyEditor.h index 24b1e5c4a..9578abda9 100644 --- a/src/platform/qt/GBAKeyEditor.h +++ b/src/platform/qt/GBAKeyEditor.h @@ -54,8 +54,8 @@ private: void setLocation(QWidget* widget, qreal x, qreal y); - void lookupBinding(const mInputMap*, KeyEditor*, GBAKey); - void bindKey(const KeyEditor*, GBAKey); + void lookupBinding(const mInputMap*, KeyEditor*, int key); + void bindKey(const KeyEditor*, int key); bool findFocus(KeyEditor* needle = nullptr); @@ -64,7 +64,7 @@ private: void lookupHats(const mInputMap*); #endif - KeyEditor* keyById(GBAKey); + KeyEditor* keyById(int); QComboBox* m_profileSelect = nullptr; QWidget* m_clear = nullptr; diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 8067bb466..bd763802a 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -383,8 +383,8 @@ void InputController::setGyroSensitivity(float sensitivity) { #endif } -GBAKey InputController::mapKeyboard(int key) const { - return static_cast(mInputMapKey(&m_inputMap, KEYBOARD, key)); +int InputController::mapKeyboard(int key) const { + return mInputMapKey(&m_inputMap, KEYBOARD, key); } void InputController::updateJoysticks() { @@ -408,8 +408,8 @@ int InputController::pollEvents() { int i; QReadLocker l(&m_eventsLock); for (i = 0; i < numButtons; ++i) { - GBAKey key = static_cast(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i)); - if (key == GBA_KEY_NONE) { + int key = mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i); + if (key == -1) { continue; } if (hasPendingEvent(key)) { @@ -430,8 +430,8 @@ int InputController::pollEvents() { for (i = 0; i < numAxes; ++i) { int value = SDL_JoystickGetAxis(joystick, i); - enum GBAKey key = static_cast(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value)); - if (key != GBA_KEY_NONE) { + int key = mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value); + if (key != -1) { activeButtons |= 1 << key; } } @@ -557,16 +557,16 @@ void InputController::testGamepad(int type) { bool newlyAboveThreshold = activeAxes.contains(axis); if (newlyAboveThreshold) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } } for (auto axis : oldAxes) { GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } @@ -579,15 +579,15 @@ void InputController::testGamepad(int type) { for (int button : activeButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } for (int button : oldButtons) { GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } @@ -596,15 +596,15 @@ void InputController::testGamepad(int type) { for (auto& hat : activeHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); - postPendingEvent(event->gbaKey()); + postPendingEvent(event->platformKey()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); } } for (auto& hat : oldHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); - clearPendingEvent(event->gbaKey()); + clearPendingEvent(event->platformKey()); sendGamepadEvent(event); } } @@ -622,15 +622,15 @@ void InputController::sendGamepadEvent(QEvent* event) { QApplication::postEvent(focusWidget, event, Qt::HighEventPriority); } -void InputController::postPendingEvent(GBAKey key) { +void InputController::postPendingEvent(int key) { m_pendingEvents.insert(key); } -void InputController::clearPendingEvent(GBAKey key) { +void InputController::clearPendingEvent(int key) { m_pendingEvents.remove(key); } -bool InputController::hasPendingEvent(GBAKey key) const { +bool InputController::hasPendingEvent(int key) const { return m_pendingEvents.contains(key); } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index e9d909684..068cf890b 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 @@ -19,8 +19,8 @@ #include +#include #include -#include #ifdef BUILD_SDL #include "platform/sdl/sdl-events.h" @@ -63,7 +63,7 @@ public: void saveProfile(uint32_t type, const QString& profile); const char* profileForType(uint32_t type); - GBAKey mapKeyboard(int key) const; + int mapKeyboard(int key) const; mInputMap* map() { return &m_inputMap; } const mInputMap* map() const { return &m_inputMap; } @@ -132,9 +132,9 @@ private slots: void teardownCam(); private: - void postPendingEvent(GBAKey); - void clearPendingEvent(GBAKey); - bool hasPendingEvent(GBAKey) const; + void postPendingEvent(int); + void clearPendingEvent(int); + bool hasPendingEvent(int) const; void sendGamepadEvent(QEvent*); struct InputControllerLux : GBALuminanceSource { @@ -180,7 +180,7 @@ private: QSet> m_activeHats; QTimer m_gamepadTimer{nullptr}; - QSet m_pendingEvents; + QSet m_pendingEvents; QReadWriteLock m_eventsLock; }; diff --git a/src/platform/qt/InputProfile.h b/src/platform/qt/InputProfile.h index 96fb8b4c3..a7670a40c 100644 --- a/src/platform/qt/InputProfile.h +++ b/src/platform/qt/InputProfile.h @@ -8,6 +8,7 @@ #include "input/GamepadAxisEvent.h" #include +#include namespace QGBA { diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 11ae9e587..89cc7f65a 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -131,13 +132,13 @@ bool LoadSaveState::eventFilter(QObject* object, QEvent* event) { if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadAxisEvent::Type()) { int column = m_currentFocus % 3; int row = m_currentFocus - column; - GBAKey key = GBA_KEY_NONE; + int key = -1; if (event->type() == GamepadButtonEvent::Down()) { - key = static_cast(event)->gbaKey(); + key = static_cast(event)->platformKey(); } else if (event->type() == GamepadAxisEvent::Type()) { GamepadAxisEvent* gae = static_cast(event); if (gae->isNew()) { - key = gae->gbaKey(); + key = gae->platformKey(); } else { return false; } diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index cdfb5b9c1..809ca02be 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -76,6 +76,7 @@ #include #endif #include +#include #include #include @@ -631,8 +632,8 @@ void Window::keyPressEvent(QKeyEvent* event) { QWidget::keyPressEvent(event); return; } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { + int key = m_inputController.mapKeyboard(event->key()); + if (key == -1) { QWidget::keyPressEvent(event); return; } @@ -647,8 +648,8 @@ void Window::keyReleaseEvent(QKeyEvent* event) { QWidget::keyReleaseEvent(event); return; } - GBAKey key = m_inputController.mapKeyboard(event->key()); - if (key == GBA_KEY_NONE) { + int key = m_inputController.mapKeyboard(event->key()); + if (key == -1) { QWidget::keyPressEvent(event); return; } diff --git a/src/platform/qt/input/GamepadAxisEvent.cpp b/src/platform/qt/input/GamepadAxisEvent.cpp index a009af367..396574720 100644 --- a/src/platform/qt/input/GamepadAxisEvent.cpp +++ b/src/platform/qt/input/GamepadAxisEvent.cpp @@ -16,11 +16,11 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in , m_axis(axis) , m_direction(direction) , m_isNew(isNew) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapAxis(controller->map(), type, axis, direction * INT_MAX)); + m_key = mInputMapAxis(controller->map(), type, axis, direction * INT_MAX); } } diff --git a/src/platform/qt/input/GamepadAxisEvent.h b/src/platform/qt/input/GamepadAxisEvent.h index 75d887425..3f88f4be0 100644 --- a/src/platform/qt/input/GamepadAxisEvent.h +++ b/src/platform/qt/input/GamepadAxisEvent.h @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -26,7 +24,7 @@ public: int axis() const { return m_axis; } Direction direction() const { return m_direction; } bool isNew() const { return m_isNew; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static enum Type Type(); @@ -36,7 +34,7 @@ private: int m_axis; Direction m_direction; bool m_isNew; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/input/GamepadButtonEvent.cpp b/src/platform/qt/input/GamepadButtonEvent.cpp index b267147e2..5ad9ba8f1 100644 --- a/src/platform/qt/input/GamepadButtonEvent.cpp +++ b/src/platform/qt/input/GamepadButtonEvent.cpp @@ -15,11 +15,11 @@ QEvent::Type GamepadButtonEvent::s_upType = QEvent::None; GamepadButtonEvent::GamepadButtonEvent(QEvent::Type pressType, int button, int type, InputController* controller) : QEvent(pressType) , m_button(button) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapKey(controller->map(), type, button)); + m_key = mInputMapKey(controller->map(), type, button); } } diff --git a/src/platform/qt/input/GamepadButtonEvent.h b/src/platform/qt/input/GamepadButtonEvent.h index 5ba444490..ed2200d91 100644 --- a/src/platform/qt/input/GamepadButtonEvent.h +++ b/src/platform/qt/input/GamepadButtonEvent.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2015 Jeffrey Pfau +/* Copyright (c) 2013-2023 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 @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -18,7 +16,7 @@ public: GamepadButtonEvent(Type pressType, int button, int type, InputController* controller = nullptr); int value() const { return m_button; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static Type Down(); static Type Up(); @@ -28,7 +26,7 @@ private: static Type s_upType; int m_button; - GBAKey m_key; + int m_key; }; } diff --git a/src/platform/qt/input/GamepadHatEvent.cpp b/src/platform/qt/input/GamepadHatEvent.cpp index ee61e931e..1ca0408b0 100644 --- a/src/platform/qt/input/GamepadHatEvent.cpp +++ b/src/platform/qt/input/GamepadHatEvent.cpp @@ -16,11 +16,11 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di : QEvent(pressType) , m_hatId(hatId) , m_direction(direction) - , m_key(GBA_KEY_NONE) + , m_key(-1) { ignore(); if (controller) { - m_key = static_cast(mInputMapHat(controller->map(), type, hatId, direction)); + m_key = mInputMapHat(controller->map(), type, hatId, direction); } } diff --git a/src/platform/qt/input/GamepadHatEvent.h b/src/platform/qt/input/GamepadHatEvent.h index ecaf1cd2b..2ba73088d 100644 --- a/src/platform/qt/input/GamepadHatEvent.h +++ b/src/platform/qt/input/GamepadHatEvent.h @@ -7,8 +7,6 @@ #include -#include - namespace QGBA { class InputController; @@ -27,7 +25,7 @@ public: int hatId() const { return m_hatId; } Direction direction() const { return m_direction; } - GBAKey gbaKey() const { return m_key; } + int platformKey() const { return m_key; } static Type Down(); static Type Up(); @@ -38,7 +36,7 @@ private: int m_hatId; Direction m_direction; - GBAKey m_key; + int m_key; }; } From 0c77227e06a503edcace2ce5d913b4be9053f50c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 Jan 2023 17:19:53 -0800 Subject: [PATCH 109/159] Qt: Finish input driver separation --- src/platform/qt/GBAApp.cpp | 23 ++ src/platform/qt/GBAApp.h | 4 + src/platform/qt/GBAKeyEditor.cpp | 5 +- src/platform/qt/InputController.cpp | 455 ++++++++--------------- src/platform/qt/InputController.h | 53 ++- src/platform/qt/InputProfile.cpp | 18 +- src/platform/qt/ReportView.cpp | 8 +- src/platform/qt/SensorView.cpp | 26 +- src/platform/qt/SensorView.h | 5 +- src/platform/qt/SettingsView.cpp | 7 +- src/platform/qt/Window.cpp | 18 +- src/platform/qt/input/InputDriver.cpp | 48 +++ src/platform/qt/input/InputDriver.h | 20 + src/platform/qt/input/InputMapper.cpp | 53 ++- src/platform/qt/input/InputMapper.h | 11 +- src/platform/qt/input/InputSource.h | 5 +- src/platform/qt/input/SDLInputDriver.cpp | 110 ++++++ src/platform/qt/input/SDLInputDriver.h | 23 ++ src/platform/qt/main.cpp | 4 + 19 files changed, 520 insertions(+), 376 deletions(-) diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 30287262a..c7c380a7c 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -32,6 +32,10 @@ #include "DiscordCoordinator.h" #endif +#ifdef BUILD_SDL +#include "input/SDLInputDriver.h" +#endif + using namespace QGBA; static GBAApp* g_app = nullptr; @@ -309,6 +313,25 @@ bool GBAApp::waitOnJob(qint64 jobId, QObject* context, std::function ca return true; } +void GBAApp::suspendScreensaver() { +#ifdef BUILD_SDL + SDL::suspendScreensaver(); +#endif +} + +void GBAApp::resumeScreensaver() { +#ifdef BUILD_SDL + SDL::resumeScreensaver(); +#endif +} + +void GBAApp::setScreensaverSuspendable(bool suspendable) { + UNUSED(suspendable); +#ifdef BUILD_SDL + SDL::setScreensaverSuspendable(suspendable); +#endif +} + void GBAApp::cleanupAfterUpdate() { // Remove leftover updater if there's one present QDir configDir(ConfigController::configDir()); diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 6245dba3f..3110b0733 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -86,6 +86,10 @@ public slots: void restartForUpdate(); Window* newWindow(); + void suspendScreensaver(); + void resumeScreensaver(); + void setScreensaverSuspendable(bool); + signals: void jobFinished(qint64 jobId); diff --git a/src/platform/qt/GBAKeyEditor.cpp b/src/platform/qt/GBAKeyEditor.cpp index 6cca18d56..88963afa2 100644 --- a/src/platform/qt/GBAKeyEditor.cpp +++ b/src/platform/qt/GBAKeyEditor.cpp @@ -398,14 +398,13 @@ void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) { #ifdef BUILD_SDL void GBAKeyEditor::updateJoysticks() { - m_controller->updateJoysticks(); - m_controller->recalibrateAxes(); + m_controller->update(); // 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); + int activeGamepad = m_controller->gamepadIndex(m_type); m_profileSelect->setCurrentIndex(activeGamepad); m_profileSelect->blockSignals(wasBlocked); diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index bd763802a..b00d2131e 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -6,6 +6,7 @@ #include "InputController.h" #include "ConfigController.h" +#include "input/Gamepad.h" #include "input/GamepadButtonEvent.h" #include "InputProfile.h" #include "LogController.h" @@ -24,11 +25,6 @@ using namespace QGBA; -#ifdef BUILD_SDL -int InputController::s_sdlInited = 0; -mSDLEvents InputController::s_sdlEvents; -#endif - InputController::InputController(int playerId, QWidget* topLevel, QObject* parent) : QObject(parent) , m_playerId(playerId) @@ -37,23 +33,17 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren { mInputMapInit(&m_inputMap, &GBAInputInfo); -#ifdef BUILD_SDL - if (s_sdlInited == 0) { - mSDLInitEvents(&s_sdlEvents); - } - ++s_sdlInited; - m_sdlPlayer.bindings = &m_inputMap; - updateJoysticks(); -#endif - -#ifdef BUILD_SDL connect(&m_gamepadTimer, &QTimer::timeout, [this]() { - testGamepad(SDL_BINDING_BUTTON); + for (auto& driver : m_inputDrivers) { + if (driver->supportsPolling() && driver->supportsGamepads()) { + testGamepad(driver->type()); + } + } if (m_playerId == 0) { - updateJoysticks(); + update(); } }); -#endif + m_gamepadTimer.setInterval(50); m_gamepadTimer.start(); @@ -140,43 +130,32 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren InputController::~InputController() { mInputMapDeinit(&m_inputMap); +} -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer); +void InputController::addInputDriver(std::shared_ptr driver) { + m_inputDrivers[driver->type()] = driver; + if (!m_sensorDriver && driver->supportsSensors()) { + m_sensorDriver = driver->type(); } - - --s_sdlInited; - if (s_sdlInited == 0) { - mSDLDeinitEvents(&s_sdlEvents); - } -#endif } void InputController::setConfiguration(ConfigController* config) { m_config = config; loadConfiguration(KEYBOARD); -#ifdef BUILD_SDL - mSDLEventsLoadConfig(&s_sdlEvents, config->input()); - if (!m_playerAttached) { - m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); + for (auto& driver : m_inputDrivers) { + driver->loadConfiguration(config); } - if (!loadConfiguration(SDL_BINDING_BUTTON)) { - mSDLInitBindingsGBA(&m_inputMap); - } - loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); -#endif } bool InputController::loadConfiguration(uint32_t type) { if (!mInputMapLoad(&m_inputMap, type, m_config->input())) { return false; } -#ifdef BUILD_SDL - if (m_playerAttached) { - mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input()); + auto driver = m_inputDrivers.value(type); + if (!driver) { + return false; } -#endif + driver->loadConfiguration(m_config); return true; } @@ -185,7 +164,6 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) { return false; } bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData()); - recalibrateAxes(); if (!loaded) { const InputProfile* ip = InputProfile::findProfile(profile); if (ip) { @@ -199,18 +177,18 @@ bool InputController::loadProfile(uint32_t type, const QString& profile) { void InputController::saveConfiguration() { saveConfiguration(KEYBOARD); -#ifdef BUILD_SDL - saveConfiguration(SDL_BINDING_BUTTON); - saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON)); - if (m_playerAttached) { - mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input()); + for (auto& driver : m_inputDrivers) { + driver->saveConfiguration(m_config); } -#endif m_config->write(); } void InputController::saveConfiguration(uint32_t type) { mInputMapSave(&m_inputMap, type, m_config->input()); + auto driver = m_inputDrivers.value(type); + if (driver) { + driver->saveConfiguration(m_config); + } m_config->write(); } @@ -222,313 +200,198 @@ void InputController::saveProfile(uint32_t type, const QString& profile) { m_config->write(); } -const char* InputController::profileForType(uint32_t type) { - UNUSED(type); -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { -#if SDL_VERSION_ATLEAST(2, 0, 0) - return SDL_JoystickName(m_sdlPlayer.joystick->joystick); -#else - return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); -#endif +QString InputController::profileForType(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return {}; } -#endif - return 0; + return driver->currentProfile(); } QStringList InputController::connectedGamepads(uint32_t type) const { - UNUSED(type); - -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - QStringList pads; - for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { - const char* name; -#if SDL_VERSION_ATLEAST(2, 0, 0) - name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick); -#else - name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick)); -#endif - if (name) { - pads.append(QString(name)); - } else { - pads.append(QString()); - } - } - return pads; + auto driver = m_inputDrivers.value(type); + if (!driver) { + return {}; } -#endif - return QStringList(); + QStringList pads; + for (auto pad : driver->connectedGamepads()) { + pads.append(pad->visibleName()); + } + return pads; } -int InputController::gamepad(uint32_t type) const { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; +int InputController::gamepadIndex(uint32_t type) const { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return -1; } -#endif - return 0; + return driver->activeGamepad(); } void InputController::setGamepad(uint32_t type, int index) { -#ifdef BUILD_SDL - if (type == SDL_BINDING_BUTTON) { - mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); + auto driver = m_inputDrivers.value(type); + if (!driver) { + return; } -#endif + driver->setActiveGamepad(index); } void InputController::setPreferredGamepad(uint32_t type, int index) { if (!m_config) { return; } -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - char name[34] = {0}; - SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick), name, sizeof(name)); -#else - const char* name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, index)->joystick)); - if (!name) { + auto driver = m_inputDrivers.value(type); + if (!driver) { return; } -#endif - mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name); -#else - UNUSED(type); - UNUSED(index); -#endif + + auto pads = driver->connectedGamepads(); + if (index >= pads.count()) { + return; + } + + QString name = pads[index]->name(); + if (name.isEmpty()) { + return; + } + mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name.toUtf8().constData()); } InputMapper InputController::mapper(uint32_t type) { return InputMapper(&m_inputMap, type); } +InputMapper InputController::mapper(InputSource* source) { + return InputMapper(&m_inputMap, source->type()); +} + mRumble* InputController::rumble() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - if (m_playerAttached) { - return &m_sdlPlayer.rumble.d; + auto driver = m_inputDrivers.value(m_sensorDriver); + if (driver) { + return driver->rumble(); } -#endif -#endif return nullptr; } mRotationSource* InputController::rotationSource() { -#ifdef BUILD_SDL - if (m_playerAttached) { - return &m_sdlPlayer.rotation.d; + auto driver = m_inputDrivers.value(m_sensorDriver); + if (driver) { + return driver->rotationSource(); } -#endif return nullptr; } -void InputController::registerTiltAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisX = axis; - } -#endif -} - -void InputController::registerTiltAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.axisY = axis; - } -#endif -} - -void InputController::registerGyroAxisX(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroX = axis; - if (m_sdlPlayer.rotation.gyroY == axis) { - m_sdlPlayer.rotation.gyroZ = axis; - } else { - m_sdlPlayer.rotation.gyroZ = -1; - } - } -#endif -} - -void InputController::registerGyroAxisY(int axis) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroY = axis; - if (m_sdlPlayer.rotation.gyroX == axis) { - m_sdlPlayer.rotation.gyroZ = axis; - } else { - m_sdlPlayer.rotation.gyroZ = -1; - } - } -#endif -} - -float InputController::gyroSensitivity() const { -#ifdef BUILD_SDL - if (m_playerAttached) { - return m_sdlPlayer.rotation.gyroSensitivity; - } -#endif - return 0; -} - -void InputController::setGyroSensitivity(float sensitivity) { -#ifdef BUILD_SDL - if (m_playerAttached) { - m_sdlPlayer.rotation.gyroSensitivity = sensitivity; - } -#endif -} - int InputController::mapKeyboard(int key) const { return mInputMapKey(&m_inputMap, KEYBOARD, key); } -void InputController::updateJoysticks() { -#ifdef BUILD_SDL - QString profile = profileForType(SDL_BINDING_BUTTON); - mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); - QString newProfile = profileForType(SDL_BINDING_BUTTON); - if (profile != newProfile) { - loadProfile(SDL_BINDING_BUTTON, newProfile); +void InputController::update() { + for (auto& driver : m_inputDrivers) { + QString profile = profileForType(driver->type()); + driver->update(); + QString newProfile = profileForType(driver->type()); + if (profile != newProfile) { + loadProfile(driver->type(), newProfile); + } } -#endif } int InputController::pollEvents() { int activeButtons = 0; -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - QReadLocker l(&m_eventsLock); - for (i = 0; i < numButtons; ++i) { - int key = mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i); - if (key == -1) { - continue; - } - if (hasPendingEvent(key)) { - continue; - } - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons |= 1 << key; - } - } - l.unlock(); - int numHats = SDL_JoystickNumHats(joystick); - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat); - } - - int numAxes = SDL_JoystickNumAxes(joystick); - for (i = 0; i < numAxes; ++i) { - int value = SDL_JoystickGetAxis(joystick, i); - - int key = mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value); - if (key != -1) { - activeButtons |= 1 << key; - } + for (auto pad : gamepads()) { + InputMapper im(mapper(pad)); + activeButtons |= im.mapKeys(pad->currentButtons()); + activeButtons |= im.mapAxes(pad->currentAxes()); + activeButtons |= im.mapHats(pad->currentHats()); + } + for (int i = 0; i < GBA_KEY_MAX; ++i) { + if ((activeButtons & (1 << i)) && hasPendingEvent(i)) { + activeButtons ^= 1 << i; } } -#endif return activeButtons; } +Gamepad* InputController::gamepad(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver) { + return nullptr; + } + if (!driver->supportsGamepads()) { + return nullptr; +} + QList driverPads(driver->connectedGamepads()); + int activeGamepad = driver->activeGamepad(); + if (activeGamepad < 0 || activeGamepad >= driverPads.count()) { + return nullptr; + } + return driverPads[activeGamepad]; +} + +QList InputController::gamepads() { + QList pads; + for (auto& driver : m_inputDrivers) { + if (!driver->supportsGamepads()) { + continue; + } + QList driverPads(driver->connectedGamepads()); + int activeGamepad = driver->activeGamepad(); + if (activeGamepad >= 0 && activeGamepad < driverPads.count()) { + pads.append(driverPads[activeGamepad]); + } + } + return pads; +} + QSet InputController::activeGamepadButtons(int type) { QSet activeButtons; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numButtons = SDL_JoystickNumButtons(joystick); - int i; - for (i = 0; i < numButtons; ++i) { - if (SDL_JoystickGetButton(joystick, i)) { - activeButtons.insert(i); - } + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + auto allButtons = pad->currentButtons(); + for (int i = 0; i < allButtons.size(); ++i) { + if (allButtons[i]) { + activeButtons.insert(i); } } -#endif return activeButtons; } -void InputController::recalibrateAxes() { -#ifdef BUILD_SDL - if (m_playerAttached && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return; - } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - m_deadzones[i] = SDL_JoystickGetAxis(joystick, i); - } - } -#endif -} - QSet> InputController::activeGamepadAxes(int type) { QSet> activeAxes; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numAxes = SDL_JoystickNumAxes(joystick); - if (numAxes < 1) { - return activeAxes; + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + InputMapper im(mapper(type)); + auto allAxes = pad->currentAxes(); + for (int i = 0; i < allAxes.size(); ++i) { + if (allAxes[i] - im.axisCenter(i) >= im.axisThreshold(i)) { + activeAxes.insert(qMakePair(i, GamepadAxisEvent::POSITIVE)); + continue; } - m_deadzones.resize(numAxes); - int i; - for (i = 0; i < numAxes; ++i) { - int32_t axis = SDL_JoystickGetAxis(joystick, i); - axis -= m_deadzones[i]; - if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) { - activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE)); - } + if (allAxes[i] - im.axisCenter(i) <= -im.axisThreshold(i)) { + activeAxes.insert(qMakePair(i, GamepadAxisEvent::NEGATIVE)); + continue; } } -#endif return activeAxes; } QSet> InputController::activeGamepadHats(int type) { QSet> activeHats; -#ifdef BUILD_SDL - if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) { - SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick; - SDL_JoystickUpdate(); - int numHats = SDL_JoystickNumHats(joystick); - if (numHats < 1) { - return activeHats; - } - - int i; - for (i = 0; i < numHats; ++i) { - int hat = SDL_JoystickGetHat(joystick, i); - if (hat & GamepadHatEvent::UP) { - activeHats.insert(qMakePair(i, GamepadHatEvent::UP)); - } - if (hat & GamepadHatEvent::RIGHT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT)); - } - if (hat & GamepadHatEvent::DOWN) { - activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN)); - } - if (hat & GamepadHatEvent::LEFT) { - activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT)); - } + Gamepad* pad = gamepad(type); + if (!pad) { + return {}; + } + auto allHats = pad->currentHats(); + for (int i = 0; i < allHats.size(); ++i) { + if (allHats[i] != GamepadHatEvent::CENTER) { + activeHats.insert(qMakePair(i, allHats[i])); } } -#endif return activeHats; } @@ -634,30 +497,6 @@ bool InputController::hasPendingEvent(int key) const { return m_pendingEvents.contains(key); } -void InputController::suspendScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSuspendScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::resumeScreensaver() { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLResumeScreensaver(&s_sdlEvents); -#endif -#endif -} - -void InputController::setScreensaverSuspendable(bool suspendable) { -#ifdef BUILD_SDL -#if SDL_VERSION_ATLEAST(2, 0, 0) - mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); -#endif -#endif -} - void InputController::stealFocus(QWidget* focus) { m_focusParent = focus; } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 068cf890b..517b547c2 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -7,8 +7,10 @@ #include "input/GamepadAxisEvent.h" #include "input/GamepadHatEvent.h" +#include "input/InputDriver.h" #include "input/InputMapper.h" +#include #include #include #include @@ -22,11 +24,6 @@ #include #include -#ifdef BUILD_SDL -#include "platform/sdl/sdl-events.h" -#endif - - #ifdef BUILD_QT_MULTIMEDIA #include "VideoDumper.h" #include @@ -38,6 +35,8 @@ struct mRumble; namespace QGBA { class ConfigController; +class Gamepad; +class InputSource; class InputController : public QObject { Q_OBJECT @@ -55,13 +54,15 @@ public: InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr); ~InputController(); + void addInputDriver(std::shared_ptr); + void setConfiguration(ConfigController* config); void saveConfiguration(); bool loadConfiguration(uint32_t type); bool loadProfile(uint32_t type, const QString& profile); void saveConfiguration(uint32_t type); void saveProfile(uint32_t type, const QString& profile); - const char* profileForType(uint32_t type); + QString profileForType(uint32_t type); int mapKeyboard(int key) const; @@ -71,25 +72,17 @@ public: int pollEvents(); static const int32_t AXIS_THRESHOLD = 0x3000; - QSet activeGamepadButtons(int type); - QSet> activeGamepadAxes(int type); - QSet> activeGamepadHats(int type); - void recalibrateAxes(); QStringList connectedGamepads(uint32_t type) const; - int gamepad(uint32_t type) const; + int gamepadIndex(uint32_t type) const; void setGamepad(uint32_t type, int index); void setPreferredGamepad(uint32_t type, int index); InputMapper mapper(uint32_t type); + InputMapper mapper(InputSource*); - void registerTiltAxisX(int axis); - void registerTiltAxisY(int axis); - void registerGyroAxisX(int axis); - void registerGyroAxisY(int axis); - - float gyroSensitivity() const; - void setGyroSensitivity(float sensitivity); + const InputDriver* sensorDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } + InputDriver* sensorDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } void stealFocus(QWidget* focus); void releaseFocus(QWidget* focus); @@ -107,12 +100,7 @@ signals: public slots: void testGamepad(int type); - void updateJoysticks(); - - // TODO: Move these to somewhere that makes sense - void suspendScreensaver(); - void resumeScreensaver(); - void setScreensaverSuspendable(bool); + void update(); void increaseLuminanceLevel(); void decreaseLuminanceLevel(); @@ -137,6 +125,13 @@ private: bool hasPendingEvent(int) const; void sendGamepadEvent(QEvent*); + Gamepad* gamepad(uint32_t type); + QList gamepads(); + + QSet activeGamepadButtons(int type); + QSet> activeGamepadAxes(int type); + QSet> activeGamepadHats(int type); + struct InputControllerLux : GBALuminanceSource { InputController* p; uint8_t value; @@ -166,14 +161,8 @@ private: QWidget* m_topLevel; QWidget* m_focusParent; -#ifdef BUILD_SDL - static int s_sdlInited; - static mSDLEvents s_sdlEvents; - mSDLPlayer m_sdlPlayer{}; - bool m_playerAttached = false; -#endif - - QVector m_deadzones; + QHash> m_inputDrivers; + uint32_t m_sensorDriver; QSet m_activeButtons; QSet> m_activeAxes; diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index 82e5090af..c6d407a09 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -10,6 +10,10 @@ #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + using namespace QGBA; const InputProfile InputProfile::s_defaultMaps[] = { @@ -218,11 +222,15 @@ void InputProfile::apply(InputController* controller) const { mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, static_cast(i)); } #endif - controller->registerTiltAxisX(m_tiltAxis.x); - controller->registerTiltAxisY(m_tiltAxis.y); - controller->registerGyroAxisX(m_gyroAxis.x); - controller->registerGyroAxisY(m_gyroAxis.y); - controller->setGyroSensitivity(m_gyroSensitivity); + + InputDriver* sensorDriver = controller->sensorDriver(); + if (sensorDriver) { + sensorDriver->registerTiltAxisX(m_tiltAxis.x); + sensorDriver->registerTiltAxisY(m_tiltAxis.y); + sensorDriver->registerGyroAxisX(m_gyroAxis.x); + sensorDriver->registerGyroAxisY(m_gyroAxis.y); + sensorDriver->setGyroSensitivity(m_gyroSensitivity); + } } bool InputProfile::lookupShortcutButton(const QString& shortcutName, int* button) const { diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index 494f45a64..545760fea 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -18,6 +18,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + #include "CoreController.h" #include "GBAApp.h" #include "Window.h" @@ -316,7 +320,7 @@ void ReportView::generateReport() { } #ifdef BUILD_SDL InputController* input = window->inputController(); - windowReport << QString("Active gamepad: %1").arg(input->gamepad(SDL_BINDING_BUTTON)); + windowReport << QString("Active gamepad: %1").arg(input->gamepadIndex(SDL_BINDING_BUTTON)); #endif windowReport << QString("Configuration: %1").arg(configs.indexOf(config) + 1); addReport(QString("Window %1").arg(winId), windowReport.join('\n')); @@ -490,7 +494,7 @@ void ReportView::addGamepadInfo(QStringList& report) { i = 0; for (Window* window : GBAApp::app()->windows()) { ++i; - report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepad(SDL_BINDING_BUTTON)); + report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepadIndex(SDL_BINDING_BUTTON)); } } #endif diff --git a/src/platform/qt/SensorView.cpp b/src/platform/qt/SensorView.cpp index a40d0a4a5..ba1cf0ce9 100644 --- a/src/platform/qt/SensorView.cpp +++ b/src/platform/qt/SensorView.cpp @@ -7,6 +7,7 @@ #include "CoreController.h" #include "input/GamepadAxisEvent.h" +#include "input/InputDriver.h" #include "InputController.h" #include "utils.h" @@ -47,14 +48,20 @@ SensorView::SensorView(InputController* input, QWidget* parent) m_timer.start(); } - jiggerer(m_ui.tiltSetX, &InputController::registerTiltAxisX); - jiggerer(m_ui.tiltSetY, &InputController::registerTiltAxisY); - jiggerer(m_ui.gyroSetX, &InputController::registerGyroAxisX); - jiggerer(m_ui.gyroSetY, &InputController::registerGyroAxisY); + jiggerer(m_ui.tiltSetX, &InputDriver::registerTiltAxisX); + jiggerer(m_ui.tiltSetY, &InputDriver::registerTiltAxisY); + jiggerer(m_ui.gyroSetX, &InputDriver::registerGyroAxisX); + jiggerer(m_ui.gyroSetY, &InputDriver::registerGyroAxisY); - m_ui.gyroSensitivity->setValue(m_input->gyroSensitivity() / 1e8f); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + m_ui.gyroSensitivity->setValue(sensorDriver->gyroSensitivity() / 1e8f); + } connect(m_ui.gyroSensitivity, static_cast(&QDoubleSpinBox::valueChanged), [this](double value) { - m_input->setGyroSensitivity(value * 1e8f); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + sensorDriver->setGyroSensitivity(value * 1e8f); + } }); m_input->stealFocus(this); connect(m_input, &InputController::luminanceValueChanged, this, &SensorView::luminanceValueChanged); @@ -84,7 +91,7 @@ void SensorView::setController(std::shared_ptr controller) { }); } -void SensorView::jiggerer(QAbstractButton* button, void (InputController::*setter)(int)) { +void SensorView::jiggerer(QAbstractButton* button, void (InputDriver::*setter)(int)) { connect(button, &QAbstractButton::toggled, [this, button, setter](bool checked) { if (!checked) { m_button = nullptr; @@ -115,7 +122,10 @@ bool SensorView::eventFilter(QObject*, QEvent* event) { m_button->removeEventFilter(this); m_button->clearFocus(); m_button->setChecked(false); - (m_input->*m_setter)(gae->axis()); + InputDriver* sensorDriver = m_input->sensorDriver(); + if (sensorDriver) { + (sensorDriver->*m_setter)(gae->axis()); + } m_button = nullptr; } return true; diff --git a/src/platform/qt/SensorView.h b/src/platform/qt/SensorView.h index 5ccc40ec8..2bb5047cd 100644 --- a/src/platform/qt/SensorView.h +++ b/src/platform/qt/SensorView.h @@ -21,6 +21,7 @@ class ConfigController; class CoreController; class GamepadAxisEvent; class InputController; +class InputDriver; class SensorView : public QDialog { Q_OBJECT @@ -43,14 +44,14 @@ private: Ui::SensorView m_ui; QAbstractButton* m_button = nullptr; - void (InputController::*m_setter)(int); + void (InputDriver::*m_setter)(int); std::shared_ptr m_controller; InputController* m_input; mRotationSource* m_rotation; QTimer m_timer; - void jiggerer(QAbstractButton*, void (InputController::*)(int)); + void jiggerer(QAbstractButton*, void (InputDriver::*)(int)); }; } diff --git a/src/platform/qt/SettingsView.cpp b/src/platform/qt/SettingsView.cpp index cba481dc8..4eca3cb46 100644 --- a/src/platform/qt/SettingsView.cpp +++ b/src/platform/qt/SettingsView.cpp @@ -25,6 +25,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + using namespace QGBA; SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, LogController* logController, QWidget* parent) @@ -326,8 +330,7 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC GBAKeyEditor* buttonEditor = nullptr; #ifdef BUILD_SDL - inputController->recalibrateAxes(); - const char* profile = inputController->profileForType(SDL_BINDING_BUTTON); + QString profile = inputController->profileForType(SDL_BINDING_BUTTON); buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile); addPage(tr("Controllers"), buttonEditor, Page::CONTROLLERS); connect(m_ui.buttonBox, &QDialogButtonBox::accepted, buttonEditor, &GBAKeyEditor::save); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 809ca02be..c07d82704 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -36,6 +36,9 @@ #include "GDBController.h" #include "GDBWindow.h" #include "GIFView.h" +#ifdef BUILD_SDL +#include "input/SDLInputDriver.h" +#endif #include "IOViewer.h" #include "LoadSaveState.h" #include "LogView.h" @@ -171,6 +174,10 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi m_mustReset.setInterval(MUST_RESTART_TIMEOUT); m_mustReset.setSingleShot(true); +#ifdef BUILD_SDL + m_inputController.addInputDriver(std::make_shared(&m_inputController)); +#endif + m_shortcutController->setConfigController(m_config); m_shortcutController->setActionMapper(&m_actions); setupMenu(menuBar()); @@ -298,7 +305,7 @@ void Window::reloadConfig() { m_display->resizeContext(); } - m_inputController.setScreensaverSuspendable(opts->suspendScreensaver); + GBAApp::app()->setScreensaverSuspendable(opts->suspendScreensaver); } void Window::saveConfig() { @@ -2000,7 +2007,6 @@ void Window::setController(CoreController* controller, const QString& fname) { } m_controller = std::shared_ptr(controller); - m_inputController.recalibrateAxes(); m_controller->setInputController(&m_inputController); m_controller->setLogger(&m_log); @@ -2013,14 +2019,14 @@ void Window::setController(CoreController* controller, const QString& fname) { }); connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted); - connect(m_controller.get(), &CoreController::started, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller.get(), &CoreController::started, GBAApp::app(), &GBAApp::suspendScreensaver); connect(m_controller.get(), &CoreController::stopping, this, &Window::gameStopped); { connect(m_controller.get(), &CoreController::stopping, [this]() { m_controller.reset(); }); } - connect(m_controller.get(), &CoreController::stopping, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller.get(), &CoreController::stopping, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, this, &Window::updateFrame); #ifndef Q_OS_MAC @@ -2032,14 +2038,14 @@ void Window::setController(CoreController* controller, const QString& fname) { }); #endif - connect(m_controller.get(), &CoreController::paused, &m_inputController, &InputController::resumeScreensaver); + connect(m_controller.get(), &CoreController::paused, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, [this]() { emit paused(true); }); connect(m_controller.get(), &CoreController::unpaused, [this]() { emit paused(false); }); - connect(m_controller.get(), &CoreController::unpaused, &m_inputController, &InputController::suspendScreensaver); + connect(m_controller.get(), &CoreController::unpaused, GBAApp::app(), &GBAApp::suspendScreensaver); connect(m_controller.get(), &CoreController::frameAvailable, this, &Window::recordFrame); connect(m_controller.get(), &CoreController::crashed, this, &Window::gameCrashed); connect(m_controller.get(), &CoreController::failed, this, &Window::gameFailed); diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp index 549fa13bb..912395e0d 100644 --- a/src/platform/qt/input/InputDriver.cpp +++ b/src/platform/qt/input/InputDriver.cpp @@ -17,6 +17,18 @@ void InputDriver::loadConfiguration(ConfigController*) { void InputDriver::saveConfiguration(ConfigController*) { } +bool InputDriver::supportsPolling() const { + return false; +} + +bool InputDriver::supportsGamepads() const { + return false; +} + +bool InputDriver::supportsSensors() const { + return false; +} + void InputDriver::bindDefaults(InputController*) { } @@ -28,6 +40,42 @@ QList InputDriver::connectedGamepads() const { return {}; } +int InputDriver::activeKeySource() const { + return -1; +} + +int InputDriver::activeGamepad() const { + return -1; +} + +void InputDriver::setActiveKeySource(int) { +} + +void InputDriver::setActiveGamepad(int) { +} + +void InputDriver::registerTiltAxisX(int) { +} + +void InputDriver::registerTiltAxisY(int) { +} + +void InputDriver::registerGyroAxisX(int) { +} + +void InputDriver::registerGyroAxisY(int) { +} + +void InputDriver::registerGyroAxisZ(int) { +} + +float InputDriver::gyroSensitivity() const { + return 0; +} + +void InputDriver::setGyroSensitivity(float) { +} + mRumble* InputDriver::rumble() { return nullptr; } diff --git a/src/platform/qt/input/InputDriver.h b/src/platform/qt/input/InputDriver.h index 139f1a500..f344b271b 100644 --- a/src/platform/qt/input/InputDriver.h +++ b/src/platform/qt/input/InputDriver.h @@ -28,6 +28,11 @@ public: virtual uint32_t type() const = 0; virtual QString visibleName() const = 0; + virtual QString currentProfile() const = 0; + + virtual bool supportsPolling() const; + virtual bool supportsGamepads() const; + virtual bool supportsSensors() const; virtual void loadConfiguration(ConfigController*); virtual void saveConfiguration(ConfigController*); @@ -39,6 +44,21 @@ public: virtual QList connectedKeySources() const; virtual QList connectedGamepads() const; + virtual int activeKeySource() const; + virtual int activeGamepad() const; + + virtual void setActiveKeySource(int); + virtual void setActiveGamepad(int); + + virtual void registerTiltAxisX(int axis); + virtual void registerTiltAxisY(int axis); + virtual void registerGyroAxisX(int axis); + virtual void registerGyroAxisY(int axis); + virtual void registerGyroAxisZ(int axis); + + virtual float gyroSensitivity() const; + virtual void setGyroSensitivity(float sensitivity); + virtual mRumble* rumble(); virtual mRotationSource* rotationSource(); }; diff --git a/src/platform/qt/input/InputMapper.cpp b/src/platform/qt/input/InputMapper.cpp index a5166e55a..41a3bc8c3 100644 --- a/src/platform/qt/input/InputMapper.cpp +++ b/src/platform/qt/input/InputMapper.cpp @@ -15,18 +15,65 @@ InputMapper::InputMapper(mInputMap* map, uint32_t type) { } -int InputMapper::mapKey(int key) { +int InputMapper::mapKey(int key) const { return mInputMapKey(m_map, m_type, key); } -int InputMapper::mapAxis(int axis, int16_t value) { +int InputMapper::mapAxis(int axis, int16_t value) const { return mInputMapAxis(m_map, m_type, axis, value); } -int InputMapper::mapHat(int hat, GamepadHatEvent::Direction direction) { +int InputMapper::mapHat(int hat, GamepadHatEvent::Direction direction) const { return mInputMapHat(m_map, m_type, hat, direction); } +int InputMapper::mapKeys(QList keys) const { + int platformKeys = 0; + for (int i = 0; i < keys.count(); ++i) { + if (!keys[i]) { + continue; + } + int platformKey = mInputMapKey(m_map, m_type, i); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapKeys(QSet keys) const { + int platformKeys = 0; + for (int key : keys) { + int platformKey = mInputMapKey(m_map, m_type, key); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapAxes(QList axes) const { + int platformKeys = 0; + for (int i = 0; i < axes.count(); ++i) { + int platformKey = mInputMapAxis(m_map, m_type, i, axes[i]); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + +int InputMapper::mapHats(QList hats) const { + int platformKeys = 0; + for (int i = 0; i < hats.count(); ++i) { + int platformKey = mInputMapHat(m_map, m_type, i, hats[i]); + if (platformKey >= 0) { + platformKeys |= 1 << platformKey; + } + } + return platformKeys; +} + void InputMapper::bindKey(int key, int platformKey) { mInputBindKey(m_map, m_type, key, platformKey); } diff --git a/src/platform/qt/input/InputMapper.h b/src/platform/qt/input/InputMapper.h index 69912ff35..0e8268356 100644 --- a/src/platform/qt/input/InputMapper.h +++ b/src/platform/qt/input/InputMapper.h @@ -24,9 +24,14 @@ public: mInputMap* inputMap() const { return m_map; } uint32_t type() const { return m_type; } - int mapKey(int key); - int mapAxis(int axis, int16_t value); - int mapHat(int hat, GamepadHatEvent::Direction); + int mapKey(int key) const; + int mapAxis(int axis, int16_t value) const; + int mapHat(int hat, GamepadHatEvent::Direction) const; + + int mapKeys(QList keys) const; + int mapKeys(QSet keys) const; + int mapAxes(QList axes) const; + int mapHats(QList hats) const; void bindKey(int key, int platformKey); void bindAxis(int axis, GamepadAxisEvent::Direction, int platformKey); diff --git a/src/platform/qt/input/InputSource.h b/src/platform/qt/input/InputSource.h index 9ba18acf5..3bf7350d7 100644 --- a/src/platform/qt/input/InputSource.h +++ b/src/platform/qt/input/InputSource.h @@ -8,9 +8,9 @@ #include #include -namespace QGBA { +#include "input/InputDriver.h" -class InputDriver; +namespace QGBA { class InputSource : public QObject { Q_OBJECT @@ -20,6 +20,7 @@ public: virtual ~InputSource() = default; InputDriver* driver() { return m_driver; } + uint32_t type() { return m_driver->type(); } virtual QString name() const = 0; virtual QString visibleName() const = 0; diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp index efd8e3f36..2eda6a4f0 100644 --- a/src/platform/qt/input/SDLInputDriver.cpp +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -15,6 +15,30 @@ using namespace QGBA; int s_sdlInited = 0; mSDLEvents s_sdlEvents; +void SDL::suspendScreensaver() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLSuspendScreensaver(&s_sdlEvents); + } +#endif +} + +void SDL::resumeScreensaver() { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLResumeScreensaver(&s_sdlEvents); + } +#endif +} + +void SDL::setScreensaverSuspendable(bool suspendable) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (s_sdlInited) { + mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable); + } +#endif +} + SDLInputDriver::SDLInputDriver(InputController* controller, QObject* parent) : InputDriver(parent) , m_controller(controller) @@ -41,6 +65,29 @@ SDLInputDriver::~SDLInputDriver() { } } +bool SDLInputDriver::supportsPolling() const { + return true; +} + +bool SDLInputDriver::supportsGamepads() const { + return true; +} + +bool SDLInputDriver::supportsSensors() const { + return true; +} + +QString SDLInputDriver::currentProfile() const { + if (m_sdlPlayer.joystick) { +#if SDL_VERSION_ATLEAST(2, 0, 0) + return SDL_JoystickName(m_sdlPlayer.joystick->joystick); +#else + return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick)); +#endif + } + return {}; +} + void SDLInputDriver::loadConfiguration(ConfigController* config) { mSDLEventsLoadConfig(&s_sdlEvents, config->input()); if (!m_playerAttached) { @@ -126,6 +173,69 @@ void SDLInputDriver::updateGamepads() { } #endif +int SDLInputDriver::activeGamepad() const { + return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; +} + +void SDLInputDriver::setActiveGamepad(int index) { + mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index); +} + +void SDLInputDriver::registerTiltAxisX(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.axisX = axis; + } +} + +void SDLInputDriver::registerTiltAxisY(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.axisY = axis; + } +} + +void SDLInputDriver::registerGyroAxisX(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroX = axis; + if (m_sdlPlayer.rotation.gyroY == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } + } +} + +void SDLInputDriver::registerGyroAxisY(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroY = axis; + if (m_sdlPlayer.rotation.gyroX == axis) { + m_sdlPlayer.rotation.gyroZ = axis; + } else { + m_sdlPlayer.rotation.gyroZ = -1; + } + } +} + +void SDLInputDriver::registerGyroAxisZ(int axis) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroZ = axis; + m_sdlPlayer.rotation.gyroX = -1; + m_sdlPlayer.rotation.gyroY = -1; + } +} + +float SDLInputDriver::gyroSensitivity() const { + if (m_playerAttached) { + return m_sdlPlayer.rotation.gyroSensitivity; + } + return 0; +} + +void SDLInputDriver::setGyroSensitivity(float sensitivity) { + if (m_playerAttached) { + m_sdlPlayer.rotation.gyroSensitivity = sensitivity; + } +} + SDLGamepad::SDLGamepad(SDLInputDriver* driver, int index, QObject* parent) : Gamepad(driver, parent) , m_index(index) diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h index 752ab7a91..79deac22c 100644 --- a/src/platform/qt/input/SDLInputDriver.h +++ b/src/platform/qt/input/SDLInputDriver.h @@ -16,6 +16,12 @@ namespace QGBA { class SDLGamepad; +namespace SDL { + void suspendScreensaver(); + void resumeScreensaver(); + void setScreensaverSuspendable(bool); +} + class SDLInputDriver : public InputDriver { Q_OBJECT @@ -25,6 +31,11 @@ public: uint32_t type() const override { return SDL_BINDING_BUTTON; } QString visibleName() const override { return QLatin1String("SDL"); } + QString currentProfile() const override; + + bool supportsPolling() const override; + bool supportsGamepads() const override; + bool supportsSensors() const override; void loadConfiguration(ConfigController* config) override; void saveConfiguration(ConfigController* config) override; @@ -35,6 +46,18 @@ public: QList connectedGamepads() const override; + int activeGamepad() const override; + void setActiveGamepad(int) override; + + void registerTiltAxisX(int axis) override; + void registerTiltAxisY(int axis) override; + void registerGyroAxisX(int axis) override; + void registerGyroAxisY(int axis) override; + void registerGyroAxisZ(int axis) override; + + float gyroSensitivity() const override; + void setGyroSensitivity(float sensitivity) override; + mRumble* rumble() override; mRotationSource* rotationSource() override; diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index 67668b011..b3d4bafc9 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -14,6 +14,10 @@ #include #include +#ifdef BUILD_SDL +#include "platform/sdl/sdl-events.h" +#endif + #include #include From 430ffc18e2fb6dbce9f2eadc5ead16e6f414ab40 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 Jan 2023 18:34:47 -0800 Subject: [PATCH 110/159] Qt: Further input cleanup --- src/platform/qt/InputController.cpp | 41 +++++++++++++++++++++++++++++ src/platform/qt/InputController.h | 13 +++++++-- src/platform/qt/InputProfile.cpp | 17 +++++------- src/platform/qt/ReportView.cpp | 14 +++------- 4 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index b00d2131e..0c94e7511 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -208,7 +208,18 @@ QString InputController::profileForType(uint32_t type) { return driver->currentProfile(); } +void InputController::setGamepadDriver(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver || !driver->supportsGamepads()) { + return; + } + m_gamepadDriver = type; +} + QStringList InputController::connectedGamepads(uint32_t type) const { + if (!type) { + type = m_gamepadDriver; + } auto driver = m_inputDrivers.value(type); if (!driver) { return {}; @@ -222,6 +233,9 @@ QStringList InputController::connectedGamepads(uint32_t type) const { } int InputController::gamepadIndex(uint32_t type) const { + if (!type) { + type = m_gamepadDriver; + } auto driver = m_inputDrivers.value(type); if (!driver) { return -1; @@ -230,6 +244,9 @@ int InputController::gamepadIndex(uint32_t type) const { } void InputController::setGamepad(uint32_t type, int index) { + if (!type) { + type = m_gamepadDriver; + } auto driver = m_inputDrivers.value(type); if (!driver) { return; @@ -237,10 +254,17 @@ void InputController::setGamepad(uint32_t type, int index) { driver->setActiveGamepad(index); } +void InputController::setGamepad(int index) { + setGamepad(0, index); +} + void InputController::setPreferredGamepad(uint32_t type, int index) { if (!m_config) { return; } + if (!type) { + type = m_gamepadDriver; + } auto driver = m_inputDrivers.value(type); if (!driver) { return; @@ -258,14 +282,31 @@ void InputController::setPreferredGamepad(uint32_t type, int index) { mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, name.toUtf8().constData()); } +void InputController::setPreferredGamepad(int index) { + setPreferredGamepad(0, index); +} + InputMapper InputController::mapper(uint32_t type) { return InputMapper(&m_inputMap, type); } +InputMapper InputController::mapper(InputDriver* driver) { + return InputMapper(&m_inputMap, driver->type()); +} + InputMapper InputController::mapper(InputSource* source) { return InputMapper(&m_inputMap, source->type()); } +void InputController::setSensorDriver(uint32_t type) { + auto driver = m_inputDrivers.value(type); + if (!driver || !driver->supportsSensors()) { + return; + } + m_sensorDriver = type; +} + + mRumble* InputController::rumble() { auto driver = m_inputDrivers.value(m_sensorDriver); if (driver) { diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 517b547c2..ab6590e7c 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -73,14 +73,22 @@ public: static const int32_t AXIS_THRESHOLD = 0x3000; - QStringList connectedGamepads(uint32_t type) const; - int gamepadIndex(uint32_t type) const; + void setGamepadDriver(uint32_t type); + const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } + InputDriver* gamepadDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } + + QStringList connectedGamepads(uint32_t type = 0) const; + int gamepadIndex(uint32_t type = 0) const; void setGamepad(uint32_t type, int index); + void setGamepad(int index); void setPreferredGamepad(uint32_t type, int index); + void setPreferredGamepad(int index); InputMapper mapper(uint32_t type); + InputMapper mapper(InputDriver*); InputMapper mapper(InputSource*); + void setSensorDriver(uint32_t type); const InputDriver* sensorDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } InputDriver* sensorDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } @@ -162,6 +170,7 @@ private: QWidget* m_focusParent; QHash> m_inputDrivers; + uint32_t m_gamepadDriver; uint32_t m_sensorDriver; QSet m_activeButtons; diff --git a/src/platform/qt/InputProfile.cpp b/src/platform/qt/InputProfile.cpp index c6d407a09..e8f7f1711 100644 --- a/src/platform/qt/InputProfile.cpp +++ b/src/platform/qt/InputProfile.cpp @@ -10,10 +10,6 @@ #include -#ifdef BUILD_SDL -#include "platform/sdl/sdl-events.h" -#endif - using namespace QGBA; const InputProfile InputProfile::s_defaultMaps[] = { @@ -215,13 +211,14 @@ const InputProfile* InputProfile::findProfile(const QString& name) { } void InputProfile::apply(InputController* controller) const { -#ifdef BUILD_SDL - InputMapper mapper = controller->mapper(SDL_BINDING_BUTTON); - for (size_t i = 0; i < GBA_KEY_MAX; ++i) { - mapper.bindKey(m_keys[i], static_cast(i)); - mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, static_cast(i)); + auto gamepadDriver = controller->gamepadDriver(); + if (gamepadDriver) { + InputMapper mapper = controller->mapper(gamepadDriver); + for (size_t i = 0; i < GBA_KEY_MAX; ++i) { + mapper.bindKey(m_keys[i], i); + mapper.bindAxis(m_axes[i].axis, m_axes[i].direction, i); + } } -#endif InputDriver* sensorDriver = controller->sensorDriver(); if (sensorDriver) { diff --git a/src/platform/qt/ReportView.cpp b/src/platform/qt/ReportView.cpp index 545760fea..c38c84a03 100644 --- a/src/platform/qt/ReportView.cpp +++ b/src/platform/qt/ReportView.cpp @@ -18,10 +18,6 @@ #include #include -#ifdef BUILD_SDL -#include "platform/sdl/sdl-events.h" -#endif - #include "CoreController.h" #include "GBAApp.h" #include "Window.h" @@ -318,10 +314,8 @@ void ReportView::generateReport() { } else { windowReport << QString("ROM open: No"); } -#ifdef BUILD_SDL InputController* input = window->inputController(); - windowReport << QString("Active gamepad: %1").arg(input->gamepadIndex(SDL_BINDING_BUTTON)); -#endif + windowReport << QString("Active gamepad: %1").arg(input->gamepadIndex()); windowReport << QString("Configuration: %1").arg(configs.indexOf(config) + 1); addReport(QString("Window %1").arg(winId), windowReport.join('\n')); } @@ -481,9 +475,8 @@ void ReportView::addGLInfo(QStringList& report) { } void ReportView::addGamepadInfo(QStringList& report) { -#ifdef BUILD_SDL InputController* input = GBAApp::app()->windows()[0]->inputController(); - QStringList gamepads = input->connectedGamepads(SDL_BINDING_BUTTON); + QStringList gamepads = input->connectedGamepads(); report << QString("Connected gamepads: %1").arg(gamepads.size()); int i = 0; for (const auto& gamepad : gamepads) { @@ -494,10 +487,9 @@ void ReportView::addGamepadInfo(QStringList& report) { i = 0; for (Window* window : GBAApp::app()->windows()) { ++i; - report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepadIndex(SDL_BINDING_BUTTON)); + report << QString("Window %1 gamepad: %2").arg(i).arg(window->inputController()->gamepadIndex()); } } -#endif } void ReportView::addROMInfo(QStringList& report, CoreController* controller) { From 62c84ab815c4a10be30b8b91679083d8c4536b22 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Tue, 10 Jan 2023 23:13:58 -0800 Subject: [PATCH 111/159] Util: Add some base vector implementations --- include/mgba-util/vector.h | 10 +++++++++- src/debugger/parser.c | 2 -- src/util/CMakeLists.txt | 1 + src/util/string.c | 4 ---- src/util/vector.c | 17 +++++++++++++++++ 5 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 src/util/vector.c diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index a4ec71b65..83112c0b5 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -97,8 +97,16 @@ CXX_GUARD_START dest->size = src->size; \ } \ -DECLARE_VECTOR(StringList, char*); DECLARE_VECTOR(IntList, int); +DECLARE_VECTOR(SInt8List, int8_t); +DECLARE_VECTOR(SInt16List, int16_t); +DECLARE_VECTOR(SInt32List, int32_t); +DECLARE_VECTOR(SIntPtrList, intptr_t); +DECLARE_VECTOR(UInt8List, uint8_t); +DECLARE_VECTOR(UInt16List, uint16_t); +DECLARE_VECTOR(UInt32List, uint32_t); +DECLARE_VECTOR(UIntPtrList, uintptr_t); +DECLARE_VECTOR(StringList, char*); CXX_GUARD_END diff --git a/src/debugger/parser.c b/src/debugger/parser.c index 0d4924ea9..3c3b037fa 100644 --- a/src/debugger/parser.c +++ b/src/debugger/parser.c @@ -11,8 +11,6 @@ DEFINE_VECTOR(LexVector, struct Token); -DEFINE_VECTOR(IntList, int32_t); - enum LexState { LEX_ERROR = -1, LEX_ROOT = 0, diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index df0d84c5c..9246b20c4 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -8,6 +8,7 @@ set(BASE_SOURCE_FILES hash.c string.c table.c + vector.c vfs.c) set(SOURCE_FILES diff --git a/src/util/string.c b/src/util/string.c index 9e007dc2d..1a2ffe7a4 100644 --- a/src/util/string.c +++ b/src/util/string.c @@ -5,12 +5,8 @@ * 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 diff --git a/src/util/vector.c b/src/util/vector.c new file mode 100644 index 000000000..28750a1b4 --- /dev/null +++ b/src/util/vector.c @@ -0,0 +1,17 @@ +/* Copyright (c) 2013-2023 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 + +DEFINE_VECTOR(IntList, int); +DEFINE_VECTOR(SInt8List, int8_t); +DEFINE_VECTOR(SInt16List, int16_t); +DEFINE_VECTOR(SInt32List, int32_t); +DEFINE_VECTOR(SIntPtrList, intptr_t); +DEFINE_VECTOR(UInt8List, uint8_t); +DEFINE_VECTOR(UInt16List, uint16_t); +DEFINE_VECTOR(UInt32List, uint32_t); +DEFINE_VECTOR(UIntPtrList, uintptr_t); +DEFINE_VECTOR(StringList, char*); From 2df70ee45e4620a5a579dc76497b858e81f04703 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 11 Jan 2023 02:24:54 -0800 Subject: [PATCH 112/159] Util: Add debug bounds checking to vector --- include/mgba-util/vector.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/mgba-util/vector.h b/include/mgba-util/vector.h index 83112c0b5..8c0f11e45 100644 --- a/include/mgba-util/vector.h +++ b/include/mgba-util/vector.h @@ -34,6 +34,16 @@ CXX_GUARD_START size_t NAME ## Index(const struct NAME* vector, const TYPE* member); \ void NAME ## Copy(struct NAME* dest, const struct NAME* src); +#ifdef NDEBUG +#define VECTOR_BOUNDS_CHECK(NAME, V, L) +#else +#define VECTOR_BOUNDS_CHECK(NAME, V, L) \ + if ((L) >= (V)->size) { \ + fprintf(stderr, "Vector type %s invalid access of index %" PRIuPTR " into vector of size %" PRIuPTR "\n", #NAME, (L), (V)->size); \ + abort(); \ + } +#endif + #define DEFINE_VECTOR(NAME, TYPE) \ void NAME ## Init(struct NAME* vector, size_t capacity) { \ vector->size = 0; \ @@ -50,9 +60,11 @@ CXX_GUARD_START vector->size = 0; \ } \ TYPE* NAME ## GetPointer(struct NAME* vector, size_t location) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ return &vector->vector[location]; \ } \ TYPE const* NAME ## GetConstPointer(const struct NAME* vector, size_t location) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ return &vector->vector[location]; \ } \ TYPE* NAME ## Append(struct NAME* vector) { \ @@ -78,10 +90,12 @@ CXX_GUARD_START vector->vector = realloc(vector->vector, vector->capacity * sizeof(TYPE)); \ } \ void NAME ## Shift(struct NAME* vector, size_t location, size_t difference) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ memmove(&vector->vector[location], &vector->vector[location + difference], (vector->size - location - difference) * sizeof(TYPE)); \ vector->size -= difference; \ } \ void NAME ## Unshift(struct NAME* vector, size_t location, size_t difference) { \ + VECTOR_BOUNDS_CHECK(NAME, vector, location); \ NAME ## Resize(vector, difference); \ memmove(&vector->vector[location + difference], &vector->vector[location], (vector->size - location - difference) * sizeof(TYPE)); \ } \ From 74e7a44da3d51dfe2be27961eca651cc3a9e05f8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 22 Jan 2023 20:01:45 -0800 Subject: [PATCH 113/159] Qt: More API cleanup --- src/platform/qt/InputController.cpp | 25 ++++++++++-------------- src/platform/qt/InputController.h | 8 ++++---- src/platform/qt/input/InputDriver.cpp | 22 +++++++++++++++++++-- src/platform/qt/input/InputDriver.h | 7 +++++-- src/platform/qt/input/SDLInputDriver.cpp | 2 +- src/platform/qt/input/SDLInputDriver.h | 2 +- 6 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index 0c94e7511..e6e741a56 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -240,7 +240,7 @@ int InputController::gamepadIndex(uint32_t type) const { if (!driver) { return -1; } - return driver->activeGamepad(); + return driver->activeGamepadIndex(); } void InputController::setGamepad(uint32_t type, int index) { @@ -361,13 +361,9 @@ Gamepad* InputController::gamepad(uint32_t type) { } if (!driver->supportsGamepads()) { return nullptr; -} - QList driverPads(driver->connectedGamepads()); - int activeGamepad = driver->activeGamepad(); - if (activeGamepad < 0 || activeGamepad >= driverPads.count()) { - return nullptr; } - return driverPads[activeGamepad]; + + return driver->activeGamepad(); } QList InputController::gamepads() { @@ -376,16 +372,15 @@ QList InputController::gamepads() { if (!driver->supportsGamepads()) { continue; } - QList driverPads(driver->connectedGamepads()); - int activeGamepad = driver->activeGamepad(); - if (activeGamepad >= 0 && activeGamepad < driverPads.count()) { - pads.append(driverPads[activeGamepad]); + Gamepad* pad = driver->activeGamepad(); + if (pad) { + pads.append(pad); } } return pads; } -QSet InputController::activeGamepadButtons(int type) { +QSet InputController::activeGamepadButtons(uint32_t type) { QSet activeButtons; Gamepad* pad = gamepad(type); if (!pad) { @@ -400,7 +395,7 @@ QSet InputController::activeGamepadButtons(int type) { return activeButtons; } -QSet> InputController::activeGamepadAxes(int type) { +QSet> InputController::activeGamepadAxes(uint32_t type) { QSet> activeAxes; Gamepad* pad = gamepad(type); if (!pad) { @@ -421,7 +416,7 @@ QSet> InputController::activeGamepadAxes return activeAxes; } -QSet> InputController::activeGamepadHats(int type) { +QSet> InputController::activeGamepadHats(uint32_t type) { QSet> activeHats; Gamepad* pad = gamepad(type); if (!pad) { @@ -436,7 +431,7 @@ QSet> InputController::activeGamepadHats( return activeHats; } -void InputController::testGamepad(int type) { +void InputController::testGamepad(uint32_t type) { QWriteLocker l(&m_eventsLock); auto activeAxes = activeGamepadAxes(type); auto oldAxes = m_activeAxes; diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index ab6590e7c..e974c1aaa 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -107,7 +107,7 @@ signals: void luminanceValueChanged(int value); public slots: - void testGamepad(int type); + void testGamepad(uint32_t type); void update(); void increaseLuminanceLevel(); @@ -136,9 +136,9 @@ private: Gamepad* gamepad(uint32_t type); QList gamepads(); - QSet activeGamepadButtons(int type); - QSet> activeGamepadAxes(int type); - QSet> activeGamepadHats(int type); + QSet activeGamepadButtons(uint32_t type); + QSet> activeGamepadAxes(uint32_t type); + QSet> activeGamepadHats(uint32_t type); struct InputControllerLux : GBALuminanceSource { InputController* p; diff --git a/src/platform/qt/input/InputDriver.cpp b/src/platform/qt/input/InputDriver.cpp index 912395e0d..f76649bd8 100644 --- a/src/platform/qt/input/InputDriver.cpp +++ b/src/platform/qt/input/InputDriver.cpp @@ -40,14 +40,32 @@ QList InputDriver::connectedGamepads() const { return {}; } -int InputDriver::activeKeySource() const { +int InputDriver::activeKeySourceIndex() const { return -1; } -int InputDriver::activeGamepad() const { +int InputDriver::activeGamepadIndex() const { return -1; } +KeySource* InputDriver::activeKeySource() { + QList ks(connectedKeySources()); + int activeKeySource = activeKeySourceIndex(); + if (activeKeySource < 0 || activeKeySource >= ks.count()) { + return nullptr; + } + return ks[activeKeySource]; +} + +Gamepad* InputDriver::activeGamepad() { + QList pads(connectedGamepads()); + int activeGamepad = activeGamepadIndex(); + if (activeGamepad < 0 || activeGamepad >= pads.count()) { + return nullptr; + } + return pads[activeGamepad]; +} + void InputDriver::setActiveKeySource(int) { } diff --git a/src/platform/qt/input/InputDriver.h b/src/platform/qt/input/InputDriver.h index f344b271b..cc0a6e0cd 100644 --- a/src/platform/qt/input/InputDriver.h +++ b/src/platform/qt/input/InputDriver.h @@ -44,8 +44,11 @@ public: virtual QList connectedKeySources() const; virtual QList connectedGamepads() const; - virtual int activeKeySource() const; - virtual int activeGamepad() const; + virtual int activeKeySourceIndex() const; + virtual int activeGamepadIndex() const; + + KeySource* activeKeySource(); + Gamepad* activeGamepad(); virtual void setActiveKeySource(int); virtual void setActiveGamepad(int); diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp index 2eda6a4f0..ca9e30b25 100644 --- a/src/platform/qt/input/SDLInputDriver.cpp +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -173,7 +173,7 @@ void SDLInputDriver::updateGamepads() { } #endif -int SDLInputDriver::activeGamepad() const { +int SDLInputDriver::activeGamepadIndex() const { return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0; } diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h index 79deac22c..09f4b9555 100644 --- a/src/platform/qt/input/SDLInputDriver.h +++ b/src/platform/qt/input/SDLInputDriver.h @@ -46,7 +46,7 @@ public: QList connectedGamepads() const override; - int activeGamepad() const override; + int activeGamepadIndex() const override; void setActiveGamepad(int) override; void registerTiltAxisX(int axis) override; From 53abcfd28e79d6316227b187c197673dc0fe40d5 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 23 Jan 2023 21:03:50 -0800 Subject: [PATCH 114/159] Scripting: Fix mSCRIPT_TYPE_MS_PCS macro --- include/mgba/script/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 470ed40ce..3099540dc 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -97,7 +97,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_MS_CS(STRUCT) (&mSTStructConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_S_METHOD(STRUCT, NAME) (&_mSTStructBindingType_ ## STRUCT ## _ ## NAME) #define mSCRIPT_TYPE_MS_PS(STRUCT) (&mSTStructPtr_ ## STRUCT) -#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructConstPtr_ ## STRUCT) +#define mSCRIPT_TYPE_MS_PCS(STRUCT) (&mSTStructPtrConst_ ## STRUCT) #define mSCRIPT_TYPE_MS_WSTR (&mSTStringWrapper) #define mSCRIPT_TYPE_MS_WLIST (&mSTListWrapper) #define mSCRIPT_TYPE_MS_W(TYPE) (&mSTWrapper_ ## TYPE) From 0da94526bcaf11f8502b364b5d9feb50b279edff Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Jan 2023 04:10:36 -0800 Subject: [PATCH 115/159] Scripting: Add some nullity tests --- src/script/test/lua.c | 20 +++++++++++++++++ src/script/test/types.c | 49 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 73954e91e..ec478fc3c 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -371,6 +371,25 @@ M_TEST_DEFINE(callCFunc) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(globalStructNull) { + SETUP_LUA; + + struct Test s = {}; + + struct mScriptValue a; + + LOAD_PROGRAM("assert(a)"); + + a = mSCRIPT_MAKE_S(Test, &s); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_true(lua->run(lua)); + + a = mSCRIPT_MAKE_S(Test, NULL); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + + mScriptContextDeinit(&context); +} M_TEST_DEFINE(globalStructFieldGet) { SETUP_LUA; @@ -709,6 +728,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(rootScope), cmocka_unit_test(callLuaFunc), cmocka_unit_test(callCFunc), + cmocka_unit_test(globalStructNull), cmocka_unit_test(globalStructFieldGet), cmocka_unit_test(globalStructFieldSet), cmocka_unit_test(globalStructMethods), diff --git a/src/script/test/types.c b/src/script/test/types.c index 6ad38563a..ae1df5ba0 100644 --- a/src/script/test/types.c +++ b/src/script/test/types.c @@ -76,6 +76,14 @@ static int isSequential(struct mScriptList* list) { return true; } +static bool isNullCharp(const char* arg) { + return !arg; +} + +static bool isNullStruct(struct Test* arg) { + return !arg; +} + mSCRIPT_BIND_FUNCTION(boundVoidOne, S32, voidOne, 0); mSCRIPT_BIND_VOID_FUNCTION(boundDiscard, discard, 1, S32, ignored); mSCRIPT_BIND_FUNCTION(boundIdentityInt, S32, identityInt, 1, S32, in); @@ -86,6 +94,8 @@ mSCRIPT_BIND_FUNCTION(boundAddInts, S32, addInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundSubInts, S32, subInts, 2, S32, a, S32, b); mSCRIPT_BIND_FUNCTION(boundIsHello, S32, isHello, 1, CHARP, str); mSCRIPT_BIND_FUNCTION(boundIsSequential, S32, isSequential, 1, LIST, list); +mSCRIPT_BIND_FUNCTION(boundIsNullCharp, BOOL, isNullCharp, 1, CHARP, arg); +mSCRIPT_BIND_FUNCTION(boundIsNullStruct, BOOL, isNullStruct, 1, S(Test), arg); M_TEST_DEFINE(voidArgs) { struct mScriptFrame frame; @@ -1261,6 +1271,43 @@ M_TEST_DEFINE(invokeList) { mScriptListDeinit(&list); } +M_TEST_DEFINE(nullString) { + struct mScriptFrame frame; + bool res; + mScriptFrameInit(&frame); + + mSCRIPT_PUSH(&frame.arguments, CHARP, "hi"); + assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_false(res); + + mSCRIPT_PUSH(&frame.arguments, CHARP, NULL); + assert_true(mScriptInvoke(&boundIsNullCharp, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(res); + + mScriptFrameDeinit(&frame); +} + +M_TEST_DEFINE(nullStruct) { + struct mScriptFrame frame; + struct Test v = {}; + bool res; + mScriptFrameInit(&frame); + + mSCRIPT_PUSH(&frame.arguments, S(Test), &v); + assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_false(res); + + mSCRIPT_PUSH(&frame.arguments, S(Test), NULL); + assert_true(mScriptInvoke(&boundIsNullStruct, &frame)); + assert_true(mScriptPopBool(&frame.returnValues, &res)); + assert_true(res); + + mScriptFrameDeinit(&frame); +} + M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(voidArgs), cmocka_unit_test(voidFunc), @@ -1295,4 +1342,6 @@ M_TEST_SUITE_DEFINE(mScript, cmocka_unit_test(stringIsHello), cmocka_unit_test(stringIsNotHello), cmocka_unit_test(invokeList), + cmocka_unit_test(nullString), + cmocka_unit_test(nullStruct), ) From e2668c25c5095a5551b536e242d07873a0af6af8 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Jan 2023 04:17:38 -0800 Subject: [PATCH 116/159] Qt: Set default gamepad and sensor drivers --- src/platform/qt/Window.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index c07d82704..7e25159eb 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -176,6 +176,8 @@ Window::Window(CoreManager* manager, ConfigController* config, int playerId, QWi #ifdef BUILD_SDL m_inputController.addInputDriver(std::make_shared(&m_inputController)); + m_inputController.setGamepadDriver(SDL_BINDING_BUTTON); + m_inputController.setSensorDriver(SDL_BINDING_BUTTON); #endif m_shortcutController->setConfigController(m_config); From dfd772d2ca9b9199cb72f3b3db29648ce396039b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Jan 2023 04:19:12 -0800 Subject: [PATCH 117/159] Scripting: Add missing type export --- include/mgba/script/types.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 3099540dc..556fff57b 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -76,6 +76,7 @@ CXX_GUARD_START #define mSCRIPT_TYPE_FIELD_W(TYPE) opaque #define mSCRIPT_TYPE_FIELD_CW(TYPE) opaque +#define mSCRIPT_TYPE_MS_VOID (&mSTVoid) #define mSCRIPT_TYPE_MS_S8 (&mSTSInt8) #define mSCRIPT_TYPE_MS_U8 (&mSTUInt8) #define mSCRIPT_TYPE_MS_S16 (&mSTSInt16) From 17ef84804bd0e43319935104e2c7c85b3a459060 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Jan 2023 04:47:15 -0800 Subject: [PATCH 118/159] Scripting: Lua nullity fixes --- src/script/engines/lua.c | 18 +++++++++++++++++- src/script/test/lua.c | 14 +++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 37327ee54..3c7266a2a 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -738,7 +738,19 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v } break; case mSCRIPT_TYPE_STRING: - lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + if (!value->value.string) { + lua_pushnil(luaContext->lua); + break; + } + if (value->type == mSCRIPT_TYPE_MS_STR) { + lua_pushlstring(luaContext->lua, value->value.string->buffer, value->value.string->size); + break; + } + if (value->type == mSCRIPT_TYPE_MS_CHARP) { + lua_pushstring(luaContext->lua, value->value.copaque); + break; + } + ok = false; break; case mSCRIPT_TYPE_LIST: newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); @@ -769,6 +781,10 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v mScriptValueDeref(value); break; case mSCRIPT_TYPE_OBJECT: + if (!value->value.opaque) { + lua_pushnil(luaContext->lua); + break; + } newValue = lua_newuserdata(luaContext->lua, sizeof(*newValue)); if (needsWeakref) { *newValue = mSCRIPT_MAKE(WEAKREF, weakref); diff --git a/src/script/test/lua.c b/src/script/test/lua.c index ec478fc3c..2c047ec39 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -371,15 +371,23 @@ M_TEST_DEFINE(callCFunc) { mScriptContextDeinit(&context); } -M_TEST_DEFINE(globalStructNull) { +M_TEST_DEFINE(globalNull) { SETUP_LUA; struct Test s = {}; - + struct mScriptValue* val; struct mScriptValue a; LOAD_PROGRAM("assert(a)"); + a = mSCRIPT_MAKE_CHARP("hello"); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_true(lua->run(lua)); + + a = mSCRIPT_MAKE_CHARP(NULL); + assert_true(lua->setGlobal(lua, "a", &a)); + assert_false(lua->run(lua)); + a = mSCRIPT_MAKE_S(Test, &s); assert_true(lua->setGlobal(lua, "a", &a)); assert_true(lua->run(lua)); @@ -728,7 +736,7 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(rootScope), cmocka_unit_test(callLuaFunc), cmocka_unit_test(callCFunc), - cmocka_unit_test(globalStructNull), + cmocka_unit_test(globalNull), cmocka_unit_test(globalStructFieldGet), cmocka_unit_test(globalStructFieldSet), cmocka_unit_test(globalStructMethods), From 5216383c28332cbed2cfe962ce9c8be5daff8595 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Wed, 25 Jan 2023 21:52:23 -0800 Subject: [PATCH 119/159] Scripting: Fix scripting console --- src/core/scripting.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/core/scripting.c b/src/core/scripting.c index 05433b747..1eca1159b 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -761,14 +761,16 @@ mSCRIPT_DEFINE_STRUCT_BINDING_DEFAULTS(mScriptConsole, createBuffer) mSCRIPT_DEFINE_DEFAULTS_END; static struct mScriptConsole* _ensureConsole(struct mScriptContext* context) { - struct mScriptValue* value = mScriptContextEnsureGlobal(context, "console", mSCRIPT_TYPE_MS_S(mScriptConsole)); - struct mScriptConsole* console = value->value.opaque; - if (!console) { - console = calloc(1, sizeof(*console)); - value->value.opaque = console; - value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; - mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); + struct mScriptValue* value = mScriptContextGetGlobal(context, "console"); + if (value) { + return value->value.opaque; } + struct mScriptConsole* console = calloc(1, sizeof(*console)); + value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptConsole)); + value->value.opaque = console; + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + mScriptContextSetGlobal(context, "console", value); + mScriptContextSetDocstring(context, "console", "Singleton instance of struct::mScriptConsole"); return console; } From 58089fb334bfdbc6ce2e0ff77eeb321623460cf3 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 03:25:51 -0800 Subject: [PATCH 120/159] Scripting: Allow mScriptList members, better testing --- src/script/test/classes.c | 50 +++++++++++++++++++++++++++++++++++++++ src/script/types.c | 6 +++++ 2 files changed, 56 insertions(+) diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 476e24455..95bb32920 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -14,6 +14,8 @@ struct TestA { int32_t i2; int8_t b8; int16_t hUnaligned; + struct mScriptValue table; + struct mScriptList list; int32_t (*ifn0)(struct TestA*); int32_t (*ifn1)(struct TestA*, int); void (*vfn0)(struct TestA*); @@ -103,6 +105,8 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i2) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S8, b8) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, TABLE, table) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, LIST, list) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn1) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, icfn0) @@ -187,6 +191,20 @@ M_TEST_DEFINE(testALayout) { assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + member = HashTableLookup(&cls->instanceMembers, "table"); + assert_non_null(member); + assert_string_equal(member->name, "table"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_TABLE); + assert_int_equal(member->offset, &((struct TestA*) 0)->table); + + member = HashTableLookup(&cls->instanceMembers, "list"); + assert_non_null(member); + assert_string_equal(member->name, "list"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_LIST); + assert_int_equal(member->offset, &((struct TestA*) 0)->list); + member = HashTableLookup(&cls->instanceMembers, "unknown"); assert_null(member); @@ -280,6 +298,12 @@ M_TEST_DEFINE(testAGet) { .hUnaligned = 4 }; + mScriptListInit(&s.list, 1); + *mScriptListAppend(&s.list) = mSCRIPT_MAKE_S32(5); + + s.table.type = mSCRIPT_TYPE_MS_TABLE; + s.table.type->alloc(&s.table); + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); struct mScriptValue val; struct mScriptValue compare; @@ -300,8 +324,34 @@ M_TEST_DEFINE(testAGet) { assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); assert_true(compare.type->equal(&compare, &val)); + compare = mSCRIPT_MAKE_S32(5); + assert_true(mScriptObjectGet(&sval, "list", &val)); + assert_ptr_equal(val.type, mSCRIPT_TYPE_MS_LIST); + assert_int_equal(mScriptListSize(val.value.list), 1); + assert_true(compare.type->equal(&compare, mScriptListGetPointer(val.value.list, 0))); + + *mScriptListAppend(&s.list) = mSCRIPT_MAKE_S32(6); + compare = mSCRIPT_MAKE_S32(6); + assert_int_equal(mScriptListSize(val.value.list), 2); + assert_true(compare.type->equal(&compare, mScriptListGetPointer(val.value.list, 1))); + + struct mScriptValue* ival = &val; + assert_true(mScriptObjectGet(&sval, "table", &val)); + if (val.type->base == mSCRIPT_TYPE_WRAPPER) { + ival = mScriptValueUnwrap(&val); + } + assert_ptr_equal(ival->type, mSCRIPT_TYPE_MS_TABLE); + assert_int_equal(mScriptTableSize(ival), 0); + compare = mSCRIPT_MAKE_S32(7); + mScriptTableInsert(&s.table, &compare, &compare); + assert_int_equal(mScriptTableSize(&s.table), 1); + assert_int_equal(mScriptTableSize(ival), 1); + assert_false(mScriptObjectGet(&sval, "unknown", &val)); + mScriptListDeinit(&s.list); + mSCRIPT_TYPE_MS_TABLE->free(&s.table); + assert_true(cls->init); mScriptClassDeinit(cls); assert_false(cls->init); diff --git a/src/script/types.c b/src/script/types.c index b63fed4a5..dc9153587 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1178,6 +1178,12 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool val->type = mSCRIPT_TYPE_MS_WRAPPER; val->value.table = raw; break; + case mSCRIPT_TYPE_LIST: + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->type = mSCRIPT_TYPE_MS_LIST; + val->value.list = raw; + break; case mSCRIPT_TYPE_FUNCTION: val->refs = mSCRIPT_VALUE_UNREF; val->flags = 0; From b5f600c0c5bb411b9d31739b6725147932296e02 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 03:29:55 -0800 Subject: [PATCH 121/159] Scripting: Allow weakrefs to be marked for auto-collection --- include/mgba/script/context.h | 1 + src/script/CMakeLists.txt | 5 +- src/script/context.c | 16 +++++- src/script/test/context.c | 103 ++++++++++++++++++++++++++++++++++ 4 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 src/script/test/context.c diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 99eefe069..396084f14 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -89,6 +89,7 @@ uint32_t mScriptContextSetWeakref(struct mScriptContext*, struct mScriptValue* v struct mScriptValue* mScriptContextMakeWeakref(struct mScriptContext*, struct mScriptValue* value); struct mScriptValue* mScriptContextAccessWeakref(struct mScriptContext*, struct mScriptValue* value); void mScriptContextClearWeakref(struct mScriptContext*, uint32_t weakref); +void mScriptContextDisownWeakref(struct mScriptContext*, uint32_t weakref); void mScriptContextAttachStdlib(struct mScriptContext* context); void mScriptContextAttachSocket(struct mScriptContext* context); diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 5cfb4ff41..504736f2f 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -12,8 +12,9 @@ set(TEST_FILES if(USE_LUA) list(APPEND SOURCE_FILES engines/lua.c) list(APPEND TEST_FILES - test/stdlib.c - test/lua.c) + test/context.c + test/lua.c + test/stdlib.c) endif() source_group("Scripting" FILES ${SOURCE_FILES}) diff --git a/src/script/context.c b/src/script/context.c index fa1a0b4a2..14775b05c 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -71,8 +71,8 @@ void mScriptContextInit(struct mScriptContext* context) { void mScriptContextDeinit(struct mScriptContext* context) { HashTableDeinit(&context->rootScope); - HashTableDeinit(&context->weakrefs); mScriptContextDrainPool(context); + HashTableDeinit(&context->weakrefs); mScriptListDeinit(&context->refPool); HashTableDeinit(&context->callbacks); TableDeinit(&context->callbackId); @@ -102,9 +102,12 @@ void mScriptContextFillPool(struct mScriptContext* context, struct mScriptValue* void mScriptContextDrainPool(struct mScriptContext* context) { size_t i; for (i = 0; i < mScriptListSize(&context->refPool); ++i) { - struct mScriptValue* value = mScriptValueUnwrap(mScriptListGetPointer(&context->refPool, i)); - if (value) { + struct mScriptValue* value = mScriptListGetPointer(&context->refPool, i); + if (value->type->base == mSCRIPT_TYPE_WRAPPER) { + value = mScriptValueUnwrap(value); mScriptValueDeref(value); + } else if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { + mScriptContextClearWeakref(context, value->value.u32); } } mScriptListClear(&context->refPool); @@ -201,6 +204,13 @@ void mScriptContextClearWeakref(struct mScriptContext* context, uint32_t weakref TableRemove(&context->weakrefs, weakref); } +void mScriptContextDisownWeakref(struct mScriptContext* context, uint32_t weakref) { + struct mScriptValue* poolEntry = mScriptListAppend(&context->refPool); + poolEntry->type = mSCRIPT_TYPE_MS_WEAKREF; + poolEntry->value.u32 = weakref; + poolEntry->refs = mSCRIPT_VALUE_UNREF; +} + void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) { struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); if (!list) { diff --git a/src/script/test/context.c b/src/script/test/context.c new file mode 100644 index 000000000..178dc25a7 --- /dev/null +++ b/src/script/test/context.c @@ -0,0 +1,103 @@ +/* Copyright (c) 2013-2023 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 "util/test/suite.h" + +#include +#include +#include + +M_TEST_DEFINE(weakrefBasic) { + struct mScriptContext context; + mScriptContextInit(&context); + + struct mScriptValue weakref = mSCRIPT_VAL(WEAKREF, 1); + struct mScriptValue fakeVal = mSCRIPT_S32(0x7E57CA5E); + struct mScriptValue* val; + + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 1); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + assert_int_equal(mScriptContextSetWeakref(&context, &fakeVal), 1); + assert_int_equal(context.nextWeakref, 2); + assert_int_equal(TableSize(&context.weakrefs), 1); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextClearWeakref(&context, 1); + + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 2); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(drainPool) { + struct mScriptContext context; + mScriptContextInit(&context); + + assert_int_equal(mScriptListSize(&context.refPool), 0); + + struct mScriptValue fakeVal = mSCRIPT_CHARP("foo"); + fakeVal.refs = 2; + + mScriptContextFillPool(&context, &fakeVal); + assert_int_equal(mScriptListSize(&context.refPool), 1); + assert_int_equal(fakeVal.refs, 2); + + mScriptContextDrainPool(&context); + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(fakeVal.refs, 1); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(disownWeakref) { + struct mScriptContext context; + mScriptContextInit(&context); + + struct mScriptValue weakref = mSCRIPT_VAL(WEAKREF, 1); + struct mScriptValue fakeVal = mSCRIPT_S32(0x7E57CA5E); + struct mScriptValue* val; + + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_int_equal(context.nextWeakref, 1); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + assert_int_equal(mScriptContextSetWeakref(&context, &fakeVal), 1); + assert_int_equal(TableSize(&context.weakrefs), 1); + assert_int_equal(context.nextWeakref, 2); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextDisownWeakref(&context, 1); + assert_int_equal(mScriptListSize(&context.refPool), 1); + assert_int_equal(TableSize(&context.weakrefs), 1); + val = mScriptContextAccessWeakref(&context, &weakref); + assert_non_null(val); + assert_int_equal(val->value.u32, 0x7E57CA5E); + + mScriptContextDrainPool(&context); + assert_int_equal(mScriptListSize(&context.refPool), 0); + assert_int_equal(TableSize(&context.weakrefs), 0); + assert_null(TableLookup(&context.weakrefs, 1)); + assert_null(mScriptContextAccessWeakref(&context, &weakref)); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE(mScript, + cmocka_unit_test(weakrefBasic), + cmocka_unit_test(drainPool), + cmocka_unit_test(disownWeakref), +) From c4157e59fc960c170fa116f7d4a70d8731d10a13 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 04:05:23 -0800 Subject: [PATCH 122/159] Qt: Fix gamepad driver lookup --- src/platform/qt/InputController.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index e974c1aaa..16670d267 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -74,8 +74,8 @@ public: static const int32_t AXIS_THRESHOLD = 0x3000; void setGamepadDriver(uint32_t type); - const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_sensorDriver).get(); } - InputDriver* gamepadDriver() { return m_inputDrivers.value(m_sensorDriver).get(); } + const InputDriver* gamepadDriver() const { return m_inputDrivers.value(m_gamepadDriver).get(); } + InputDriver* gamepadDriver() { return m_inputDrivers.value(m_gamepadDriver).get(); } QStringList connectedGamepads(uint32_t type = 0) const; int gamepadIndex(uint32_t type = 0) const; From 8e671b083029f4afcc1d8927556b2619ee752c2b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 05:57:08 -0800 Subject: [PATCH 123/159] Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes #2794) --- CHANGES | 2 ++ src/platform/qt/DisplayGL.cpp | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 637fdf28f..ddbff820a 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,8 @@ Features: Emulation fixes: - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) +Other fixes: + - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs diff --git a/src/platform/qt/DisplayGL.cpp b/src/platform/qt/DisplayGL.cpp index 7a4f73858..469b7e315 100644 --- a/src/platform/qt/DisplayGL.cpp +++ b/src/platform/qt/DisplayGL.cpp @@ -513,10 +513,10 @@ void PainterGL::create() { #if defined(BUILD_GLES2) || defined(BUILD_GLES3) if (m_supportsShaders) { - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); gl2Backend = static_cast(malloc(sizeof(mGLES2Context))); mGLES2ContextCreate(gl2Backend); m_backend = &gl2Backend->d; + QOpenGLFunctions* fn = m_gl->functions(); fn->glGenTextures(m_bridgeTexes.size(), m_bridgeTexes.data()); for (auto tex : m_bridgeTexes) { m_freeTex.enqueue(tex); @@ -543,7 +543,7 @@ void PainterGL::create() { #if defined(BUILD_GLES2) || defined(BUILD_GLES3) mGLES2Context* gl2Backend = reinterpret_cast(painter->m_backend); if (painter->m_widget && painter->supportsShaders()) { - QOpenGLFunctions_Baseline* fn = painter->m_gl->versionFunctions(); + QOpenGLFunctions* fn = painter->m_gl->functions(); fn->glFinish(); painter->m_widget->setTex(painter->m_finalTex[painter->m_finalTexIdx]); painter->m_finalTexIdx ^= 1; @@ -589,7 +589,7 @@ void PainterGL::destroy() { } makeCurrent(); #if defined(BUILD_GLES2) || defined(BUILD_GLES3) - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + QOpenGLFunctions* fn = m_gl->functions(); if (m_shader.passes) { mGLES2ShaderFree(&m_shader); } @@ -680,7 +680,7 @@ void PainterGL::start() { if (glContextHasBug(OpenGLBug::GLTHREAD_BLOCKS_SWAP)) { // Suggested on Discord as a way to strongly hint that glthread should be disabled // See https://gitlab.freedesktop.org/mesa/mesa/-/issues/8035 - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + QOpenGLFunctions* fn = m_gl->functions(); fn->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); } #endif @@ -972,7 +972,7 @@ QOpenGLContext* PainterGL::shareContext() { } void PainterGL::updateFramebufferHandle() { - QOpenGLFunctions_Baseline* fn = m_gl->versionFunctions(); + QOpenGLFunctions* fn = m_gl->functions(); // TODO: Figure out why glFlush doesn't work here on Intel/Windows if (glContextHasBug(OpenGLBug::CROSS_THREAD_FLUSH)) { fn->glFinish(); From 1dedd1d7a7514c11ad461e3ef14bb31a6a8eee37 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 07:17:53 -0800 Subject: [PATCH 124/159] Qt: Include wayland QPA in AppImage (fixes #2796) --- CHANGES | 1 + src/platform/qt/CMakeLists.txt | 2 +- src/platform/qt/main.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index ddbff820a..4803ef370 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,7 @@ Other fixes: Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs + - Qt: Include wayland QPA in AppImage (fixes mgba.io/i/2796) 0.10.1: (2023-01-10) Emulation fixes: diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index 327309acf..7bd8e095b 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -426,7 +426,7 @@ if(QT_STATIC) list(APPEND QT_LIBRARIES "-framework AVFoundation" "-framework CoreMedia" "-framework SystemConfiguration" "-framework Security") set_target_properties(${QT}::Core PROPERTIES INTERFACE_LINK_LIBRARIES "${QTPCRE}") elseif(UNIX) - list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa) + list(APPEND QT_LIBRARIES ${QT}::FontDatabaseSupport ${QT}::XcbQpa ${QT}::QWaylandIntegrationPlugin) endif() endif() target_link_libraries(${BINARY_NAME}-qt ${PLATFORM_LIBRARY} ${BINARY_NAME} ${QT_LIBRARIES}) diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index b3d4bafc9..c53408e8e 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -42,6 +42,7 @@ Q_IMPORT_PLUGIN(AVFServicePlugin); #endif #elif defined(Q_OS_UNIX) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); +Q_IMPORT_PLUGIN(QWaylandIntegrationPlugin); #endif #endif From 0cfec878c8f706cdc9709cd581a0d7dbfa29cdee Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 15:32:00 -0800 Subject: [PATCH 125/159] Qt: mInputMapHat returns a mask, not a single key (fixes #2800) --- src/platform/qt/input/InputMapper.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/platform/qt/input/InputMapper.cpp b/src/platform/qt/input/InputMapper.cpp index 41a3bc8c3..c84316efd 100644 --- a/src/platform/qt/input/InputMapper.cpp +++ b/src/platform/qt/input/InputMapper.cpp @@ -66,10 +66,7 @@ int InputMapper::mapAxes(QList axes) const { int InputMapper::mapHats(QList hats) const { int platformKeys = 0; for (int i = 0; i < hats.count(); ++i) { - int platformKey = mInputMapHat(m_map, m_type, i, hats[i]); - if (platformKey >= 0) { - platformKeys |= 1 << platformKey; - } + platformKeys |= mInputMapHat(m_map, m_type, i, hats[i]); } return platformKeys; } From 6b63e42146bf8d29cc88fdc7a145dcf7f515626b Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 16:18:10 -0800 Subject: [PATCH 126/159] Qt: These return multiple keys --- src/platform/qt/InputController.cpp | 22 +++++++++++++++++++--- src/platform/qt/InputController.h | 8 +++++--- src/platform/qt/input/GamepadHatEvent.cpp | 4 ++-- src/platform/qt/input/GamepadHatEvent.h | 4 ++-- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index e6e741a56..f036d6f52 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -495,15 +495,15 @@ void InputController::testGamepad(uint32_t type) { for (auto& hat : activeHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this); - postPendingEvent(event->platformKey()); + postPendingEvents(event->platformKeys()); sendGamepadEvent(event); if (!event->isAccepted()) { - clearPendingEvent(event->platformKey()); + clearPendingEvents(event->platformKeys()); } } for (auto& hat : oldHats) { GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this); - clearPendingEvent(event->platformKey()); + clearPendingEvents(event->platformKeys()); sendGamepadEvent(event); } } @@ -529,6 +529,22 @@ void InputController::clearPendingEvent(int key) { m_pendingEvents.remove(key); } +void InputController::postPendingEvents(int keys) { + for (int i = 0; keys; ++i, keys >>= 1) { + if (keys & 1) { + m_pendingEvents.insert(i); + } + } +} + +void InputController::clearPendingEvents(int keys) { + for (int i = 0; keys; ++i, keys >>= 1) { + if (keys & 1) { + m_pendingEvents.remove(i); + } + } +} + bool InputController::hasPendingEvent(int key) const { return m_pendingEvents.contains(key); } diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index 16670d267..d74cd3148 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -128,9 +128,11 @@ private slots: void teardownCam(); private: - void postPendingEvent(int); - void clearPendingEvent(int); - bool hasPendingEvent(int) const; + void postPendingEvent(int key); + void clearPendingEvent(int key); + void postPendingEvents(int keys); + void clearPendingEvents(int keys); + bool hasPendingEvent(int key) const; void sendGamepadEvent(QEvent*); Gamepad* gamepad(uint32_t type); diff --git a/src/platform/qt/input/GamepadHatEvent.cpp b/src/platform/qt/input/GamepadHatEvent.cpp index 1ca0408b0..25033997f 100644 --- a/src/platform/qt/input/GamepadHatEvent.cpp +++ b/src/platform/qt/input/GamepadHatEvent.cpp @@ -16,11 +16,11 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di : QEvent(pressType) , m_hatId(hatId) , m_direction(direction) - , m_key(-1) + , m_keys(0) { ignore(); if (controller) { - m_key = mInputMapHat(controller->map(), type, hatId, direction); + m_keys = mInputMapHat(controller->map(), type, hatId, direction); } } diff --git a/src/platform/qt/input/GamepadHatEvent.h b/src/platform/qt/input/GamepadHatEvent.h index 2ba73088d..c6c49fe3c 100644 --- a/src/platform/qt/input/GamepadHatEvent.h +++ b/src/platform/qt/input/GamepadHatEvent.h @@ -25,7 +25,7 @@ public: int hatId() const { return m_hatId; } Direction direction() const { return m_direction; } - int platformKey() const { return m_key; } + int platformKeys() const { return m_keys; } static Type Down(); static Type Up(); @@ -36,7 +36,7 @@ private: int m_hatId; Direction m_direction; - int m_key; + int m_keys; }; } From 8545271e9ee275b9e733bbfa13195365b1fb82e1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 18:59:35 -0800 Subject: [PATCH 127/159] GBA Memory: Modernize constant names --- include/mgba/internal/gba/memory.h | 94 +-- include/mgba/internal/gba/serialize.h | 12 +- src/core/test/scripting.c | 2 +- src/gba/audio.c | 4 +- src/gba/bios.c | 240 ++++---- src/gba/cart/vfame.c | 2 +- src/gba/cheats.c | 44 +- src/gba/cheats/codebreaker.c | 4 +- src/gba/cheats/gameshark.c | 4 +- src/gba/cheats/parv3.c | 6 +- src/gba/core.c | 160 ++--- src/gba/dma.c | 14 +- src/gba/extra/audio-mixer.c | 2 +- src/gba/extra/proxy.c | 18 +- src/gba/gba.c | 80 +-- src/gba/hle-bios.c | 2 +- src/gba/hle-bios.make | 2 +- src/gba/io.c | 6 +- src/gba/memory.c | 814 +++++++++++++------------- src/gba/renderers/cache-set.c | 2 +- src/gba/savedata.c | 112 ++-- src/gba/serialize.c | 4 +- src/gba/sharkport.c | 18 +- src/gba/video.c | 20 +- src/platform/libretro/libretro.c | 36 +- src/platform/python/mgba/gba.py | 16 +- src/platform/qt/AssetTile.cpp | 4 +- src/platform/qt/IOViewer.cpp | 2 +- src/platform/qt/MapView.cpp | 2 +- src/platform/qt/ObjView.cpp | 2 +- src/platform/qt/SaveConverter.cpp | 36 +- 31 files changed, 882 insertions(+), 882 deletions(-) diff --git a/include/mgba/internal/gba/memory.h b/include/mgba/internal/gba/memory.h index 93d565a16..e455e1cc1 100644 --- a/include/mgba/internal/gba/memory.h +++ b/include/mgba/internal/gba/memory.h @@ -21,60 +21,60 @@ CXX_GUARD_START #include enum GBAMemoryRegion { - REGION_BIOS = 0x0, - REGION_WORKING_RAM = 0x2, - REGION_WORKING_IRAM = 0x3, - REGION_IO = 0x4, - REGION_PALETTE_RAM = 0x5, - REGION_VRAM = 0x6, - REGION_OAM = 0x7, - REGION_CART0 = 0x8, - REGION_CART0_EX = 0x9, - REGION_CART1 = 0xA, - REGION_CART1_EX = 0xB, - REGION_CART2 = 0xC, - REGION_CART2_EX = 0xD, - REGION_CART_SRAM = 0xE, - REGION_CART_SRAM_MIRROR = 0xF + GBA_REGION_BIOS = 0x0, + GBA_REGION_EWRAM = 0x2, + GBA_REGION_IWRAM = 0x3, + GBA_REGION_IO = 0x4, + GBA_REGION_PALETTE_RAM = 0x5, + GBA_REGION_VRAM = 0x6, + GBA_REGION_OAM = 0x7, + GBA_REGION_ROM0 = 0x8, + GBA_REGION_ROM0_EX = 0x9, + GBA_REGION_ROM1 = 0xA, + GBA_REGION_ROM1_EX = 0xB, + GBA_REGION_ROM2 = 0xC, + GBA_REGION_ROM2_EX = 0xD, + GBA_REGION_SRAM = 0xE, + GBA_REGION_SRAM_MIRROR = 0xF }; enum GBAMemoryBase { - BASE_BIOS = 0x00000000, - BASE_WORKING_RAM = 0x02000000, - BASE_WORKING_IRAM = 0x03000000, - BASE_IO = 0x04000000, - BASE_PALETTE_RAM = 0x05000000, - BASE_VRAM = 0x06000000, - BASE_OAM = 0x07000000, - BASE_CART0 = 0x08000000, - BASE_CART0_EX = 0x09000000, - BASE_CART1 = 0x0A000000, - BASE_CART1_EX = 0x0B000000, - BASE_CART2 = 0x0C000000, - BASE_CART2_EX = 0x0D000000, - BASE_CART_SRAM = 0x0E000000, - BASE_CART_SRAM_MIRROR = 0x0F000000 + GBA_BASE_BIOS = 0x00000000, + GBA_BASE_EWRAM = 0x02000000, + GBA_BASE_IWRAM = 0x03000000, + GBA_BASE_IO = 0x04000000, + GBA_BASE_PALETTE_RAM = 0x05000000, + GBA_BASE_VRAM = 0x06000000, + GBA_BASE_OAM = 0x07000000, + GBA_BASE_ROM0 = 0x08000000, + GBA_BASE_ROM0_EX = 0x09000000, + GBA_BASE_ROM1 = 0x0A000000, + GBA_BASE_ROM1_EX = 0x0B000000, + GBA_BASE_ROM2 = 0x0C000000, + GBA_BASE_ROM2_EX = 0x0D000000, + GBA_BASE_SRAM = 0x0E000000, + GBA_BASE_SRAM_MIRROR = 0x0F000000 }; enum { - SIZE_BIOS = 0x00004000, - SIZE_WORKING_RAM = 0x00040000, - SIZE_WORKING_IRAM = 0x00008000, - SIZE_IO = 0x00000400, - SIZE_PALETTE_RAM = 0x00000400, - SIZE_VRAM = 0x00018000, - SIZE_OAM = 0x00000400, - SIZE_CART0 = 0x02000000, - SIZE_CART1 = 0x02000000, - SIZE_CART2 = 0x02000000, - SIZE_CART_SRAM = 0x00008000, - SIZE_CART_SRAM512 = 0x00010000, - SIZE_CART_FLASH512 = 0x00010000, - SIZE_CART_FLASH1M = 0x00020000, - SIZE_CART_EEPROM = 0x00002000, - SIZE_CART_EEPROM512 = 0x00000200, + GBA_SIZE_BIOS = 0x00004000, + GBA_SIZE_EWRAM = 0x00040000, + GBA_SIZE_IWRAM = 0x00008000, + GBA_SIZE_IO = 0x00000400, + GBA_SIZE_PALETTE_RAM = 0x00000400, + GBA_SIZE_VRAM = 0x00018000, + GBA_SIZE_OAM = 0x00000400, + GBA_SIZE_ROM0 = 0x02000000, + GBA_SIZE_ROM1 = 0x02000000, + GBA_SIZE_ROM2 = 0x02000000, + GBA_SIZE_SRAM = 0x00008000, + GBA_SIZE_SRAM512 = 0x00010000, + GBA_SIZE_FLASH512 = 0x00010000, + GBA_SIZE_FLASH1M = 0x00020000, + GBA_SIZE_EEPROM = 0x00002000, + GBA_SIZE_EEPROM512 = 0x00000200, - SIZE_AGB_PRINT = 0x10000 + GBA_SIZE_AGB_PRINT = 0x10000 }; enum { diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index ec44dc8fd..25bd170d6 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -407,12 +407,12 @@ struct GBASerializedState { uint32_t reserved[12]; - uint16_t io[SIZE_IO >> 1]; - uint16_t pram[SIZE_PALETTE_RAM >> 1]; - uint16_t oam[SIZE_OAM >> 1]; - uint16_t vram[SIZE_VRAM >> 1]; - uint8_t iwram[SIZE_WORKING_IRAM]; - uint8_t wram[SIZE_WORKING_RAM]; + uint16_t io[GBA_SIZE_IO >> 1]; + uint16_t pram[GBA_SIZE_PALETTE_RAM >> 1]; + uint16_t oam[GBA_SIZE_OAM >> 1]; + uint16_t vram[GBA_SIZE_VRAM >> 1]; + uint8_t iwram[GBA_SIZE_IWRAM]; + uint8_t wram[GBA_SIZE_EWRAM]; }; static_assert(sizeof(struct GBASerializedState) == 0x61000, "GBA savestate struct sized wrong"); diff --git a/src/core/test/scripting.c b/src/core/test/scripting.c index 2e26b096f..b9cfa03e8 100644 --- a/src/core/test/scripting.c +++ b/src/core/test/scripting.c @@ -17,7 +17,7 @@ #ifdef M_CORE_GBA #include #define TEST_PLATFORM mPLATFORM_GBA -#define RAM_BASE BASE_WORKING_IRAM +#define RAM_BASE GBA_BASE_IWRAM #elif defined(M_CORE_GB) #include #define TEST_PLATFORM mPLATFORM_GB diff --git a/src/gba/audio.c b/src/gba/audio.c index 5d7dd347b..f957ef191 100644 --- a/src/gba/audio.c +++ b/src/gba/audio.c @@ -118,10 +118,10 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA* info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); info->reg = GBADMARegisterSetWidth(info->reg, 1); switch (info->dest) { - case BASE_IO | REG_FIFO_A_LO: + case GBA_BASE_IO | REG_FIFO_A_LO: audio->chA.dmaSource = number; break; - case BASE_IO | REG_FIFO_B_LO: + case GBA_BASE_IO | REG_FIFO_B_LO: audio->chB.dmaSource = number; break; default: diff --git a/src/gba/bios.c b/src/gba/bios.c index 1d0c0dd10..ad610a287 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -49,11 +49,11 @@ static void _SoftReset(struct GBA* gba) { cpu->gprs[ARM_LR] = 0; cpu->gprs[ARM_SP] = GBA_SP_BASE_SYSTEM; int8_t flag = ((int8_t*) gba->memory.iwram)[0x7FFA]; - memset(((int8_t*) gba->memory.iwram) + SIZE_WORKING_IRAM - 0x200, 0, 0x200); + memset(((int8_t*) gba->memory.iwram) + GBA_SIZE_IWRAM - 0x200, 0, 0x200); if (flag) { - cpu->gprs[ARM_PC] = BASE_WORKING_RAM; + cpu->gprs[ARM_PC] = GBA_BASE_EWRAM; } else { - cpu->gprs[ARM_PC] = BASE_CART0; + cpu->gprs[ARM_PC] = GBA_BASE_ROM0; } _ARMSetMode(cpu, MODE_ARM); ARMWritePC(cpu); @@ -62,120 +62,120 @@ static void _SoftReset(struct GBA* gba) { static void _RegisterRamReset(struct GBA* gba) { uint32_t registers = gba->cpu->gprs[0]; struct ARMCore* cpu = gba->cpu; - cpu->memory.store16(cpu, BASE_IO | REG_DISPCNT, 0x0080, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DISPCNT, 0x0080, 0); if (registers & 0x01) { - memset(gba->memory.wram, 0, SIZE_WORKING_RAM); + memset(gba->memory.wram, 0, GBA_SIZE_EWRAM); } if (registers & 0x02) { - memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM - 0x200); + memset(gba->memory.iwram, 0, GBA_SIZE_IWRAM - 0x200); } if (registers & 0x04) { - memset(gba->video.palette, 0, SIZE_PALETTE_RAM); + memset(gba->video.palette, 0, GBA_SIZE_PALETTE_RAM); } if (registers & 0x08) { - memset(gba->video.vram, 0, SIZE_VRAM); + memset(gba->video.vram, 0, GBA_SIZE_VRAM); } if (registers & 0x10) { - memset(gba->video.oam.raw, 0, SIZE_OAM); + memset(gba->video.oam.raw, 0, GBA_SIZE_OAM); } if (registers & 0x20) { - cpu->memory.store16(cpu, BASE_IO | REG_SIOCNT, 0x0000, 0); - cpu->memory.store16(cpu, BASE_IO | REG_RCNT, RCNT_INITIAL, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SIOMLT_SEND, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_JOYCNT, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_JOY_RECV_LO, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_JOY_TRANS_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SIOCNT, 0x0000, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_RCNT, RCNT_INITIAL, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SIOMLT_SEND, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_JOYCNT, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_JOY_RECV_LO, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_JOY_TRANS_LO, 0, 0); } if (registers & 0x40) { - cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND1CNT_X, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND2CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND3CNT_X, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUND4CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDCNT_X, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_SOUNDBIAS, 0x200, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND1CNT_X, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND2CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND2CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND3CNT_X, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND4CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUND4CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDCNT_X, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_SOUNDBIAS, 0x200, 0); memset(gba->audio.psg.ch3.wavedata32, 0, sizeof(gba->audio.psg.ch3.wavedata32)); } if (registers & 0x80) { - cpu->memory.store16(cpu, BASE_IO | REG_DISPSTAT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_VCOUNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG0CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG1CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3CNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG0HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG0VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG1HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG1VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3HOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3VOFS, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PA, 0x100, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PB, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PC, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG2PD, 0x100, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG2X_LO, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG2Y_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PA, 0x100, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PB, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PC, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BG3PD, 0x100, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG3X_LO, 0, 0); - cpu->memory.store32(cpu, BASE_IO | REG_BG3Y_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN0H, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN1H, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN0V, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WIN1V, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WININ, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WINOUT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_MOSAIC, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BLDCNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BLDALPHA, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_BLDY, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA0CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA1CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA2CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3SAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3DAD_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_DMA3CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM0CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM1CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM2CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_LO, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_TM3CNT_HI, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_IE, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_IF, 0xFFFF, 0); - cpu->memory.store16(cpu, BASE_IO | REG_WAITCNT, 0, 0); - cpu->memory.store16(cpu, BASE_IO | REG_IME, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DISPSTAT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_VCOUNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3CNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG0VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG1VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3HOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3VOFS, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PA, 0x100, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PB, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PC, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG2PD, 0x100, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG2X_LO, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG2Y_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PA, 0x100, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PB, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PC, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BG3PD, 0x100, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG3X_LO, 0, 0); + cpu->memory.store32(cpu, GBA_BASE_IO | REG_BG3Y_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN0H, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN1H, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN0V, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WIN1V, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WININ, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WINOUT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_MOSAIC, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDCNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDALPHA, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_BLDY, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA0CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA1CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA2CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3SAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3SAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3DAD_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3DAD_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_DMA3CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM0CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM0CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM1CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM1CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM2CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM2CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM3CNT_LO, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_TM3CNT_HI, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_IE, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_IF, 0xFFFF, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_WAITCNT, 0, 0); + cpu->memory.store16(cpu, GBA_BASE_IO | REG_IME, 0, 0); } if (registers & 0x9C) { gba->video.renderer->reset(gba->video.renderer); @@ -267,7 +267,7 @@ static void _MidiKey2Freq(struct GBA* gba) { struct ARMCore* cpu = gba->cpu; int oldRegion = gba->memory.activeRegion; - gba->memory.activeRegion = REGION_BIOS; + gba->memory.activeRegion = GBA_REGION_BIOS; uint32_t key = cpu->memory.load32(cpu, cpu->gprs[0] + 4, 0); gba->memory.activeRegion = oldRegion; @@ -486,7 +486,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { break; case GBA_SWI_CPU_SET: case GBA_SWI_CPU_FAST_SET: - if (cpu->gprs[0] >> BASE_OFFSET < REGION_WORKING_RAM) { + if (cpu->gprs[0] >> BASE_OFFSET < GBA_REGION_EWRAM) { mLOG(GBA_BIOS, GAME_ERROR, "Cannot CpuSet from BIOS"); break; } @@ -501,7 +501,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { case GBA_SWI_GET_BIOS_CHECKSUM: cpu->gprs[0] = GBA_BIOS_CHECKSUM; cpu->gprs[1] = 1; - cpu->gprs[3] = SIZE_BIOS; + cpu->gprs[3] = GBA_SIZE_BIOS; break; case GBA_SWI_BG_AFFINE_SET: _BgAffineSet(gba); @@ -510,7 +510,7 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { _ObjAffineSet(gba); break; case GBA_SWI_BIT_UNPACK: - if (cpu->gprs[0] < BASE_WORKING_RAM) { + if (cpu->gprs[0] < GBA_BASE_EWRAM) { mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack source"); break; } @@ -518,9 +518,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unBitPack(gba); break; } @@ -535,9 +535,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad LZ77 destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: useStall = true; _unLz77(gba, immediate == GBA_SWI_LZ77_UNCOMP_WRAM ? 1 : 2); break; @@ -552,9 +552,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad Huffman destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unHuffman(gba); break; } @@ -569,9 +569,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad RL destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unRl(gba, immediate == GBA_SWI_RL_UNCOMP_WRAM ? 1 : 2); break; } @@ -587,9 +587,9 @@ void GBASwi16(struct ARMCore* cpu, int immediate) { default: mLOG(GBA_BIOS, GAME_ERROR, "Bad UnFilter destination"); // Fall through - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_VRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_VRAM: _unFilter(gba, immediate == GBA_SWI_DIFF_16BIT_UNFILTER ? 2 : 1, immediate == GBA_SWI_DIFF_8BIT_UNFILTER_WRAM ? 1 : 2); break; } diff --git a/src/gba/cart/vfame.c b/src/gba/cart/vfame.c index 57b6d903b..aedcf5d3e 100644 --- a/src/gba/cart/vfame.c +++ b/src/gba/cart/vfame.c @@ -246,7 +246,7 @@ void GBAVFameSramWrite(struct GBAVFameCart* cart, uint32_t address, uint8_t valu // if mode has been set - the address and value of the SRAM write will be modified address = _modifySramAddress(cart->cartType, address, cart->sramMode); value = _modifySramValue(cart->cartType, value, cart->sramMode); - address &= (SIZE_CART_SRAM - 1); + address &= (GBA_SIZE_SRAM - 1); sramData[address] = value; } diff --git a/src/gba/cheats.c b/src/gba/cheats.c index 04082d48a..d055194be 100644 --- a/src/gba/cheats.c +++ b/src/gba/cheats.c @@ -323,49 +323,49 @@ static void GBACheatDumpDirectives(struct mCheatSet* set, struct StringList* dir int GBACheatAddressIsReal(uint32_t address) { switch (address >> BASE_OFFSET) { - case REGION_BIOS: + case GBA_REGION_BIOS: return -0x80; break; - case REGION_WORKING_RAM: - if ((address & OFFSET_MASK) > SIZE_WORKING_RAM) { + case GBA_REGION_EWRAM: + if ((address & OFFSET_MASK) > GBA_SIZE_EWRAM) { return -0x40; } return 0x20; - case REGION_WORKING_IRAM: - if ((address & OFFSET_MASK) > SIZE_WORKING_IRAM) { + case GBA_REGION_IWRAM: + if ((address & OFFSET_MASK) > GBA_SIZE_IWRAM) { return -0x40; } return 0x20; - case REGION_IO: - if ((address & OFFSET_MASK) > SIZE_IO) { + case GBA_REGION_IO: + if ((address & OFFSET_MASK) > GBA_SIZE_IO) { return -0x80; } return 0x10; - case REGION_OAM: - if ((address & OFFSET_MASK) > SIZE_OAM) { + case GBA_REGION_OAM: + if ((address & OFFSET_MASK) > GBA_SIZE_OAM) { return -0x80; } return -0x8; - case REGION_VRAM: - if ((address & OFFSET_MASK) > SIZE_VRAM) { + case GBA_REGION_VRAM: + if ((address & OFFSET_MASK) > GBA_SIZE_VRAM) { return -0x80; } return -0x8; - case REGION_PALETTE_RAM: - if ((address & OFFSET_MASK) > SIZE_PALETTE_RAM) { + case GBA_REGION_PALETTE_RAM: + if ((address & OFFSET_MASK) > GBA_SIZE_PALETTE_RAM) { return -0x80; } return -0x8; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: return -0x8; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: - if ((address & OFFSET_MASK) > SIZE_CART_FLASH512) { + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: + if ((address & OFFSET_MASK) > GBA_SIZE_FLASH512) { return -0x80; } return -0x8; diff --git a/src/gba/cheats/codebreaker.c b/src/gba/cheats/codebreaker.c index 0ba5c80ea..611222425 100644 --- a/src/gba/cheats/codebreaker.c +++ b/src/gba/cheats/codebreaker.c @@ -215,7 +215,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o return false; } cheats->hook = malloc(sizeof(*cheats->hook)); - cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); + cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 1)); cheats->hook->mode = MODE_THUMB; cheats->hook->refs = 1; cheats->hook->reentries = 0; @@ -278,7 +278,7 @@ bool GBACheatAddCodeBreaker(struct GBACheatSet* cheats, uint32_t op1, uint16_t o cheat = mCheatListAppend(&cheats->d.list); cheat->type = CHEAT_IF_NAND; cheat->width = 2; - cheat->address = BASE_IO | REG_KEYINPUT; + cheat->address = GBA_BASE_IO | REG_KEYINPUT; cheat->operand = op2; cheat->repeat = 1; return true; diff --git a/src/gba/cheats/gameshark.c b/src/gba/cheats/gameshark.c index 88f48ab49..72071d962 100644 --- a/src/gba/cheats/gameshark.c +++ b/src/gba/cheats/gameshark.c @@ -150,7 +150,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t break; case GSA_PATCH: romPatch = mCheatPatchListAppend(&cheats->d.romPatches); - romPatch->address = BASE_CART0 | ((op1 & 0xFFFFFF) << 1); + romPatch->address = GBA_BASE_ROM0 | ((op1 & 0xFFFFFF) << 1); romPatch->value = op2; romPatch->applied = false; romPatch->width = 2; @@ -207,7 +207,7 @@ bool GBACheatAddGameSharkRaw(struct GBACheatSet* cheats, uint32_t op1, uint32_t return false; } cheats->hook = malloc(sizeof(*cheats->hook)); - cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 1)); + cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 1)); cheats->hook->mode = MODE_THUMB; cheats->hook->refs = 1; cheats->hook->reentries = 0; diff --git a/src/gba/cheats/parv3.c b/src/gba/cheats/parv3.c index 30cc5515b..e254ac94d 100644 --- a/src/gba/cheats/parv3.c +++ b/src/gba/cheats/parv3.c @@ -233,7 +233,7 @@ static bool _addPAR3Special(struct GBACheatSet* cheats, uint32_t op2) { } if (romPatch >= 0) { struct mCheatPatch* patch = mCheatPatchListAppend(&cheats->d.romPatches); - patch->address = BASE_CART0 | ((op2 & 0xFFFFFF) << 1); + patch->address = GBA_BASE_ROM0 | ((op2 & 0xFFFFFF) << 1); patch->applied = false; patch->check = false; patch->width = 2; @@ -282,7 +282,7 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin return false; } cheats->hook = malloc(sizeof(*cheats->hook)); - cheats->hook->address = BASE_CART0 | (op1 & (SIZE_CART0 - 2)); + cheats->hook->address = GBA_BASE_ROM0 | (op1 & (GBA_SIZE_ROM0 - 2)); cheats->hook->mode = MODE_THUMB; cheats->hook->refs = 1; cheats->hook->reentries = 0; @@ -320,7 +320,7 @@ bool GBACheatAddProActionReplayRaw(struct GBACheatSet* cheats, uint32_t op1, uin case PAR3_BASE_OTHER: width = ((op1 >> 24) & 1) + 1; cheat->type = CHEAT_ASSIGN; - cheat->address = BASE_IO | (op1 & OFFSET_MASK); + cheat->address = GBA_BASE_IO | (op1 & OFFSET_MASK); break; } if (op1 & 0x01000000 && (op1 & 0xFE000000) != 0xC6000000) { diff --git a/src/gba/core.c b/src/gba/core.c index a50c11175..c5f1946d6 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -57,76 +57,76 @@ static const struct mCoreChannelInfo _GBAAudioChannels[] = { static const struct mCoreMemoryBlock _GBAMemoryBlocks[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksSRAM[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM, "sram", "SRAM", "Static RAM (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_SRAM, SIZE_CART_SRAM, true }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM, "sram", "SRAM", "Static RAM (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_SRAM, GBA_SIZE_SRAM, true }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash512[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM, "sram", "Flash", "Flash Memory (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_FLASH512, GBA_SIZE_FLASH512, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksFlash1M[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM, "sram", "Flash", "Flash Memory (64kiB)", BASE_CART_SRAM, BASE_CART_SRAM + SIZE_CART_FLASH512, SIZE_CART_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM, "sram", "Flash", "Flash Memory (64kiB)", GBA_BASE_SRAM, GBA_BASE_SRAM + GBA_SIZE_FLASH512, GBA_SIZE_FLASH1M, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED, 1 }, }; static const struct mCoreMemoryBlock _GBAMemoryBlocksEEPROM[] = { { -1, "mem", "All", "All", 0, 0x10000000, 0x10000000, mCORE_MEMORY_VIRTUAL }, - { REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", BASE_BIOS, SIZE_BIOS, SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_RAM, "wram", "EWRAM", "Working RAM (256kiB)", BASE_WORKING_RAM, BASE_WORKING_RAM + SIZE_WORKING_RAM, SIZE_WORKING_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_WORKING_IRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", BASE_WORKING_IRAM, BASE_WORKING_IRAM + SIZE_WORKING_IRAM, SIZE_WORKING_IRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_IO, "io", "MMIO", "Memory-Mapped I/O", BASE_IO, BASE_IO + SIZE_IO, SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", BASE_PALETTE_RAM, BASE_PALETTE_RAM + SIZE_PALETTE_RAM, SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", BASE_VRAM, BASE_VRAM + SIZE_VRAM, SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", BASE_OAM, BASE_OAM + SIZE_OAM, SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, - { REGION_CART0, "cart0", "ROM", "Game Pak (32MiB)", BASE_CART0, BASE_CART0 + SIZE_CART0, SIZE_CART0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", BASE_CART1, BASE_CART1 + SIZE_CART1, SIZE_CART1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", BASE_CART2, BASE_CART2 + SIZE_CART2, SIZE_CART2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, - { REGION_CART_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, SIZE_CART_EEPROM, SIZE_CART_EEPROM, mCORE_MEMORY_RW }, + { GBA_REGION_BIOS, "bios", "BIOS", "BIOS (16kiB)", GBA_BASE_BIOS, GBA_SIZE_BIOS, GBA_SIZE_BIOS, mCORE_MEMORY_READ | mCORE_MEMORY_MAPPED }, + { GBA_REGION_EWRAM, "wram", "EWRAM", "Working RAM (256kiB)", GBA_BASE_EWRAM, GBA_BASE_EWRAM + GBA_SIZE_EWRAM, GBA_SIZE_EWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IWRAM, "iwram", "IWRAM", "Internal Working RAM (32kiB)", GBA_BASE_IWRAM, GBA_BASE_IWRAM + GBA_SIZE_IWRAM, GBA_SIZE_IWRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_IO, "io", "MMIO", "Memory-Mapped I/O", GBA_BASE_IO, GBA_BASE_IO + GBA_SIZE_IO, GBA_SIZE_IO, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_PALETTE_RAM, "palette", "Palette", "Palette RAM (1kiB)", GBA_BASE_PALETTE_RAM, GBA_BASE_PALETTE_RAM + GBA_SIZE_PALETTE_RAM, GBA_SIZE_PALETTE_RAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_VRAM, "vram", "VRAM", "Video RAM (96kiB)", GBA_BASE_VRAM, GBA_BASE_VRAM + GBA_SIZE_VRAM, GBA_SIZE_VRAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_OAM, "oam", "OAM", "OBJ Attribute Memory (1kiB)", GBA_BASE_OAM, GBA_BASE_OAM + GBA_SIZE_OAM, GBA_SIZE_OAM, mCORE_MEMORY_RW | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM0, "cart0", "ROM", "Game Pak (32MiB)", GBA_BASE_ROM0, GBA_BASE_ROM0 + GBA_SIZE_ROM0, GBA_SIZE_ROM0, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM1, "cart1", "ROM WS1", "Game Pak (Waitstate 1)", GBA_BASE_ROM1, GBA_BASE_ROM1 + GBA_SIZE_ROM1, GBA_SIZE_ROM1, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_ROM2, "cart2", "ROM WS2", "Game Pak (Waitstate 2)", GBA_BASE_ROM2, GBA_BASE_ROM2 + GBA_SIZE_ROM2, GBA_SIZE_ROM2, mCORE_MEMORY_READ | mCORE_MEMORY_WORM | mCORE_MEMORY_MAPPED }, + { GBA_REGION_SRAM_MIRROR, "eeprom", "EEPROM", "EEPROM (8kiB)", 0, GBA_SIZE_EEPROM, GBA_SIZE_EEPROM, mCORE_MEMORY_RW }, }; static const struct mCoreRegisterInfo _GBARegisters[] = { @@ -512,7 +512,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - if (GBAVerifyELFEntry(elf, BASE_CART0)) { + if (GBAVerifyELFEntry(elf, GBA_BASE_ROM0)) { GBALoadNull(core->board); } bool success = mCoreLoadELF(core, elf); @@ -907,36 +907,36 @@ void* _GBACoreGetMemoryBlock(struct mCore* core, size_t id, size_t* sizeOut) { switch (id) { default: return NULL; - case REGION_BIOS: - *sizeOut = SIZE_BIOS; + case GBA_REGION_BIOS: + *sizeOut = GBA_SIZE_BIOS; return gba->memory.bios; - case REGION_WORKING_RAM: - *sizeOut = SIZE_WORKING_RAM; + case GBA_REGION_EWRAM: + *sizeOut = GBA_SIZE_EWRAM; return gba->memory.wram; - case REGION_WORKING_IRAM: - *sizeOut = SIZE_WORKING_IRAM; + case GBA_REGION_IWRAM: + *sizeOut = GBA_SIZE_IWRAM; return gba->memory.iwram; - case REGION_PALETTE_RAM: - *sizeOut = SIZE_PALETTE_RAM; + case GBA_REGION_PALETTE_RAM: + *sizeOut = GBA_SIZE_PALETTE_RAM; return gba->video.palette; - case REGION_VRAM: - *sizeOut = SIZE_VRAM; + case GBA_REGION_VRAM: + *sizeOut = GBA_SIZE_VRAM; return gba->video.vram; - case REGION_OAM: - *sizeOut = SIZE_OAM; + case GBA_REGION_OAM: + *sizeOut = GBA_SIZE_OAM; return gba->video.oam.raw; - case REGION_CART0: - case REGION_CART1: - case REGION_CART2: + case GBA_REGION_ROM0: + case GBA_REGION_ROM1: + case GBA_REGION_ROM2: *sizeOut = gba->memory.romSize; return gba->memory.rom; - case REGION_CART_SRAM: + case GBA_REGION_SRAM: if (gba->memory.savedata.type == SAVEDATA_FLASH1M) { - *sizeOut = SIZE_CART_FLASH1M; + *sizeOut = GBA_SIZE_FLASH1M; return gba->memory.savedata.currentBank; } // Fall through - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM_MIRROR: *sizeOut = GBASavedataSize(&gba->memory.savedata); return gba->memory.savedata.data; } @@ -1153,7 +1153,7 @@ static bool _GBACoreLookupIdentifier(struct mCore* core, const char* name, int32 for (i = 0; i < REG_MAX; i += 2) { const char* reg = GBAIORegisterNames[i >> 1]; if (reg && strcasecmp(reg, name) == 0) { - *value = BASE_IO | i; + *value = GBA_BASE_IO | i; return true; } } @@ -1321,7 +1321,7 @@ static void _GBACoreStartVideoLog(struct mCore* core, struct mVideoLogContext* c struct GBASerializedState* state = mVideoLogContextInitialState(context, NULL); state->id = 0; - state->cpu.gprs[ARM_PC] = BASE_WORKING_RAM; + state->cpu.gprs[ARM_PC] = GBA_BASE_EWRAM; int channelId = mVideoLoggerAddChannel(context); gbacore->vlProxy.logger = malloc(sizeof(struct mVideoLogger)); @@ -1490,8 +1490,8 @@ static void _GBAVLPReset(struct mCore* core) { // 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); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IE, 0, NULL); } static bool _GBAVLPLoadROM(struct mCore* core, struct VFile* vf) { @@ -1510,13 +1510,13 @@ 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->gprs[ARM_PC] = GBA_BASE_EWRAM; 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); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IME, 0, NULL); + gba->cpu->memory.store16(gba->cpu, GBA_BASE_IO | REG_IE, 0, NULL); GBAVideoDeserialize(&gba->video, state); GBAIODeserialize(gba, state); GBAAudioReset(&gba->audio); diff --git a/src/gba/dma.c b/src/gba/dma.c index 60a8eb056..a5d94dd26 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -33,14 +33,14 @@ void GBADMAReset(struct GBA* gba) { gba->memory.activeDMA = -1; } static bool _isValidDMASAD(int dma, uint32_t address) { - if (dma == 0 && address >= BASE_CART0 && address < BASE_CART_SRAM) { + if (dma == 0 && address >= GBA_BASE_ROM0 && address < GBA_BASE_SRAM) { return false; } - return address >= BASE_WORKING_RAM; + return address >= GBA_BASE_EWRAM; } static bool _isValidDMADAD(int dma, uint32_t address) { - return dma == 3 || address < BASE_CART0; + return dma == 3 || address < GBA_BASE_ROM0; } uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) { @@ -274,14 +274,14 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { } cpu->memory.store32(cpu, dest, memory->dmaTransferRegister, 0); } else { - if (sourceRegion == REGION_CART2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { + if (sourceRegion == GBA_REGION_ROM2_EX && (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512)) { memory->dmaTransferRegister = GBASavedataReadEEPROM(&memory->savedata); memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } else if (source) { memory->dmaTransferRegister = cpu->memory.load16(cpu, source, 0); memory->dmaTransferRegister |= memory->dmaTransferRegister << 16; } - if (destRegion == REGION_CART2_EX) { + if (destRegion == GBA_REGION_ROM2_EX) { if (memory->savedata.type == SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); GBASavedataInitEEPROM(&memory->savedata); @@ -296,7 +296,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { gba->bus = memory->dmaTransferRegister; int sourceOffset; - if (info->nextSource >= BASE_CART0 && info->nextSource < BASE_CART_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) { + if (info->nextSource >= GBA_BASE_ROM0 && info->nextSource < GBA_BASE_SRAM && GBADMARegisterGetSrcControl(info->reg) < 3) { sourceOffset = width; } else { sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width; @@ -321,7 +321,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { if (!info->nextCount) { info->nextCount |= 0x80000000; - if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) { + if (sourceRegion < GBA_REGION_ROM0 || destRegion < GBA_REGION_ROM0) { info->when += 2; } } diff --git a/src/gba/extra/audio-mixer.c b/src/gba/extra/audio-mixer.c index 503fdc8ad..0bb9b5b35 100644 --- a/src/gba/extra/audio-mixer.c +++ b/src/gba/extra/audio-mixer.c @@ -245,7 +245,7 @@ static void _mp2kReload(struct GBAAudioMixer* mixer) { } bool _mp2kEngage(struct GBAAudioMixer* mixer, uint32_t address) { - if (address < BASE_WORKING_RAM) { + if (address < GBA_BASE_EWRAM) { return false; } if (address != mixer->contextAddress) { diff --git a/src/gba/extra/proxy.c b/src/gba/extra/proxy.c index a7f28d153..82bbd3ca9 100644 --- a/src/gba/extra/proxy.c +++ b/src/gba/extra/proxy.c @@ -63,9 +63,9 @@ void GBAVideoProxyRendererCreate(struct GBAVideoProxyRenderer* renderer, struct renderer->logger->parsePacket = _parsePacket; renderer->logger->handleEvent = _handleEvent; renderer->logger->vramBlock = _vramBlock; - renderer->logger->paletteSize = SIZE_PALETTE_RAM; - renderer->logger->vramSize = SIZE_VRAM; - renderer->logger->oamSize = SIZE_OAM; + renderer->logger->paletteSize = GBA_SIZE_PALETTE_RAM; + renderer->logger->vramSize = GBA_SIZE_VRAM; + renderer->logger->oamSize = GBA_SIZE_OAM; renderer->backend = backend; } @@ -82,9 +82,9 @@ 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->oam, &proxyRenderer->d.oam->raw, GBA_SIZE_OAM); + memcpy(proxyRenderer->logger->palette, proxyRenderer->d.palette, GBA_SIZE_PALETTE_RAM); + memcpy(proxyRenderer->logger->vram, proxyRenderer->d.vram, GBA_SIZE_VRAM); mVideoLoggerRendererReset(proxyRenderer->logger); } @@ -199,19 +199,19 @@ static bool _parsePacket(struct mVideoLogger* logger, const struct mVideoLoggerD proxyRenderer->backend->writeVideoRegister(proxyRenderer->backend, item->address, item->value); break; case DIRTY_PALETTE: - if (item->address < SIZE_PALETTE_RAM) { + if (item->address < GBA_SIZE_PALETTE_RAM) { STORE_16LE(item->value, item->address, logger->palette); proxyRenderer->backend->writePalette(proxyRenderer->backend, item->address, item->value); } break; case DIRTY_OAM: - if (item->address < SIZE_OAM) { + if (item->address < GBA_SIZE_OAM) { STORE_16LE(item->value, item->address << 1, logger->oam); proxyRenderer->backend->writeOAM(proxyRenderer->backend, item->address); } break; case DIRTY_VRAM: - if (item->address <= SIZE_VRAM - 0x1000) { + if (item->address <= GBA_SIZE_VRAM - 0x1000) { logger->readData(logger, &logger->vram[item->address >> 1], 0x1000, true); proxyRenderer->backend->writeVRAM(proxyRenderer->backend, item->address); } else { diff --git a/src/gba/gba.c b/src/gba/gba.c index b26ec85af..588a138f3 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -108,7 +108,7 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) { gba->keyCallback = NULL; mCoreCallbacksListInit(&gba->coreCallbacks, 0); - gba->biosChecksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); + gba->biosChecksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS); gba->idleOptimization = IDLE_LOOP_REMOVE; gba->idleLoop = IDLE_LOOP_NONE; @@ -137,7 +137,7 @@ void GBAUnloadROM(struct GBA* gba) { gba->yankedRomSize = 0; } #ifndef FIXED_ROM_BUFFER - mappedMemoryFree(gba->memory.rom, SIZE_CART0); + mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0); #endif } @@ -172,7 +172,7 @@ void GBADestroy(struct GBA* gba) { GBAUnloadMB(gba); if (gba->biosVf) { - gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); + gba->biosVf->unmap(gba->biosVf, gba->memory.bios, GBA_SIZE_BIOS); gba->biosVf->close(gba->biosVf); gba->biosVf = 0; } @@ -247,7 +247,7 @@ void GBAReset(struct ARMCore* cpu) { if (GBAIsMB(gba->mbVf) && !isELF) { gba->mbVf->seek(gba->mbVf, 0, SEEK_SET); - gba->mbVf->read(gba->mbVf, gba->memory.wram, SIZE_WORKING_RAM); + gba->mbVf->read(gba->mbVf, gba->memory.wram, GBA_SIZE_EWRAM); } gba->lastJump = 0; @@ -259,7 +259,7 @@ void GBAReset(struct ARMCore* cpu) { memset(gba->debugString, 0, sizeof(gba->debugString)); - if (gba->romVf && gba->romVf->size(gba->romVf) > SIZE_CART0) { + if (gba->romVf && gba->romVf->size(gba->romVf) > GBA_SIZE_ROM0) { char ident; gba->romVf->seek(gba->romVf, 0xAC, SEEK_SET); gba->romVf->read(gba->romVf, &ident, 1); @@ -274,11 +274,11 @@ void GBASkipBIOS(struct GBA* gba) { struct ARMCore* cpu = gba->cpu; if (cpu->gprs[ARM_PC] == BASE_RESET + WORD_SIZE_ARM) { if (gba->memory.rom) { - cpu->gprs[ARM_PC] = BASE_CART0; + cpu->gprs[ARM_PC] = GBA_BASE_ROM0; } else if (gba->memory.wram[0x30]) { - cpu->gprs[ARM_PC] = BASE_WORKING_RAM + 0xC0; + cpu->gprs[ARM_PC] = GBA_BASE_EWRAM + 0xC0; } else { - cpu->gprs[ARM_PC] = BASE_WORKING_RAM; + cpu->gprs[ARM_PC] = GBA_BASE_EWRAM; } gba->video.vcount = 0x7E; gba->memory.io[REG_VCOUNT >> 1] = 0x7E; @@ -360,14 +360,14 @@ bool GBALoadNull(struct GBA* gba) { gba->romVf = NULL; gba->pristineRomSize = 0; #ifndef FIXED_ROM_BUFFER - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); + gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0); #else gba->memory.rom = romBuffer; #endif gba->isPristine = false; gba->yankedRomSize = 0; - gba->memory.romSize = SIZE_CART0; - gba->memory.romMask = SIZE_CART0 - 1; + gba->memory.romSize = GBA_SIZE_ROM0; + gba->memory.romMask = GBA_SIZE_ROM0 - 1; gba->romCrc32 = 0; if (gba->cpu) { @@ -381,9 +381,9 @@ bool GBALoadMB(struct GBA* gba, struct VFile* vf) { GBAUnloadMB(gba); gba->mbVf = vf; vf->seek(vf, 0, SEEK_SET); - memset(gba->memory.wram, 0, SIZE_WORKING_RAM); - vf->read(vf, gba->memory.wram, SIZE_WORKING_RAM); - if (gba->cpu && gba->memory.activeRegion == REGION_WORKING_RAM) { + memset(gba->memory.wram, 0, GBA_SIZE_EWRAM); + vf->read(vf, gba->memory.wram, GBA_SIZE_EWRAM); + if (gba->cpu && gba->memory.activeRegion == GBA_REGION_IWRAM) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } return true; @@ -405,7 +405,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { gba->isPristine = true; gba->pristineRomSize = vf->size(vf); vf->seek(vf, 0, SEEK_SET); - if (gba->pristineRomSize > SIZE_CART0) { + if (gba->pristineRomSize > GBA_SIZE_ROM0) { char ident; vf->seek(vf, 0xAC, SEEK_SET); vf->read(vf, &ident, 1); @@ -415,13 +415,13 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { #ifdef FIXED_ROM_BUFFER gba->memory.rom = romBuffer; #else - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); + gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0); #endif } else { - gba->memory.rom = vf->map(vf, SIZE_CART0, MAP_READ); - gba->memory.romSize = SIZE_CART0; + gba->memory.rom = vf->map(vf, GBA_SIZE_ROM0, MAP_READ); + gba->memory.romSize = GBA_SIZE_ROM0; } - gba->pristineRomSize = SIZE_CART0; + gba->pristineRomSize = GBA_SIZE_ROM0; } else if (gba->pristineRomSize == 0x00100000) { // 1 MiB ROMs (e.g. Classic NES) all appear as 4x mirrored, but not more gba->isPristine = false; @@ -429,7 +429,7 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { #ifdef FIXED_ROM_BUFFER gba->memory.rom = romBuffer; #else - gba->memory.rom = anonymousMemoryMap(SIZE_CART0); + gba->memory.rom = anonymousMemoryMap(GBA_SIZE_ROM0); #endif vf->read(vf, gba->memory.rom, gba->pristineRomSize); memcpy(&gba->memory.rom[0x40000], gba->memory.rom, 0x00100000); @@ -450,15 +450,15 @@ bool GBALoadROM(struct GBA* gba, struct VFile* vf) { if (popcount32(gba->memory.romSize) != 1) { // This ROM is either a bad dump or homebrew. Emulate flash cart behavior. #ifndef FIXED_ROM_BUFFER - void* newRom = anonymousMemoryMap(SIZE_CART0); + void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0); memcpy(newRom, gba->memory.rom, gba->pristineRomSize); gba->memory.rom = newRom; #endif - gba->memory.romSize = SIZE_CART0; - gba->memory.romMask = SIZE_CART0 - 1; + gba->memory.romSize = GBA_SIZE_ROM0; + gba->memory.romMask = GBA_SIZE_ROM0 - 1; gba->isPristine = false; } - if (gba->cpu && gba->memory.activeRegion >= REGION_CART0) { + if (gba->cpu && gba->memory.activeRegion >= GBA_REGION_ROM0) { gba->cpu->memory.setActiveRegion(gba->cpu, gba->cpu->gprs[ARM_PC]); } GBAHardwareInit(&gba->memory.hw, &((uint16_t*) gba->memory.rom)[GPIO_REG_DATA >> 1]); @@ -485,23 +485,23 @@ void GBAYankROM(struct GBA* gba) { } void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { - if (vf->size(vf) != SIZE_BIOS) { + if (vf->size(vf) != GBA_SIZE_BIOS) { mLOG(GBA, WARN, "Incorrect BIOS size"); return; } - uint32_t* bios = vf->map(vf, SIZE_BIOS, MAP_READ); + uint32_t* bios = vf->map(vf, GBA_SIZE_BIOS, MAP_READ); if (!bios) { mLOG(GBA, WARN, "Couldn't map BIOS"); return; } if (gba->biosVf) { - gba->biosVf->unmap(gba->biosVf, gba->memory.bios, SIZE_BIOS); + gba->biosVf->unmap(gba->biosVf, gba->memory.bios, GBA_SIZE_BIOS); gba->biosVf->close(gba->biosVf); } gba->biosVf = vf; gba->memory.bios = bios; gba->memory.fullBios = 1; - uint32_t checksum = GBAChecksum(gba->memory.bios, SIZE_BIOS); + uint32_t checksum = GBAChecksum(gba->memory.bios, GBA_SIZE_BIOS); mLOG(GBA, DEBUG, "BIOS Checksum: 0x%X", checksum); if (checksum == GBA_BIOS_CHECKSUM) { mLOG(GBA, INFO, "Official GBA BIOS detected"); @@ -511,7 +511,7 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { mLOG(GBA, WARN, "BIOS checksum incorrect"); } gba->biosChecksum = checksum; - if (gba->memory.activeRegion == REGION_BIOS) { + if (gba->memory.activeRegion == GBA_REGION_BIOS) { gba->cpu->memory.activeRegion = gba->memory.bios; } // TODO: error check @@ -519,18 +519,18 @@ void GBALoadBIOS(struct GBA* gba, struct VFile* vf) { void GBAApplyPatch(struct GBA* gba, struct Patch* patch) { size_t patchedSize = patch->outputSize(patch, gba->memory.romSize); - if (!patchedSize || patchedSize > SIZE_CART0) { + if (!patchedSize || patchedSize > GBA_SIZE_ROM0) { return; } - void* newRom = anonymousMemoryMap(SIZE_CART0); + void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0); if (!patch->applyPatch(patch, gba->memory.rom, gba->pristineRomSize, newRom, patchedSize)) { - mappedMemoryFree(newRom, SIZE_CART0); + mappedMemoryFree(newRom, GBA_SIZE_ROM0); return; } if (gba->romVf) { #ifndef FIXED_ROM_BUFFER if (!gba->isPristine) { - mappedMemoryFree(gba->memory.rom, SIZE_CART0); + mappedMemoryFree(gba->memory.rom, GBA_SIZE_ROM0); } else { gba->romVf->unmap(gba->romVf, gba->memory.rom, gba->pristineRomSize); } @@ -666,7 +666,7 @@ bool GBAIsROM(struct VFile* vf) { uint32_t entry = ELFEntry(elf); bool isGBA = true; isGBA = isGBA && ELFMachine(elf) == EM_ARM; - isGBA = isGBA && (GBAVerifyELFEntry(elf, BASE_CART0) || GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0)); + isGBA = isGBA && (GBAVerifyELFEntry(elf, GBA_BASE_ROM0) || GBAVerifyELFEntry(elf, GBA_BASE_EWRAM + 0xC0)); ELFClose(elf); return isGBA; } @@ -722,12 +722,12 @@ bool GBAIsMB(struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - bool isMB = GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0); + bool isMB = GBAVerifyELFEntry(elf, GBA_BASE_EWRAM + 0xC0); ELFClose(elf); return isMB; } #endif - if (vf->size(vf) > SIZE_WORKING_RAM) { + if (vf->size(vf) > GBA_SIZE_EWRAM) { return false; } if (vf->seek(vf, GBA_MB_MAGIC_OFFSET, SEEK_SET) < 0) { @@ -764,10 +764,10 @@ bool GBAIsMB(struct VFile* vf) { } pc += 4; LOAD_32(opcode, 0, &signature); - if ((opcode & ~0x1FFFF) == BASE_WORKING_RAM) { + if ((opcode & ~0x1FFFF) == GBA_BASE_EWRAM) { ++wramAddrs; } - if ((opcode & ~0x1FFFF) == BASE_CART0) { + if ((opcode & ~0x1FFFF) == GBA_BASE_ROM0) { ++romAddrs; } ARMDecodeARM(opcode, &info); @@ -790,10 +790,10 @@ bool GBAIsMB(struct VFile* vf) { if (vf->seek(vf, pc, SEEK_SET) < 0) { break; } - if ((immediate & ~0x1FFFF) == BASE_WORKING_RAM) { + if ((immediate & ~0x1FFFF) == GBA_BASE_EWRAM) { ++wramLoads; } - if ((immediate & ~0x1FFFF) == BASE_CART0) { + if ((immediate & ~0x1FFFF) == GBA_BASE_ROM0) { ++romLoads; } } diff --git a/src/gba/hle-bios.c b/src/gba/hle-bios.c index b68d063a3..84737afd4 100644 --- a/src/gba/hle-bios.c +++ b/src/gba/hle-bios.c @@ -2,7 +2,7 @@ #include -const uint8_t hleBios[SIZE_BIOS] = { +const uint8_t hleBios[GBA_SIZE_BIOS] = { 0xd3, 0x00, 0x00, 0xea, 0x66, 0x00, 0x00, 0xea, 0x0c, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe1, 0x59, 0x00, 0x00, 0xea, 0xfe, 0xff, 0xff, 0xea, 0x00, 0x00, 0x00, 0x00, diff --git a/src/gba/hle-bios.make b/src/gba/hle-bios.make index b890b43ef..5ac7f1e9b 100644 --- a/src/gba/hle-bios.make +++ b/src/gba/hle-bios.make @@ -15,4 +15,4 @@ hle-bios.c: hle-bios.bin echo >> $@ echo '#include ' >> $@ echo >> $@ - xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@ + xxd -i $< | sed -e 's/unsigned char hle_bios_bin\[\]/const uint8_t hleBios[GBA_SIZE_BIOS]/' -e 's/^ \+/\t/' | grep -v hle_bios_bin_len >> $@ diff --git a/src/gba/io.c b/src/gba/io.c index ca0cbe5b5..4e3763536 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -625,18 +625,18 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { return; } if (address == REG_POSTFLG) { - gba->memory.io[(address & (SIZE_IO - 1)) >> 1] = value; + gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1] = value; return; } if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { gba->debugString[address - REG_DEBUG_STRING] = value; return; } - if (address > SIZE_IO) { + if (address > GBA_SIZE_IO) { return; } uint16_t value16 = value << (8 * (address & 1)); - value16 |= (gba->memory.io[(address & (SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); + value16 |= (gba->memory.io[(address & (GBA_SIZE_IO - 1)) >> 1]) & ~(0xFF << (8 * (address & 1))); GBAIOWrite(gba, address & 0xFFFFFFFE, value16); } diff --git a/src/gba/memory.c b/src/gba/memory.c index ba862afb9..4f3063cfb 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -89,8 +89,8 @@ void GBAMemoryInit(struct GBA* gba) { gba->memory.agbPrintBuffer = NULL; gba->memory.agbPrintBufferBackup = NULL; - gba->memory.wram = anonymousMemoryMap(SIZE_WORKING_RAM + SIZE_WORKING_IRAM); - gba->memory.iwram = &gba->memory.wram[SIZE_WORKING_RAM >> 2]; + gba->memory.wram = anonymousMemoryMap(GBA_SIZE_EWRAM + GBA_SIZE_IWRAM); + gba->memory.iwram = &gba->memory.wram[GBA_SIZE_EWRAM >> 2]; GBADMAInit(gba); GBAVFameInit(&gba->memory.vfame); @@ -101,15 +101,15 @@ void GBAMemoryInit(struct GBA* gba) { } void GBAMemoryDeinit(struct GBA* gba) { - mappedMemoryFree(gba->memory.wram, SIZE_WORKING_RAM + SIZE_WORKING_IRAM); + mappedMemoryFree(gba->memory.wram, GBA_SIZE_EWRAM + GBA_SIZE_IWRAM); if (gba->memory.rom) { mappedMemoryFree(gba->memory.rom, gba->memory.romSize); } if (gba->memory.agbPrintBuffer) { - mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBuffer, GBA_SIZE_AGB_PRINT); } if (gba->memory.agbPrintBufferBackup) { - mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBufferBackup, GBA_SIZE_AGB_PRINT); } GBACartEReaderDeinit(&gba->memory.ereader); @@ -117,11 +117,11 @@ void GBAMemoryDeinit(struct GBA* gba) { void GBAMemoryReset(struct GBA* gba) { if (gba->memory.wram && gba->memory.rom) { - memset(gba->memory.wram, 0, SIZE_WORKING_RAM); + memset(gba->memory.wram, 0, GBA_SIZE_EWRAM); } if (gba->memory.iwram) { - memset(gba->memory.iwram, 0, SIZE_WORKING_IRAM); + memset(gba->memory.iwram, 0, GBA_SIZE_IWRAM); } memset(gba->memory.io, 0, sizeof(gba->memory.io)); @@ -133,11 +133,11 @@ void GBAMemoryReset(struct GBA* gba) { gba->memory.agbPrintBase = 0; memset(&gba->memory.agbPrintCtx, 0, sizeof(gba->memory.agbPrintCtx)); if (gba->memory.agbPrintBuffer) { - mappedMemoryFree(gba->memory.agbPrintBuffer, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBuffer, GBA_SIZE_AGB_PRINT); gba->memory.agbPrintBuffer = NULL; } if (gba->memory.agbPrintBufferBackup) { - mappedMemoryFree(gba->memory.agbPrintBufferBackup, SIZE_AGB_PRINT); + mappedMemoryFree(gba->memory.agbPrintBufferBackup, GBA_SIZE_AGB_PRINT); gba->memory.agbPrintBufferBackup = NULL; } @@ -186,11 +186,11 @@ static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t a } else { loadAddress += offset; } - if ((loadAddress >> BASE_OFFSET) == REGION_IO && !GBAIOIsReadConstant(loadAddress)) { + if ((loadAddress >> BASE_OFFSET) == GBA_REGION_IO && !GBAIOIsReadConstant(loadAddress)) { gba->idleDetectionStep = -1; return; } - if ((loadAddress >> BASE_OFFSET) < REGION_CART0 || (loadAddress >> BASE_OFFSET) > REGION_CART2_EX) { + if ((loadAddress >> BASE_OFFSET) < GBA_REGION_ROM0 || (loadAddress >> BASE_OFFSET) > GBA_REGION_ROM2_EX) { gba->taintedRegisters[info.op1.reg] = true; } else { switch (info.memory.width) { @@ -232,7 +232,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { struct GBAMemory* memory = &gba->memory; int newRegion = address >> BASE_OFFSET; - if (gba->idleOptimization >= IDLE_LOOP_REMOVE && memory->activeRegion != REGION_BIOS) { + if (gba->idleOptimization >= IDLE_LOOP_REMOVE && memory->activeRegion != GBA_REGION_BIOS) { if (address == gba->idleLoop) { if (gba->haltPending) { gba->haltPending = false; @@ -273,33 +273,33 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } else { cpu->memory.activeMask &= -WORD_SIZE_ARM; } - if (newRegion < REGION_CART0 || (address & (SIZE_CART0 - 1)) < memory->romSize) { + if (newRegion < GBA_REGION_ROM0 || (address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { return; } } - if (memory->activeRegion == REGION_BIOS) { + if (memory->activeRegion == GBA_REGION_BIOS) { memory->biosPrefetch = cpu->prefetch[1]; } memory->activeRegion = newRegion; switch (newRegion) { - case REGION_BIOS: + case GBA_REGION_BIOS: cpu->memory.activeRegion = memory->bios; - cpu->memory.activeMask = SIZE_BIOS - 1; + cpu->memory.activeMask = GBA_SIZE_BIOS - 1; break; - case REGION_WORKING_RAM: + case GBA_REGION_EWRAM: cpu->memory.activeRegion = memory->wram; - cpu->memory.activeMask = SIZE_WORKING_RAM - 1; + cpu->memory.activeMask = GBA_SIZE_EWRAM - 1; break; - case REGION_WORKING_IRAM: + case GBA_REGION_IWRAM: cpu->memory.activeRegion = memory->iwram; - cpu->memory.activeMask = SIZE_WORKING_IRAM - 1; + cpu->memory.activeMask = GBA_SIZE_IWRAM - 1; break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: cpu->memory.activeRegion = (uint32_t*) gba->video.palette; - cpu->memory.activeMask = SIZE_PALETTE_RAM - 1; + cpu->memory.activeMask = GBA_SIZE_PALETTE_RAM - 1; break; - case REGION_VRAM: + case GBA_REGION_VRAM: if (address & 0x10000) { cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000]; cpu->memory.activeMask = 0x00007FFF; @@ -308,19 +308,19 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { cpu->memory.activeMask = 0x0000FFFF; } break; - case REGION_OAM: + case GBA_REGION_OAM: cpu->memory.activeRegion = (uint32_t*) gba->video.oam.raw; - cpu->memory.activeMask = SIZE_OAM - 1; + cpu->memory.activeMask = GBA_SIZE_OAM - 1; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: cpu->memory.activeRegion = memory->rom; cpu->memory.activeMask = memory->romMask; - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { break; } if ((address & 0x00FFFFFE) == AGB_PRINT_FLUSH_ADDR && memory->agbPrintProtect == 0x20) { @@ -362,8 +362,8 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { value = GBALoadBad(cpu); #define LOAD_BIOS \ - if (address < SIZE_BIOS) { \ - if (memory->activeRegion == REGION_BIOS) { \ + if (address < GBA_SIZE_BIOS) { \ + if (memory->activeRegion == GBA_REGION_BIOS) { \ LOAD_32(value, address & -4, memory->bios); \ } else { \ mLOG(GBA_MEM, GAME_ERROR, "Bad BIOS Load32: 0x%08X", address); \ @@ -374,20 +374,20 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { value = GBALoadBad(cpu); \ } -#define LOAD_WORKING_RAM \ - LOAD_32(value, address & (SIZE_WORKING_RAM - 4), memory->wram); \ - wait += waitstatesRegion[REGION_WORKING_RAM]; +#define LOAD_EWRAM \ + LOAD_32(value, address & (GBA_SIZE_EWRAM - 4), memory->wram); \ + wait += waitstatesRegion[GBA_REGION_EWRAM]; -#define LOAD_WORKING_IRAM LOAD_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); +#define LOAD_IWRAM LOAD_32(value, address & (GBA_SIZE_IWRAM - 4), memory->iwram); #define LOAD_IO value = GBAIORead(gba, address & OFFSET_MASK & ~3) | (GBAIORead(gba, (address & OFFSET_MASK & ~1) | 2) << 16); #define LOAD_PALETTE_RAM \ - LOAD_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ - wait += waitstatesRegion[REGION_PALETTE_RAM]; + LOAD_32(value, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); \ + wait += waitstatesRegion[GBA_REGION_PALETTE_RAM]; #define LOAD_VRAM \ - if ((address & 0x0001FFFF) >= SIZE_VRAM) { \ - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { \ + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load32: 0x%08X", address); \ value = 0; \ } else { \ @@ -401,12 +401,12 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { wait += GBAMemoryStallVRAM(gba, wait, 1); \ } -#define LOAD_OAM LOAD_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); +#define LOAD_OAM LOAD_32(value, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); #define LOAD_CART \ wait += waitstatesRegion[address >> BASE_OFFSET]; \ - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { \ - LOAD_32(value, address & (SIZE_CART0 - 4), memory->rom); \ + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { \ + LOAD_32(value, address & (GBA_SIZE_ROM0 - 4), memory->rom); \ } else if (memory->vfame.cartType) { \ value = GBAVFameGetPatternValue(address, 32); \ } else { \ @@ -431,13 +431,13 @@ uint32_t GBALoadBad(struct ARMCore* cpu) { if (cpu->executionMode == MODE_THUMB) { /* http://ngemu.com/threads/gba-open-bus.170809/ */ switch (cpu->gprs[ARM_PC] >> BASE_OFFSET) { - case REGION_BIOS: - case REGION_OAM: + case GBA_REGION_BIOS: + case GBA_REGION_OAM: /* This isn't right half the time, but we don't have $+6 handy */ value <<= 16; value |= cpu->prefetch[0]; break; - case REGION_WORKING_IRAM: + case GBA_REGION_IWRAM: /* This doesn't handle prefetch clobbering */ if (cpu->gprs[ARM_PC] & 2) { value <<= 16; @@ -462,37 +462,37 @@ uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { char* waitstatesRegion = memory->waitstatesNonseq32; switch (address >> BASE_OFFSET) { - case REGION_BIOS: + case GBA_REGION_BIOS: LOAD_BIOS; break; - case REGION_WORKING_RAM: - LOAD_WORKING_RAM; + case GBA_REGION_EWRAM: + LOAD_EWRAM; break; - case REGION_WORKING_IRAM: - LOAD_WORKING_IRAM; + case GBA_REGION_IWRAM: + LOAD_IWRAM; break; - case REGION_IO: + case GBA_REGION_IO: LOAD_IO; break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: LOAD_PALETTE_RAM; break; - case REGION_VRAM: + case GBA_REGION_VRAM: LOAD_VRAM; break; - case REGION_OAM: + case GBA_REGION_OAM: LOAD_OAM; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: LOAD_CART; break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: LOAD_SRAM; break; default: @@ -503,7 +503,7 @@ uint32_t GBALoad32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (cycleCounter) { wait += 2; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -520,9 +520,9 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { int wait = 0; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { - if (memory->activeRegion == REGION_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { + if (memory->activeRegion == GBA_REGION_BIOS) { LOAD_16(value, address & -2, memory->bios); } else { mLOG(GBA_MEM, GAME_ERROR, "Bad BIOS Load16: 0x%08X", address); @@ -533,22 +533,22 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (GBALoadBad(cpu) >> ((address & 2) * 8)) & 0xFFFF; } break; - case REGION_WORKING_RAM: - LOAD_16(value, address & (SIZE_WORKING_RAM - 2), memory->wram); - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + LOAD_16(value, address & (GBA_SIZE_EWRAM - 2), memory->wram); + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - LOAD_16(value, address & (SIZE_WORKING_IRAM - 2), memory->iwram); + case GBA_REGION_IWRAM: + LOAD_16(value, address & (GBA_SIZE_IWRAM - 2), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: value = GBAIORead(gba, address & (OFFSET_MASK - 1)); break; - case REGION_PALETTE_RAM: - LOAD_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + case GBA_REGION_PALETTE_RAM: + LOAD_16(value, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); break; - case REGION_VRAM: - if ((address & 0x0001FFFF) >= SIZE_VRAM) { - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load16: 0x%08X", address); value = 0; break; @@ -561,20 +561,20 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: - LOAD_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); + case GBA_REGION_OAM: + LOAD_16(value, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { - LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom); + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { + LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 16); - } else if ((address & (SIZE_CART0 - 1)) >= AGB_PRINT_BASE) { + } else if ((address & (GBA_SIZE_ROM0 - 1)) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; if (agbPrintAddr == AGB_PRINT_PROTECT) { value = memory->agbPrintProtect; @@ -589,14 +589,14 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (address >> 1) & 0xFFFF; } break; - case REGION_CART2_EX: + case GBA_REGION_ROM2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if (memory->savedata.type == SAVEDATA_EEPROM || memory->savedata.type == SAVEDATA_EEPROM512) { value = GBASavedataReadEEPROM(&memory->savedata); } else if ((address & 0x0DFC0000) >= 0x0DF80000 && memory->hw.devices & HW_EREADER) { value = GBACartEReaderRead(&memory->ereader, address); - } else if ((address & (SIZE_CART0 - 1)) < memory->romSize) { - LOAD_16(value, address & (SIZE_CART0 - 2), memory->rom); + } else if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { + LOAD_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 16); } else { @@ -604,8 +604,8 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (address >> 1) & 0xFFFF; } break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; value = GBALoad8(cpu, address, 0); value |= value << 8; @@ -618,7 +618,7 @@ uint32_t GBALoad16(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (cycleCounter) { wait += 2; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -635,9 +635,9 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { int wait = 0; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { - if (memory->activeRegion == REGION_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { + if (memory->activeRegion == GBA_REGION_BIOS) { value = ((uint8_t*) memory->bios)[address]; } else { mLOG(GBA_MEM, GAME_ERROR, "Bad BIOS Load8: 0x%08X", address); @@ -648,22 +648,22 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = (GBALoadBad(cpu) >> ((address & 3) * 8)) & 0xFF; } break; - case REGION_WORKING_RAM: - value = ((uint8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + value = ((uint8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)]; + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - value = ((uint8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; + case GBA_REGION_IWRAM: + value = ((uint8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)]; break; - case REGION_IO: + case GBA_REGION_IO: value = (GBAIORead(gba, address & 0xFFFE) >> ((address & 0x0001) << 3)) & 0xFF; break; - case REGION_PALETTE_RAM: - value = ((uint8_t*) gba->video.palette)[address & (SIZE_PALETTE_RAM - 1)]; + case GBA_REGION_PALETTE_RAM: + value = ((uint8_t*) gba->video.palette)[address & (GBA_SIZE_PALETTE_RAM - 1)]; break; - case REGION_VRAM: - if ((address & 0x0001FFFF) >= SIZE_VRAM) { - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Load8: 0x%08X", address); value = 0; break; @@ -676,18 +676,18 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: - value = ((uint8_t*) gba->video.oam.raw)[address & (SIZE_OAM - 1)]; + case GBA_REGION_OAM: + value = ((uint8_t*) gba->video.oam.raw)[address & (GBA_SIZE_OAM - 1)]; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; - if ((address & (SIZE_CART0 - 1)) < memory->romSize) { - value = ((uint8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; + if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { + value = ((uint8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)]; } else if (memory->vfame.cartType) { value = GBAVFameGetPatternValue(address, 8); } else { @@ -695,8 +695,8 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { value = ((address >> 1) >> ((address & 1) * 8)) & 0xFF; } break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: wait = memory->waitstatesNonseq16[address >> BASE_OFFSET]; if (memory->savedata.type == SAVEDATA_AUTODETECT) { mLOG(GBA_MEM, INFO, "Detected SRAM savegame"); @@ -708,13 +708,13 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (memory->hw.devices & HW_EREADER && (address & 0xE00FF80) >= 0xE00FF80) { value = GBACartEReaderReadFlash(&memory->ereader, address); } else if (memory->savedata.type == SAVEDATA_SRAM) { - value = memory->savedata.data[address & (SIZE_CART_SRAM - 1)]; + value = memory->savedata.data[address & (GBA_SIZE_SRAM - 1)]; } else if (memory->savedata.type == SAVEDATA_FLASH512 || memory->savedata.type == SAVEDATA_FLASH1M) { value = GBASavedataReadFlash(&memory->savedata, address); } else if (memory->hw.devices & HW_TILT) { value = GBAHardwareTiltRead(&memory->hw, address & OFFSET_MASK); } else if (memory->savedata.type == SAVEDATA_SRAM512) { - value = memory->savedata.data[address & (SIZE_CART_SRAM512 - 1)]; + value = memory->savedata.data[address & (GBA_SIZE_SRAM512 - 1)]; } else { mLOG(GBA_MEM, GAME_ERROR, "Reading from non-existent SRAM: 0x%08X", address); value = 0xFF; @@ -729,7 +729,7 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { if (cycleCounter) { wait += 2; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -737,28 +737,28 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { return value; } -#define STORE_WORKING_RAM \ - STORE_32(value, address & (SIZE_WORKING_RAM - 4), memory->wram); \ - wait += waitstatesRegion[REGION_WORKING_RAM]; +#define STORE_EWRAM \ + STORE_32(value, address & (GBA_SIZE_EWRAM - 4), memory->wram); \ + wait += waitstatesRegion[GBA_REGION_EWRAM]; -#define STORE_WORKING_IRAM \ - STORE_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); +#define STORE_IWRAM \ + STORE_32(value, address & (GBA_SIZE_IWRAM - 4), memory->iwram); #define STORE_IO \ GBAIOWrite32(gba, address & (OFFSET_MASK - 3), value); #define STORE_PALETTE_RAM \ - LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ + LOAD_32(oldValue, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); \ if (oldValue != value) { \ - STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); \ - gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); \ + STORE_32(value, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); \ + gba->video.renderer->writePalette(gba->video.renderer, (address & (GBA_SIZE_PALETTE_RAM - 4)) + 2, value >> 16); \ + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 4), value); \ } \ - wait += waitstatesRegion[REGION_PALETTE_RAM]; + wait += waitstatesRegion[GBA_REGION_PALETTE_RAM]; #define STORE_VRAM \ - if ((address & 0x0001FFFF) >= SIZE_VRAM) { \ - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { \ + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { \ mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store32: 0x%08X", address); \ } else { \ LOAD_32(oldValue, address & 0x00017FFC, gba->video.vram); \ @@ -782,11 +782,11 @@ uint32_t GBALoad8(struct ARMCore* cpu, uint32_t address, int* cycleCounter) { } #define STORE_OAM \ - LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); \ + LOAD_32(oldValue, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); \ if (oldValue != value) { \ - STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); \ - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); \ - gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) >> 1) + 1); \ + STORE_32(value, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); \ + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 4)) >> 1); \ + gba->video.renderer->writeOAM(gba->video.renderer, ((address & (GBA_SIZE_OAM - 4)) >> 1) + 1); \ } #define STORE_CART \ @@ -818,34 +818,34 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle char* waitstatesRegion = memory->waitstatesNonseq32; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - STORE_WORKING_RAM; + case GBA_REGION_EWRAM: + STORE_EWRAM; break; - case REGION_WORKING_IRAM: - STORE_WORKING_IRAM + case GBA_REGION_IWRAM: + STORE_IWRAM break; - case REGION_IO: + case GBA_REGION_IO: STORE_IO; break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: STORE_PALETTE_RAM; break; - case REGION_VRAM: + case GBA_REGION_VRAM: STORE_VRAM; break; - case REGION_OAM: + case GBA_REGION_OAM: STORE_OAM; break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: STORE_CART; break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: STORE_SRAM; break; default: @@ -855,7 +855,7 @@ void GBAStore32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -869,26 +869,26 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle int16_t oldValue; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - STORE_16(value, address & (SIZE_WORKING_RAM - 2), memory->wram); - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + STORE_16(value, address & (GBA_SIZE_EWRAM - 2), memory->wram); + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - STORE_16(value, address & (SIZE_WORKING_IRAM - 2), memory->iwram); + case GBA_REGION_IWRAM: + STORE_16(value, address & (GBA_SIZE_IWRAM - 2), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: GBAIOWrite(gba, address & (OFFSET_MASK - 1), value); break; - case REGION_PALETTE_RAM: - LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); + case GBA_REGION_PALETTE_RAM: + LOAD_16(oldValue, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); if (oldValue != value) { - STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + STORE_16(value, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 2), value); } break; - case REGION_VRAM: - if ((address & 0x0001FFFF) >= SIZE_VRAM) { - if ((address & (SIZE_VRAM | 0x00014000)) == SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) >= GBA_SIZE_VRAM) { + if ((address & (GBA_SIZE_VRAM | 0x00014000)) == GBA_SIZE_VRAM && (GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3)) { mLOG(GBA_MEM, GAME_ERROR, "Bad VRAM Store16: 0x%08X", address); break; } @@ -908,14 +908,14 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: - LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw); + case GBA_REGION_OAM: + LOAD_16(oldValue, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); if (value != oldValue) { - STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + STORE_16(value, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 2)) >> 1); } break; - case REGION_CART0: + case GBA_REGION_ROM0: if (IS_GPIO_REGISTER(address & 0xFFFFFE)) { if (memory->hw.devices == HW_NONE) { mLOG(GBA_HW, WARN, "Write to GPIO address %08X on cartridge without GPIO", address); @@ -930,22 +930,22 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle break; } // Fall through - case REGION_CART0_EX: + case GBA_REGION_ROM0_EX: if ((address & 0x00FFFFFF) >= AGB_PRINT_BASE) { uint32_t agbPrintAddr = address & 0x00FFFFFF; if (agbPrintAddr == AGB_PRINT_PROTECT) { memory->agbPrintProtect = value; if (!memory->agbPrintBuffer) { - memory->agbPrintBuffer = anonymousMemoryMap(SIZE_AGB_PRINT); - if (memory->romSize >= SIZE_CART0 / 2) { + memory->agbPrintBuffer = anonymousMemoryMap(GBA_SIZE_AGB_PRINT); + if (memory->romSize >= GBA_SIZE_ROM0 / 2) { int base = 0; - if (memory->romSize == SIZE_CART0) { + if (memory->romSize == GBA_SIZE_ROM0) { base = address & 0x01000000; } memory->agbPrintBase = base; - memory->agbPrintBufferBackup = anonymousMemoryMap(SIZE_AGB_PRINT); - memcpy(memory->agbPrintBufferBackup, &memory->rom[(AGB_PRINT_TOP | base) >> 2], SIZE_AGB_PRINT); + memory->agbPrintBufferBackup = anonymousMemoryMap(GBA_SIZE_AGB_PRINT); + memcpy(memory->agbPrintBufferBackup, &memory->rom[(AGB_PRINT_TOP | base) >> 2], GBA_SIZE_AGB_PRINT); LOAD_16(memory->agbPrintProtectBackup, AGB_PRINT_PROTECT | base, memory->rom); LOAD_16(memory->agbPrintCtxBackup.request, AGB_PRINT_STRUCT | base, memory->rom); LOAD_16(memory->agbPrintCtxBackup.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); @@ -967,7 +967,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle } mLOG(GBA_MEM, GAME_ERROR, "Bad cartridge Store16: 0x%08X", address); break; - case REGION_CART2_EX: + case GBA_REGION_ROM2_EX: if ((address & 0x0DFC0000) >= 0x0DF80000 && memory->hw.devices & HW_EREADER) { GBACartEReaderWrite(&memory->ereader, address, value); break; @@ -981,8 +981,8 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle } mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store16: 0x%08X", address); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (address & 1) { mLOG(GBA_MEM, GAME_ERROR, "Unaligned SRAM Store16: 0x%08X", address); break; @@ -997,7 +997,7 @@ void GBAStore16(struct ARMCore* cpu, uint32_t address, int16_t value, int* cycle if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1011,20 +1011,20 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo uint16_t oldValue; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; - wait = memory->waitstatesNonseq16[REGION_WORKING_RAM]; + case GBA_REGION_EWRAM: + ((int8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)] = value; + wait = memory->waitstatesNonseq16[GBA_REGION_EWRAM]; break; - case REGION_WORKING_IRAM: - ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; + case GBA_REGION_IWRAM: + ((int8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)] = value; break; - case REGION_IO: + case GBA_REGION_IO: GBAIOWrite8(gba, address & OFFSET_MASK, value); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: GBAStore16(cpu, address & ~1, ((uint8_t) value) | ((uint8_t) value << 8), cycleCounter); break; - case REGION_VRAM: + case GBA_REGION_VRAM: if ((address & 0x0001FFFF) >= ((GBARegisterDISPCNTGetMode(gba->memory.io[REG_DISPCNT >> 1]) >= 3) ? 0x00014000 : 0x00010000)) { mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OBJ: 0x%08X", address); break; @@ -1038,14 +1038,14 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo wait += GBAMemoryStallVRAM(gba, wait, 0); } break; - case REGION_OAM: + case GBA_REGION_OAM: mLOG(GBA_MEM, GAME_ERROR, "Cannot Store8 to OAM: 0x%08X", address); break; - case REGION_CART0: + case GBA_REGION_ROM0: mLOG(GBA_MEM, STUB, "Unimplemented memory Store8: 0x%08X", address); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_AUTODETECT) { if (address == SAVEDATA_FLASH_BASE) { mLOG(GBA_MEM, INFO, "Detected Flash savegame"); @@ -1063,18 +1063,18 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo if (memory->vfame.cartType) { GBAVFameSramWrite(&memory->vfame, address, value, memory->savedata.data); } else { - memory->savedata.data[address & (SIZE_CART_SRAM - 1)] = value; + memory->savedata.data[address & (GBA_SIZE_SRAM - 1)] = value; } memory->savedata.dirty |= mSAVEDATA_DIRT_NEW; } else if (memory->hw.devices & HW_TILT) { GBAHardwareTiltWrite(&memory->hw, address & OFFSET_MASK, value); } else if (memory->savedata.type == SAVEDATA_SRAM512) { - memory->savedata.data[address & (SIZE_CART_SRAM512 - 1)] = value; + memory->savedata.data[address & (GBA_SIZE_SRAM512 - 1)] = value; memory->savedata.dirty |= mSAVEDATA_DIRT_NEW; } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } - wait = memory->waitstatesNonseq16[REGION_CART_SRAM]; + wait = memory->waitstatesNonseq16[GBA_REGION_SRAM]; break; default: mLOG(GBA_MEM, GAME_ERROR, "Bad memory Store8: 0x%08X", address); @@ -1083,7 +1083,7 @@ void GBAStore8(struct ARMCore* cpu, uint32_t address, int8_t value, int* cycleCo if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1095,31 +1095,31 @@ uint32_t GBAView32(struct ARMCore* cpu, uint32_t address) { uint32_t value = 0; address &= ~3; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { LOAD_32(value, address, gba->memory.bios); } break; - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_PALETTE_RAM: - case REGION_VRAM: - case REGION_OAM: - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_PALETTE_RAM: + case GBA_REGION_VRAM: + case GBA_REGION_OAM: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: value = GBALoad32(cpu, address, 0); break; - case REGION_IO: + case GBA_REGION_IO: if ((address & OFFSET_MASK) < REG_MAX) { value = gba->memory.io[(address & OFFSET_MASK) >> 1]; value |= gba->memory.io[((address & OFFSET_MASK) >> 1) + 1] << 16; } break; - case REGION_CART_SRAM: + case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); value |= GBALoad8(cpu, address + 1, 0) << 8; value |= GBALoad8(cpu, address + 2, 0) << 16; @@ -1136,30 +1136,30 @@ uint16_t GBAView16(struct ARMCore* cpu, uint32_t address) { uint16_t value = 0; address &= ~1; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { LOAD_16(value, address, gba->memory.bios); } break; - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_PALETTE_RAM: - case REGION_VRAM: - case REGION_OAM: - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_PALETTE_RAM: + case GBA_REGION_VRAM: + case GBA_REGION_OAM: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: value = GBALoad16(cpu, address, 0); break; - case REGION_IO: + case GBA_REGION_IO: if ((address & OFFSET_MASK) < REG_MAX) { value = gba->memory.io[(address & OFFSET_MASK) >> 1]; } break; - case REGION_CART_SRAM: + case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); value |= GBALoad8(cpu, address + 1, 0) << 8; break; @@ -1173,26 +1173,26 @@ uint8_t GBAView8(struct ARMCore* cpu, uint32_t address) { struct GBA* gba = (struct GBA*) cpu->master; uint8_t value = 0; switch (address >> BASE_OFFSET) { - case REGION_BIOS: - if (address < SIZE_BIOS) { + case GBA_REGION_BIOS: + if (address < GBA_SIZE_BIOS) { value = ((uint8_t*) gba->memory.bios)[address]; } break; - case REGION_WORKING_RAM: - case REGION_WORKING_IRAM: - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: - case REGION_CART_SRAM: + case GBA_REGION_EWRAM: + case GBA_REGION_IWRAM: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: + case GBA_REGION_SRAM: value = GBALoad8(cpu, address, 0); break; - case REGION_IO: - case REGION_PALETTE_RAM: - case REGION_VRAM: - case REGION_OAM: + case GBA_REGION_IO: + case GBA_REGION_PALETTE_RAM: + case GBA_REGION_VRAM: + case GBA_REGION_OAM: value = GBAView16(cpu, address) >> ((address & 1) * 8); break; default: @@ -1207,25 +1207,25 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o int32_t oldValue = -1; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - LOAD_32(oldValue, address & (SIZE_WORKING_RAM - 4), memory->wram); - STORE_32(value, address & (SIZE_WORKING_RAM - 4), memory->wram); + case GBA_REGION_EWRAM: + LOAD_32(oldValue, address & (GBA_SIZE_EWRAM - 4), memory->wram); + STORE_32(value, address & (GBA_SIZE_EWRAM - 4), memory->wram); break; - case REGION_WORKING_IRAM: - LOAD_32(oldValue, address & (SIZE_WORKING_IRAM - 4), memory->iwram); - STORE_32(value, address & (SIZE_WORKING_IRAM - 4), memory->iwram); + case GBA_REGION_IWRAM: + LOAD_32(oldValue, address & (GBA_SIZE_IWRAM - 4), memory->iwram); + STORE_32(value, address & (GBA_SIZE_IWRAM - 4), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch32: 0x%08X", address); break; - case REGION_PALETTE_RAM: - LOAD_32(oldValue, address & (SIZE_PALETTE_RAM - 1), gba->video.palette); - STORE_32(value, address & (SIZE_PALETTE_RAM - 4), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 4), value); - gba->video.renderer->writePalette(gba->video.renderer, (address & (SIZE_PALETTE_RAM - 4)) + 2, value >> 16); + case GBA_REGION_PALETTE_RAM: + LOAD_32(oldValue, address & (GBA_SIZE_PALETTE_RAM - 1), gba->video.palette); + STORE_32(value, address & (GBA_SIZE_PALETTE_RAM - 4), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 4), value); + gba->video.renderer->writePalette(gba->video.renderer, (address & (GBA_SIZE_PALETTE_RAM - 4)) + 2, value >> 16); break; - case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) < GBA_SIZE_VRAM) { LOAD_32(oldValue, address & 0x0001FFFC, gba->video.vram); STORE_32(value, address & 0x0001FFFC, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFC); @@ -1237,31 +1237,31 @@ void GBAPatch32(struct ARMCore* cpu, uint32_t address, int32_t value, int32_t* o gba->video.renderer->writeVRAM(gba->video.renderer, (address & 0x00017FFC) | 2); } break; - case REGION_OAM: - LOAD_32(oldValue, address & (SIZE_OAM - 4), gba->video.oam.raw); - STORE_32(value, address & (SIZE_OAM - 4), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 4)) >> 1); - gba->video.renderer->writeOAM(gba->video.renderer, ((address & (SIZE_OAM - 4)) + 2) >> 1); + case GBA_REGION_OAM: + LOAD_32(oldValue, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); + STORE_32(value, address & (GBA_SIZE_OAM - 4), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 4)) >> 1); + gba->video.renderer->writeOAM(gba->video.renderer, ((address & (GBA_SIZE_OAM - 4)) + 2) >> 1); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: _pristineCow(gba); - if ((address & (SIZE_CART0 - 4)) >= gba->memory.romSize) { - gba->memory.romSize = (address & (SIZE_CART0 - 4)) + 4; + if ((address & (GBA_SIZE_ROM0 - 4)) >= gba->memory.romSize) { + gba->memory.romSize = (address & (GBA_SIZE_ROM0 - 4)) + 4; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } - LOAD_32(oldValue, address & (SIZE_CART0 - 4), gba->memory.rom); - STORE_32(value, address & (SIZE_CART0 - 4), gba->memory.rom); + LOAD_32(oldValue, address & (GBA_SIZE_ROM0 - 4), gba->memory.rom); + STORE_32(value, address & (GBA_SIZE_ROM0 - 4), gba->memory.rom); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_SRAM) { - LOAD_32(oldValue, address & (SIZE_CART_SRAM - 4), memory->savedata.data); - STORE_32(value, address & (SIZE_CART_SRAM - 4), memory->savedata.data); + LOAD_32(oldValue, address & (GBA_SIZE_SRAM - 4), memory->savedata.data); + STORE_32(value, address & (GBA_SIZE_SRAM - 4), memory->savedata.data); } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } @@ -1281,24 +1281,24 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o int16_t oldValue = -1; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - LOAD_16(oldValue, address & (SIZE_WORKING_RAM - 2), memory->wram); - STORE_16(value, address & (SIZE_WORKING_RAM - 2), memory->wram); + case GBA_REGION_EWRAM: + LOAD_16(oldValue, address & (GBA_SIZE_EWRAM - 2), memory->wram); + STORE_16(value, address & (GBA_SIZE_EWRAM - 2), memory->wram); break; - case REGION_WORKING_IRAM: - LOAD_16(oldValue, address & (SIZE_WORKING_IRAM - 2), memory->iwram); - STORE_16(value, address & (SIZE_WORKING_IRAM - 2), memory->iwram); + case GBA_REGION_IWRAM: + LOAD_16(oldValue, address & (GBA_SIZE_IWRAM - 2), memory->iwram); + STORE_16(value, address & (GBA_SIZE_IWRAM - 2), memory->iwram); break; - case REGION_IO: + case GBA_REGION_IO: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch16: 0x%08X", address); break; - case REGION_PALETTE_RAM: - LOAD_16(oldValue, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - STORE_16(value, address & (SIZE_PALETTE_RAM - 2), gba->video.palette); - gba->video.renderer->writePalette(gba->video.renderer, address & (SIZE_PALETTE_RAM - 2), value); + case GBA_REGION_PALETTE_RAM: + LOAD_16(oldValue, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); + STORE_16(value, address & (GBA_SIZE_PALETTE_RAM - 2), gba->video.palette); + gba->video.renderer->writePalette(gba->video.renderer, address & (GBA_SIZE_PALETTE_RAM - 2), value); break; - case REGION_VRAM: - if ((address & 0x0001FFFF) < SIZE_VRAM) { + case GBA_REGION_VRAM: + if ((address & 0x0001FFFF) < GBA_SIZE_VRAM) { LOAD_16(oldValue, address & 0x0001FFFE, gba->video.vram); STORE_16(value, address & 0x0001FFFE, gba->video.vram); gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x0001FFFE); @@ -1308,30 +1308,30 @@ void GBAPatch16(struct ARMCore* cpu, uint32_t address, int16_t value, int16_t* o gba->video.renderer->writeVRAM(gba->video.renderer, address & 0x00017FFE); } break; - case REGION_OAM: - LOAD_16(oldValue, address & (SIZE_OAM - 2), gba->video.oam.raw); - STORE_16(value, address & (SIZE_OAM - 2), gba->video.oam.raw); - gba->video.renderer->writeOAM(gba->video.renderer, (address & (SIZE_OAM - 2)) >> 1); + case GBA_REGION_OAM: + LOAD_16(oldValue, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); + STORE_16(value, address & (GBA_SIZE_OAM - 2), gba->video.oam.raw); + gba->video.renderer->writeOAM(gba->video.renderer, (address & (GBA_SIZE_OAM - 2)) >> 1); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: _pristineCow(gba); - if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { - gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + if ((address & (GBA_SIZE_ROM0 - 1)) >= gba->memory.romSize) { + gba->memory.romSize = (address & (GBA_SIZE_ROM0 - 2)) + 2; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } - LOAD_16(oldValue, address & (SIZE_CART0 - 2), gba->memory.rom); - STORE_16(value, address & (SIZE_CART0 - 2), gba->memory.rom); + LOAD_16(oldValue, address & (GBA_SIZE_ROM0 - 2), gba->memory.rom); + STORE_16(value, address & (GBA_SIZE_ROM0 - 2), gba->memory.rom); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_SRAM) { - LOAD_16(oldValue, address & (SIZE_CART_SRAM - 2), memory->savedata.data); - STORE_16(value, address & (SIZE_CART_SRAM - 2), memory->savedata.data); + LOAD_16(oldValue, address & (GBA_SIZE_SRAM - 2), memory->savedata.data); + STORE_16(value, address & (GBA_SIZE_SRAM - 2), memory->savedata.data); } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } @@ -1351,45 +1351,45 @@ void GBAPatch8(struct ARMCore* cpu, uint32_t address, int8_t value, int8_t* old) int8_t oldValue = -1; switch (address >> BASE_OFFSET) { - case REGION_WORKING_RAM: - oldValue = ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)]; - ((int8_t*) memory->wram)[address & (SIZE_WORKING_RAM - 1)] = value; + case GBA_REGION_EWRAM: + oldValue = ((int8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)]; + ((int8_t*) memory->wram)[address & (GBA_SIZE_EWRAM - 1)] = value; break; - case REGION_WORKING_IRAM: - oldValue = ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)]; - ((int8_t*) memory->iwram)[address & (SIZE_WORKING_IRAM - 1)] = value; + case GBA_REGION_IWRAM: + oldValue = ((int8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)]; + ((int8_t*) memory->iwram)[address & (GBA_SIZE_IWRAM - 1)] = value; break; - case REGION_IO: + case GBA_REGION_IO: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_VRAM: + case GBA_REGION_VRAM: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_OAM: + case GBA_REGION_OAM: mLOG(GBA_MEM, STUB, "Unimplemented memory Patch8: 0x%08X", address); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: _pristineCow(gba); - if ((address & (SIZE_CART0 - 1)) >= gba->memory.romSize) { - gba->memory.romSize = (address & (SIZE_CART0 - 2)) + 2; + if ((address & (GBA_SIZE_ROM0 - 1)) >= gba->memory.romSize) { + gba->memory.romSize = (address & (GBA_SIZE_ROM0 - 2)) + 2; gba->memory.romMask = toPow2(gba->memory.romSize) - 1; } - oldValue = ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)]; - ((int8_t*) memory->rom)[address & (SIZE_CART0 - 1)] = value; + oldValue = ((int8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)]; + ((int8_t*) memory->rom)[address & (GBA_SIZE_ROM0 - 1)] = value; break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: if (memory->savedata.type == SAVEDATA_SRAM) { - oldValue = ((int8_t*) memory->savedata.data)[address & (SIZE_CART_SRAM - 1)]; - ((int8_t*) memory->savedata.data)[address & (SIZE_CART_SRAM - 1)] = value; + oldValue = ((int8_t*) memory->savedata.data)[address & (GBA_SIZE_SRAM - 1)]; + ((int8_t*) memory->savedata.data)[address & (GBA_SIZE_SRAM - 1)] = value; } else { mLOG(GBA_MEM, GAME_ERROR, "Writing to non-existent SRAM: 0x%08X", address); } @@ -1458,43 +1458,43 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L uint32_t addressMisalign = address & 0x3; int region = address >> BASE_OFFSET; - if (region < REGION_CART_SRAM) { + if (region < GBA_REGION_SRAM) { address &= 0xFFFFFFFC; } int wait = memory->waitstatesSeq32[region] - memory->waitstatesNonseq32[region]; switch (region) { - case REGION_BIOS: + case GBA_REGION_BIOS: LDM_LOOP(LOAD_BIOS); break; - case REGION_WORKING_RAM: - LDM_LOOP(LOAD_WORKING_RAM); + case GBA_REGION_EWRAM: + LDM_LOOP(LOAD_EWRAM); break; - case REGION_WORKING_IRAM: - LDM_LOOP(LOAD_WORKING_IRAM); + case GBA_REGION_IWRAM: + LDM_LOOP(LOAD_IWRAM); break; - case REGION_IO: + case GBA_REGION_IO: LDM_LOOP(LOAD_IO); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: LDM_LOOP(LOAD_PALETTE_RAM); break; - case REGION_VRAM: + case GBA_REGION_VRAM: LDM_LOOP(LOAD_VRAM); break; - case REGION_OAM: + case GBA_REGION_OAM: LDM_LOOP(LOAD_OAM); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: LDM_LOOP(LOAD_CART); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: LDM_LOOP(LOAD_SRAM); break; default: @@ -1504,7 +1504,7 @@ uint32_t GBALoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L if (cycleCounter) { ++wait; - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1580,40 +1580,40 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum uint32_t addressMisalign = address & 0x3; int region = address >> BASE_OFFSET; - if (region < REGION_CART_SRAM) { + if (region < GBA_REGION_SRAM) { address &= 0xFFFFFFFC; } int wait = memory->waitstatesSeq32[region] - memory->waitstatesNonseq32[region]; switch (region) { - case REGION_WORKING_RAM: - STM_LOOP(STORE_WORKING_RAM); + case GBA_REGION_EWRAM: + STM_LOOP(STORE_EWRAM); break; - case REGION_WORKING_IRAM: - STM_LOOP(STORE_WORKING_IRAM); + case GBA_REGION_IWRAM: + STM_LOOP(STORE_IWRAM); break; - case REGION_IO: + case GBA_REGION_IO: STM_LOOP(STORE_IO); break; - case REGION_PALETTE_RAM: + case GBA_REGION_PALETTE_RAM: STM_LOOP(STORE_PALETTE_RAM); break; - case REGION_VRAM: + case GBA_REGION_VRAM: STM_LOOP(STORE_VRAM); break; - case REGION_OAM: + case GBA_REGION_OAM: STM_LOOP(STORE_OAM); break; - case REGION_CART0: - case REGION_CART0_EX: - case REGION_CART1: - case REGION_CART1_EX: - case REGION_CART2: - case REGION_CART2_EX: + case GBA_REGION_ROM0: + case GBA_REGION_ROM0_EX: + case GBA_REGION_ROM1: + case GBA_REGION_ROM1_EX: + case GBA_REGION_ROM2: + case GBA_REGION_ROM2_EX: STM_LOOP(STORE_CART); break; - case REGION_CART_SRAM: - case REGION_CART_SRAM_MIRROR: + case GBA_REGION_SRAM: + case GBA_REGION_SRAM_MIRROR: STM_LOOP(STORE_SRAM); break; default: @@ -1622,7 +1622,7 @@ uint32_t GBAStoreMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum } if (cycleCounter) { - if (address < BASE_CART0) { + if (address < GBA_BASE_ROM0) { wait = GBAMemoryStall(cpu, wait); } *cycleCounter += wait; @@ -1651,26 +1651,26 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { int ws2seq = (parameters & 0x0400) >> 10; int prefetch = parameters & 0x4000; - memory->waitstatesNonseq16[REGION_CART_SRAM] = memory->waitstatesNonseq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; - memory->waitstatesSeq16[REGION_CART_SRAM] = memory->waitstatesSeq16[REGION_CART_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; - memory->waitstatesNonseq32[REGION_CART_SRAM] = memory->waitstatesNonseq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; - memory->waitstatesSeq32[REGION_CART_SRAM] = memory->waitstatesSeq32[REGION_CART_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; + memory->waitstatesNonseq16[GBA_REGION_SRAM] = memory->waitstatesNonseq16[GBA_REGION_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; + memory->waitstatesSeq16[GBA_REGION_SRAM] = memory->waitstatesSeq16[GBA_REGION_SRAM_MIRROR] = GBA_ROM_WAITSTATES[sram]; + memory->waitstatesNonseq32[GBA_REGION_SRAM] = memory->waitstatesNonseq32[GBA_REGION_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; + memory->waitstatesSeq32[GBA_REGION_SRAM] = memory->waitstatesSeq32[GBA_REGION_SRAM_MIRROR] = 2 * GBA_ROM_WAITSTATES[sram] + 1; - memory->waitstatesNonseq16[REGION_CART0] = memory->waitstatesNonseq16[REGION_CART0_EX] = GBA_ROM_WAITSTATES[ws0]; - memory->waitstatesNonseq16[REGION_CART1] = memory->waitstatesNonseq16[REGION_CART1_EX] = GBA_ROM_WAITSTATES[ws1]; - memory->waitstatesNonseq16[REGION_CART2] = memory->waitstatesNonseq16[REGION_CART2_EX] = GBA_ROM_WAITSTATES[ws2]; + memory->waitstatesNonseq16[GBA_REGION_ROM0] = memory->waitstatesNonseq16[GBA_REGION_ROM0_EX] = GBA_ROM_WAITSTATES[ws0]; + memory->waitstatesNonseq16[GBA_REGION_ROM1] = memory->waitstatesNonseq16[GBA_REGION_ROM1_EX] = GBA_ROM_WAITSTATES[ws1]; + memory->waitstatesNonseq16[GBA_REGION_ROM2] = memory->waitstatesNonseq16[GBA_REGION_ROM2_EX] = GBA_ROM_WAITSTATES[ws2]; - memory->waitstatesSeq16[REGION_CART0] = memory->waitstatesSeq16[REGION_CART0_EX] = GBA_ROM_WAITSTATES_SEQ[ws0seq]; - memory->waitstatesSeq16[REGION_CART1] = memory->waitstatesSeq16[REGION_CART1_EX] = GBA_ROM_WAITSTATES_SEQ[ws1seq + 2]; - memory->waitstatesSeq16[REGION_CART2] = memory->waitstatesSeq16[REGION_CART2_EX] = GBA_ROM_WAITSTATES_SEQ[ws2seq + 4]; + memory->waitstatesSeq16[GBA_REGION_ROM0] = memory->waitstatesSeq16[GBA_REGION_ROM0_EX] = GBA_ROM_WAITSTATES_SEQ[ws0seq]; + memory->waitstatesSeq16[GBA_REGION_ROM1] = memory->waitstatesSeq16[GBA_REGION_ROM1_EX] = GBA_ROM_WAITSTATES_SEQ[ws1seq + 2]; + memory->waitstatesSeq16[GBA_REGION_ROM2] = memory->waitstatesSeq16[GBA_REGION_ROM2_EX] = GBA_ROM_WAITSTATES_SEQ[ws2seq + 4]; - memory->waitstatesNonseq32[REGION_CART0] = memory->waitstatesNonseq32[REGION_CART0_EX] = memory->waitstatesNonseq16[REGION_CART0] + 1 + memory->waitstatesSeq16[REGION_CART0]; - memory->waitstatesNonseq32[REGION_CART1] = memory->waitstatesNonseq32[REGION_CART1_EX] = memory->waitstatesNonseq16[REGION_CART1] + 1 + memory->waitstatesSeq16[REGION_CART1]; - memory->waitstatesNonseq32[REGION_CART2] = memory->waitstatesNonseq32[REGION_CART2_EX] = memory->waitstatesNonseq16[REGION_CART2] + 1 + memory->waitstatesSeq16[REGION_CART2]; + memory->waitstatesNonseq32[GBA_REGION_ROM0] = memory->waitstatesNonseq32[GBA_REGION_ROM0_EX] = memory->waitstatesNonseq16[GBA_REGION_ROM0] + 1 + memory->waitstatesSeq16[GBA_REGION_ROM0]; + memory->waitstatesNonseq32[GBA_REGION_ROM1] = memory->waitstatesNonseq32[GBA_REGION_ROM1_EX] = memory->waitstatesNonseq16[GBA_REGION_ROM1] + 1 + memory->waitstatesSeq16[GBA_REGION_ROM1]; + memory->waitstatesNonseq32[GBA_REGION_ROM2] = memory->waitstatesNonseq32[GBA_REGION_ROM2_EX] = memory->waitstatesNonseq16[GBA_REGION_ROM2] + 1 + memory->waitstatesSeq16[GBA_REGION_ROM2]; - memory->waitstatesSeq32[REGION_CART0] = memory->waitstatesSeq32[REGION_CART0_EX] = 2 * memory->waitstatesSeq16[REGION_CART0] + 1; - memory->waitstatesSeq32[REGION_CART1] = memory->waitstatesSeq32[REGION_CART1_EX] = 2 * memory->waitstatesSeq16[REGION_CART1] + 1; - memory->waitstatesSeq32[REGION_CART2] = memory->waitstatesSeq32[REGION_CART2_EX] = 2 * memory->waitstatesSeq16[REGION_CART2] + 1; + memory->waitstatesSeq32[GBA_REGION_ROM0] = memory->waitstatesSeq32[GBA_REGION_ROM0_EX] = 2 * memory->waitstatesSeq16[GBA_REGION_ROM0] + 1; + memory->waitstatesSeq32[GBA_REGION_ROM1] = memory->waitstatesSeq32[GBA_REGION_ROM1_EX] = 2 * memory->waitstatesSeq16[GBA_REGION_ROM1] + 1; + memory->waitstatesSeq32[GBA_REGION_ROM2] = memory->waitstatesSeq32[GBA_REGION_ROM2_EX] = 2 * memory->waitstatesSeq16[GBA_REGION_ROM2] + 1; memory->prefetch = prefetch; @@ -1684,7 +1684,7 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { int phi = (parameters >> 11) & 3; uint32_t base = memory->agbPrintBase; if (phi == 3) { - memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBuffer, SIZE_AGB_PRINT); + memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBuffer, GBA_SIZE_AGB_PRINT); STORE_16(memory->agbPrintProtect, AGB_PRINT_PROTECT | base, memory->rom); STORE_16(memory->agbPrintCtx.request, AGB_PRINT_STRUCT | base, memory->rom); STORE_16(memory->agbPrintCtx.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); @@ -1692,7 +1692,7 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) { STORE_16(memory->agbPrintCtx.put, (AGB_PRINT_STRUCT | base) + 6, memory->rom); STORE_32(_agbPrintFunc, AGB_PRINT_FLUSH_ADDR | base, memory->rom); } else { - memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBufferBackup, SIZE_AGB_PRINT); + memcpy(&memory->rom[(AGB_PRINT_TOP | base) >> 2], memory->agbPrintBufferBackup, GBA_SIZE_AGB_PRINT); STORE_16(memory->agbPrintProtectBackup, AGB_PRINT_PROTECT | base, memory->rom); STORE_16(memory->agbPrintCtxBackup.request, AGB_PRINT_STRUCT | base, memory->rom); STORE_16(memory->agbPrintCtxBackup.bank, (AGB_PRINT_STRUCT | base) + 2, memory->rom); @@ -1709,10 +1709,10 @@ void GBAAdjustEWRAMWaitstates(struct GBA* gba, uint16_t parameters) { int wait = 15 - ((parameters >> 8) & 0xF); if (wait) { - memory->waitstatesNonseq16[REGION_WORKING_RAM] = wait; - memory->waitstatesSeq16[REGION_WORKING_RAM] = wait; - memory->waitstatesNonseq32[REGION_WORKING_RAM] = 2 * wait + 1; - memory->waitstatesSeq32[REGION_WORKING_RAM] = 2 * wait + 1; + memory->waitstatesNonseq16[GBA_REGION_EWRAM] = wait; + memory->waitstatesSeq16[GBA_REGION_EWRAM] = wait; + memory->waitstatesNonseq32[GBA_REGION_EWRAM] = 2 * wait + 1; + memory->waitstatesSeq32[GBA_REGION_EWRAM] = 2 * wait + 1; cpu->memory.activeSeqCycles32 = memory->waitstatesSeq32[memory->activeRegion]; cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion]; @@ -1732,7 +1732,7 @@ int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) { struct GBA* gba = (struct GBA*) cpu->master; struct GBAMemory* memory = &gba->memory; - if (memory->activeRegion < REGION_CART0 || !memory->prefetch) { + if (memory->activeRegion < GBA_REGION_ROM0 || !memory->prefetch) { // The wait is the stall return wait; } @@ -1797,13 +1797,13 @@ int32_t GBAMemoryStallVRAM(struct GBA* gba, int32_t wait, int extra) { } void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state) { - memcpy(state->wram, memory->wram, SIZE_WORKING_RAM); - memcpy(state->iwram, memory->iwram, SIZE_WORKING_IRAM); + memcpy(state->wram, memory->wram, GBA_SIZE_EWRAM); + memcpy(state->iwram, memory->iwram, GBA_SIZE_IWRAM); } void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state) { - memcpy(memory->wram, state->wram, SIZE_WORKING_RAM); - memcpy(memory->iwram, state->iwram, SIZE_WORKING_IRAM); + memcpy(memory->wram, state->wram, GBA_SIZE_EWRAM); + memcpy(memory->iwram, state->iwram, GBA_SIZE_IWRAM); } void _pristineCow(struct GBA* gba) { @@ -1811,9 +1811,9 @@ void _pristineCow(struct GBA* gba) { return; } #if !defined(FIXED_ROM_BUFFER) && !defined(__wii__) - void* newRom = anonymousMemoryMap(SIZE_CART0); + void* newRom = anonymousMemoryMap(GBA_SIZE_ROM0); memcpy(newRom, gba->memory.rom, gba->memory.romSize); - memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, SIZE_CART0 - gba->memory.romSize); + memset(((uint8_t*) newRom) + gba->memory.romSize, 0xFF, GBA_SIZE_ROM0 - gba->memory.romSize); if (gba->cpu->memory.activeRegion == gba->memory.rom) { gba->cpu->memory.activeRegion = newRom; } @@ -1855,16 +1855,16 @@ void GBAPrintFlush(struct GBA* gba) { static void _agbPrintStore(struct GBA* gba, uint32_t address, int16_t value) { struct GBAMemory* memory = &gba->memory; if ((address & 0x00FFFFFF) < AGB_PRINT_TOP) { - STORE_16(value, address & (SIZE_AGB_PRINT - 2), memory->agbPrintBuffer); + STORE_16(value, address & (GBA_SIZE_AGB_PRINT - 2), memory->agbPrintBuffer); } else if ((address & 0x00FFFFF8) == AGB_PRINT_STRUCT) { (&memory->agbPrintCtx.request)[(address & 7) >> 1] = value; } - if (memory->romSize == SIZE_CART0) { + if (memory->romSize == GBA_SIZE_ROM0) { _pristineCow(gba); - STORE_16(value, address & (SIZE_CART0 - 2), memory->rom); - } else if (memory->agbPrintCtx.bank == 0xFD && memory->romSize >= SIZE_CART0 / 2) { + STORE_16(value, address & (GBA_SIZE_ROM0 - 2), memory->rom); + } else if (memory->agbPrintCtx.bank == 0xFD && memory->romSize >= GBA_SIZE_ROM0 / 2) { _pristineCow(gba); - STORE_16(value, address & (SIZE_CART0 / 2 - 2), memory->rom); + STORE_16(value, address & (GBA_SIZE_ROM0 / 2 - 2), memory->rom); } } @@ -1872,7 +1872,7 @@ static int16_t _agbPrintLoad(struct GBA* gba, uint32_t address) { struct GBAMemory* memory = &gba->memory; int16_t value = address >> 1; if (address < AGB_PRINT_TOP && memory->agbPrintBuffer) { - LOAD_16(value, address & (SIZE_AGB_PRINT - 1), memory->agbPrintBuffer); + LOAD_16(value, address & (GBA_SIZE_AGB_PRINT - 1), memory->agbPrintBuffer); } else if ((address & 0x00FFFFF8) == AGB_PRINT_STRUCT) { value = (&memory->agbPrintCtx.request)[(address & 7) >> 1]; } diff --git a/src/gba/renderers/cache-set.c b/src/gba/renderers/cache-set.c index 51512721d..40272bedd 100644 --- a/src/gba/renderers/cache-set.c +++ b/src/gba/renderers/cache-set.c @@ -61,7 +61,7 @@ void GBAVideoCacheAssociate(struct mCacheSet* cache, struct GBAVideo* video) { mCacheSetAssignVRAM(cache, video->vram); video->renderer->cache = cache; size_t i; - for (i = 0; i < SIZE_PALETTE_RAM / 2; ++i) { + for (i = 0; i < GBA_SIZE_PALETTE_RAM / 2; ++i) { mCacheSetWritePalette(cache, i, mColorFrom555(video->palette[i])); } GBAVideoCacheWriteVideoRegister(cache, REG_DISPCNT, video->p->memory.io[REG_DISPCNT >> 1]); diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 443e8015d..455ee0802 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -69,22 +69,22 @@ void GBASavedataDeinit(struct GBASavedata* savedata) { } else { switch (savedata->type) { case SAVEDATA_SRAM: - mappedMemoryFree(savedata->data, SIZE_CART_SRAM); + mappedMemoryFree(savedata->data, GBA_SIZE_SRAM); break; case SAVEDATA_SRAM512: - mappedMemoryFree(savedata->data, SIZE_CART_SRAM512); + mappedMemoryFree(savedata->data, GBA_SIZE_SRAM512); break; case SAVEDATA_FLASH512: - mappedMemoryFree(savedata->data, SIZE_CART_FLASH512); + mappedMemoryFree(savedata->data, GBA_SIZE_FLASH512); break; case SAVEDATA_FLASH1M: - mappedMemoryFree(savedata->data, SIZE_CART_FLASH1M); + mappedMemoryFree(savedata->data, GBA_SIZE_FLASH1M); break; case SAVEDATA_EEPROM: - mappedMemoryFree(savedata->data, SIZE_CART_EEPROM); + mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM); break; case SAVEDATA_EEPROM512: - mappedMemoryFree(savedata->data, SIZE_CART_EEPROM512); + mappedMemoryFree(savedata->data, GBA_SIZE_EEPROM512); break; case SAVEDATA_FORCE_NONE: case SAVEDATA_AUTODETECT: @@ -129,17 +129,17 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { if (savedata->data) { switch (savedata->type) { case SAVEDATA_SRAM: - return out->write(out, savedata->data, SIZE_CART_SRAM) == SIZE_CART_SRAM; + return out->write(out, savedata->data, GBA_SIZE_SRAM) == GBA_SIZE_SRAM; case SAVEDATA_SRAM512: - return out->write(out, savedata->data, SIZE_CART_SRAM512) == SIZE_CART_SRAM512; + return out->write(out, savedata->data, GBA_SIZE_SRAM512) == GBA_SIZE_SRAM512; case SAVEDATA_FLASH512: - return out->write(out, savedata->data, SIZE_CART_FLASH512) == SIZE_CART_FLASH512; + return out->write(out, savedata->data, GBA_SIZE_FLASH512) == GBA_SIZE_FLASH512; case SAVEDATA_FLASH1M: - return out->write(out, savedata->data, SIZE_CART_FLASH1M) == SIZE_CART_FLASH1M; + return out->write(out, savedata->data, GBA_SIZE_FLASH1M) == GBA_SIZE_FLASH1M; case SAVEDATA_EEPROM: - return out->write(out, savedata->data, SIZE_CART_EEPROM) == SIZE_CART_EEPROM; + return out->write(out, savedata->data, GBA_SIZE_EEPROM) == GBA_SIZE_EEPROM; case SAVEDATA_EEPROM512: - return out->write(out, savedata->data, SIZE_CART_EEPROM512) == SIZE_CART_EEPROM512; + return out->write(out, savedata->data, GBA_SIZE_EEPROM512) == GBA_SIZE_EEPROM512; case SAVEDATA_AUTODETECT: case SAVEDATA_FORCE_NONE: return true; @@ -160,17 +160,17 @@ bool GBASavedataClone(struct GBASavedata* savedata, struct VFile* out) { size_t GBASavedataSize(const struct GBASavedata* savedata) { switch (savedata->type) { case SAVEDATA_SRAM: - return SIZE_CART_SRAM; + return GBA_SIZE_SRAM; case SAVEDATA_SRAM512: - return SIZE_CART_SRAM512; + return GBA_SIZE_SRAM512; case SAVEDATA_FLASH512: - return SIZE_CART_FLASH512; + return GBA_SIZE_FLASH512; case SAVEDATA_FLASH1M: - return SIZE_CART_FLASH1M; + return GBA_SIZE_FLASH1M; case SAVEDATA_EEPROM: - return SIZE_CART_EEPROM; + return GBA_SIZE_EEPROM; case SAVEDATA_EEPROM512: - return SIZE_CART_EEPROM512; + return GBA_SIZE_EEPROM512; case SAVEDATA_FORCE_NONE: return 0; case SAVEDATA_AUTODETECT: @@ -262,14 +262,14 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; } - int32_t flashSize = SIZE_CART_FLASH512; + int32_t flashSize = GBA_SIZE_FLASH512; if (savedata->type == SAVEDATA_FLASH1M) { - flashSize = SIZE_CART_FLASH1M; + flashSize = GBA_SIZE_FLASH1M; } off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_FLASH1M); + savedata->data = anonymousMemoryMap(GBA_SIZE_FLASH1M); } else { end = savedata->vf->size(savedata->vf); if (end < flashSize) { @@ -279,7 +279,7 @@ void GBASavedataInitFlash(struct GBASavedata* savedata) { } savedata->currentBank = savedata->data; - if (end < SIZE_CART_FLASH512) { + if (end < GBA_SIZE_FLASH512) { memset(&savedata->data[end], 0xFF, flashSize - end); } } @@ -291,14 +291,14 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) { mLOG(GBA_SAVE, WARN, "Can't re-initialize savedata"); return; } - int32_t eepromSize = SIZE_CART_EEPROM512; + int32_t eepromSize = GBA_SIZE_EEPROM512; if (savedata->type == SAVEDATA_EEPROM) { - eepromSize = SIZE_CART_EEPROM; + eepromSize = GBA_SIZE_EEPROM; } off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_EEPROM); + savedata->data = anonymousMemoryMap(GBA_SIZE_EEPROM); } else { end = savedata->vf->size(savedata->vf); if (end < eepromSize) { @@ -306,8 +306,8 @@ void GBASavedataInitEEPROM(struct GBASavedata* savedata) { } savedata->data = savedata->vf->map(savedata->vf, eepromSize, savedata->mapMode); } - if (end < SIZE_CART_EEPROM512) { - memset(&savedata->data[end], 0xFF, SIZE_CART_EEPROM512 - end); + if (end < GBA_SIZE_EEPROM512) { + memset(&savedata->data[end], 0xFF, GBA_SIZE_EEPROM512 - end); } } @@ -321,17 +321,17 @@ void GBASavedataInitSRAM(struct GBASavedata* savedata) { off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_SRAM); + savedata->data = anonymousMemoryMap(GBA_SIZE_SRAM); } else { end = savedata->vf->size(savedata->vf); - if (end < SIZE_CART_SRAM) { - savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM); + if (end < GBA_SIZE_SRAM) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_SRAM); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM, savedata->mapMode); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_SRAM, savedata->mapMode); } - if (end < SIZE_CART_SRAM) { - memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM - end); + if (end < GBA_SIZE_SRAM) { + memset(&savedata->data[end], 0xFF, GBA_SIZE_SRAM - end); } } @@ -345,17 +345,17 @@ void GBASavedataInitSRAM512(struct GBASavedata* savedata) { off_t end; if (!savedata->vf) { end = 0; - savedata->data = anonymousMemoryMap(SIZE_CART_SRAM512); + savedata->data = anonymousMemoryMap(GBA_SIZE_SRAM512); } else { end = savedata->vf->size(savedata->vf); - if (end < SIZE_CART_SRAM512) { - savedata->vf->truncate(savedata->vf, SIZE_CART_SRAM512); + if (end < GBA_SIZE_SRAM512) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_SRAM512); } - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_SRAM512, savedata->mapMode); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_SRAM512, savedata->mapMode); } - if (end < SIZE_CART_SRAM512) { - memset(&savedata->data[end], 0xFF, SIZE_CART_SRAM512 - end); + if (end < GBA_SIZE_SRAM512) { + memset(&savedata->data[end], 0xFF, GBA_SIZE_SRAM512 - end); } } @@ -465,7 +465,7 @@ void GBASavedataWriteFlash(struct GBASavedata* savedata, uint16_t address, uint8 } static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) { - if (size < SIZE_CART_EEPROM512) { + if (size < GBA_SIZE_EEPROM512) { return; } if (savedata->type == SAVEDATA_EEPROM) { @@ -475,13 +475,13 @@ static void _ensureEeprom(struct GBASavedata* savedata, uint32_t size) { if (!savedata->vf) { return; } - savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_EEPROM512); - if (savedata->vf->size(savedata->vf) < SIZE_CART_EEPROM) { - savedata->vf->truncate(savedata->vf, SIZE_CART_EEPROM); - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode); - memset(&savedata->data[SIZE_CART_EEPROM512], 0xFF, SIZE_CART_EEPROM - SIZE_CART_EEPROM512); + savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_EEPROM512); + if (savedata->vf->size(savedata->vf) < GBA_SIZE_EEPROM) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_EEPROM); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_EEPROM, savedata->mapMode); + memset(&savedata->data[GBA_SIZE_EEPROM512], 0xFF, GBA_SIZE_EEPROM - GBA_SIZE_EEPROM512); } else { - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_EEPROM, savedata->mapMode); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_EEPROM, savedata->mapMode); } } @@ -509,7 +509,7 @@ void GBASavedataWriteEEPROM(struct GBASavedata* savedata, uint16_t value, uint32 savedata->writeAddress |= (value & 0x1) << 6; } else if (writeSize == 1) { savedata->command = EEPROM_COMMAND_NULL; - } else if ((savedata->writeAddress >> 3) < SIZE_CART_EEPROM) { + } else if ((savedata->writeAddress >> 3) < GBA_SIZE_EEPROM) { _ensureEeprom(savedata, savedata->writeAddress >> 3); uint8_t current = savedata->data[savedata->writeAddress >> 3]; current &= ~(1 << (0x7 - (savedata->writeAddress & 0x7))); @@ -551,7 +551,7 @@ uint16_t GBASavedataReadEEPROM(struct GBASavedata* savedata) { int step = 63 - savedata->readBitsRemaining; uint32_t address = (savedata->readAddress + step) >> 3; _ensureEeprom(savedata, address); - if (address >= SIZE_CART_EEPROM) { + if (address >= GBA_SIZE_EEPROM) { mLOG(GBA_SAVE, GAME_ERROR, "Reading beyond end of EEPROM: %08X", address); return 0xFF; } @@ -699,13 +699,13 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) { mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb"); savedata->type = SAVEDATA_FLASH1M; if (savedata->vf) { - savedata->vf->unmap(savedata->vf, savedata->data, SIZE_CART_FLASH512); - if (savedata->vf->size(savedata->vf) < SIZE_CART_FLASH1M) { - savedata->vf->truncate(savedata->vf, SIZE_CART_FLASH1M); - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); - memset(&savedata->data[SIZE_CART_FLASH512], 0xFF, SIZE_CART_FLASH512); + savedata->vf->unmap(savedata->vf, savedata->data, GBA_SIZE_FLASH512); + if (savedata->vf->size(savedata->vf) < GBA_SIZE_FLASH1M) { + savedata->vf->truncate(savedata->vf, GBA_SIZE_FLASH1M); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_FLASH1M, MAP_WRITE); + memset(&savedata->data[GBA_SIZE_FLASH512], 0xFF, GBA_SIZE_FLASH512); } else { - savedata->data = savedata->vf->map(savedata->vf, SIZE_CART_FLASH1M, MAP_WRITE); + savedata->data = savedata->vf->map(savedata->vf, GBA_SIZE_FLASH1M, MAP_WRITE); } } } @@ -715,9 +715,9 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) { void _flashErase(struct GBASavedata* savedata) { mLOG(GBA_SAVE, DEBUG, "Performing flash chip erase"); savedata->dirty |= mSAVEDATA_DIRT_NEW; - size_t size = SIZE_CART_FLASH512; + size_t size = GBA_SIZE_FLASH512; if (savedata->type == SAVEDATA_FLASH1M) { - size = SIZE_CART_FLASH1M; + size = GBA_SIZE_FLASH1M; } memset(savedata->data, 0xFF, size); } diff --git a/src/gba/serialize.c b/src/gba/serialize.c index 1079932be..7e6ed36f8 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -102,7 +102,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { mLOG(GBA_STATE, WARN, "Savestate created using a different version of the BIOS: expected %08X, got %08X", gba->biosChecksum, ucheck); uint32_t pc; LOAD_32(pc, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs); - if ((ucheck == GBA_BIOS_CHECKSUM || gba->biosChecksum == GBA_BIOS_CHECKSUM) && pc < SIZE_BIOS && pc >= 0x20) { + if ((ucheck == GBA_BIOS_CHECKSUM || gba->biosChecksum == GBA_BIOS_CHECKSUM) && pc < GBA_SIZE_BIOS && pc >= 0x20) { error = true; } } @@ -128,7 +128,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { } LOAD_32(check, ARM_PC * sizeof(state->cpu.gprs[0]), state->cpu.gprs); int region = (check >> BASE_OFFSET); - if ((region == REGION_CART0 || region == REGION_CART1 || region == REGION_CART2) && ((check - WORD_SIZE_ARM) & SIZE_CART0) >= gba->memory.romSize - WORD_SIZE_ARM) { + if ((region == GBA_REGION_ROM0 || region == GBA_REGION_ROM1 || region == GBA_REGION_ROM2) && ((check - WORD_SIZE_ARM) & GBA_SIZE_ROM0) >= gba->memory.romSize - WORD_SIZE_ARM) { mLOG(GBA_STATE, WARN, "Savestate created using a differently sized version of the ROM"); error = true; } diff --git a/src/gba/sharkport.c b/src/gba/sharkport.c index 9922f425f..9e1e64518 100644 --- a/src/gba/sharkport.c +++ b/src/gba/sharkport.c @@ -19,7 +19,7 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) { bool success = false; switch (gba->memory.savedata.type) { case SAVEDATA_FLASH512: - if (size > SIZE_CART_FLASH512) { + if (size > GBA_SIZE_FLASH512) { GBASavedataForceType(&gba->memory.savedata, SAVEDATA_FLASH1M); } // Fall through @@ -33,7 +33,7 @@ static bool _importSavedata(struct GBA* gba, void* payload, size_t size) { goto cleanup; } - if (size == SIZE_CART_EEPROM || size == SIZE_CART_EEPROM512) { + if (size == GBA_SIZE_EEPROM || size == GBA_SIZE_EEPROM512) { size_t i; for (i = 0; i < size; i += 8) { uint32_t lo, hi; @@ -119,7 +119,7 @@ int GBASavedataSharkPortPayloadSize(struct VFile* vf) { void* GBASavedataSharkPortGetPayload(struct VFile* vf, size_t* osize, uint8_t* oheader, bool testChecksum) { int32_t size = GBASavedataSharkPortPayloadSize(vf); - if (size < 0x1C || size > SIZE_CART_FLASH1M + 0x1C) { + if (size < 0x1C || size > GBA_SIZE_FLASH1M + 0x1C) { return NULL; } size -= 0x1C; @@ -336,15 +336,15 @@ int GBASavedataGSVPayloadSize(struct VFile* vf) { LOAD_32(type, 0, &header.type); switch (type) { case 2: - return SIZE_CART_SRAM; + return GBA_SIZE_SRAM; case 3: - return SIZE_CART_EEPROM512; + return GBA_SIZE_EEPROM512; case 4: - return SIZE_CART_EEPROM; + return GBA_SIZE_EEPROM; case 5: - return SIZE_CART_FLASH512; + return GBA_SIZE_FLASH512; case 6: - return SIZE_CART_FLASH1M; // Unconfirmed + return GBA_SIZE_FLASH1M; // Unconfirmed default: return vf->size(vf) - GSV_PAYLOAD_OFFSET; } @@ -352,7 +352,7 @@ int GBASavedataGSVPayloadSize(struct VFile* vf) { void* GBASavedataGSVGetPayload(struct VFile* vf, size_t* osize, uint8_t* ident, bool testChecksum) { int32_t size = GBASavedataGSVPayloadSize(vf); - if (!size || size > SIZE_CART_FLASH1M) { + if (!size || size > GBA_SIZE_FLASH1M) { return NULL; } diff --git a/src/gba/video.c b/src/gba/video.c index 7268f19de..fd22d1dd7 100644 --- a/src/gba/video.c +++ b/src/gba/video.c @@ -54,7 +54,7 @@ MGBA_EXPORT const int GBAVideoObjSizes[16][2] = { void GBAVideoInit(struct GBAVideo* video) { video->renderer = NULL; - video->vram = anonymousMemoryMap(SIZE_VRAM); + video->vram = anonymousMemoryMap(GBA_SIZE_VRAM); video->frameskip = 0; video->event.name = "GBA Video"; video->event.callback = NULL; @@ -93,7 +93,7 @@ void GBAVideoReset(struct GBAVideo* video) { void GBAVideoDeinit(struct GBAVideo* video) { video->renderer->deinit(video->renderer); - mappedMemoryFree(video->vram, SIZE_VRAM); + mappedMemoryFree(video->vram, GBA_SIZE_VRAM); } void GBAVideoDummyRendererCreate(struct GBAVideoRenderer* renderer) { @@ -325,9 +325,9 @@ static void GBAVideoDummyRendererPutPixels(struct GBAVideoRenderer* renderer, si } void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* state) { - memcpy(state->vram, video->vram, SIZE_VRAM); - memcpy(state->oam, video->oam.raw, SIZE_OAM); - memcpy(state->pram, video->palette, SIZE_PALETTE_RAM); + memcpy(state->vram, video->vram, GBA_SIZE_VRAM); + memcpy(state->oam, video->oam.raw, GBA_SIZE_OAM); + memcpy(state->pram, video->palette, GBA_SIZE_PALETTE_RAM); STORE_32(video->event.when - mTimingCurrentTime(&video->p->timing), 0, &state->video.nextEvent); int32_t flags = 0; if (video->event.callback == _startHdraw) { @@ -340,16 +340,16 @@ void GBAVideoSerialize(const struct GBAVideo* video, struct GBASerializedState* } void GBAVideoDeserialize(struct GBAVideo* video, const struct GBASerializedState* state) { - memcpy(video->vram, state->vram, SIZE_VRAM); + memcpy(video->vram, state->vram, GBA_SIZE_VRAM); uint16_t value; int i; - for (i = 0; i < SIZE_OAM; i += 2) { + for (i = 0; i < GBA_SIZE_OAM; i += 2) { LOAD_16(value, i, state->oam); - GBAStore16(video->p->cpu, BASE_OAM | i, value, 0); + GBAStore16(video->p->cpu, GBA_BASE_OAM | i, value, 0); } - for (i = 0; i < SIZE_PALETTE_RAM; i += 2) { + for (i = 0; i < GBA_SIZE_PALETTE_RAM; i += 2) { LOAD_16(value, i, state->pram); - GBAStore16(video->p->cpu, BASE_PALETTE_RAM | i, value, 0); + GBAStore16(video->p->cpu, GBA_BASE_PALETTE_RAM | i, value, 0); } LOAD_32(video->frameCounter, 0, &state->video.frameCounter); diff --git a/src/platform/libretro/libretro.c b/src/platform/libretro/libretro.c index 2c0184d99..68f54f2c7 100644 --- a/src/platform/libretro/libretro.c +++ b/src/platform/libretro/libretro.c @@ -676,66 +676,66 @@ static void _setupMaps(struct mCore* core) { /* Map internal working RAM */ descs[0].ptr = gba->memory.iwram; - descs[0].start = BASE_WORKING_IRAM; - descs[0].len = SIZE_WORKING_IRAM; + descs[0].start = GBA_BASE_IWRAM; + descs[0].len = GBA_SIZE_IWRAM; descs[0].select = 0xFF000000; /* Map working RAM */ descs[1].ptr = gba->memory.wram; - descs[1].start = BASE_WORKING_RAM; - descs[1].len = SIZE_WORKING_RAM; + descs[1].start = GBA_BASE_EWRAM; + descs[1].len = GBA_SIZE_EWRAM; descs[1].select = 0xFF000000; /* Map save RAM */ /* TODO: if SRAM is flash, use start=0 addrspace="S" instead */ descs[2].ptr = savedataSize ? savedata : NULL; - descs[2].start = BASE_CART_SRAM; + descs[2].start = GBA_BASE_SRAM; descs[2].len = savedataSize; /* Map ROM */ descs[3].ptr = gba->memory.rom; - descs[3].start = BASE_CART0; + descs[3].start = GBA_BASE_ROM0; descs[3].len = romSize; descs[3].flags = RETRO_MEMDESC_CONST; descs[4].ptr = gba->memory.rom; - descs[4].start = BASE_CART1; + descs[4].start = GBA_BASE_ROM1; descs[4].len = romSize; descs[4].flags = RETRO_MEMDESC_CONST; descs[5].ptr = gba->memory.rom; - descs[5].start = BASE_CART2; + descs[5].start = GBA_BASE_ROM2; descs[5].len = romSize; descs[5].flags = RETRO_MEMDESC_CONST; /* Map BIOS */ descs[6].ptr = gba->memory.bios; - descs[6].start = BASE_BIOS; - descs[6].len = SIZE_BIOS; + descs[6].start = GBA_BASE_BIOS; + descs[6].len = GBA_SIZE_BIOS; descs[6].flags = RETRO_MEMDESC_CONST; /* Map VRAM */ descs[7].ptr = gba->video.vram; - descs[7].start = BASE_VRAM; - descs[7].len = SIZE_VRAM; + descs[7].start = GBA_BASE_VRAM; + descs[7].len = GBA_SIZE_VRAM; descs[7].select = 0xFF000000; /* Map palette RAM */ descs[8].ptr = gba->video.palette; - descs[8].start = BASE_PALETTE_RAM; - descs[8].len = SIZE_PALETTE_RAM; + descs[8].start = GBA_BASE_PALETTE_RAM; + descs[8].len = GBA_SIZE_PALETTE_RAM; descs[8].select = 0xFF000000; /* Map OAM */ descs[9].ptr = &gba->video.oam; /* video.oam is a structure */ - descs[9].start = BASE_OAM; - descs[9].len = SIZE_OAM; + descs[9].start = GBA_BASE_OAM; + descs[9].len = GBA_SIZE_OAM; descs[9].select = 0xFF000000; /* Map mmapped I/O */ descs[10].ptr = gba->memory.io; - descs[10].start = BASE_IO; - descs[10].len = SIZE_IO; + descs[10].start = GBA_BASE_IO; + descs[10].len = GBA_SIZE_IO; mmaps.descriptors = descs; mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]); diff --git a/src/platform/python/mgba/gba.py b/src/platform/python/mgba/gba.py index 09deafc6c..a0c86ac84 100644 --- a/src/platform/python/mgba/gba.py +++ b/src/platform/python/mgba/gba.py @@ -123,19 +123,19 @@ class GBAMemory(Memory): def __init__(self, core, romSize=lib.SIZE_CART0): super(GBAMemory, self).__init__(core, 0x100000000) - self.bios = Memory(core, lib.SIZE_BIOS, lib.BASE_BIOS) - self.wram = Memory(core, lib.SIZE_WORKING_RAM, lib.BASE_WORKING_RAM) - self.iwram = Memory(core, lib.SIZE_WORKING_IRAM, lib.BASE_WORKING_IRAM) - self.io = Memory(core, lib.SIZE_IO, lib.BASE_IO) # pylint: disable=invalid-name - self.palette = Memory(core, lib.SIZE_PALETTE_RAM, lib.BASE_PALETTE_RAM) - self.vram = Memory(core, lib.SIZE_VRAM, lib.BASE_VRAM) - self.oam = Memory(core, lib.SIZE_OAM, lib.BASE_OAM) + self.bios = Memory(core, lib.GBA_SIZE_BIOS, lib.GBA_BASE_BIOS) + self.wram = Memory(core, lib.GBA_SIZE_EWRAM, lib.GBA_BASE_EWRAM) + self.iwram = Memory(core, lib.GBA_SIZE_IWRAM, lib.GBA_BASE_IWRAM) + self.io = Memory(core, lib.GBA_SIZE_IO, lib.GBA_BASE_IO) # pylint: disable=invalid-name + self.palette = Memory(core, lib.GBA_SIZE_PALETTE_RAM, lib.GBA_BASE_PALETTE_RAM) + self.vram = Memory(core, lib.GBA_SIZE_VRAM, lib.GBA_BASE_VRAM) + self.oam = Memory(core, lib.GBA_SIZE_OAM, lib.GBA_BASE_OAM) self.cart0 = Memory(core, romSize, lib.BASE_CART0) self.cart1 = Memory(core, romSize, lib.BASE_CART1) self.cart2 = Memory(core, romSize, lib.BASE_CART2) self.cart = self.cart0 self.rom = self.cart0 - self.sram = Memory(core, lib.SIZE_CART_SRAM, lib.BASE_CART_SRAM) + self.sram = Memory(core, lib.GBA_SIZE_SRAM, lib.GBA_BASE_SRAM) class GBASprite(Sprite): diff --git a/src/platform/qt/AssetTile.cpp b/src/platform/qt/AssetTile.cpp index bd9f3a188..78db4ccd9 100644 --- a/src/platform/qt/AssetTile.cpp +++ b/src/platform/qt/AssetTile.cpp @@ -51,8 +51,8 @@ void AssetTile::setController(std::shared_ptr controller) { #ifdef M_CORE_GBA case mPLATFORM_GBA: m_addressWidth = 8; - m_addressBase = BASE_VRAM; - m_boundaryBase = BASE_VRAM | 0x10000; + m_addressBase = GBA_BASE_VRAM; + m_boundaryBase = GBA_BASE_VRAM | 0x10000; break; #endif #ifdef M_CORE_GB diff --git a/src/platform/qt/IOViewer.cpp b/src/platform/qt/IOViewer.cpp index 46c0d8dbb..f89b6c091 100644 --- a/src/platform/qt/IOViewer.cpp +++ b/src/platform/qt/IOViewer.cpp @@ -1580,7 +1580,7 @@ IOViewer::IOViewer(std::shared_ptr controller, QWidget* parent) case mPLATFORM_GBA: regs = GBAIORegisterNames; maxRegs = REG_MAX >> 1; - m_base = BASE_IO; + m_base = GBA_BASE_IO; m_width = 1; break; #endif diff --git a/src/platform/qt/MapView.cpp b/src/platform/qt/MapView.cpp index ba2255ee9..da65abf9d 100644 --- a/src/platform/qt/MapView.cpp +++ b/src/platform/qt/MapView.cpp @@ -43,7 +43,7 @@ MapView::MapView(std::shared_ptr controller, QWidget* parent) case mPLATFORM_GBA: m_boundary = 2048; m_ui.tile->setMaxTile(3096); - m_addressBase = BASE_VRAM; + m_addressBase = GBA_BASE_VRAM; m_addressWidth = 8; m_ui.bgInfo->addCustomProperty("priority", tr("Priority")); m_ui.bgInfo->addCustomProperty("screenBase", tr("Map base")); diff --git a/src/platform/qt/ObjView.cpp b/src/platform/qt/ObjView.cpp index f1b605c0d..f4482aba6 100644 --- a/src/platform/qt/ObjView.cpp +++ b/src/platform/qt/ObjView.cpp @@ -157,7 +157,7 @@ void ObjView::updateTilesGBA(bool force) { m_ui.w->setText(QString::number(newInfo.width * 8)); m_ui.h->setText(QString::number(newInfo.height * 8)); - m_ui.address->setText(tr("0x%0").arg(BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0'))); + m_ui.address->setText(tr("0x%0").arg(GBA_BASE_OAM + m_objId * sizeof(*obj), 8, 16, QChar('0'))); m_ui.priority->setText(QString::number(newInfo.priority)); m_ui.flippedH->setChecked(newInfo.hflip); m_ui.flippedV->setChecked(newInfo.vflip); diff --git a/src/platform/qt/SaveConverter.cpp b/src/platform/qt/SaveConverter.cpp index fd242af98..712396517 100644 --- a/src/platform/qt/SaveConverter.cpp +++ b/src/platform/qt/SaveConverter.cpp @@ -197,20 +197,20 @@ void SaveConverter::detectFromSavestate(VFile* vf) { void SaveConverter::detectFromSize(std::shared_ptr vf) { #ifdef M_CORE_GBA switch (vf->size()) { - case SIZE_CART_SRAM: + case GBA_SIZE_SRAM: m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, vf}); break; - case SIZE_CART_FLASH512: + case GBA_SIZE_FLASH512: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, vf}); break; - case SIZE_CART_FLASH1M: + case GBA_SIZE_FLASH1M: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, vf}); break; - case SIZE_CART_EEPROM: + case GBA_SIZE_EEPROM: m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, vf, Endian::LITTLE}); m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, vf, Endian::BIG}); break; - case SIZE_CART_EEPROM512: + case GBA_SIZE_EEPROM512: m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, vf, Endian::LITTLE}); m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, vf, Endian::BIG}); break; @@ -267,13 +267,13 @@ void SaveConverter::detectFromHeaders(std::shared_ptr vf) { if (data) { QByteArray bytes = QByteArray::fromRawData(static_cast(data), size); bytes.data(); // Trigger a deep copy before we delete the backing - if (size == SIZE_CART_FLASH1M) { + if (size == GBA_SIZE_FLASH1M) { m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::SHARKPORT}); } else { - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(SIZE_CART_SRAM)), Endian::NONE, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, std::make_shared(bytes.left(SIZE_CART_FLASH512)), Endian::NONE, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(SIZE_CART_EEPROM)), Endian::BIG, Container::SHARKPORT}); - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(SIZE_CART_EEPROM512)), Endian::BIG, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, std::make_shared(bytes.left(GBA_SIZE_FLASH512)), Endian::NONE, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::SHARKPORT}); + m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::SHARKPORT}); } free(data); } @@ -284,21 +284,21 @@ void SaveConverter::detectFromHeaders(std::shared_ptr vf) { QByteArray bytes = QByteArray::fromRawData(static_cast(data), size); bytes.data(); // Trigger a deep copy before we delete the backing switch (size) { - case SIZE_CART_FLASH1M: + case GBA_SIZE_FLASH1M: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); break; - case SIZE_CART_FLASH512: + case GBA_SIZE_FLASH512: m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH512, std::make_shared(bytes), Endian::NONE, Container::GSV}); m_validSaves.append(AnnotatedSave{SAVEDATA_FLASH1M, std::make_shared(bytes), Endian::NONE, Container::GSV}); break; - case SIZE_CART_SRAM: - m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(SIZE_CART_SRAM)), Endian::NONE, Container::GSV}); + case GBA_SIZE_SRAM: + m_validSaves.append(AnnotatedSave{SAVEDATA_SRAM, std::make_shared(bytes.left(GBA_SIZE_SRAM)), Endian::NONE, Container::GSV}); break; - case SIZE_CART_EEPROM: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(SIZE_CART_EEPROM)), Endian::BIG, Container::GSV}); + case GBA_SIZE_EEPROM: + m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM, std::make_shared(bytes.left(GBA_SIZE_EEPROM)), Endian::BIG, Container::GSV}); break; - case SIZE_CART_EEPROM512: - m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(SIZE_CART_EEPROM512)), Endian::BIG, Container::GSV}); + case GBA_SIZE_EEPROM512: + m_validSaves.append(AnnotatedSave{SAVEDATA_EEPROM512, std::make_shared(bytes.left(GBA_SIZE_EEPROM512)), Endian::BIG, Container::GSV}); break; } free(data); From 6bea763b23f36b9983e603b440e6cbe0aa57a714 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 21:38:00 -0800 Subject: [PATCH 128/159] Scripting: Allow struct access to inline strings --- src/script/test/classes.c | 15 +++++++++++++++ src/script/types.c | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/src/script/test/classes.c b/src/script/test/classes.c index 95bb32920..85afb627c 100644 --- a/src/script/test/classes.c +++ b/src/script/test/classes.c @@ -14,6 +14,7 @@ struct TestA { int32_t i2; int8_t b8; int16_t hUnaligned; + char str[6]; struct mScriptValue table; struct mScriptList list; int32_t (*ifn0)(struct TestA*); @@ -105,6 +106,7 @@ mSCRIPT_DEFINE_STRUCT(TestA) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S32, i2) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S8, b8) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, S16, hUnaligned) + mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, CHARP, str) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, TABLE, table) mSCRIPT_DEFINE_STRUCT_MEMBER(TestA, LIST, list) mSCRIPT_DEFINE_STRUCT_METHOD(TestA, ifn0) @@ -191,6 +193,13 @@ M_TEST_DEFINE(testALayout) { assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_S16); assert_int_not_equal(member->offset, sizeof(int32_t) * 2 + 1); + member = HashTableLookup(&cls->instanceMembers, "str"); + assert_non_null(member); + assert_string_equal(member->name, "str"); + assert_null(member->docstring); + assert_ptr_equal(member->type, mSCRIPT_TYPE_MS_CHARP); + assert_int_equal(member->offset, &((struct TestA*) 0)->str); + member = HashTableLookup(&cls->instanceMembers, "table"); assert_non_null(member); assert_string_equal(member->name, "table"); @@ -304,6 +313,8 @@ M_TEST_DEFINE(testAGet) { s.table.type = mSCRIPT_TYPE_MS_TABLE; s.table.type->alloc(&s.table); + strcpy(s.str, "test"); + struct mScriptValue sval = mSCRIPT_MAKE_S(TestA, &s); struct mScriptValue val; struct mScriptValue compare; @@ -324,6 +335,10 @@ M_TEST_DEFINE(testAGet) { assert_true(mScriptObjectGet(&sval, "hUnaligned", &val)); assert_true(compare.type->equal(&compare, &val)); + compare = mSCRIPT_MAKE_CHARP("test"); + assert_true(mScriptObjectGet(&sval, "str", &val)); + assert_true(compare.type->equal(&compare, &val)); + compare = mSCRIPT_MAKE_S32(5); assert_true(mScriptObjectGet(&sval, "list", &val)); assert_ptr_equal(val.type, mSCRIPT_TYPE_MS_LIST); diff --git a/src/script/types.c b/src/script/types.c index dc9153587..0b35fc58a 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -1178,6 +1178,15 @@ static bool _accessRawMember(struct mScriptClassMember* member, void* raw, bool val->type = mSCRIPT_TYPE_MS_WRAPPER; val->value.table = raw; break; + case mSCRIPT_TYPE_STRING: + if (member->type == mSCRIPT_TYPE_MS_CHARP) { + val->refs = mSCRIPT_VALUE_UNREF; + val->flags = 0; + val->type = mSCRIPT_TYPE_MS_CHARP; + val->value.opaque = raw; + break; + } + return false; case mSCRIPT_TYPE_LIST: val->refs = mSCRIPT_VALUE_UNREF; val->flags = 0; From dbffb46c4e7d2e7a2cbed7c3488cece4c2176d4c Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 21:49:48 -0800 Subject: [PATCH 129/159] Wii: Fix build --- src/platform/wii/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/wii/main.c b/src/platform/wii/main.c index 312388347..cdfe00b2f 100644 --- a/src/platform/wii/main.c +++ b/src/platform/wii/main.c @@ -271,7 +271,7 @@ int main(int argc, char* argv[]) { memset(audioBuffer, 0, sizeof(audioBuffer)); #ifdef FIXED_ROM_BUFFER - romBufferSize = SIZE_CART0; + romBufferSize = GBA_SIZE_ROM0; romBuffer = SYS_GetArena2Lo(); SYS_SetArena2Lo((void*)((intptr_t) romBuffer + romBufferSize)); #endif From 6d8060034f3471de2a98d90a0e9a5d7b93d410b1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 27 Jan 2023 19:32:27 -0800 Subject: [PATCH 130/159] Qt: Disable sync while running scripts from main thread (fixes #2738) --- CHANGES | 1 + src/platform/qt/scripting/ScriptingController.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 4803ef370..9b971cbe8 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Emulation fixes: - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) + - Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index cd1e716d4..5e92a81aa 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "scripting/ScriptingController.h" +#include "AudioProcessor.h" #include "CoreController.h" #include "scripting/ScriptingTextBuffer.h" #include "scripting/ScriptingTextBufferModel.h" @@ -73,11 +74,20 @@ bool ScriptingController::load(VFileDevice& vf, const QString& name) { } QByteArray utf8 = name.toUtf8(); CoreController::Interrupter interrupter(m_controller); + if (m_controller) { + m_controller->setSync(false); + m_controller->unpaused(); + } + bool ok = true; if (!m_activeEngine->load(m_activeEngine, utf8.constData(), vf) || !m_activeEngine->run(m_activeEngine)) { emit error(QString::fromUtf8(m_activeEngine->getError(m_activeEngine))); - return false; + ok = false; } - return true; + if (m_controller && m_controller->isPaused()) { + m_controller->setSync(true); + m_controller->paused(); + } + return ok; } void ScriptingController::clearController() { From fc2b94f9f7fabe454843a8433970ca0339ad3bef Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 04:03:32 -0800 Subject: [PATCH 131/159] Scripting: Allow basic pointer following --- include/mgba/script/types.h | 2 ++ src/script/engines/lua.c | 25 ++++++++++++++++++++++++ src/script/test/lua.c | 39 +++++++++++++++++++++++++++++++++++++ src/script/types.c | 15 ++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/include/mgba/script/types.h b/include/mgba/script/types.h index 556fff57b..ed8ae02e3 100644 --- a/include/mgba/script/types.h +++ b/include/mgba/script/types.h @@ -304,6 +304,8 @@ void mScriptValueWrap(struct mScriptValue* val, struct mScriptValue* out); struct mScriptValue* mScriptValueUnwrap(struct mScriptValue* val); const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* val); +void mScriptValueFollowPointer(struct mScriptValue* ptr, struct mScriptValue* out); + struct mScriptValue* mScriptStringCreateEmpty(size_t size); struct mScriptValue* mScriptStringCreateFromBytes(const void* string, size_t size); struct mScriptValue* mScriptStringCreateFromUTF8(const char* string); diff --git a/src/script/engines/lua.c b/src/script/engines/lua.c index 3c7266a2a..db7a70075 100644 --- a/src/script/engines/lua.c +++ b/src/script/engines/lua.c @@ -693,6 +693,31 @@ bool _luaWrap(struct mScriptEngineContextLua* luaContext, struct mScriptValue* v return true; } } + struct mScriptValue derefPtr; + if (value->type->base == mSCRIPT_TYPE_OPAQUE) { + if (!value->type->details.type) { + return false; + } + mScriptValueFollowPointer(value, &derefPtr); + switch (derefPtr.type->base) { + case mSCRIPT_TYPE_VOID: + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + value = &derefPtr; + break; + case mSCRIPT_TYPE_OBJECT: + value = mScriptValueAlloc(derefPtr.type); + value->value.opaque = derefPtr.value.opaque; + weakref = mScriptContextSetWeakref(luaContext->d.context, value); + needsWeakref = true; + mScriptContextDisownWeakref(luaContext->d.context, weakref); + mScriptValueDeref(value); + break; + default: + return false; + } + } if (value->type == mSCRIPT_TYPE_MS_WEAKREF) { weakref = value->value.u32; value = mScriptContextAccessWeakref(luaContext->d.context, value); diff --git a/src/script/test/lua.c b/src/script/test/lua.c index 2c047ec39..5731b0705 100644 --- a/src/script/test/lua.c +++ b/src/script/test/lua.c @@ -22,6 +22,7 @@ struct Test { void (*vfn0)(struct Test*); void (*vfn1)(struct Test*, int); int32_t (*icfn0)(const struct Test*); + struct Test* next; }; static int identityInt(int in) { @@ -83,6 +84,7 @@ mSCRIPT_DECLARE_STRUCT_VOID_METHOD(Test, v1, testV1, 1, S32, b); mSCRIPT_DEFINE_STRUCT(Test) mSCRIPT_DEFINE_STRUCT_MEMBER(Test, S32, i) + mSCRIPT_DEFINE_STRUCT_MEMBER(Test, PS(Test), next) mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn0) mSCRIPT_DEFINE_STRUCT_METHOD(Test, ifn1) mSCRIPT_DEFINE_STRUCT_METHOD(Test, icfn0) @@ -726,6 +728,42 @@ M_TEST_DEFINE(callList) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(linkedList) { + SETUP_LUA; + + struct Test first = { + .i = 1 + }; + struct Test second = { + .i = 2 + }; + struct mScriptValue a = mSCRIPT_MAKE_S(Test, &first); + + assert_true(lua->setGlobal(lua, "l", &a)); + TEST_PROGRAM("assert(l)"); + TEST_PROGRAM("assert(l.i == 1)"); + TEST_PROGRAM("assert(not l.next)"); + + first.next = &second; + TEST_PROGRAM("assert(l)"); + TEST_PROGRAM("assert(l.i == 1)"); + TEST_PROGRAM("assert(l.next)"); + TEST_PROGRAM("assert(l.next.i == 2)"); + TEST_PROGRAM("assert(not l.next.next)"); + + TEST_PROGRAM( + "n = l.next\n" + "function readN()\n" + " assert(n)\n" + " assert(n.i or not n.i)\n" + "end\n" + "assert(pcall(readN))\n"); + // The weakref stored in `n` gets pruned between executions to avoid stale pointers + TEST_PROGRAM("assert(not pcall(readN))"); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(create), cmocka_unit_test(loadGood), @@ -744,4 +782,5 @@ M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptLua, cmocka_unit_test(tableLookup), cmocka_unit_test(tableIterate), cmocka_unit_test(callList), + cmocka_unit_test(linkedList) ) diff --git a/src/script/types.c b/src/script/types.c index 0b35fc58a..279351535 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -853,6 +853,21 @@ const struct mScriptValue* mScriptValueUnwrapConst(const struct mScriptValue* va return NULL; } +void mScriptValueFollowPointer(struct mScriptValue* ptr, struct mScriptValue* out) { + if (ptr->type->base != mSCRIPT_TYPE_OPAQUE || !ptr->type->details.type) { + return; + } + + out->value.opaque = *(void**) ptr->value.opaque; + if (out->value.opaque) { + out->type = ptr->type->details.type; + } else { + out->type = mSCRIPT_TYPE_MS_VOID; + } + out->refs = mSCRIPT_VALUE_UNREF; + out->flags = 0; +} + struct mScriptValue* mScriptStringCreateEmpty(size_t size) { struct mScriptValue* val = mScriptValueAlloc(mSCRIPT_TYPE_MS_STR); struct mScriptString* internal = val->value.opaque; From fcfab847fc73bb0e2f6784cd928dd69aa5747f45 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 2 Jan 2023 01:04:50 -0800 Subject: [PATCH 132/159] Scripting: Add callback arguments --- include/mgba/script/context.h | 2 +- src/core/scripting.c | 2 +- src/core/thread.c | 10 +++++----- src/script/context.c | 5 ++++- src/script/test/stdlib.c | 6 +++--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/include/mgba/script/context.h b/include/mgba/script/context.h index 396084f14..dcec138e1 100644 --- a/include/mgba/script/context.h +++ b/include/mgba/script/context.h @@ -96,7 +96,7 @@ void mScriptContextAttachSocket(struct mScriptContext* context); void mScriptContextExportConstants(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* constants); void mScriptContextExportNamespace(struct mScriptContext* context, const char* nspace, struct mScriptKVPair* value); -void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback); +void mScriptContextTriggerCallback(struct mScriptContext*, const char* callback, struct mScriptList* args); uint32_t mScriptContextAddCallback(struct mScriptContext*, const char* callback, struct mScriptValue* value); void mScriptContextRemoveCallback(struct mScriptContext*, uint32_t cbid); diff --git a/src/core/scripting.c b/src/core/scripting.c index 1eca1159b..a67297598 100644 --- a/src/core/scripting.c +++ b/src/core/scripting.c @@ -651,7 +651,7 @@ static struct mScriptValue* _mScriptCoreAdapterGet(struct mScriptCoreAdapter* ad static void _mScriptCoreAdapterReset(struct mScriptCoreAdapter* adapter) { adapter->core->reset(adapter->core); - mScriptContextTriggerCallback(adapter->context, "reset"); + mScriptContextTriggerCallback(adapter->context, "reset", NULL); } mSCRIPT_DECLARE_STRUCT(mScriptCoreAdapter); diff --git a/src/core/thread.c b/src/core/thread.c index 7101b5a54..2753a45e7 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -194,7 +194,7 @@ void _script_ ## NAME(void* context) { \ if (!threadContext->scriptContext) { \ return; \ } \ - mScriptContextTriggerCallback(threadContext->scriptContext, #NAME); \ + mScriptContextTriggerCallback(threadContext->scriptContext, #NAME, NULL); \ } ADD_CALLBACK(frame) @@ -283,7 +283,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } } if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "start"); + mScriptContextTriggerCallback(scriptContext, "start", NULL); } #endif @@ -304,7 +304,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } } if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "reset"); + mScriptContextTriggerCallback(scriptContext, "reset", NULL); } #endif @@ -404,7 +404,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } #ifdef ENABLE_SCRIPTING if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "reset"); + mScriptContextTriggerCallback(scriptContext, "reset", NULL); } #endif } @@ -428,7 +428,7 @@ static THREAD_ENTRY _mCoreThreadRun(void* context) { } #ifdef ENABLE_SCRIPTING if (scriptContext) { - mScriptContextTriggerCallback(scriptContext, "shutdown"); + mScriptContextTriggerCallback(scriptContext, "shutdown", NULL); mScriptContextDetachCore(scriptContext); } #endif diff --git a/src/script/context.c b/src/script/context.c index 14775b05c..bf5fcfa54 100644 --- a/src/script/context.c +++ b/src/script/context.c @@ -211,7 +211,7 @@ void mScriptContextDisownWeakref(struct mScriptContext* context, uint32_t weakre poolEntry->refs = mSCRIPT_VALUE_UNREF; } -void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback) { +void mScriptContextTriggerCallback(struct mScriptContext* context, const char* callback, struct mScriptList* args) { struct mScriptValue* list = HashTableLookup(&context->callbacks, callback); if (!list) { return; @@ -224,6 +224,9 @@ void mScriptContextTriggerCallback(struct mScriptContext* context, const char* c continue; } mScriptFrameInit(&frame); + if (args) { + mScriptListCopy(&frame.arguments, args); + } if (fn->type->base == mSCRIPT_TYPE_WRAPPER) { fn = mScriptValueUnwrap(fn); } diff --git a/src/script/test/stdlib.c b/src/script/test/stdlib.c index 63ddda4fc..82a4c425b 100644 --- a/src/script/test/stdlib.c +++ b/src/script/test/stdlib.c @@ -82,15 +82,15 @@ M_TEST_DEFINE(callbacks) { TEST_VALUE(S32, "val", 0); - mScriptContextTriggerCallback(&context, "test"); + mScriptContextTriggerCallback(&context, "test", NULL); TEST_VALUE(S32, "val", 1); - mScriptContextTriggerCallback(&context, "test"); + mScriptContextTriggerCallback(&context, "test", NULL); TEST_VALUE(S32, "val", 2); TEST_PROGRAM("callbacks:remove(id)"); - mScriptContextTriggerCallback(&context, "test"); + mScriptContextTriggerCallback(&context, "test", NULL); TEST_VALUE(S32, "val", 2); mScriptContextDeinit(&context); From e5ed2b4119bf82b9832080333cbbfa0755ce1c8e Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 2 Jan 2023 01:05:41 -0800 Subject: [PATCH 133/159] Scripting: Add base input, supports key events --- include/mgba/script/input.h | 217 +++++++++++++++++++++++++++++++ src/script/CMakeLists.txt | 2 + src/script/input.c | 249 ++++++++++++++++++++++++++++++++++++ src/script/test/input.c | 86 +++++++++++++ 4 files changed, 554 insertions(+) create mode 100644 include/mgba/script/input.h create mode 100644 src/script/input.c create mode 100644 src/script/test/input.c diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h new file mode 100644 index 000000000..f81ecda45 --- /dev/null +++ b/include/mgba/script/input.h @@ -0,0 +1,217 @@ +/* Copyright (c) 2013-2022 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 M_SCRIPT_INPUT_H +#define M_SCRIPT_INPUT_H + +#include + +#include +#include + +CXX_GUARD_START + +enum mScriptEventType { + mSCRIPT_EV_TYPE_NONE = 0, + mSCRIPT_EV_TYPE_KEY, + mSCRIPT_EV_TYPE_MOUSE_BUTTON, + mSCRIPT_EV_TYPE_MOUSE_MOVE, + mSCRIPT_EV_TYPE_MOUSE_WHEEL, + mSCRIPT_EV_TYPE_GAMEPAD_BUTTON, + mSCRIPT_EV_TYPE_GAMEPAD_HAT, + mSCRIPT_EV_TYPE_TRIGGER, + mSCRIPT_EV_TYPE_MAX +}; + +enum mScriptInputState { + mSCRIPT_INPUT_STATE_UP = 0, + mSCRIPT_INPUT_STATE_DOWN = 1, + mSCRIPT_INPUT_STATE_HELD = 2, +}; + +enum mScriptInputDirection { + mSCRIPT_INPUT_DIR_NONE = 0, + + mSCRIPT_INPUT_DIR_NORTH = 1, + mSCRIPT_INPUT_DIR_EAST = 2, + mSCRIPT_INPUT_DIR_SOUTH = 4, + mSCRIPT_INPUT_DIR_WEST = 8, + + mSCRIPT_INPUT_DIR_UP = mSCRIPT_INPUT_DIR_NORTH, + mSCRIPT_INPUT_DIR_RIGHT = mSCRIPT_INPUT_DIR_EAST, + mSCRIPT_INPUT_DIR_DOWN = mSCRIPT_INPUT_DIR_SOUTH, + mSCRIPT_INPUT_DIR_LEFT = mSCRIPT_INPUT_DIR_WEST, + + mSCRIPT_INPUT_DIR_NORTHEAST = mSCRIPT_INPUT_DIR_NORTH | mSCRIPT_INPUT_DIR_EAST, + mSCRIPT_INPUT_DIR_NORTHWEST = mSCRIPT_INPUT_DIR_NORTH | mSCRIPT_INPUT_DIR_WEST, + mSCRIPT_INPUT_DIR_SOUTHEAST = mSCRIPT_INPUT_DIR_SOUTH | mSCRIPT_INPUT_DIR_EAST, + mSCRIPT_INPUT_DIR_SOUTHWEST = mSCRIPT_INPUT_DIR_SOUTH | mSCRIPT_INPUT_DIR_WEST, +}; + +enum mScriptKeyModifier { + mSCRIPT_KMOD_NONE = 0, + + mSCRIPT_KMOD_LSHIFT = 0x1, + mSCRIPT_KMOD_RSHIFT = 0x2, + mSCRIPT_KMOD_SHIFT = 0x3, + + mSCRIPT_KMOD_LCONTROL = 0x4, + mSCRIPT_KMOD_RCONTROL = 0x8, + mSCRIPT_KMOD_CONTROL = 0xC, + + mSCRIPT_KMOD_LALT = 0x10, + mSCRIPT_KMOD_RALT = 0x20, + mSCRIPT_KMOD_ALT = 0x30, + + mSCRIPT_KMOD_LSUPER = 0x40, + mSCRIPT_KMOD_RSUPER = 0x80, + mSCRIPT_KMOD_SUPER = 0xC0, + + mSCRIPT_KMOD_CAPS_LOCK = 0x100, + mSCRIPT_KMOD_NUM_LOCK = 0x200, + mSCRIPT_KMOD_SCROLL_LOCK = 0x400, +}; + +#define mSCRIPT_KEYBASE 0x200000 + +enum mScriptKey { + mSCRIPT_KEY_NONE = 0, + + mSCRIPT_KEY_BACKSPACE = 0x000008, + mSCRIPT_KEY_TAB = 0x000009, + mSCRIPT_KEY_LINE_FEED = 0x00000A, + mSCRIPT_KEY_ESCAPE = 0x00001B, + mSCRIPT_KEY_DELETE = 0x00007F, + + mSCRIPT_KEY_F1 = mSCRIPT_KEYBASE | 1, + mSCRIPT_KEY_F2, + mSCRIPT_KEY_F3, + mSCRIPT_KEY_F4, + mSCRIPT_KEY_F5, + mSCRIPT_KEY_F6, + mSCRIPT_KEY_F7, + mSCRIPT_KEY_F8, + mSCRIPT_KEY_F9, + mSCRIPT_KEY_F10, + mSCRIPT_KEY_F11, + mSCRIPT_KEY_F12, + mSCRIPT_KEY_F13, + mSCRIPT_KEY_F14, + mSCRIPT_KEY_F15, + mSCRIPT_KEY_F16, + mSCRIPT_KEY_F17, + mSCRIPT_KEY_F18, + mSCRIPT_KEY_F19, + mSCRIPT_KEY_F20, + mSCRIPT_KEY_F21, + mSCRIPT_KEY_F22, + mSCRIPT_KEY_F23, + mSCRIPT_KEY_F24, + + mSCRIPT_KEY_UP = mSCRIPT_KEYBASE | 0x20, + mSCRIPT_KEY_RIGHT, + mSCRIPT_KEY_DOWN, + mSCRIPT_KEY_LEFT, + mSCRIPT_KEY_PAGE_UP, + mSCRIPT_KEY_PAGE_DOWN, + mSCRIPT_KEY_HOME, + mSCRIPT_KEY_END, + mSCRIPT_KEY_INSERT, + mSCRIPT_KEY_BREAK, + mSCRIPT_KEY_CLEAR, + mSCRIPT_KEY_PRNTSCR, + mSCRIPT_KEY_SYSRQ, + mSCRIPT_KEY_MENU, + + mSCRIPT_KEY_LSHIFT = mSCRIPT_KEYBASE | 0x30, + mSCRIPT_KEY_RSHIFT, + mSCRIPT_KEY_SHIFT, + mSCRIPT_KEY_LCONTROL, + mSCRIPT_KEY_RCONTROL, + mSCRIPT_KEY_CONTROL, + mSCRIPT_KEY_LALT, + mSCRIPT_KEY_RALT, + mSCRIPT_KEY_ALT, + mSCRIPT_KEY_LSUPER, + mSCRIPT_KEY_RSUPER, + mSCRIPT_KEY_SUPER, + mSCRIPT_KEY_CAPS_LOCK, + mSCRIPT_KEY_NUM_LOCK, + mSCRIPT_KEY_SCROLL_LOCK, +}; + +struct mScriptEvent { + int32_t type; + int32_t reserved; + uint64_t seq; +}; + +struct mScriptKeyEvent { + struct mScriptEvent d; + uint8_t state; + uint8_t reserved; + uint16_t modifiers; + uint32_t key; +}; + +struct mScriptMouseButtonEvent { + struct mScriptEvent d; + uint8_t state; + uint8_t mouse; + uint8_t button; +}; + +struct mScriptMouseMoveEvent { + struct mScriptEvent d; + bool relative; + uint8_t mouse; + int32_t x; + int32_t y; +}; + +struct mScriptMouseWheelEvent { + struct mScriptEvent d; + uint8_t mouse; + int32_t x; + int32_t y; +}; + +struct mScriptGamepadButtonEvent { + struct mScriptEvent d; + uint8_t state; + uint8_t pad; + uint16_t button; +}; + +struct mScriptGamepadHatEvent { + struct mScriptEvent d; + uint8_t pad; + uint8_t hat; + uint8_t direction; +}; + +struct mScriptTriggerEvent { + struct mScriptEvent d; + uint8_t trigger; + bool state; +}; + +mSCRIPT_DECLARE_STRUCT(mScriptEvent); +mSCRIPT_DECLARE_STRUCT(mScriptKeyEvent); +mSCRIPT_DECLARE_STRUCT(mScriptMouseButtonEvent); +mSCRIPT_DECLARE_STRUCT(mScriptMouseMoveEvent); +mSCRIPT_DECLARE_STRUCT(mScriptMouseWheelEvent); +mSCRIPT_DECLARE_STRUCT(mScriptGamepadButtonEvent); +mSCRIPT_DECLARE_STRUCT(mScriptGamepadHatEvent); +mSCRIPT_DECLARE_STRUCT(mScriptSensorEvent); +mSCRIPT_DECLARE_STRUCT(mScriptTriggerEvent); + +void mScriptContextAttachInput(struct mScriptContext* context); + +void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*); + +CXX_GUARD_END + +#endif diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 504736f2f..d84659453 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -1,6 +1,7 @@ include(ExportDirectory) set(SOURCE_FILES context.c + input.c socket.c stdlib.c types.c) @@ -13,6 +14,7 @@ if(USE_LUA) list(APPEND SOURCE_FILES engines/lua.c) list(APPEND TEST_FILES test/context.c + test/input.c test/lua.c test/stdlib.c) endif() diff --git a/src/script/input.c b/src/script/input.c new file mode 100644 index 000000000..9436cd78c --- /dev/null +++ b/src/script/input.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2013-2022 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 + +static const char* eventNames[mSCRIPT_EV_TYPE_MAX] = { + [mSCRIPT_EV_TYPE_KEY] = "key", + [mSCRIPT_EV_TYPE_MOUSE_BUTTON] = "mouseButton", + [mSCRIPT_EV_TYPE_MOUSE_MOVE] = "mouseMove", + [mSCRIPT_EV_TYPE_MOUSE_WHEEL] = "mouseWheel", + [mSCRIPT_EV_TYPE_GAMEPAD_BUTTON] = "gamepadButton", + [mSCRIPT_EV_TYPE_GAMEPAD_HAT] = "gamepadHat", + [mSCRIPT_EV_TYPE_TRIGGER] = "trigger", +}; + +static const struct mScriptType* eventTypes[mSCRIPT_EV_TYPE_MAX] = { + [mSCRIPT_EV_TYPE_KEY] = mSCRIPT_TYPE_MS_S(mScriptKeyEvent), + [mSCRIPT_EV_TYPE_MOUSE_BUTTON] = mSCRIPT_TYPE_MS_S(mScriptMouseButtonEvent), + [mSCRIPT_EV_TYPE_MOUSE_MOVE] = mSCRIPT_TYPE_MS_S(mScriptMouseMoveEvent), + [mSCRIPT_EV_TYPE_MOUSE_WHEEL] = mSCRIPT_TYPE_MS_S(mScriptMouseWheelEvent), + [mSCRIPT_EV_TYPE_GAMEPAD_BUTTON] = mSCRIPT_TYPE_MS_S(mScriptGamepadButtonEvent), + [mSCRIPT_EV_TYPE_GAMEPAD_HAT] = mSCRIPT_TYPE_MS_S(mScriptGamepadHatEvent), +}; + +struct mScriptInputContext { + struct Table activeKeys; +}; + +static void _mScriptInputDeinit(struct mScriptInputContext*); +static bool _mScriptInputIsKeyActive(const struct mScriptInputContext*, struct mScriptValue*); + +mSCRIPT_DECLARE_STRUCT(mScriptInputContext); +mSCRIPT_DECLARE_STRUCT_VOID_METHOD(mScriptInputContext, _deinit, _mScriptInputDeinit, 0); +mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, BOOL, isKeyActive, _mScriptInputIsKeyActive, 1, WRAPPER, key); + +mSCRIPT_DEFINE_STRUCT(mScriptInputContext) + mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptInputContext) + mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptEvent, S32, type) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptEvent, U64, seq) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptKeyEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, U8, state) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, S16, modifiers) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, S32, key) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptMouseButtonEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, mouse) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, state) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, button) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptMouseMoveEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, U8, mouse) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, S32, x) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, S32, y) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptMouseWheelEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, U8, mouse) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, S32, x) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, S32, y) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptGamepadButtonEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U8, state) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U8, pad) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U16, button) +mSCRIPT_DEFINE_END; + +mSCRIPT_DEFINE_STRUCT(mScriptGamepadHatEvent) + mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, pad) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, hat) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, direction) +mSCRIPT_DEFINE_END; + +void mScriptContextAttachInput(struct mScriptContext* context) { + struct mScriptInputContext* inputContext = calloc(1, sizeof(*inputContext)); + struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptInputContext)); + value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; + value->value.opaque = inputContext; + + TableInit(&inputContext->activeKeys, 0, NULL); + + mScriptContextSetGlobal(context, "input", value); + mScriptContextSetDocstring(context, "input", "Singleton instance of struct::mScriptInputContext"); + + mScriptContextExportConstants(context, "EV_TYPE", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, KEY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, MOUSE_BUTTON), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, MOUSE_MOVE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, MOUSE_WHEEL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, GAMEPAD_BUTTON), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_EV_TYPE, TRIGGER), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "INPUT_STATE", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_STATE, UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_STATE, DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_STATE, HELD), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "INPUT_DIR", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NORTH), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, EAST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, SOUTH), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, WEST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, RIGHT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, LEFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NORTHEAST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, NORTHWEST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, SOUTHEAST), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_INPUT_DIR, SOUTHWEST), + mSCRIPT_KV_SENTINEL + }); + + mScriptContextExportConstants(context, "KMOD", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, CONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, ALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, RSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, CAPS_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, NUM_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SCROLL_LOCK), + mSCRIPT_KV_SENTINEL + }); +} + +void _mScriptInputDeinit(struct mScriptInputContext* context) { + TableDeinit(&context->activeKeys); +} + +bool _mScriptInputIsKeyActive(const struct mScriptInputContext* context, struct mScriptValue* value) { + uint32_t key; + struct mScriptValue intValue; + size_t length; + const char* strbuf; + + switch (value->type->base) { + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + if (!mScriptCast(mSCRIPT_TYPE_MS_U32, value, &intValue)) { + return false; + } + key = intValue.value.u32; + break; + case mSCRIPT_TYPE_STRING: + if (value->value.string->length > 1) { + return false; + } + strbuf = value->value.string->buffer; + length = value->value.string->size; + key = utf8Char(&strbuf, &length); + break; + default: + return false; + } + + void* down = TableLookup(&context->activeKeys, key); + return down != NULL; +} + +static bool _updateKeys(struct mScriptContext* context, struct mScriptKeyEvent* event) { + int offset = 0; + switch (event->state) { + case mSCRIPT_INPUT_STATE_UP: + offset = -1; + break; + case mSCRIPT_INPUT_STATE_DOWN: + offset = 1; + break; + default: + return true; + } + + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + intptr_t value = (intptr_t) TableLookup(&inputContext->activeKeys, event->key); + value += offset; + if (value < 1) { + TableRemove(&inputContext->activeKeys, event->key); + } else { + TableInsert(&inputContext->activeKeys, event->key, (void*) value); + } + if (offset < 0 && value > 0) { + return false; + } + if (offset > 0 && value != 1) { + event->state = mSCRIPT_INPUT_STATE_HELD; + } + return true; +} + +void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent* event) { + switch (event->type) { + case mSCRIPT_EV_TYPE_KEY: + if (!_updateKeys(context, (struct mScriptKeyEvent*) event)) { + return; + } + break; + case mSCRIPT_EV_TYPE_NONE: + return; + } + + struct mScriptList args; + mScriptListInit(&args, 1); + struct mScriptValue* value = mScriptListAppend(&args); + value->type = eventTypes[event->type]; + value->refs = mSCRIPT_VALUE_UNREF; + value->flags = 0; + value->value.opaque = event; + mScriptContextTriggerCallback(context, eventNames[event->type], &args); + mScriptListDeinit(&args); +} diff --git a/src/script/test/input.c b/src/script/test/input.c new file mode 100644 index 000000000..1d4c85662 --- /dev/null +++ b/src/script/test/input.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2013-2022 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 "util/test/suite.h" + +#include +#include +#include +#include + +#include "script/test.h" + +#define SETUP_LUA \ + struct mScriptContext context; \ + mScriptContextInit(&context); \ + struct mScriptEngineContext* lua = mScriptContextRegisterEngine(&context, mSCRIPT_ENGINE_LUA); \ + mScriptContextAttachStdlib(&context); \ + mScriptContextAttachInput(&context) + +M_TEST_SUITE_SETUP(mScriptInput) { + if (mSCRIPT_ENGINE_LUA->init) { + mSCRIPT_ENGINE_LUA->init(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_SUITE_TEARDOWN(mScriptInput) { + if (mSCRIPT_ENGINE_LUA->deinit) { + mSCRIPT_ENGINE_LUA->deinit(mSCRIPT_ENGINE_LUA); + } + return 0; +} + +M_TEST_DEFINE(members) { + SETUP_LUA; + + TEST_PROGRAM("assert(input)"); + TEST_PROGRAM("assert(input.isKeyActive)"); + + mScriptContextDeinit(&context); +} + +M_TEST_DEFINE(fireKey) { + SETUP_LUA; + + TEST_PROGRAM("assert(not input:isKeyActive('a'))"); + + TEST_PROGRAM( + "activeKey = false\n" + "state = nil\n" + "function cb(ev)\n" + " assert(ev.type == C.EV_TYPE.KEY)\n" + " activeKey = string.char(ev.key)\n" + " state = ev.state\n" + "end\n" + "id = callbacks:add('key', cb)\n" + "assert(id)\n" + "assert(not activeKey)\n" + ); + + struct mScriptKeyEvent keyEvent = { + .d = { .type = mSCRIPT_EV_TYPE_KEY }, + .state = mSCRIPT_INPUT_STATE_DOWN, + .key = 'a' + }; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(input:isKeyActive('a'))"); + TEST_PROGRAM("assert(activeKey == 'a')"); + TEST_PROGRAM("assert(state == C.INPUT_STATE.DOWN)"); + + keyEvent.state = mSCRIPT_INPUT_STATE_UP; + mScriptContextFireEvent(&context, &keyEvent.d); + + TEST_PROGRAM("assert(not input:isKeyActive('a'))"); + TEST_PROGRAM("assert(state == C.INPUT_STATE.UP)"); + + mScriptContextDeinit(&context); +} + +M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, + cmocka_unit_test(members), + cmocka_unit_test(fireKey), +) From 47bf00da5eeb158eef1a72d6fe039315261082c0 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Mon, 2 Jan 2023 01:30:04 -0800 Subject: [PATCH 134/159] Scripting: Implement input sequence numbers --- src/script/input.c | 11 +++++++++++ src/script/test/input.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/script/input.c b/src/script/input.c index 9436cd78c..3803b4ba6 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -28,6 +28,7 @@ static const struct mScriptType* eventTypes[mSCRIPT_EV_TYPE_MAX] = { }; struct mScriptInputContext { + uint64_t seq; struct Table activeKeys; }; @@ -40,6 +41,7 @@ mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, BOOL, isKeyActive, _mScript mSCRIPT_DEFINE_STRUCT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptInputContext) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, U64, seq) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) mSCRIPT_DEFINE_END; @@ -96,6 +98,7 @@ void mScriptContextAttachInput(struct mScriptContext* context) { value->flags = mSCRIPT_VALUE_FLAG_FREE_BUFFER; value->value.opaque = inputContext; + inputContext->seq = 0; TableInit(&inputContext->activeKeys, 0, NULL); mScriptContextSetGlobal(context, "input", value); @@ -227,6 +230,12 @@ static bool _updateKeys(struct mScriptContext* context, struct mScriptKeyEvent* } void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent* event) { + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return; + } + struct mScriptInputContext* inputContext = input->value.opaque; + switch (event->type) { case mSCRIPT_EV_TYPE_KEY: if (!_updateKeys(context, (struct mScriptKeyEvent*) event)) { @@ -244,6 +253,8 @@ void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent value->refs = mSCRIPT_VALUE_UNREF; value->flags = 0; value->value.opaque = event; + event->seq = inputContext->seq; + ++inputContext->seq; mScriptContextTriggerCallback(context, eventNames[event->type], &args); mScriptListDeinit(&args); } diff --git a/src/script/test/input.c b/src/script/test/input.c index 1d4c85662..b98c73e72 100644 --- a/src/script/test/input.c +++ b/src/script/test/input.c @@ -37,10 +37,39 @@ M_TEST_DEFINE(members) { SETUP_LUA; TEST_PROGRAM("assert(input)"); + TEST_PROGRAM("assert(input.seq == 0)"); TEST_PROGRAM("assert(input.isKeyActive)"); mScriptContextDeinit(&context); } +M_TEST_DEFINE(seq) { + SETUP_LUA; + + TEST_PROGRAM("assert(input.seq == 0)"); + + TEST_PROGRAM( + "seq = nil\n" + "function cb(ev)\n" + " seq = ev.seq\n" + "end\n" + "id = callbacks:add('key', cb)\n" + ); + + struct mScriptKeyEvent keyEvent = { + .d = { .type = mSCRIPT_EV_TYPE_KEY }, + .state = mSCRIPT_INPUT_STATE_DOWN, + }; + + mScriptContextFireEvent(&context, &keyEvent.d); + TEST_PROGRAM("assert(input.seq == 1)"); + TEST_PROGRAM("assert(seq == 0)"); + + mScriptContextFireEvent(&context, &keyEvent.d); + TEST_PROGRAM("assert(input.seq == 2)"); + TEST_PROGRAM("assert(seq == 1)"); + + mScriptContextDeinit(&context); +} M_TEST_DEFINE(fireKey) { SETUP_LUA; @@ -82,5 +111,6 @@ M_TEST_DEFINE(fireKey) { M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, cmocka_unit_test(members), + cmocka_unit_test(seq), cmocka_unit_test(fireKey), ) From 697e80a5a1ac34122d94fdf101ecdacc08806467 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 Jan 2023 14:36:17 -0800 Subject: [PATCH 135/159] Qt: Start hooking up input events into scripting --- include/mgba/script/input.h | 35 ++- src/platform/qt/Display.cpp | 30 +++ src/platform/qt/Display.h | 2 + src/platform/qt/ShortcutController.cpp | 30 ++- src/platform/qt/ShortcutController.h | 3 + src/platform/qt/Window.cpp | 8 + .../qt/scripting/ScriptingController.cpp | 222 +++++++++++++++++- .../qt/scripting/ScriptingController.h | 9 +- 8 files changed, 328 insertions(+), 11 deletions(-) diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h index f81ecda45..3815433f0 100644 --- a/include/mgba/script/input.h +++ b/include/mgba/script/input.h @@ -74,14 +74,14 @@ enum mScriptKeyModifier { mSCRIPT_KMOD_SCROLL_LOCK = 0x400, }; -#define mSCRIPT_KEYBASE 0x200000 +#define mSCRIPT_KEYBASE 0x800000 enum mScriptKey { mSCRIPT_KEY_NONE = 0, mSCRIPT_KEY_BACKSPACE = 0x000008, mSCRIPT_KEY_TAB = 0x000009, - mSCRIPT_KEY_LINE_FEED = 0x00000A, + mSCRIPT_KEY_ENTER = 0x00000A, mSCRIPT_KEY_ESCAPE = 0x00001B, mSCRIPT_KEY_DELETE = 0x00007F, @@ -121,9 +121,10 @@ enum mScriptKey { mSCRIPT_KEY_INSERT, mSCRIPT_KEY_BREAK, mSCRIPT_KEY_CLEAR, - mSCRIPT_KEY_PRNTSCR, + mSCRIPT_KEY_PRINT_SCREEN, mSCRIPT_KEY_SYSRQ, mSCRIPT_KEY_MENU, + mSCRIPT_KEY_HELP, mSCRIPT_KEY_LSHIFT = mSCRIPT_KEYBASE | 0x30, mSCRIPT_KEY_RSHIFT, @@ -140,6 +141,24 @@ enum mScriptKey { mSCRIPT_KEY_CAPS_LOCK, mSCRIPT_KEY_NUM_LOCK, mSCRIPT_KEY_SCROLL_LOCK, + + mSCRIPT_KEY_KP_0 = mSCRIPT_KEYBASE | 0x40, + mSCRIPT_KEY_KP_1, + mSCRIPT_KEY_KP_2, + mSCRIPT_KEY_KP_3, + mSCRIPT_KEY_KP_4, + mSCRIPT_KEY_KP_5, + mSCRIPT_KEY_KP_6, + mSCRIPT_KEY_KP_7, + mSCRIPT_KEY_KP_8, + mSCRIPT_KEY_KP_9, + mSCRIPT_KEY_KP_PLUS, + mSCRIPT_KEY_KP_MINUS, + mSCRIPT_KEY_KP_MULTIPLY, + mSCRIPT_KEY_KP_DIVIDE, + mSCRIPT_KEY_KP_COMMA, + mSCRIPT_KEY_KP_POINT, + mSCRIPT_KEY_KP_ENTER, }; struct mScriptEvent { @@ -151,22 +170,22 @@ struct mScriptEvent { struct mScriptKeyEvent { struct mScriptEvent d; uint8_t state; - uint8_t reserved; uint16_t modifiers; uint32_t key; }; struct mScriptMouseButtonEvent { struct mScriptEvent d; - uint8_t state; uint8_t mouse; + uint8_t context; + uint8_t state; uint8_t button; }; struct mScriptMouseMoveEvent { struct mScriptEvent d; - bool relative; uint8_t mouse; + uint8_t context; int32_t x; int32_t y; }; @@ -174,8 +193,8 @@ struct mScriptMouseMoveEvent { struct mScriptMouseWheelEvent { struct mScriptEvent d; uint8_t mouse; - int32_t x; - int32_t y; + int16_t x; + int16_t y; }; struct mScriptGamepadButtonEvent { diff --git a/src/platform/qt/Display.cpp b/src/platform/qt/Display.cpp index 7548e674c..704f9463a 100644 --- a/src/platform/qt/Display.cpp +++ b/src/platform/qt/Display.cpp @@ -10,6 +10,7 @@ #include "DisplayGL.h" #include "DisplayQt.h" #include "LogController.h" +#include "utils.h" #include @@ -169,3 +170,32 @@ void QGBA::Display::mouseMoveEvent(QMouseEvent*) { m_mouseTimer.stop(); m_mouseTimer.start(); } + +QPoint QGBA::Display::normalizedPoint(CoreController* controller, const QPoint& localRef) { + QSize screen(controller->screenDimensions()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + QSize newSize((QSizeF(size()) * devicePixelRatioF()).toSize()); +#else + QSize newSize((QSizeF(size()) * devicePixelRatio()).toSize()); +#endif + + if (m_lockAspectRatio) { + QGBA::lockAspectRatio(screen, newSize); + } + + if (m_lockIntegerScaling) { + QGBA::lockIntegerScaling(screen, newSize); + } + + QPointF newPos(localRef); + newPos -= QPointF(width() / 2.0, height() / 2.0); + newPos = QPointF(newPos.x() * screen.width(), newPos.y() * screen.height()); + newPos = QPointF(newPos.x() / newSize.width(), newPos.y() / newSize.height()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) + newPos *= devicePixelRatioF(); +#else + newPos *= devicePixelRatio(); +#endif + newPos += QPointF(screen.width() / 2.0, screen.height() / 2.0); + return newPos.toPoint(); +} diff --git a/src/platform/qt/Display.h b/src/platform/qt/Display.h index af3cf7f00..574a0e8e9 100644 --- a/src/platform/qt/Display.h +++ b/src/platform/qt/Display.h @@ -44,6 +44,8 @@ public: bool isShowOSD() const { return m_showOSD; } bool isShowFrameCounter() const { return m_showFrameCounter; } + QPoint normalizedPoint(CoreController*, const QPoint& localRef); + virtual void attach(std::shared_ptr); virtual void configure(ConfigController*); virtual void startDrawing(std::shared_ptr) = 0; diff --git a/src/platform/qt/ShortcutController.cpp b/src/platform/qt/ShortcutController.cpp index e81c01b26..ccfcb10d7 100644 --- a/src/platform/qt/ShortcutController.cpp +++ b/src/platform/qt/ShortcutController.cpp @@ -7,7 +7,9 @@ #include "ConfigController.h" #include "input/GamepadButtonEvent.h" +#include "input/GamepadHatEvent.h" #include "InputProfile.h" +#include "scripting/ScriptingController.h" #include #include @@ -32,6 +34,10 @@ void ShortcutController::setActionMapper(ActionMapper* actions) { rebuildItems(); } +void ShortcutController::setScriptingController(ScriptingController* controller) { + m_scripting = controller; +} + void ShortcutController::updateKey(const QString& name, int keySequence) { auto item = m_items[name]; if (!item) { @@ -132,9 +138,14 @@ void ShortcutController::rebuildItems() { onSubitems({}, std::bind(&ShortcutController::generateItem, this, std::placeholders::_1)); } -bool ShortcutController::eventFilter(QObject*, QEvent* event) { +bool ShortcutController::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { QKeyEvent* keyEvent = static_cast(event); +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->event(obj, event); + } +#endif if (keyEvent->isAutoRepeat()) { return false; } @@ -153,6 +164,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { } } if (event->type() == GamepadButtonEvent::Down()) { +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->event(obj, event); + } +#endif auto item = m_buttons.find(static_cast(event)->value()); if (item == m_buttons.end()) { return false; @@ -169,6 +185,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { return true; } if (event->type() == GamepadButtonEvent::Up()) { +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_scripting->event(obj, event); + } +#endif auto item = m_buttons.find(static_cast(event)->value()); if (item == m_buttons.end()) { return false; @@ -201,6 +222,13 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) { event->accept(); return true; } +#ifdef ENABLE_SCRIPTING + if (event->type() == GamepadHatEvent::Type()) { + if (m_scripting) { + m_scripting->event(obj, event); + } + } +#endif return false; } diff --git a/src/platform/qt/ShortcutController.h b/src/platform/qt/ShortcutController.h index c4db4241e..7eed7e1d7 100644 --- a/src/platform/qt/ShortcutController.h +++ b/src/platform/qt/ShortcutController.h @@ -21,6 +21,7 @@ namespace QGBA { class ConfigController; class InputProfile; +class ScriptingController; class Shortcut : public QObject { Q_OBJECT @@ -74,6 +75,7 @@ public: void setConfigController(ConfigController* controller); void setActionMapper(ActionMapper* actionMapper); + void setScriptingController(ScriptingController* scriptingController); void setProfile(const QString& profile); @@ -121,6 +123,7 @@ private: QHash> m_heldKeys; ActionMapper* m_actions = nullptr; ConfigController* m_config = nullptr; + ScriptingController* m_scripting = nullptr; QString m_profileName; const InputProfile* m_profile = nullptr; }; diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 7e25159eb..ae80b8454 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -627,8 +627,10 @@ void Window::consoleOpen() { void Window::scriptingOpen() { if (!m_scripting) { m_scripting = std::make_unique(); + m_shortcutController->setScriptingController(m_scripting.get()); if (m_controller) { m_scripting->setController(m_controller); + m_display->installEventFilter(m_scripting.get()); } } ScriptingView* view = new ScriptingView(m_scripting.get(), m_config); @@ -2125,6 +2127,12 @@ void Window::attachDisplay() { m_display->attach(m_controller); connect(m_display.get(), &QGBA::Display::drawingStarted, this, &Window::changeRenderer); m_display->startDrawing(m_controller); + +#ifdef ENABLE_SCRIPTING + if (m_scripting) { + m_display->installEventFilter(m_scripting.get()); + } +#endif } void Window::updateMute() { diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index 5e92a81aa..b6932d602 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -5,11 +5,21 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "scripting/ScriptingController.h" -#include "AudioProcessor.h" +#include +#include +#include +#include + #include "CoreController.h" +#include "Display.h" +#include "input/GamepadButtonEvent.h" +#include "input/GamepadHatEvent.h" #include "scripting/ScriptingTextBuffer.h" #include "scripting/ScriptingTextBufferModel.h" +#include +#include + using namespace QGBA; ScriptingController::ScriptingController(QObject* parent) @@ -120,10 +130,93 @@ void ScriptingController::runCode(const QString& code) { load(vf, "*prompt"); } +bool ScriptingController::eventFilter(QObject* obj, QEvent* ev) { + event(obj, ev); + return false; +} + +void ScriptingController::event(QObject* obj, QEvent* event) { + if (!m_controller) { + return; + } + + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { + struct mScriptKeyEvent ev{mSCRIPT_EV_TYPE_KEY}; + auto keyEvent = static_cast(event); + ev.state = event->type() == QEvent::KeyRelease ? mSCRIPT_INPUT_STATE_UP : + static_cast(event)->isAutoRepeat() ? mSCRIPT_INPUT_STATE_HELD : mSCRIPT_INPUT_STATE_DOWN; + ev.key = qtToScriptingKey(keyEvent); + ev.modifiers = qtToScriptingModifiers(keyEvent->modifiers()); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: { + struct mScriptMouseButtonEvent ev{mSCRIPT_EV_TYPE_MOUSE_BUTTON}; + auto mouseEvent = static_cast(event); + ev.mouse = 0; + ev.state = event->type() == QEvent::MouseButtonPress ? mSCRIPT_INPUT_STATE_DOWN : mSCRIPT_INPUT_STATE_UP; + ev.button = 31 - clz32(mouseEvent->button()); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + case QEvent::MouseMove: { + struct mScriptMouseMoveEvent ev{mSCRIPT_EV_TYPE_MOUSE_MOVE}; + auto mouseEvent = static_cast(event); + QPoint pos = mouseEvent->pos(); + pos = static_cast(obj)->normalizedPoint(m_controller.get(), pos); + ev.mouse = 0; + ev.x = pos.x(); + ev.y = pos.y(); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + case QEvent::Wheel: { + struct mScriptMouseWheelEvent ev{mSCRIPT_EV_TYPE_MOUSE_WHEEL}; + auto wheelEvent = static_cast(event); + QPoint adelta = wheelEvent->angleDelta(); + QPoint pdelta = wheelEvent->pixelDelta(); + ev.mouse = 0; + if (!pdelta.isNull()) { + ev.x = pdelta.x(); + ev.y = pdelta.y(); + } else { + ev.x = adelta.x(); + ev.y = adelta.y(); + } + mScriptContextFireEvent(&m_scriptContext, &ev.d); + return; + } + default: + break; + } + + auto type = event->type(); + if (type == GamepadButtonEvent::Down() || type == GamepadButtonEvent::Up()) { + struct mScriptGamepadButtonEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_BUTTON}; + auto gamepadEvent = static_cast(event); + ev.pad = 0; + ev.state = event->type() == GamepadButtonEvent::Down() ? mSCRIPT_INPUT_STATE_DOWN : mSCRIPT_INPUT_STATE_UP; + ev.button = gamepadEvent->value(); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + } + if (type == GamepadHatEvent::Type()) { + struct mScriptGamepadHatEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_HAT}; + auto gamepadEvent = static_cast(event); + ev.pad = 0; + ev.hat = gamepadEvent->hatId(); + ev.direction = gamepadEvent->direction(); + mScriptContextFireEvent(&m_scriptContext, &ev.d); + } +} + void ScriptingController::init() { mScriptContextInit(&m_scriptContext); mScriptContextAttachStdlib(&m_scriptContext); mScriptContextAttachSocket(&m_scriptContext); + mScriptContextAttachInput(&m_scriptContext); mScriptContextRegisterEngines(&m_scriptContext); mScriptContextAttachLogger(&m_scriptContext, &m_logger); @@ -138,3 +231,130 @@ void ScriptingController::init() { m_activeEngine = *m_engines.begin(); } } + +uint32_t ScriptingController::qtToScriptingKey(const QKeyEvent* event) { + QString text(event->text()); + int key = event->key(); + + static const QHash keypadMap{ + {'0', mSCRIPT_KEY_KP_0}, + {'1', mSCRIPT_KEY_KP_1}, + {'2', mSCRIPT_KEY_KP_2}, + {'3', mSCRIPT_KEY_KP_3}, + {'4', mSCRIPT_KEY_KP_4}, + {'5', mSCRIPT_KEY_KP_5}, + {'6', mSCRIPT_KEY_KP_6}, + {'7', mSCRIPT_KEY_KP_7}, + {'8', mSCRIPT_KEY_KP_8}, + {'9', mSCRIPT_KEY_KP_9}, + {'+', mSCRIPT_KEY_KP_PLUS}, + {'-', mSCRIPT_KEY_KP_MINUS}, + {'*', mSCRIPT_KEY_KP_MULTIPLY}, + {'/', mSCRIPT_KEY_KP_DIVIDE}, + {',', mSCRIPT_KEY_KP_COMMA}, + {'.', mSCRIPT_KEY_KP_POINT}, + {Qt::Key_Enter, mSCRIPT_KEY_KP_ENTER}, + }; + static const QHash extraKeyMap{ + {Qt::Key_Escape, mSCRIPT_KEY_ESCAPE}, + {Qt::Key_Tab, mSCRIPT_KEY_TAB}, + {Qt::Key_Backtab, mSCRIPT_KEY_BACKSPACE}, + {Qt::Key_Backspace, mSCRIPT_KEY_BACKSPACE}, + {Qt::Key_Return, mSCRIPT_KEY_ENTER}, + {Qt::Key_Enter, mSCRIPT_KEY_ENTER}, + {Qt::Key_Insert, mSCRIPT_KEY_INSERT}, + {Qt::Key_Delete, mSCRIPT_KEY_DELETE}, + {Qt::Key_Pause, mSCRIPT_KEY_BREAK}, + {Qt::Key_Print, mSCRIPT_KEY_PRINT_SCREEN}, + {Qt::Key_SysReq, mSCRIPT_KEY_SYSRQ}, + {Qt::Key_Clear, mSCRIPT_KEY_CLEAR}, + {Qt::Key_Home, mSCRIPT_KEY_HOME}, + {Qt::Key_End, mSCRIPT_KEY_END}, + {Qt::Key_Left, mSCRIPT_KEY_LEFT}, + {Qt::Key_Up, mSCRIPT_KEY_UP}, + {Qt::Key_Right, mSCRIPT_KEY_RIGHT}, + {Qt::Key_Down, mSCRIPT_KEY_DOWN}, + {Qt::Key_PageUp, mSCRIPT_KEY_PAGE_UP}, + {Qt::Key_PageDown, mSCRIPT_KEY_DOWN}, + {Qt::Key_Shift, mSCRIPT_KEY_SHIFT}, + {Qt::Key_Control, mSCRIPT_KEY_CONTROL}, + {Qt::Key_Meta, mSCRIPT_KEY_SUPER}, + {Qt::Key_Alt, mSCRIPT_KEY_ALT}, + {Qt::Key_CapsLock, mSCRIPT_KEY_CAPS_LOCK}, + {Qt::Key_NumLock, mSCRIPT_KEY_NUM_LOCK}, + {Qt::Key_ScrollLock, mSCRIPT_KEY_SCROLL_LOCK}, + {Qt::Key_F1, mSCRIPT_KEY_F1}, + {Qt::Key_F2, mSCRIPT_KEY_F2}, + {Qt::Key_F3, mSCRIPT_KEY_F3}, + {Qt::Key_F4, mSCRIPT_KEY_F4}, + {Qt::Key_F5, mSCRIPT_KEY_F5}, + {Qt::Key_F6, mSCRIPT_KEY_F6}, + {Qt::Key_F7, mSCRIPT_KEY_F7}, + {Qt::Key_F8, mSCRIPT_KEY_F8}, + {Qt::Key_F9, mSCRIPT_KEY_F9}, + {Qt::Key_F10, mSCRIPT_KEY_F10}, + {Qt::Key_F11, mSCRIPT_KEY_F11}, + {Qt::Key_F12, mSCRIPT_KEY_F12}, + {Qt::Key_F13, mSCRIPT_KEY_F13}, + {Qt::Key_F14, mSCRIPT_KEY_F14}, + {Qt::Key_F15, mSCRIPT_KEY_F15}, + {Qt::Key_F16, mSCRIPT_KEY_F16}, + {Qt::Key_F17, mSCRIPT_KEY_F17}, + {Qt::Key_F18, mSCRIPT_KEY_F18}, + {Qt::Key_F19, mSCRIPT_KEY_F19}, + {Qt::Key_F20, mSCRIPT_KEY_F20}, + {Qt::Key_F21, mSCRIPT_KEY_F21}, + {Qt::Key_F22, mSCRIPT_KEY_F22}, + {Qt::Key_F23, mSCRIPT_KEY_F23}, + {Qt::Key_F24, mSCRIPT_KEY_F24}, + {Qt::Key_Menu, mSCRIPT_KEY_MENU}, + {Qt::Key_Super_L, mSCRIPT_KEY_SUPER}, + {Qt::Key_Super_R, mSCRIPT_KEY_SUPER}, + {Qt::Key_Help, mSCRIPT_KEY_HELP}, + {Qt::Key_Hyper_L, mSCRIPT_KEY_SUPER}, + {Qt::Key_Hyper_R, mSCRIPT_KEY_SUPER}, + }; + + if (event->modifiers() & Qt::KeypadModifier && keypadMap.contains(key)) { + return keypadMap[key]; + } + if (key >= 0 && key < 0x100) { + return key; + } + if (key < 0x01000000) { + if (text.isEmpty()) { + return 0; + } + QChar high = text[0]; + if (!high.isSurrogate()) { + return high.unicode(); + } + if (text.size() < 2) { + return 0; + } + return QChar::surrogateToUcs4(high, text[1]); + } + + if (extraKeyMap.contains(key)) { + return extraKeyMap[key]; + } + return 0; +} + + +uint16_t ScriptingController::qtToScriptingModifiers(Qt::KeyboardModifiers modifiers) { + uint16_t mod = 0; + if (modifiers & Qt::ShiftModifier) { + mod |= mSCRIPT_KMOD_SHIFT; + } + if (modifiers & Qt::ControlModifier) { + mod |= mSCRIPT_KMOD_CONTROL; + } + if (modifiers & Qt::AltModifier) { + mod |= mSCRIPT_KMOD_ALT; + } + if (modifiers & Qt::MetaModifier) { + mod |= mSCRIPT_KMOD_SUPER; + } + return mod; +} diff --git a/src/platform/qt/scripting/ScriptingController.h b/src/platform/qt/scripting/ScriptingController.h index 03e40f7cf..d0fb73fbc 100644 --- a/src/platform/qt/scripting/ScriptingController.h +++ b/src/platform/qt/scripting/ScriptingController.h @@ -15,6 +15,7 @@ #include +class QKeyEvent; class QTextDocument; namespace QGBA { @@ -35,6 +36,8 @@ public: bool loadFile(const QString& path); bool load(VFileDevice& vf, const QString& name); + void event(QObject* obj, QEvent* ev); + mScriptContext* context() { return &m_scriptContext; } ScriptingTextBufferModel* textBufferModel() const { return m_bufferModel; } @@ -49,10 +52,14 @@ public slots: void reset(); void runCode(const QString& code); +protected: + bool eventFilter(QObject*, QEvent*) override; + private: void init(); - static mScriptTextBuffer* createTextBuffer(void* context); + static uint32_t qtToScriptingKey(const QKeyEvent*); + static uint16_t qtToScriptingModifiers(Qt::KeyboardModifiers); struct Logger : mLogger { ScriptingController* p; From a1546906942b8a0a728858caae8196259640902a Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 8 Jan 2023 16:00:21 -0800 Subject: [PATCH 136/159] Scripting: Migrate some stuff from docgen into libmgba --- include/mgba/internal/script/types.h | 20 +++++++++ src/script/docgen.c | 67 +++------------------------- src/script/types.c | 54 ++++++++++++++++++++++ 3 files changed, 80 insertions(+), 61 deletions(-) create mode 100644 include/mgba/internal/script/types.h diff --git a/include/mgba/internal/script/types.h b/include/mgba/internal/script/types.h new file mode 100644 index 000000000..fde5ce0ae --- /dev/null +++ b/include/mgba/internal/script/types.h @@ -0,0 +1,20 @@ +/* Copyright (c) 2013-2023 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 M_SCRIPT_TYPES_INTERNAL_H +#define M_SCRIPT_TYPES_INTERNAL_H + +#include + +CXX_GUARD_START + +struct Table; +void mScriptContextGetInputTypes(struct Table*); + +void mScriptTypeAdd(struct Table*, const struct mScriptType* type); + +CXX_GUARD_END + +#endif diff --git a/src/script/docgen.c b/src/script/docgen.c index 55d12eedc..2054862bf 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -36,62 +37,6 @@ void explainValue(struct mScriptValue* value, const char* name, int level); void explainValueScoped(struct mScriptValue* value, const char* name, const char* scope, int level); void explainType(struct mScriptType* type, int level); -void addTypesFromTuple(const struct mScriptTypeTuple*); -void addTypesFromTable(struct Table*); - -void addType(const struct mScriptType* type) { - if (HashTableLookup(&types, type->name) || type->isConst) { - return; - } - HashTableInsert(&types, type->name, (struct mScriptType*) type); - switch (type->base) { - case mSCRIPT_TYPE_FUNCTION: - addTypesFromTuple(&type->details.function.parameters); - addTypesFromTuple(&type->details.function.returnType); - break; - case mSCRIPT_TYPE_OBJECT: - mScriptClassInit(type->details.cls); - if (type->details.cls->parent) { - addType(type->details.cls->parent); - } - addTypesFromTable(&type->details.cls->instanceMembers); - break; - case mSCRIPT_TYPE_OPAQUE: - case mSCRIPT_TYPE_WRAPPER: - if (type->details.type) { - addType(type->details.type); - } - case mSCRIPT_TYPE_VOID: - case mSCRIPT_TYPE_SINT: - case mSCRIPT_TYPE_UINT: - case mSCRIPT_TYPE_FLOAT: - case mSCRIPT_TYPE_STRING: - case mSCRIPT_TYPE_LIST: - case mSCRIPT_TYPE_TABLE: - case mSCRIPT_TYPE_WEAKREF: - // No subtypes - break; - } -} - -void addTypesFromTuple(const struct mScriptTypeTuple* tuple) { - size_t i; - for (i = 0; i < tuple->count; ++i) { - addType(tuple->entries[i]); - } -} - -void addTypesFromTable(struct Table* table) { - struct TableIterator iter; - if (!HashTableIteratorStart(table, &iter)) { - return; - } - do { - struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter); - addType(member->type); - } while(HashTableIteratorNext(table, &iter)); -} - void printchomp(const char* string, int level) { char indent[(level + 1) * 2 + 1]; memset(indent, ' ', sizeof(indent) - 1); @@ -265,7 +210,7 @@ void explainObject(struct mScriptValue* value, int level) { continue; } fprintf(out, "%s%s:\n", indent, details->info.member.name); - addType(details->info.member.type); + mScriptTypeAdd(&types, details->info.member.type); if (mScriptObjectGet(value, details->info.member.name, &member)) { struct mScriptValue* unwrappedMember; if (member.type->base == mSCRIPT_TYPE_WRAPPER) { @@ -288,7 +233,7 @@ void explainValueScoped(struct mScriptValue* value, const char* name, const char memset(indent, ' ', sizeof(indent) - 1); indent[sizeof(indent) - 1] = '\0'; value = mScriptContextAccessWeakref(&context, value); - addType(value->type); + mScriptTypeAdd(&types, value->type); fprintf(out, "%stype: %s\n", indent, value->type->name); const char* docstring = NULL; @@ -421,7 +366,7 @@ void explainCore(struct mCore* core) { mScriptContextAttachCore(&context, core); struct mScriptValue* emu = mScriptContextGetGlobal(&context, "emu"); - addType(emu->type); + mScriptTypeAdd(&types, emu->type); if (mScriptObjectGet(emu, "memory", &wrapper)) { struct mScriptValue* memory = mScriptValueUnwrap(&wrapper); @@ -434,7 +379,7 @@ void explainCore(struct mCore* core) { fprintf(out, " %s:\n", name->value.string->buffer); value = mScriptContextAccessWeakref(&context, value); - addType(value->type); + mScriptTypeAdd(&types, value->type); struct mScriptFrame frame; uint32_t baseVal; @@ -486,7 +431,7 @@ void initTypes(void) { size_t i; for (i = 0; baseTypes[i]; ++i) { - addType(baseTypes[i]); + mScriptTypeAdd(&types, baseTypes[i]); } } diff --git a/src/script/types.c b/src/script/types.c index 279351535..0237cd4cb 100644 --- a/src/script/types.c +++ b/src/script/types.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include #include @@ -1524,3 +1525,56 @@ bool mScriptCoerceFrame(const struct mScriptTypeTuple* types, struct mScriptList } return true; } + +static void addTypesFromTuple(struct Table* types, const struct mScriptTypeTuple* tuple) { + size_t i; + for (i = 0; i < tuple->count; ++i) { + mScriptTypeAdd(types, tuple->entries[i]); + } +} + +static void addTypesFromTable(struct Table* types, struct Table* table) { + struct TableIterator iter; + if (!HashTableIteratorStart(table, &iter)) { + return; + } + do { + struct mScriptClassMember* member = HashTableIteratorGetValue(table, &iter); + mScriptTypeAdd(types, member->type); + } while(HashTableIteratorNext(table, &iter)); +} + +void mScriptTypeAdd(struct Table* types, const struct mScriptType* type) { + if (HashTableLookup(types, type->name) || type->isConst) { + return; + } + HashTableInsert(types, type->name, (struct mScriptType*) type); + switch (type->base) { + case mSCRIPT_TYPE_FUNCTION: + addTypesFromTuple(types, &type->details.function.parameters); + addTypesFromTuple(types, &type->details.function.returnType); + break; + case mSCRIPT_TYPE_OBJECT: + mScriptClassInit(type->details.cls); + if (type->details.cls->parent) { + mScriptTypeAdd(types, type->details.cls->parent); + } + addTypesFromTable(types, &type->details.cls->instanceMembers); + break; + case mSCRIPT_TYPE_OPAQUE: + case mSCRIPT_TYPE_WRAPPER: + if (type->details.type) { + mScriptTypeAdd(types, type->details.type); + } + case mSCRIPT_TYPE_VOID: + case mSCRIPT_TYPE_SINT: + case mSCRIPT_TYPE_UINT: + case mSCRIPT_TYPE_FLOAT: + case mSCRIPT_TYPE_STRING: + case mSCRIPT_TYPE_LIST: + case mSCRIPT_TYPE_TABLE: + case mSCRIPT_TYPE_WEAKREF: + // No subtypes + break; + } +} From dfe2f62f162fd8258e725f3f9fcff7669aea320d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 04:04:43 -0800 Subject: [PATCH 137/159] Scripting: Basic gamepad support --- include/mgba/script/input.h | 26 ++++++ src/script/input.c | 152 ++++++++++++++++++++++++++++++++++++ src/script/test/input.c | 41 ++++++++++ 3 files changed, 219 insertions(+) diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h index 3815433f0..53ef15c30 100644 --- a/include/mgba/script/input.h +++ b/include/mgba/script/input.h @@ -217,6 +217,14 @@ struct mScriptTriggerEvent { bool state; }; +struct mScriptGamepad { + unsigned pad; + + struct mScriptList axes; + struct mScriptList buttons; + struct mScriptList hats; +}; + mSCRIPT_DECLARE_STRUCT(mScriptEvent); mSCRIPT_DECLARE_STRUCT(mScriptKeyEvent); mSCRIPT_DECLARE_STRUCT(mScriptMouseButtonEvent); @@ -227,10 +235,28 @@ mSCRIPT_DECLARE_STRUCT(mScriptGamepadHatEvent); mSCRIPT_DECLARE_STRUCT(mScriptSensorEvent); mSCRIPT_DECLARE_STRUCT(mScriptTriggerEvent); +mSCRIPT_DECLARE_STRUCT(mScriptGamepad); + void mScriptContextAttachInput(struct mScriptContext* context); void mScriptContextFireEvent(struct mScriptContext*, struct mScriptEvent*); +int mScriptContextGamepadAttach(struct mScriptContext*, struct mScriptGamepad*); +bool mScriptContextGamepadDetach(struct mScriptContext*, int pad); +struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext*, int pad); + +void mScriptGamepadInit(struct mScriptGamepad*); +void mScriptGamepadDeinit(struct mScriptGamepad*); +void mScriptGamepadSetAxisCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetButtonCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetHatCount(struct mScriptGamepad*, unsigned); +void mScriptGamepadSetAxis(struct mScriptGamepad*, unsigned, int16_t value); +void mScriptGamepadSetButton(struct mScriptGamepad*, unsigned, bool down); +void mScriptGamepadSetHat(struct mScriptGamepad*, unsigned, int direction); +int16_t mScriptGamepadGetAxis(struct mScriptGamepad*, unsigned); +bool mScriptGamepadGetButton(struct mScriptGamepad*, unsigned); +int mScriptGamepadGetHat(struct mScriptGamepad*, unsigned); + CXX_GUARD_END #endif diff --git a/src/script/input.c b/src/script/input.c index 3803b4ba6..5f0901353 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -30,6 +30,7 @@ static const struct mScriptType* eventTypes[mSCRIPT_EV_TYPE_MAX] = { struct mScriptInputContext { uint64_t seq; struct Table activeKeys; + struct mScriptGamepad* activeGamepad; }; static void _mScriptInputDeinit(struct mScriptInputContext*); @@ -43,6 +44,7 @@ mSCRIPT_DEFINE_STRUCT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, U64, seq) mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, PCS(mScriptGamepad), activeGamepad) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptEvent) @@ -92,6 +94,12 @@ mSCRIPT_DEFINE_STRUCT(mScriptGamepadHatEvent) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, direction) mSCRIPT_DEFINE_END; +mSCRIPT_DEFINE_STRUCT(mScriptGamepad) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, axes) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, buttons) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, hats) +mSCRIPT_DEFINE_END; + void mScriptContextAttachInput(struct mScriptContext* context) { struct mScriptInputContext* inputContext = calloc(1, sizeof(*inputContext)); struct mScriptValue* value = mScriptValueAlloc(mSCRIPT_TYPE_MS_S(mScriptInputContext)); @@ -258,3 +266,147 @@ void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent mScriptContextTriggerCallback(context, eventNames[event->type], &args); mScriptListDeinit(&args); } + + +int mScriptContextGamepadAttach(struct mScriptContext* context, struct mScriptGamepad* pad) { + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + if (inputContext->activeGamepad) { + return -1; + } + inputContext->activeGamepad = pad; + + return 0; +} + +bool mScriptContextGamepadDetach(struct mScriptContext* context, int pad) { + if (pad != 0) { + return false; + } + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + if (!inputContext->activeGamepad) { + return false; + } + inputContext->activeGamepad = NULL; + return true; +} + +struct mScriptGamepad* mScriptContextGamepadLookup(struct mScriptContext* context, int pad) { + if (pad != 0) { + return NULL; + } + struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); + if (!input) { + return false; + } + struct mScriptInputContext* inputContext = input->value.opaque; + return inputContext->activeGamepad; +} + +void mScriptGamepadInit(struct mScriptGamepad* gamepad) { + memset(gamepad, 0, sizeof(*gamepad)); + + mScriptListInit(&gamepad->axes, 8); + mScriptListInit(&gamepad->buttons, 1); + mScriptListInit(&gamepad->hats, 1); +} + +void mScriptGamepadDeinit(struct mScriptGamepad* gamepad) { + mScriptListDeinit(&gamepad->axes); + mScriptListDeinit(&gamepad->buttons); + mScriptListDeinit(&gamepad->hats); +} + +void mScriptGamepadSetAxisCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT8_MAX) { + count = UINT8_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->axes); + mScriptListResize(&gamepad->axes, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->axes, i) = mSCRIPT_MAKE_S16(0); + } +} + +void mScriptGamepadSetButtonCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT16_MAX) { + count = UINT16_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->buttons); + mScriptListResize(&gamepad->buttons, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->buttons, i) = mSCRIPT_MAKE_BOOL(false); + } +} + +void mScriptGamepadSetHatCount(struct mScriptGamepad* gamepad, unsigned count) { + if (count > UINT8_MAX) { + count = UINT8_MAX; + } + + unsigned oldSize = mScriptListSize(&gamepad->hats); + mScriptListResize(&gamepad->hats, (ssize_t) count - oldSize); + unsigned i; + for (i = oldSize; i < count; ++i) { + *mScriptListGetPointer(&gamepad->hats, i) = mSCRIPT_MAKE_U8(0); + } +} + +void mScriptGamepadSetAxis(struct mScriptGamepad* gamepad, unsigned id, int16_t value) { + if (id >= mScriptListSize(&gamepad->axes)) { + return; + } + + mScriptListGetPointer(&gamepad->axes, id)->value.s32 = value; +} + +void mScriptGamepadSetButton(struct mScriptGamepad* gamepad, unsigned id, bool down) { + if (id >= mScriptListSize(&gamepad->buttons)) { + return; + } + + mScriptListGetPointer(&gamepad->buttons, id)->value.u32 = down; +} + +void mScriptGamepadSetHat(struct mScriptGamepad* gamepad, unsigned id, int direction) { + if (id >= mScriptListSize(&gamepad->hats)) { + return; + } + + mScriptListGetPointer(&gamepad->hats, id)->value.u32 = direction; +} + +int16_t mScriptGamepadGetAxis(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->axes)) { + return 0; + } + + return mScriptListGetPointer(&gamepad->axes, id)->value.s32; +} + +bool mScriptGamepadGetButton(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->buttons)) { + return false; + } + + return mScriptListGetPointer(&gamepad->buttons, id)->value.u32; +} + +int mScriptGamepadGetHat(struct mScriptGamepad* gamepad, unsigned id) { + if (id >= mScriptListSize(&gamepad->hats)) { + return mSCRIPT_INPUT_DIR_NONE; + } + + return mScriptListGetPointer(&gamepad->hats, id)->value.u32; +} diff --git a/src/script/test/input.c b/src/script/test/input.c index b98c73e72..de3ae0e0c 100644 --- a/src/script/test/input.c +++ b/src/script/test/input.c @@ -109,8 +109,49 @@ M_TEST_DEFINE(fireKey) { mScriptContextDeinit(&context); } +M_TEST_DEFINE(gamepadExport) { + SETUP_LUA; + + struct mScriptGamepad m_gamepad; + mScriptGamepadInit(&m_gamepad); + + TEST_PROGRAM("assert(not input.activeGamepad)"); + assert_int_equal(mScriptContextGamepadAttach(&context, &m_gamepad), 0); + TEST_PROGRAM("assert(input.activeGamepad)"); + + TEST_PROGRAM("assert(#input.activeGamepad.axes == 0)"); + TEST_PROGRAM("assert(#input.activeGamepad.buttons == 0)"); + TEST_PROGRAM("assert(#input.activeGamepad.hats == 0)"); + + mScriptGamepadSetAxisCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.axes == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.axes[1] == 0)"); + mScriptGamepadSetAxis(&m_gamepad, 0, 123); + TEST_PROGRAM("assert(input.activeGamepad.axes[1] == 123)"); + + mScriptGamepadSetButtonCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.buttons == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.buttons[1] == false)"); + mScriptGamepadSetButton(&m_gamepad, 0, true); + TEST_PROGRAM("assert(input.activeGamepad.buttons[1] == true)"); + + mScriptGamepadSetHatCount(&m_gamepad, 1); + TEST_PROGRAM("assert(#input.activeGamepad.hats == 1)"); + TEST_PROGRAM("assert(input.activeGamepad.hats[1] == C.INPUT_DIR.NONE)"); + mScriptGamepadSetHat(&m_gamepad, 0, mSCRIPT_INPUT_DIR_NORTHWEST); + TEST_PROGRAM("assert(input.activeGamepad.hats[1] == C.INPUT_DIR.NORTHWEST)"); + + mScriptContextGamepadDetach(&context, 0); + TEST_PROGRAM("assert(not input.activeGamepad)"); + + mScriptGamepadDeinit(&m_gamepad); + + mScriptContextDeinit(&context); +} + M_TEST_SUITE_DEFINE_SETUP_TEARDOWN(mScriptInput, cmocka_unit_test(members), cmocka_unit_test(seq), cmocka_unit_test(fireKey), + cmocka_unit_test(gamepadExport), ) From 0dd7cfd44aa8f71983d1870c738df7aeba328b28 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 04:06:57 -0800 Subject: [PATCH 138/159] Qt: Hook up gamepad to scripting --- src/platform/qt/InputController.cpp | 3 +- src/platform/qt/InputController.h | 3 + src/platform/qt/Window.cpp | 1 + .../qt/scripting/ScriptingController.cpp | 68 +++++++++++++++++++ .../qt/scripting/ScriptingController.h | 12 ++++ 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/platform/qt/InputController.cpp b/src/platform/qt/InputController.cpp index f036d6f52..d8b5dd783 100644 --- a/src/platform/qt/InputController.cpp +++ b/src/platform/qt/InputController.cpp @@ -44,7 +44,7 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren } }); - m_gamepadTimer.setInterval(50); + m_gamepadTimer.setInterval(15); m_gamepadTimer.start(); #ifdef BUILD_QT_MULTIMEDIA @@ -336,6 +336,7 @@ void InputController::update() { loadProfile(driver->type(), newProfile); } } + emit updated(); } int InputController::pollEvents() { diff --git a/src/platform/qt/InputController.h b/src/platform/qt/InputController.h index d74cd3148..12f4e8915 100644 --- a/src/platform/qt/InputController.h +++ b/src/platform/qt/InputController.h @@ -56,6 +56,8 @@ public: void addInputDriver(std::shared_ptr); + int playerId() const { return m_playerId; } + void setConfiguration(ConfigController* config); void saveConfiguration(); bool loadConfiguration(uint32_t type); @@ -103,6 +105,7 @@ public: GBALuminanceSource* luminance() { return &m_lux; } signals: + void updated(); void profileLoaded(const QString& profile); void luminanceValueChanged(int value); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index ae80b8454..6626d7204 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -627,6 +627,7 @@ void Window::consoleOpen() { void Window::scriptingOpen() { if (!m_scripting) { m_scripting = std::make_unique(); + m_scripting->setInputController(&m_inputController); m_shortcutController->setScriptingController(m_scripting.get()); if (m_controller) { m_scripting->setController(m_controller); diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index b6932d602..47cd08674 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -12,8 +12,10 @@ #include "CoreController.h" #include "Display.h" +#include "input/Gamepad.h" #include "input/GamepadButtonEvent.h" #include "input/GamepadHatEvent.h" +#include "InputController.h" #include "scripting/ScriptingTextBuffer.h" #include "scripting/ScriptingTextBufferModel.h" @@ -48,12 +50,15 @@ ScriptingController::ScriptingController(QObject* parent) m_bufferModel = new ScriptingTextBufferModel(this); QObject::connect(m_bufferModel, &ScriptingTextBufferModel::textBufferCreated, this, &ScriptingController::textBufferCreated); + mScriptGamepadInit(&m_gamepad); + init(); } ScriptingController::~ScriptingController() { clearController(); mScriptContextDeinit(&m_scriptContext); + mScriptGamepadDeinit(&m_gamepad); } void ScriptingController::setController(std::shared_ptr controller) { @@ -70,6 +75,14 @@ void ScriptingController::setController(std::shared_ptr controll connect(m_controller.get(), &CoreController::stopping, this, &ScriptingController::clearController); } +void ScriptingController::setInputController(InputController* input) { + if (m_inputController) { + m_inputController->disconnect(this); + } + m_inputController = input; + connect(m_inputController, &InputController::updated, this, &ScriptingController::updateGamepad); +} + bool ScriptingController::loadFile(const QString& path) { VFileDevice vf(path, QIODevice::ReadOnly); if (!vf.isOpen()) { @@ -204,6 +217,7 @@ void ScriptingController::event(QObject* obj, QEvent* event) { } if (type == GamepadHatEvent::Type()) { struct mScriptGamepadHatEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_HAT}; + updateGamepad(); auto gamepadEvent = static_cast(event); ev.pad = 0; ev.hat = gamepadEvent->hatId(); @@ -212,6 +226,60 @@ void ScriptingController::event(QObject* obj, QEvent* event) { } } +void ScriptingController::updateGamepad() { + InputDriver* driver = m_inputController->gamepadDriver(); + if (!driver) { + detachGamepad(); + return; + } + Gamepad* gamepad = driver->activeGamepad(); + if (!gamepad) { + detachGamepad(); + return; + } + attachGamepad(); + + QList buttons = gamepad->currentButtons(); + int nButtons = gamepad->buttonCount(); + mScriptGamepadSetButtonCount(&m_gamepad, nButtons); + for (int i = 0; i < nButtons; ++i) { + mScriptGamepadSetButton(&m_gamepad, i, buttons.at(i)); + } + + QList axes = gamepad->currentAxes(); + int nAxes = gamepad->axisCount(); + mScriptGamepadSetAxisCount(&m_gamepad, nAxes); + for (int i = 0; i < nAxes; ++i) { + mScriptGamepadSetAxis(&m_gamepad, i, axes.at(i)); + } + + QList hats = gamepad->currentHats(); + int nHats = gamepad->hatCount(); + mScriptGamepadSetHatCount(&m_gamepad, nHats); + for (int i = 0; i < nHats; ++i) { + mScriptGamepadSetHat(&m_gamepad, i, hats.at(i)); + } +} + +void ScriptingController::attachGamepad() { + mScriptGamepad* pad = mScriptContextGamepadLookup(&m_scriptContext, 0); + if (pad == &m_gamepad) { + return; + } + if (pad) { + mScriptContextGamepadDetach(&m_scriptContext, 0); + } + mScriptContextGamepadAttach(&m_scriptContext, &m_gamepad); +} + +void ScriptingController::detachGamepad() { + mScriptGamepad* pad = mScriptContextGamepadLookup(&m_scriptContext, 0); + if (pad != &m_gamepad) { + return; + } + mScriptContextGamepadDetach(&m_scriptContext, 0); +} + void ScriptingController::init() { mScriptContextInit(&m_scriptContext); mScriptContextAttachStdlib(&m_scriptContext); diff --git a/src/platform/qt/scripting/ScriptingController.h b/src/platform/qt/scripting/ScriptingController.h index d0fb73fbc..0e275561d 100644 --- a/src/platform/qt/scripting/ScriptingController.h +++ b/src/platform/qt/scripting/ScriptingController.h @@ -9,6 +9,7 @@ #include #include +#include #include #include "VFileDevice.h" @@ -21,6 +22,7 @@ class QTextDocument; namespace QGBA { class CoreController; +class InputController; class ScriptingTextBuffer; class ScriptingTextBufferModel; @@ -32,6 +34,7 @@ public: ~ScriptingController(); void setController(std::shared_ptr controller); + void setInputController(InputController* controller); bool loadFile(const QString& path); bool load(VFileDevice& vf, const QString& name); @@ -55,9 +58,15 @@ public slots: protected: bool eventFilter(QObject*, QEvent*) override; +private slots: + void updateGamepad(); + private: void init(); + void attachGamepad(); + void detachGamepad(); + static uint32_t qtToScriptingKey(const QKeyEvent*); static uint16_t qtToScriptingModifiers(Qt::KeyboardModifiers); @@ -71,7 +80,10 @@ private: QHash m_engines; ScriptingTextBufferModel* m_bufferModel; + mScriptGamepad m_gamepad; + std::shared_ptr m_controller; + InputController* m_inputController = nullptr; }; } From a62a3eb811eaa687a1fa0a7a5778da71981c37c1 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 04:20:36 -0800 Subject: [PATCH 139/159] Res: Add example gamepad demo --- res/scripts/gamepad-demo.lua | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 res/scripts/gamepad-demo.lua diff --git a/res/scripts/gamepad-demo.lua b/res/scripts/gamepad-demo.lua new file mode 100644 index 000000000..f211767ea --- /dev/null +++ b/res/scripts/gamepad-demo.lua @@ -0,0 +1,32 @@ +inputBuffer = console:createBuffer("Input") + +function readPad() + inputBuffer:clear() + + if not input.activeGamepad then + inputBuffer:print("No gamepad detected\n") + return + end + + local gamepad = input.activeGamepad + local axes = gamepad.axes + local buttons = gamepad.buttons + local hats = gamepad.hats + + inputBuffer:print(string.format("%i buttons, %i axes, %i hats\n", #buttons, #axes, #hats)) + + local sbuttons = {} + for k, v in ipairs(buttons) do + if v then + sbuttons[k] = "down" + else + sbuttons[k] = " up" + end + end + + inputBuffer:print(string.format("Buttons: %s\n", table.concat(sbuttons, ", "))) + inputBuffer:print(string.format("Axes: %s\n", table.concat(axes, ", "))) + inputBuffer:print(string.format("Hats: %s\n", table.concat(hats, ", "))) +end + +callbacks:add("frame", readPad) From c0d4e2c34792dca4033e6256619b6a19863ba522 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Thu, 26 Jan 2023 21:38:47 -0800 Subject: [PATCH 140/159] Scripting: Expose gamepad name to scripts --- include/mgba/script/input.h | 3 +++ res/scripts/gamepad-demo.lua | 1 + src/platform/qt/scripting/ScriptingController.cpp | 6 ++++++ src/script/input.c | 2 ++ 4 files changed, 12 insertions(+) diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h index 53ef15c30..135605452 100644 --- a/include/mgba/script/input.h +++ b/include/mgba/script/input.h @@ -220,6 +220,9 @@ struct mScriptTriggerEvent { struct mScriptGamepad { unsigned pad; + char visibleName[128]; + char internalName[64]; + struct mScriptList axes; struct mScriptList buttons; struct mScriptList hats; diff --git a/res/scripts/gamepad-demo.lua b/res/scripts/gamepad-demo.lua index f211767ea..90327c11c 100644 --- a/res/scripts/gamepad-demo.lua +++ b/res/scripts/gamepad-demo.lua @@ -13,6 +13,7 @@ function readPad() local buttons = gamepad.buttons local hats = gamepad.hats + inputBuffer:print(gamepad.visibleName .. "\n") inputBuffer:print(string.format("%i buttons, %i axes, %i hats\n", #buttons, #axes, #hats)) local sbuttons = {} diff --git a/src/platform/qt/scripting/ScriptingController.cpp b/src/platform/qt/scripting/ScriptingController.cpp index 47cd08674..d8babf06d 100644 --- a/src/platform/qt/scripting/ScriptingController.cpp +++ b/src/platform/qt/scripting/ScriptingController.cpp @@ -21,6 +21,7 @@ #include #include +#include using namespace QGBA; @@ -237,6 +238,11 @@ void ScriptingController::updateGamepad() { detachGamepad(); return; } + + QString name = gamepad->name(); + strlcpy(m_gamepad.internalName, name.toUtf8().constData(), sizeof(m_gamepad.internalName)); + name = gamepad->visibleName(); + strlcpy(m_gamepad.visibleName, name.toUtf8().constData(), sizeof(m_gamepad.visibleName)); attachGamepad(); QList buttons = gamepad->currentButtons(); diff --git a/src/script/input.c b/src/script/input.c index 5f0901353..24818ebc5 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -95,6 +95,8 @@ mSCRIPT_DEFINE_STRUCT(mScriptGamepadHatEvent) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptGamepad) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, CHARP, visibleName) + mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, CHARP, internalName) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, axes) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, buttons) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, hats) From 7ee2be6c96222dca12a9a579b747fe5ff1829def Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Fri, 27 Jan 2023 21:22:33 -0800 Subject: [PATCH 141/159] Scripting: Export Input API docs --- include/mgba/script/input.h | 6 ++ src/script/docgen.c | 3 + src/script/input.c | 152 +++++++++++++++++++++++++++++++++++- 3 files changed, 160 insertions(+), 1 deletion(-) diff --git a/include/mgba/script/input.h b/include/mgba/script/input.h index 135605452..ae1260ad1 100644 --- a/include/mgba/script/input.h +++ b/include/mgba/script/input.h @@ -161,6 +161,12 @@ enum mScriptKey { mSCRIPT_KEY_KP_ENTER, }; +enum mScriptMouseButton { + mSCRIPT_MOUSE_BUTTON_PRIMARY = 0, + mSCRIPT_MOUSE_BUTTON_SECONDARY = 1, + mSCRIPT_MOUSE_BUTTON_MIDDLE = 2, +}; + struct mScriptEvent { int32_t type; int32_t reserved; diff --git a/src/script/docgen.c b/src/script/docgen.c index 2054862bf..65fcc87ce 100644 --- a/src/script/docgen.c +++ b/src/script/docgen.c @@ -8,6 +8,7 @@ #include #include #include +#include #include struct mScriptContext context; @@ -468,9 +469,11 @@ int main(int argc, char* argv[]) { mScriptContextInit(&context); mScriptContextAttachStdlib(&context); mScriptContextAttachSocket(&context); + mScriptContextAttachInput(&context); mScriptContextSetTextBufferFactory(&context, NULL, NULL); initTypes(); + mScriptContextGetInputTypes(&types); fputs("version:\n", out); fprintf(out, " string: \"%s\"\n", projectVersion); diff --git a/src/script/input.c b/src/script/input.c index 24818ebc5..fdb6593cb 100644 --- a/src/script/input.c +++ b/src/script/input.c @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include +#include #include #include @@ -42,63 +43,115 @@ mSCRIPT_DECLARE_STRUCT_C_METHOD(mScriptInputContext, BOOL, isKeyActive, _mScript mSCRIPT_DEFINE_STRUCT(mScriptInputContext) mSCRIPT_DEFINE_STRUCT_DEINIT(mScriptInputContext) + mSCRIPT_DEFINE_DOCSTRING("Sequence number of the next event to be emitted") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, U64, seq) + mSCRIPT_DEFINE_DOCSTRING("Check if a given keyboard key is currently held. The input can be either the printable character for a key, or the numerical Unicode codepoint") mSCRIPT_DEFINE_STRUCT_METHOD(mScriptInputContext, isKeyActive) + mSCRIPT_DEFINE_DOCSTRING("The currently active gamepad, if any") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptInputContext, PCS(mScriptGamepad), activeGamepad) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING( + "The base class for all event types. Different events have their own subclasses." + ) + mSCRIPT_DEFINE_DOCSTRING("The type of this event. See C.EV_TYPE for a list of possible types.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptEvent, S32, type) + mSCRIPT_DEFINE_DOCSTRING("Sequence number of this event. This value increases monotinically.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptEvent, U64, seq) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptKeyEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A keyboard key event.") mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("The state of the key, represented by a C.INPUT_STATE value") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, U8, state) + mSCRIPT_DEFINE_DOCSTRING("A bitmask of current modifiers, represented by ORed C.KMOD values") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, S16, modifiers) + mSCRIPT_DEFINE_DOCSTRING( + "The relevant key for this event. For most printable characters, this will be the Unicode " + "codepoint of the character. Some special values are present as C.KEY as well." + ) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptKeyEvent, S32, key) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptMouseButtonEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A mouse button event.") mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which mouse this event pertains to. Currently, this will always be 0.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, mouse) + mSCRIPT_DEFINE_DOCSTRING("The state of the button, represented by a C.INPUT_STATE value") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, state) + mSCRIPT_DEFINE_DOCSTRING( + "Which mouse button this event pertains to. Symbolic names for primary (usually left), " + "secondary (usually right), and middle are in C.MOUSE_BUTTON, and further buttons " + "are numeric starting from 3." + ) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseButtonEvent, U8, button) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptMouseMoveEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A mouse movement event.") mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which mouse this event pertains to. Currently, this will always be 0.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, U8, mouse) + mSCRIPT_DEFINE_DOCSTRING( + "The x coordinate of the mouse in the context of game screen pixels. " + "This can be out of bounds of the game screen depending on the size of the window in question." + ) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, S32, x) + mSCRIPT_DEFINE_DOCSTRING( + "The y coordinate of the mouse in the context of game screen pixels. " + "This can be out of bounds of the game screen depending on the size of the window in question." + ) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseMoveEvent, S32, y) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptMouseWheelEvent) mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which mouse this event pertains to. Currently, this will always be 0.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, U8, mouse) + mSCRIPT_DEFINE_DOCSTRING("The amount scrolled horizontally") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, S32, x) + mSCRIPT_DEFINE_DOCSTRING("The amount scrolled vertically") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptMouseWheelEvent, S32, y) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptGamepadButtonEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A gamead button event.") mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("The state of the button, represented by a C.INPUT_STATE value") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U8, state) + mSCRIPT_DEFINE_DOCSTRING("Which gamepad this event pertains to. Currently, this will always be 0.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U8, pad) + mSCRIPT_DEFINE_DOCSTRING( + "Which button this event pertains to. There is currently no guaranteed button mapping, " + "and it might change between different controllers." + ) mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadButtonEvent, U16, button) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptGamepadHatEvent) + mSCRIPT_DEFINE_CLASS_DOCSTRING("A gamepad POV hat event.") mSCRIPT_DEFINE_INHERIT(mScriptEvent) + mSCRIPT_DEFINE_DOCSTRING("Which gamepad this event pertains to. Currently, this will always be 0.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, pad) + mSCRIPT_DEFINE_DOCSTRING("Which hat this event pertains to. For most gamepads this will be 0.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, hat) + mSCRIPT_DEFINE_DOCSTRING("The current direction of the hat. See C.INPUT_DIR for possible values.") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepadHatEvent, U8, direction) mSCRIPT_DEFINE_END; mSCRIPT_DEFINE_STRUCT(mScriptGamepad) + mSCRIPT_DEFINE_DOCSTRING("The human-readable name of this gamepad") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, CHARP, visibleName) + mSCRIPT_DEFINE_DOCSTRING("The internal name of this gamepad, generally unique to the specific type of gamepad") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, CHARP, internalName) + mSCRIPT_DEFINE_DOCSTRING("An indexed list of the current values of each axis") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, axes) + mSCRIPT_DEFINE_DOCSTRING("An indexed list of the current values of each button") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, buttons) + mSCRIPT_DEFINE_DOCSTRING("An indexed list of the current values of POV hat") mSCRIPT_DEFINE_STRUCT_MEMBER(mScriptGamepad, LIST, hats) mSCRIPT_DEFINE_END; @@ -149,6 +202,87 @@ void mScriptContextAttachInput(struct mScriptContext* context) { mSCRIPT_KV_SENTINEL }); + mScriptContextExportConstants(context, "KEY", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, NONE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, BACKSPACE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, TAB), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, ENTER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, ESCAPE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, DELETE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F1), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F2), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F3), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F4), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F5), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F6), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F7), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F8), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F9), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F10), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F11), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F12), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F13), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F14), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F15), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F16), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F17), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F18), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F19), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F20), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F21), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F22), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F23), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, F24), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RIGHT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LEFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, PAGE_UP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, PAGE_DOWN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, HOME), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, END), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, INSERT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, BREAK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, CLEAR), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, PRINT_SCREEN), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SYSRQ), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, MENU), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, HELP), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RSHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SHIFT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RCONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, CONTROL), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, ALT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, LSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, RSUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SUPER), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, CAPS_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, NUM_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, SCROLL_LOCK), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_0), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_1), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_2), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_3), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_4), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_5), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_6), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_7), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_8), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_9), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_PLUS), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_MINUS), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_MULTIPLY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_DIVIDE), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_COMMA), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_POINT), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_KEY, KP_ENTER), + mSCRIPT_KV_SENTINEL + }); + mScriptContextExportConstants(context, "KMOD", (struct mScriptKVPair[]) { mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, NONE), mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, LSHIFT), @@ -168,6 +302,13 @@ void mScriptContextAttachInput(struct mScriptContext* context) { mSCRIPT_CONSTANT_PAIR(mSCRIPT_KMOD, SCROLL_LOCK), mSCRIPT_KV_SENTINEL }); + + mScriptContextExportConstants(context, "MOUSE_BUTTON", (struct mScriptKVPair[]) { + mSCRIPT_CONSTANT_PAIR(mSCRIPT_MOUSE_BUTTON, PRIMARY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_MOUSE_BUTTON, SECONDARY), + mSCRIPT_CONSTANT_PAIR(mSCRIPT_MOUSE_BUTTON, MIDDLE), + mSCRIPT_KV_SENTINEL + }); } void _mScriptInputDeinit(struct mScriptInputContext* context) { @@ -269,7 +410,6 @@ void mScriptContextFireEvent(struct mScriptContext* context, struct mScriptEvent mScriptListDeinit(&args); } - int mScriptContextGamepadAttach(struct mScriptContext* context, struct mScriptGamepad* pad) { struct mScriptValue* input = mScriptContextGetGlobal(context, "input"); if (!input) { @@ -412,3 +552,13 @@ int mScriptGamepadGetHat(struct mScriptGamepad* gamepad, unsigned id) { return mScriptListGetPointer(&gamepad->hats, id)->value.u32; } + +void mScriptContextGetInputTypes(struct Table* types) { + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptKeyEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptMouseMoveEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptMouseButtonEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptGamepadButtonEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptGamepadHatEvent)); + mScriptTypeAdd(types, mSCRIPT_TYPE_MS_S(mScriptGamepad)); +} From a4d1268db47d3d6a668b88b77433fea6846be0e2 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 17:40:24 -0800 Subject: [PATCH 142/159] Core: Export screenshot dimensions in savedata extdata --- include/mgba/core/serialize.h | 1 + src/core/serialize.c | 22 +++++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/mgba/core/serialize.h b/include/mgba/core/serialize.h index 59ca1c19f..ee9de69a1 100644 --- a/include/mgba/core/serialize.h +++ b/include/mgba/core/serialize.h @@ -16,6 +16,7 @@ enum mStateExtdataTag { EXTDATA_SAVEDATA = 2, EXTDATA_CHEATS = 3, EXTDATA_RTC = 4, + EXTDATA_SCREENSHOT_DIMENSIONS = 5, EXTDATA_META_TIME = 0x101, EXTDATA_META_CREATOR = 0x102, EXTDATA_MAX diff --git a/src/core/serialize.c b/src/core/serialize.c index c562d97e0..53e0aa168 100644 --- a/src/core/serialize.c +++ b/src/core/serialize.c @@ -262,8 +262,18 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx PNGReadClose(png, info, end); return false; } - unsigned width, height; - core->desiredVideoDimensions(core, &width, &height); + + if (!PNGReadHeader(png, info)) { + PNGReadClose(png, info, end); + return false; + } + unsigned width = png_get_image_width(png, info); + unsigned height = png_get_image_height(png, info); + if (width > 0x4000 || height > 0x4000) { + // These images are ridiculously large...let's assume a DOS attempt and reject + PNGReadClose(png, info, end); + return false; + } uint32_t* pixels = malloc(width * height * 4); if (!pixels) { PNGReadClose(png, info, end); @@ -278,8 +288,8 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx .extdata = extdata }; + bool success = true; PNGInstallChunkHandler(png, &bundle, _loadPNGChunkHandler, "gbAs gbAx"); - bool success = PNGReadHeader(png, info); success = success && PNGReadPixels(png, info, pixels, width, height, width); success = success && PNGReadFooter(png, end); PNGReadClose(png, info, end); @@ -295,6 +305,12 @@ static void* _loadPNGState(struct mCore* core, struct VFile* vf, struct mStateEx .clean = free }; mStateExtdataPut(extdata, EXTDATA_SCREENSHOT, &item); + + uint16_t dims[2] = { width, height }; + item.size = sizeof(dims); + item.data = malloc(item.size); + memcpy(item.data, dims, item.size); + mStateExtdataPut(extdata, EXTDATA_SCREENSHOT_DIMENSIONS, &item); } else { free(pixels); } From 7bd0e9173507f736a5bb052bff681add5baed515 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 17:43:03 -0800 Subject: [PATCH 143/159] Qt: Fix savestate preview sizes with different scales (fixes #2560) --- CHANGES | 1 + src/platform/qt/LoadSaveState.cpp | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 9b971cbe8..e6fcacfd4 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ Emulation fixes: Other fixes: - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) - Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738) + - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs diff --git a/src/platform/qt/LoadSaveState.cpp b/src/platform/qt/LoadSaveState.cpp index 89cc7f65a..f5dad2336 100644 --- a/src/platform/qt/LoadSaveState.cpp +++ b/src/platform/qt/LoadSaveState.cpp @@ -43,13 +43,12 @@ LoadSaveState::LoadSaveState(std::shared_ptr controller, QWidget m_slots[7] = m_ui.state8; m_slots[8] = m_ui.state9; - unsigned width, height; - controller->thread()->core->desiredVideoDimensions(controller->thread()->core, &width, &height); + QSize size = controller->screenDimensions(); int i; for (i = 0; i < NUM_SLOTS; ++i) { loadState(i + 1); m_slots[i]->installEventFilter(this); - m_slots[i]->setMaximumSize(width + 2, height + 2); + m_slots[i]->setMaximumSize(size.width() + 2, size.height() + 2); connect(m_slots[i], &QAbstractButton::clicked, this, [this, i]() { triggerState(i + 1); }); } @@ -199,11 +198,17 @@ void LoadSaveState::loadState(int slot) { QDateTime creation; QImage stateImage; - unsigned width, height; - thread->core->desiredVideoDimensions(thread->core, &width, &height); + QSize size = m_controller->screenDimensions(); mStateExtdataItem item; - if (mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item) && item.size >= static_cast(width * height * 4)) { - stateImage = QImage((uchar*) item.data, width, height, QImage::Format_ARGB32).rgbSwapped(); + if (mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT, &item)) { + mStateExtdataItem dims; + if (mStateExtdataGet(&extdata, EXTDATA_SCREENSHOT_DIMENSIONS, &dims) && dims.size == sizeof(uint16_t[2])) { + size.setWidth(static_cast(dims.data)[0]); + size.setHeight(static_cast(dims.data)[1]); + } + if (item.size >= static_cast(size.width() * size.height() * 4)) { + stateImage = QImage((uchar*) item.data, size.width(), size.height(), QImage::Format_ARGB32).rgbSwapped(); + } } if (mStateExtdataGet(&extdata, EXTDATA_META_TIME, &item) && item.size == sizeof(uint64_t)) { From 70e31df68334085125a94d57a2e0c70c824bb4a7 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 20:29:06 -0800 Subject: [PATCH 144/159] mGUI: Improve savestate screenshot handling --- src/feature/gui/gui-runner.c | 45 ++++++++++++++++++++---------------- src/feature/gui/gui-runner.h | 2 ++ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/feature/gui/gui-runner.c b/src/feature/gui/gui-runner.c index 2e837891f..33146ef88 100644 --- a/src/feature/gui/gui-runner.c +++ b/src/feature/gui/gui-runner.c @@ -123,34 +123,39 @@ static void _drawState(struct GUIBackground* background, void* id) { struct mGUIBackground* gbaBackground = (struct mGUIBackground*) background; unsigned stateId = ((uint32_t) id) >> 16; if (gbaBackground->p->drawScreenshot) { - unsigned w, h; - gbaBackground->p->core->desiredVideoDimensions(gbaBackground->p->core, &w, &h); - size_t size = w * h * BYTES_PER_PIXEL; - if (size != gbaBackground->imageSize) { - mappedMemoryFree(gbaBackground->image, gbaBackground->imageSize); - gbaBackground->image = NULL; - } - if (gbaBackground->image && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) { - gbaBackground->p->drawScreenshot(gbaBackground->p, gbaBackground->image, w, h, true); + color_t* pixels = gbaBackground->image; + if (pixels && gbaBackground->screenshotId == (stateId | SCREENSHOT_VALID)) { + gbaBackground->p->drawScreenshot(gbaBackground->p, pixels, gbaBackground->w, gbaBackground->h, true); return; } else if (gbaBackground->screenshotId != (stateId | SCREENSHOT_INVALID)) { struct VFile* vf = mCoreGetState(gbaBackground->p->core, stateId, false); - color_t* pixels = gbaBackground->image; - if (!pixels) { - pixels = anonymousMemoryMap(size); - gbaBackground->image = pixels; - gbaBackground->imageSize = size; - } bool success = false; - if (vf && isPNG(vf) && pixels) { + unsigned w, h; + if (vf && isPNG(vf)) { png_structp png = PNGReadOpen(vf, PNG_HEADER_BYTES); png_infop info = png_create_info_struct(png); png_infop end = png_create_info_struct(png); - if (png && info && end) { - success = PNGReadHeader(png, info); - success = success && PNGReadPixels(png, info, pixels, w, h, w); - success = success && PNGReadFooter(png, end); + success = png && info && end; + success = success && PNGReadHeader(png, info); + w = png_get_image_width(png, info); + h = png_get_image_height(png, info); + size_t size = w * h * BYTES_PER_PIXEL; + success = success && (w < 0x4000) && (h < 0x4000); + if (success) { + if (size != gbaBackground->imageSize) { + mappedMemoryFree(pixels, gbaBackground->imageSize); + pixels = anonymousMemoryMap(size); + gbaBackground->image = pixels; + gbaBackground->imageSize = size; + } + success = pixels; } + success = success && PNGReadPixels(png, info, pixels, w, h, w); + if (success) { + gbaBackground->w = w; + gbaBackground->h = h; + } + success = success && PNGReadFooter(png, end); PNGReadClose(png, info, end); } if (vf) { diff --git a/src/feature/gui/gui-runner.h b/src/feature/gui/gui-runner.h index fc8e85227..0dc412838 100644 --- a/src/feature/gui/gui-runner.h +++ b/src/feature/gui/gui-runner.h @@ -33,6 +33,8 @@ struct mGUIBackground { color_t* image; size_t imageSize; + uint16_t w; + uint16_t h; unsigned screenshotId; }; From 1a29a92c3a13e50cf05571774215b0b1c4afa25d Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 20:31:46 -0800 Subject: [PATCH 145/159] Switch: Improve screenshot texture handling --- src/platform/switch/main.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/platform/switch/main.c b/src/platform/switch/main.c index 9f1e84563..14996d5b0 100644 --- a/src/platform/switch/main.c +++ b/src/platform/switch/main.c @@ -82,6 +82,7 @@ static GLuint insizeLocation; static GLuint colorLocation; static GLuint tex; static GLuint oldTex; +static GLuint screenshotTex; static struct GUIFont* font; static color_t* frameBuffer; @@ -379,8 +380,6 @@ static void _gameUnloaded(struct mGUIRunner* runner) { static void _drawTex(struct mGUIRunner* runner, unsigned width, unsigned height, bool faded, bool blendTop) { glViewport(0, 1080 - vheight, vwidth, vheight); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glUseProgram(program); glBindVertexArray(vao); @@ -493,11 +492,16 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) { } if (interframeBlending) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, oldTex); _drawTex(runner, width, height, faded, false); glBindTexture(GL_TEXTURE_2D, tex); _drawTex(runner, width, height, faded, true); } else { + glDisable(GL_BLEND); + _drawTex(runner, width, height, faded, false); } @@ -523,10 +527,15 @@ static void _drawFrame(struct mGUIRunner* runner, bool faded) { static void _drawScreenshot(struct mGUIRunner* runner, const color_t* pixels, unsigned width, unsigned height, bool faded) { glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + glBindTexture(GL_TEXTURE_2D, screenshotTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + runner->core->desiredVideoDimensions(runner->core, &width, &height); + glDisable(GL_BLEND); + bool wasPbo = usePbo; + usePbo = false; _drawTex(runner, width, height, faded, false); + usePbo = wasPbo; } static uint16_t _pollGameInput(struct mGUIRunner* runner) { @@ -711,6 +720,13 @@ static void glInit(void) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenTextures(1, &screenshotTex); + glBindTexture(GL_TEXTURE_2D, screenshotTex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenBuffers(1, &pbo); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 256 * 4, NULL, GL_STREAM_DRAW); @@ -790,6 +806,7 @@ static void glDeinit(void) { glDeleteFramebuffers(1, ©Fbo); glDeleteTextures(1, &tex); glDeleteTextures(1, &oldTex); + glDeleteTextures(1, &screenshotTex); glDeleteBuffers(1, &vbo); glDeleteProgram(program); glDeleteVertexArrays(1, &vao); From 0701fb1997069aa5914525c671434cf42f66e019 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 22:10:00 -0800 Subject: [PATCH 146/159] Qt: It's 2023 now --- src/platform/qt/AboutScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/AboutScreen.cpp b/src/platform/qt/AboutScreen.cpp index 1bf672f98..dae9e8dc9 100644 --- a/src/platform/qt/AboutScreen.cpp +++ b/src/platform/qt/AboutScreen.cpp @@ -74,7 +74,7 @@ AboutScreen::AboutScreen(QWidget* parent) { QString copyright = m_ui.copyright->text(); - copyright.replace("{year}", QLatin1String("2022")); + copyright.replace("{year}", QLatin1String("2023")); m_ui.copyright->setText(copyright); } } From c84c31bdc0e60e6fac88e13ad7625b088f5e72ca Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 22:39:00 -0800 Subject: [PATCH 147/159] Core: Allow sending thread requests to a crashed core (fixes #2785) --- CHANGES | 1 + src/core/thread.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e6fcacfd4..cfa33b515 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ Emulation fixes: - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: + - Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2785) - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) - Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738) - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) diff --git a/src/core/thread.c b/src/core/thread.c index 2753a45e7..9f4be082b 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -58,8 +58,19 @@ static void _waitOnInterrupt(struct mCoreThreadInternal* threadContext) { } static void _pokeRequest(struct mCoreThreadInternal* threadContext) { - if (threadContext->state == mTHREAD_RUNNING || threadContext->state == mTHREAD_PAUSED) { + switch (threadContext->state) { + case mTHREAD_RUNNING: + case mTHREAD_PAUSED: + case mTHREAD_CRASHED: threadContext->state = mTHREAD_REQUEST; + break; + case mTHREAD_INITIALIZED: + case mTHREAD_REQUEST: + case mTHREAD_INTERRUPTED: + case mTHREAD_INTERRUPTING: + case mTHREAD_EXITING: + case mTHREAD_SHUTDOWN: + break; } } From ff6c74397a8b03930c8b097634eff0088cec03e6 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 22:40:57 -0800 Subject: [PATCH 148/159] CHANGES: Wrong bug report --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index cfa33b515..4d91d6ec9 100644 --- a/CHANGES +++ b/CHANGES @@ -6,7 +6,7 @@ Emulation fixes: - GBA Memory: Make VRAM access stalls only apply to BG RAM - GBA Video: Disable BG target 1 blending when OBJ blending (fixes mgba.io/i/2722) Other fixes: - - Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2785) + - Core: Allow sending thread requests to a crashed core (fixes mgba.io/i/2784) - Qt: Fix crash when attempting to use OpenGL 2.1 to 3.1 (fixes mgba.io/i/2794) - Qt: Disable sync while running scripts from main thread (fixes mgba.io/i/2738) - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) From 38fa501a0868f720182f2f6325a45399bdeb4132 Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sat, 28 Jan 2023 23:42:56 -0800 Subject: [PATCH 149/159] Qt: Fix controller hotplugging --- src/platform/qt/input/SDLInputDriver.cpp | 16 +++++++++++----- src/platform/qt/input/SDLInputDriver.h | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/platform/qt/input/SDLInputDriver.cpp b/src/platform/qt/input/SDLInputDriver.cpp index ca9e30b25..5b68462d1 100644 --- a/src/platform/qt/input/SDLInputDriver.cpp +++ b/src/platform/qt/input/SDLInputDriver.cpp @@ -89,6 +89,7 @@ QString SDLInputDriver::currentProfile() const { } void SDLInputDriver::loadConfiguration(ConfigController* config) { + m_config = config; mSDLEventsLoadConfig(&s_sdlEvents, config->input()); if (!m_playerAttached) { m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer); @@ -126,7 +127,7 @@ mRotationSource* SDLInputDriver::rotationSource() { bool SDLInputDriver::update() { - if (!m_playerAttached || !m_sdlPlayer.joystick) { + if (!m_playerAttached) { return false; } @@ -148,6 +149,9 @@ QList SDLInputDriver::connectedGamepads() const { #if SDL_VERSION_ATLEAST(2, 0, 0) void SDLInputDriver::updateGamepads() { + if (m_config) { + mSDLUpdateJoysticks(&s_sdlEvents, m_config->input()); + } for (int i = 0; i < m_gamepads.size(); ++i) { if (m_gamepads.at(i)->updateIndex()) { continue; @@ -160,10 +164,12 @@ void SDLInputDriver::updateGamepads() { }); for (size_t i = 0, j = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) { - std::shared_ptr gamepad = m_gamepads.at(j); - if (gamepad->m_index == i) { - ++j; - continue; + if ((ssize_t) j < m_gamepads.size()) { + std::shared_ptr gamepad = m_gamepads.at(j); + if (gamepad->m_index == i) { + ++j; + continue; + } } m_gamepads.append(std::make_shared(this, i)); } diff --git a/src/platform/qt/input/SDLInputDriver.h b/src/platform/qt/input/SDLInputDriver.h index 09f4b9555..0ca2f4030 100644 --- a/src/platform/qt/input/SDLInputDriver.h +++ b/src/platform/qt/input/SDLInputDriver.h @@ -62,6 +62,7 @@ public: mRotationSource* rotationSource() override; private: + ConfigController* m_config = nullptr; InputController* m_controller; mSDLPlayer m_sdlPlayer{}; bool m_playerAttached = false; From 92b7b347bf63b40dc1827da0006f8b9c5b42ae52 Mon Sep 17 00:00:00 2001 From: Felipe Date: Tue, 11 Oct 2022 12:39:07 +0000 Subject: [PATCH 150/159] Qt: Update translation (Portuguese (Brazil)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/pt_BR/ --- src/platform/qt/ts/mgba-pt_BR.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/platform/qt/ts/mgba-pt_BR.ts b/src/platform/qt/ts/mgba-pt_BR.ts index d54c32360..26e5f53fa 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -259,7 +259,7 @@ Tamanho do download: %3 Show advanced - Mostrar opções avançadas + Mostrar as opções avançadas @@ -729,7 +729,7 @@ Tamanho do download: %3 Frameskip - Pulo dos Frames + Frameskip @@ -4783,7 +4783,7 @@ Tamanho do download: %3 Pause - Pausa + Pausar @@ -4979,7 +4979,7 @@ Tamanho do download: %3 Force integer scaling - Forçar dimensionamento do inteiro + Forçar o dimensionamento do inteiro @@ -5608,7 +5608,7 @@ Tamanho do download: %3 WavPack - + WavPack @@ -5678,7 +5678,7 @@ Tamanho do download: %3 Show advanced - Mostrar as opções avançadas + Mostrar opções avançadas @@ -6200,7 +6200,7 @@ Tamanho do download: %3 Force integer scaling - Forçar o dimensionamento do inteiro + Forçar dimensionamento do inteiro From a90c09882ec3a8faeec26803b621899440b92e11 Mon Sep 17 00:00:00 2001 From: Lothar Serra Mari Date: Wed, 12 Oct 2022 04:22:05 +0000 Subject: [PATCH 151/159] Qt: Update translation (German) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/de/ --- src/platform/qt/ts/mgba-de.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index 7bc27a274..cbd273705 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -4087,7 +4087,7 @@ Download-Größe: %3 <html><head/><body><p>To file a bug report, please first generate a report file to attach to the bug report you're about to file. It is recommended that you include the save files, as these often help with debugging issues. This will collect some information about the version of {projectName} you're running, your configuration, your computer, and the game you currently have open (if any). Once this collection is completed you can review all of the information gathered below and save it to a zip file. The collection will automatically attempt to redact any personal information, such as your username if it's in any of the paths gathered, but just in case you can edit it afterwards. After you have generated and saved it, please click the button below or go to <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> to file the bug report on GitHub. Make sure to attach the report you generated!</p></body></html> - <html><head/><body><p>Um einen Fehler zu melden, erstelle bitte zuerst eine berichtsdatei, welche Du an deinen Fehlerbericht anhängen kannst. Es wird empfohlen, dass du in dem Archiv auch eine Spielstand-Datei beilegst, da diese häufig bei der Fehlersuche behilflich ist. Der Fehlerbericht wird einige Informationen über die verwendete Version von {projectName}, dein System, deinen Computer und das derzeit geöffnete Spiel. Sobald die Sammlung vollständig ist, kannst du alle gesammelten Informationen einsehen und in eine ZIP-Datei speichern. Wir versuchen automatisch, so viele persönlichen Daten wie möglich zu entfernen. Nachdem du den Bericht erstellt und abgespeichert hast, klicke bitte auf den untenstehenden Button oder besuche <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> um einen Fehlerbericht auf GitHub zu erzeugen. </p></body></html> + <html><head/><body><p>Um einen Fehler zu melden, erstelle bitte zuerst eine Berichtsdatei, welche Du an deinen Fehlerbericht anhängen kannst. Es wird empfohlen, dass du in dem Archiv auch eine Spielstand-Datei beilegst, da diese häufig bei der Fehlersuche behilflich ist. Der Fehlerbericht wird einige Informationen über die verwendete Version von {projectName}, dein System, deinen Computer und das derzeit geöffnete Spiel. Sobald die Sammlung vollständig ist, kannst du alle gesammelten Informationen einsehen und in eine ZIP-Datei speichern. Wir versuchen automatisch, so viele persönlichen Daten wie möglich zu entfernen. Nachdem du den Bericht erstellt und abgespeichert hast, klicke bitte auf den untenstehenden Button oder besuche <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> um einen Fehlerbericht auf GitHub zu erzeugen. </p></body></html> @@ -5608,7 +5608,7 @@ Download-Größe: %3 WavPack - + WavPack From 253dca58400c6877b71e8794d7aa5900ce99c4c8 Mon Sep 17 00:00:00 2001 From: Alex <8bitfenixofficial@gmail.com> Date: Thu, 13 Oct 2022 17:43:20 +0000 Subject: [PATCH 152/159] Qt: Update translation (Russian) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/ru/ --- src/platform/qt/ts/mgba-ru.ts | 138 +++++++++++++++++----------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/platform/qt/ts/mgba-ru.ts b/src/platform/qt/ts/mgba-ru.ts index 33874b707..ecd21d9f7 100644 --- a/src/platform/qt/ts/mgba-ru.ts +++ b/src/platform/qt/ts/mgba-ru.ts @@ -651,7 +651,7 @@ Download size: %3 Internal change detection - + Обнаружение внутренних изменений @@ -753,42 +753,42 @@ Download size: %3 Autodetect - Автоопределение + Автоопределение Game Boy (DMG) - + Game Boy (DMG) Game Boy Pocket (MGB) - + Game Boy Pocket (MGB) Super Game Boy (SGB) - + Super Game Boy (SGB) Super Game Boy 2 (SGB) - + Super Game Boy 2 (SGB) Game Boy Color (CGB) - + Game Boy Color (CGB) Game Boy Advance (AGB) - + Game Boy Advance (AGB) Super Game Boy Color (SGB + CGB) - + Super Game Boy Color (SGB + CGB) @@ -798,52 +798,52 @@ Download size: %3 MBC1 - + MBC1 MBC2 - + MBC2 MBC3 - + MBC3 MBC3 + RTC - + MBC3 + RTC MBC5 - + MBC5 MBC5 + Rumble - + MBC5 + Rumble MBC6 - + MBC6 MBC7 (Tilt) - + MBC7 (Tilt) MMM01 - + MMM01 HuC-1 - + HuC-1 @@ -2125,7 +2125,7 @@ Download size: %3 Value - Значение + Значение @@ -2142,7 +2142,7 @@ Download size: %3 1/64 - 1/64 + 1/64 @@ -2151,7 +2151,7 @@ Download size: %3 1/256 - 1/256 + 1/256 @@ -2160,7 +2160,7 @@ Download size: %3 1/1024 - 1/1024 + 1/1024 @@ -2479,7 +2479,7 @@ Download size: %3 1/16 - 1/16 + 1/16 @@ -2590,7 +2590,7 @@ Download size: %3 Mode - Режим + Режим @@ -2926,12 +2926,12 @@ Download size: %3 Fatal - Фатальная ошибка + Фатальная ошибка Error - Ошибка + Ошибка @@ -2956,7 +2956,7 @@ Download size: %3 Game Error - Ошибка игры + Ошибка игры @@ -3123,7 +3123,7 @@ Download size: %3 Xform - + Xform @@ -3657,7 +3657,7 @@ Download size: %3 0x%0 - + 0x%0 @@ -3669,7 +3669,7 @@ Download size: %3 --- - + --- @@ -3679,7 +3679,7 @@ Download size: %3 OBJWIN - + OBJWIN @@ -3919,12 +3919,12 @@ Download size: %3 #%0 - + #%0 0x%0 - + 0x%0 @@ -3932,7 +3932,7 @@ Download size: %3 0x%0 (%1) - 0x%0 (%1) + 0x%0 (%1) @@ -4064,7 +4064,7 @@ Download size: %3 CRC32: - CRC32: + CRC32: @@ -4097,7 +4097,7 @@ Download size: %3 Save - Сохранить + Сохранить @@ -4216,7 +4216,7 @@ Download size: %3 SRAM - SRAM + SRAM @@ -4332,7 +4332,7 @@ Download size: %3 0 - 0 + 0 @@ -4441,12 +4441,12 @@ Download size: %3 Qt Multimedia - + Qt Multimedia SDL - + SDL @@ -4457,7 +4457,7 @@ Download size: %3 OpenGL - + OpenGL @@ -4508,7 +4508,7 @@ Download size: %3 (%1×%2) - + (%1×%2) @@ -4596,7 +4596,7 @@ Download size: %3 Game Boy - Game Boy + Game Boy @@ -5144,7 +5144,7 @@ Download size: %3 (240×160) - + (240×160) @@ -5518,7 +5518,7 @@ Download size: %3 WebM - + WebM @@ -5528,17 +5528,17 @@ Download size: %3 &1080p - + &1080p &720p - + &720p &480p - + &480p @@ -5553,18 +5553,18 @@ Download size: %3 MKV - + MKV AVI - + AVI MP4 - + MP4 @@ -5574,27 +5574,27 @@ Download size: %3 HEVC - + HEVC HEVC (NVENC) - + HEVC (NVENC) VP8 - + VP8 VP9 - + VP9 FFV1 - + FFV1 @@ -5605,17 +5605,17 @@ Download size: %3 FLAC - + FLAC WavPack - + WavPack Opus - + Opus @@ -5840,12 +5840,12 @@ Download size: %3 %1 - %2 - + %1 - %2 %1 - %2 - %3 - + %1 - %2 - %3 @@ -6147,7 +6147,7 @@ Download size: %3 %0x - %0x + %0x @@ -6217,7 +6217,7 @@ Download size: %3 %1× - %1× + %1× @@ -6272,7 +6272,7 @@ Download size: %3 F12 - + F12 @@ -6467,7 +6467,7 @@ Download size: %3 Clear - Очистить + Очистить @@ -6490,17 +6490,17 @@ Download size: %3 GBA - + GBA GB - + GB ? - + ? From 7cf3535e033e7895dff70876ea087a2f225a0a15 Mon Sep 17 00:00:00 2001 From: Luna Lyday Date: Mon, 7 Nov 2022 04:09:31 +0000 Subject: [PATCH 153/159] Qt: Update translation (Polish) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/pl/ --- src/platform/qt/ts/mgba-pl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ts/mgba-pl.ts b/src/platform/qt/ts/mgba-pl.ts index b1a95e463..3d764faae 100644 --- a/src/platform/qt/ts/mgba-pl.ts +++ b/src/platform/qt/ts/mgba-pl.ts @@ -5610,7 +5610,7 @@ Rozmiar pobierania: %3 WavPack - + WavPack From 88c4c7857ce3d16b516fac760236deba3f4a81ec Mon Sep 17 00:00:00 2001 From: shinyoyo Date: Fri, 11 Nov 2022 12:04:18 +0000 Subject: [PATCH 154/159] Qt: Update translation (Chinese (Simplified)) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/zh_Hans/ --- src/platform/qt/ts/mgba-zh_CN.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ts/mgba-zh_CN.ts b/src/platform/qt/ts/mgba-zh_CN.ts index 7c73ad9d8..6ef74e12e 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -5285,7 +5285,7 @@ Download size: %3 Preprocessing - 正在预处理 + 预处理 From 60fdbfd9acf1982dc5f5dccdac31c41ebcf927b3 Mon Sep 17 00:00:00 2001 From: Hoseok Seo Date: Fri, 25 Nov 2022 02:22:54 +0000 Subject: [PATCH 155/159] Qt: Update translation (Korean) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/ko/ --- src/platform/qt/ts/mgba-ko.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index 4f47fe85e..b34ac9e87 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -5616,7 +5616,7 @@ Download size: %3 WavPack - + Wav팩 From c15f80a855dfbb103d298fc22d39b3e32c37913b Mon Sep 17 00:00:00 2001 From: Momo cao Date: Mon, 26 Dec 2022 00:44:33 +0000 Subject: [PATCH 156/159] Qt: Update translation (Spanish) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/es/ --- src/platform/qt/ts/mgba-es.ts | 426 +++++++++++++++++----------------- 1 file changed, 213 insertions(+), 213 deletions(-) diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index 8cf07904c..c72676dba 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -21,7 +21,7 @@ {projectName} would like to thank the following patrons from Patreon: - {projectName} desea agradecer a los siguientes patrocinadores desde Patreon: + {projectName} desea agradecer a los siguientes patrocinadores de Patreon: @@ -55,14 +55,14 @@ Game Boy Advance es una marca registrada de Nintendo Co., Ltd. Do you want to download and install it now? You will need to restart the emulator when the download is complete. -¿Quieres descargarlo e instalarlo ahora? Deberá reiniciar el emulador cuando se complete la descarga. +¿Quieres descargarlo e instalarlo ahora? Deberás reiniciar el emulador cuando se complete la descarga. Auto-update is not available on this platform. If you wish to update you will need to do it manually. -La actualización automática no está disponible en esta plataforma. Si desea actualizar, deberá hacerlo manualmente. +La actualización automática no está disponible en esta plataforma. Si deseas actualizar, deberás hacerlo manualmente. @@ -71,7 +71,7 @@ New version: %2 Download size: %3 Versión actual: %1 Nueva versión: %2 -Tamaño de la descarga: %3 +Tamaño de descarga: %3 @@ -104,7 +104,7 @@ Tamaño de la descarga: %3 Unknown - Desconocido + Desconocido @@ -117,7 +117,7 @@ Tamaño de la descarga: %3 Open in archive... - Abrir desde contenedor... + Abrir desde archivo comprimido... @@ -130,12 +130,12 @@ Tamaño de la descarga: %3 Tile # - Tile Nº + Tesela nº Palette # - Paleta Nº + Paleta nº @@ -170,12 +170,12 @@ Tamaño de la descarga: %3 Can't set format of context-less audio device - + No se puede establecer el formato de un dispositivo de audio sin contexto Audio device is missing its core - + No se encuentra el sistema del dispositivo de audio @@ -188,7 +188,7 @@ Tamaño de la descarga: %3 Can't start an audio processor without input - Sin entrada no se puede iniciar un procesador de audio + No se puede iniciar el procesador de audio sin entrada @@ -196,7 +196,7 @@ Tamaño de la descarga: %3 Can't start an audio processor without input - Sin entrada no se puede iniciar un procesador de audio + No se puede iniciar el procesador de audio sin entrada @@ -229,7 +229,7 @@ Tamaño de la descarga: %3 Add - Agregar + Añadir @@ -259,7 +259,7 @@ Tamaño de la descarga: %3 Show advanced - Mostrar ajustes avanzados + Mostrar configuración avanzada @@ -269,23 +269,23 @@ Tamaño de la descarga: %3 BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? - Faltan los datos de BattleChip. BattleChip Gates seguirán funcionando, pero faltarán algunos gráficos. ¿Quiere descargar los datos ahora? + Faltan los datos de BattleChip. Los BattleChip Gates seguirán funcionando pero faltarán algunos gráficos. ¿Quieres descargar los datos ahora? Select deck file - + Seleccionar fichero de baraja Incompatible deck - + Baraja no compatible The selected deck is not compatible with this Chip Gate - + La baraja seleccionada no es compatible con este Chip Gate @@ -358,7 +358,7 @@ Tamaño de la descarga: %3 Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. - Algunos trucos no se pudieron añadir. Asegúrese de que están en el formato correcto y/o pruebe otro tipo de trucos. + Algunos trucos no se pudieron añadir. Asegúrate de que están en el formato correcto y/o prueba otro tipo de trucos. @@ -397,7 +397,7 @@ Tamaño de la descarga: %3 Can't yank pack in unexpected platform! - ¡No se puede remover el cartucho en esta plataforma! + ¡No se puede quitar el cartucho en esta plataforma! @@ -425,7 +425,7 @@ Tamaño de la descarga: %3 Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). - Error al abrir el archivo de guardado; las partidas guardadas no se pueden actualizar. Por favor, asegúrese de que es posible escribir en el directorio de partidas guardadas sin necesidad de privilegios adicionales (Por ejemplo, UAC en Windows). + Error al abrir el archivo de guardado; las partidas guardadas no se pueden actualizar. Por favor, asegúrate de que es posible escribir en el directorio de partidas guardadas sin necesidad de privilegios adicionales (por ejemplo, UAC en Windows). @@ -451,7 +451,7 @@ Tamaño de la descarga: %3 Could not open CLI history for writing - No se ha podido abrir el historial de la linea de comandos para escritura + No se ha podido abrir el historial de la línea de comandos para escritura @@ -464,7 +464,7 @@ Tamaño de la descarga: %3 Local computer - Computadora local + Ordenador local @@ -494,12 +494,12 @@ Tamaño de la descarga: %3 Couldn't Connect - + No se pudo conectar Could not connect to Dolphin. - + No se pudo conectar a Dolphin. @@ -522,12 +522,12 @@ Tamaño de la descarga: %3 Backdrop color - Color de telón de fondo (backdrop) + Color de telón de fondo Disable scanline effects - Desactivar efectos de línea de trazado + Desactivar efectos de scanline @@ -537,7 +537,7 @@ Tamaño de la descarga: %3 Reset - Reinicializar + Reiniciar @@ -557,12 +557,12 @@ Tamaño de la descarga: %3 Background - Fondo (BG) + Fondo Window - Ventana (WIN) + Ventana @@ -603,12 +603,12 @@ Tamaño de la descarga: %3 Clear Button - Limpiar botones + Reestablecer botones Clear Analog - Limpiar análogo + Reestablecer sticks analógicos @@ -641,7 +641,7 @@ Tamaño de la descarga: %3 Write watchpoints behavior - + Guardar el comportamiento de las expresiones vigiladas @@ -753,42 +753,42 @@ Tamaño de la descarga: %3 Autodetect - Detección automática + Detección automática Game Boy (DMG) - + Game Boy (DMG) Game Boy Pocket (MGB) - + Game Boy Pocket (MGB) Super Game Boy (SGB) - + Super Game Boy (SGB) Super Game Boy 2 (SGB) - + Super Game Boy 2 (SGB) Game Boy Color (CGB) - + Game Boy Color (CGB) Game Boy Advance (AGB) - + Game Boy Advance (AGB) Super Game Boy Color (SGB + CGB) - + Super Game Boy Color (SGB + CGB) @@ -798,102 +798,102 @@ Tamaño de la descarga: %3 MBC1 - + MBC1 MBC2 - + MBC2 MBC3 - + MBC3 MBC3 + RTC - + MBC3 + RTC MBC5 - + MBC5 MBC5 + Rumble - + MBC5 + Vibración MBC6 - + MBC6 MBC7 (Tilt) - + MBC7 (Ladeado) MMM01 - + MMM01 HuC-1 - + HuC-1 HuC-3 - + HuC-3 Pocket Cam - + Pocket Cam TAMA5 - TAMA5 + TAMA5 Wisdom Tree - + Wisdom Tree NT (new) - + NT (nuevo) Pokémon Jade/Diamond - + Pokémon Jade/Diamante BBD - + BBD Hitek - + Hitek Sachen (MMC1) - + Sachen (MMC1) Sachen (MMC2) - + Sachen (MMC2) @@ -918,22 +918,22 @@ Tamaño de la descarga: %3 Background mode - Modo de fondo (BG) + Modo de fondo Mode 0: 4 tile layers - Modo 0: 4 capas de tiles + Modo 0: 4 capas de tesela Mode 1: 2 tile layers + 1 rotated/scaled tile layer - Modo 1: 2 capas de tiles + 1 capa de tiles con rotación/escalado + Modo 1: 2 capas de teselas + 1 capa de tesela con rotación/escalado Mode 2: 2 rotated/scaled tile layers - Modo 2: 2 capas de tiles con rotación/escalado + Modo 2: 2 capas de teselas con rotación/escalado @@ -968,7 +968,7 @@ Tamaño de la descarga: %3 Linear OBJ tile mapping - Asignación de tiles OBJ lineal + Asignación de teselas OBJ lineal @@ -978,22 +978,22 @@ Tamaño de la descarga: %3 Enable background 0 - Habilitar BG 0 + Habilitar fondo 0 Enable background 1 - Habilitar BG 1 + Habilitar fondo 1 Enable background 2 - Habilitar BG 2 + Habilitar fondo 2 Enable background 3 - Habilitar BG 3 + Habilitar fondo 3 @@ -1023,17 +1023,17 @@ Tamaño de la descarga: %3 Currently in VBlank - En VBlank ahora + Ahora en VBlank Currently in HBlank - En HBlank ahora + Ahora en HBlank Currently in VCounter - En VCounter ahora + Ahora en VCounter @@ -1053,12 +1053,12 @@ Tamaño de la descarga: %3 VCounter scanline - Línea de exploración VCounter + Scanline VCounter Current scanline - Línea de exploración actual + Scanline actual @@ -1074,7 +1074,7 @@ Tamaño de la descarga: %3 Tile data base (* 16kB) - Dirección base de tiles (* 16kB) + Dirección base de tesela (* 16kB) @@ -1098,7 +1098,7 @@ Tamaño de la descarga: %3 Tile map base (* 2kB) - Dirección base de asignación de tiles (* 2kB) + Dirección base de asignación de tesela (* 2kB) @@ -1106,13 +1106,13 @@ Tamaño de la descarga: %3 Background dimensions - Dimensiones de BG + Dimensiones de fondo Overflow wraps - Envolver en desbordamiento + Envolver desbordamiento @@ -1160,7 +1160,7 @@ Tamaño de la descarga: %3 Integer part - Parte entera + Parte de entero @@ -1168,7 +1168,7 @@ Tamaño de la descarga: %3 Integer part (low) - Parte entera (baja) + Parte de entero (baja) @@ -1176,7 +1176,7 @@ Tamaño de la descarga: %3 Integer part (high) - Parte entera (alta) + Parte de entero (alta) @@ -1205,112 +1205,112 @@ Tamaño de la descarga: %3 Window 0 enable BG 0 - Window 0 habilitar BG 0 + Ventana 0 habilitar fondo 0 Window 0 enable BG 1 - Window 0 habilitar BG 1 + Ventana 0 habilitar fondo 1 Window 0 enable BG 2 - Window 0 habilitar BG 2 + Ventana 0 habilitar fondo 2 Window 0 enable BG 3 - Window 0 habilitar BG 3 + Ventana 0 habilitar fondo 3 Window 0 enable OBJ - Window 0 habilitar OBJ + Ventana 0 habilitar OBJ Window 0 enable blend - Window 0 habilitar mezcla + Ventana 0 habilitar mezcla Window 1 enable BG 0 - Window 1 habilitar BG 0 + Ventana 1 habilitar fondo 0 Window 1 enable BG 1 - Window 1 habilitar BG 1 + Ventana 1 habilitar fondo 1 Window 1 enable BG 2 - Window 1 habilitar BG 2 + Ventana 1 habilitar fondo 2 Window 1 enable BG 3 - Window 1 habilitar BG 3 + Ventana 1 habilitar fondo 3 Window 1 enable OBJ - Window 1 habilitar OBJ + Ventana 1 habilitar OBJ Window 1 enable blend - Window 1 habilitar mezcla + Ventana 1 habilitar mezcla Outside window enable BG 0 - Window externa habilitar BG 0 + Ventana externa habilitar fondo 0 Outside window enable BG 1 - Window externa habilitar BG 1 + Ventana externa habilitar fondo 1 Outside window enable BG 2 - Window externa habilitar BG 2 + Ventana externa habilitar fondo 2 Outside window enable BG 3 - Window externa habilitar BG 3 + Ventana externa habilitar fondo 3 Outside window enable OBJ - Window externa habilitar OBJ + Ventana externa habilitar OBJ Outside window enable blend - Outside window habilitar mezcla + Ventana externa habilitar mezcla OBJ window enable BG 0 - OBJ window habilitar BG 0 + Ventana OBJ habilitar fondo 0 OBJ window enable BG 1 - OBJ window habilitar BG 1 + Ventana OBJ habilitar fondo 1 OBJ window enable BG 2 - OBJ window habilitar BG 2 + Ventana OBJ habilitar fondo 2 OBJ window enable BG 3 - OBJ window habilitar BG 3 + Ventana OBJ habilitar fondo 3 @@ -1325,42 +1325,42 @@ Tamaño de la descarga: %3 Background mosaic size vertical - Tamaño vertical mosaico BG + Tamaño vertical de mosaico de fondo Background mosaic size horizontal - Tamaño horizontal mosaico BG + Tamaño horizontal de mosaico de fondo Object mosaic size vertical - Tamaño vertical mosaico OBJ + Tamaño vertical de mosaico de objeto Object mosaic size horizontal - Tamaño horizontal mosaico OBJ + Tamaño horizontal de mosaico de objeto BG 0 target 1 - BG 0 objetivo 1 + Fondo 0 objetivo 1 BG 1 target 1 - BG 1 objetivo 1 + Fondo 1 objetivo 1 BG 2 target 1 - BG 2 objetivo 1 + Fondo 2 objetivo 1 BG 3 target 1 - BG 3 objetivo 1 + Fondo 3 objetivo 1 @@ -1400,22 +1400,22 @@ Tamaño de la descarga: %3 BG 0 target 2 - BG 0 objetivo 2 + Fondo 0 objetivo 2 BG 1 target 2 - BG 1 objetivo 2 + Fondo 1 objetivo 2 BG 2 target 2 - BG 2 objetivo 2 + Fondo 2 objetivo 2 BG 3 target 2 - BG 3 objetivo 2 + Fondo 3 objetivo 2 @@ -1539,7 +1539,7 @@ Tamaño de la descarga: %3 Reset - Reinicializar + Reiniciar @@ -1738,7 +1738,7 @@ Tamaño de la descarga: %3 Channel A reset - Canal A reinicializar + Reiniciar canal A @@ -1758,7 +1758,7 @@ Tamaño de la descarga: %3 Channel B reset - Canal B reinicializar + Reiniciar canal B @@ -2108,7 +2108,7 @@ Tamaño de la descarga: %3 Video Capture - Captura de video + Captura de vídeo @@ -2543,7 +2543,7 @@ Tamaño de la descarga: %3 Background tile map - Mapa de tiles en fondo (BG) + Mapa de tesela de fondo @@ -2560,7 +2560,7 @@ Tamaño de la descarga: %3 Background tile data - Datos de tiles de fondo + Datos de tesela de fondo @@ -2580,7 +2580,7 @@ Tamaño de la descarga: %3 Window tile map - Mapa de tiles en window + Mapa de tesela en pantalla @@ -3107,7 +3107,7 @@ Tamaño de la descarga: %3 Tile base - Base tiles + Base de tesela @@ -3550,7 +3550,7 @@ Tamaño de la descarga: %3 Transform - Transform + Transformar @@ -3631,7 +3631,7 @@ Tamaño de la descarga: %3 Tile - Tile + Tesela @@ -3909,7 +3909,7 @@ Tamaño de la descarga: %3 Export BG - Exportar BG + Exportar fondo @@ -4008,12 +4008,12 @@ Tamaño de la descarga: %3 Save Printout - + Guardar impresión Portable Network Graphics (*.png) - + Portable Network Graphics (*.png) @@ -4072,7 +4072,7 @@ Tamaño de la descarga: %3 Bug report archive - Archivo del reporte de bugs + Archivo del informe de errores @@ -4082,12 +4082,12 @@ Tamaño de la descarga: %3 Generate Bug Report - Generar informe de defecto + Generar informe de error <html><head/><body><p>To file a bug report, please first generate a report file to attach to the bug report you're about to file. It is recommended that you include the save files, as these often help with debugging issues. This will collect some information about the version of {projectName} you're running, your configuration, your computer, and the game you currently have open (if any). Once this collection is completed you can review all of the information gathered below and save it to a zip file. The collection will automatically attempt to redact any personal information, such as your username if it's in any of the paths gathered, but just in case you can edit it afterwards. After you have generated and saved it, please click the button below or go to <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> to file the bug report on GitHub. Make sure to attach the report you generated!</p></body></html> - <html><head/><body><p>Antes de enviar un reporte de errores, primero genera un archivo de reporte para enviarlo como adjunto. Recomendamos adjuntar los archivos de guardado ya que puede ayudar con la investigación de reportes. Esto recopilara alguna información sobre la versión de {projectName} que corres, su configuración, su computadora, y el juego que tiene abierto (si alguno). Cuando esta colección termine, puede visualizar toda la información y guardarla a un archivo ZIP. Este proceso intentara eliminar automáticamente sus datos personales (como su usuario, si se encuentra en algunas de las rutas de directorio generadas), pero las puede modificar luego por si acaso. Luego generar y guardar el reporte, pulse el botón inferior o visite <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> para presentar el reporte en GitHub. ¡Asegúrese de agregar el archivo del reporte que ha generado!</p></body></html> + <html><head/><body><p>Antes de enviar un informe de errores, por favor primero genera un archivo de reporte para enviarlo como adjunto. Recomendamos adjuntar los archivos de guardado ya que puede ayudar con la investigación de reportes. Esto recopilará información sobre la versión de {projectName} que estás ejecutando y su configuración, tu ordenador, y el juego que tienes abierto (si hay alguno). Cuando esto termine, puedes ver toda la información y guardarla en un archivo ZIP. Este proceso intentará eliminar automáticamente tus datos personales (como tu usuario, si se encuentra en algunas de las rutas de directorio generadas), pero siempre se puede modificiar luego. Tras generar y guardar el reporte, pulsa el botón inferior o visita <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> para subirlo a GitHub. ¡Asegúrate de adjuntar el informe que has generado!</p></body></html> @@ -4107,12 +4107,12 @@ Tamaño de la descarga: %3 Include save file - Incluir archivo de guardado + Incluir partida guardada Create and include savestate - Crear y incluir archivo de estado + Crear e incluir archivo de estado @@ -4294,7 +4294,7 @@ Tamaño de la descarga: %3 Untitled buffer - + Búfer sin nombre @@ -4302,52 +4302,52 @@ Tamaño de la descarga: %3 Scripting - + Scripts Run - + Ejecutar File - + Archivo Load recent script - + Cargar script reciente Load script... - + Cargar script... &Reset - &Reinicializar + &Reiniciar 0 - 0 + 0 Select script to load - + Elegir script Lua scripts (*.lua) - + Scripts de Lua (*.lua) All files (*.*) - + Todos los archivos (*.*) @@ -4385,12 +4385,12 @@ Tamaño de la descarga: %3 Offset time - + Tiempo de compensación sec - + seg @@ -4467,7 +4467,7 @@ Tamaño de la descarga: %3 None - Ninguno + Ninguno @@ -4482,7 +4482,7 @@ Tamaño de la descarga: %3 Controllers - Controladores + Mandos @@ -4549,12 +4549,12 @@ Tamaño de la descarga: %3 Audio/Video - Audio/video + Audio/vídeo Gameplay - + Jugabilidad @@ -4599,7 +4599,7 @@ Tamaño de la descarga: %3 Audio driver: - Sistema de audio: + Controlador de audio: @@ -4719,7 +4719,7 @@ Tamaño de la descarga: %3 Display driver: - Sistema de video: + Controlador de vídeo: @@ -4756,7 +4756,7 @@ Tamaño de la descarga: %3 Video - Video + Vídeo @@ -4777,7 +4777,7 @@ Tamaño de la descarga: %3 Show filename instead of ROM name in library view - Mostrar el nombre del archivo en vez del nombre de la ROM en la vista de librería + Mostrar nombre del archivo en la vista de biblioteca @@ -4788,32 +4788,32 @@ Tamaño de la descarga: %3 When inactive: - Cuando este inactivo: + Cuando esté inactiva: On loading a game: - + Al guardar la partida: Load last state - + Cargar último estado Load cheats - + Cargar trucos Save entered cheats - + Guardar trucos introducidos When minimized: - Cuando este minimizada: + Cuando esté minimizada: @@ -4838,7 +4838,7 @@ Tamaño de la descarga: %3 Update channel: - Actualizar canal: + Canal de actualizaciones: @@ -4853,7 +4853,7 @@ Tamaño de la descarga: %3 Last checked: - Ultima vez comprobado: + Comprobado por última vez: @@ -4913,7 +4913,7 @@ Tamaño de la descarga: %3 Dynamically update window title - Actualizar titulo de ventana dinámicamente + Actualizar título de ventana dinámicamente @@ -4949,7 +4949,7 @@ Tamaño de la descarga: %3 Show filename instead of ROM name in title bar - Enseñar el nombre de archivo en lugar del nombre de ROM en el titulo de la ventana + Mostrar nombre de archivo en el título de ventana @@ -4964,12 +4964,12 @@ Tamaño de la descarga: %3 Log to file - Guardar a archivo + Guardar en archivo Log to console - Guardar a consola + Escribir en consola @@ -5014,7 +5014,7 @@ Tamaño de la descarga: %3 Allow opposing input directions - Permitir direcciones opuestas al mismo tiempo + Permitir introducir teclas opuestas al mismo tiempo @@ -5050,7 +5050,7 @@ Tamaño de la descarga: %3 Rewind history: - Hist. de rebobinado: + Historial de rebobinado: @@ -5076,7 +5076,7 @@ Tamaño de la descarga: %3 Screenshot - Pantallazo + Captura de pantalla @@ -5087,7 +5087,7 @@ Tamaño de la descarga: %3 Preload entire ROM into memory - Cargar ROM completa a la memoria + Cargar ROM completa en la memoria @@ -5102,7 +5102,7 @@ Tamaño de la descarga: %3 Video renderer: - Renderizador de video: + Renderizador de vídeo: @@ -5145,12 +5145,12 @@ Tamaño de la descarga: %3 Use BIOS file if found - Usar archivo BIOS si fue encontrado + Usar archivo BIOS si está disponible Skip BIOS intro - Saltar animación de entrada del BIOS + Saltar animación de entrada de la BIOS @@ -5170,7 +5170,7 @@ Tamaño de la descarga: %3 Periodically autosave state - + Guardar estado automáticamente @@ -5184,7 +5184,7 @@ Tamaño de la descarga: %3 Same directory as the ROM - Al mismo directorio que la ROM + En el mismo directorio que la ROM @@ -5194,7 +5194,7 @@ Tamaño de la descarga: %3 Screenshots - Pantallazos + Capturas de pantalla @@ -5376,7 +5376,7 @@ Tamaño de la descarga: %3 Export tiles - Exportar tiles + Exportar teselas @@ -5392,7 +5392,7 @@ Tamaño de la descarga: %3 Tiles - Tiles + Teselas @@ -5412,7 +5412,7 @@ Tamaño de la descarga: %3 Palette - Paleta + Paleta @@ -5422,7 +5422,7 @@ Tamaño de la descarga: %3 Tiles per row - Tiles por fila + Teselas por fila @@ -5432,22 +5432,22 @@ Tamaño de la descarga: %3 Displayed tiles - + Teselas mostradas Only BG tiles - Solo BG tiles + Sólo teselas de fondo Only OBJ tiles - Solo OBJ tiles + Sólo teselas de objeto Both - Ambos + Ambos @@ -5465,12 +5465,12 @@ Tamaño de la descarga: %3 Failed to open output video file: %1 - Error al abrir el archivo de video de salida: %1 + Error al abrir el archivo de vídeo de salida: %1 Native (%0x%1) - Native (%0x%1) + Nativo (%0x%1) @@ -5480,7 +5480,7 @@ Tamaño de la descarga: %3 Record Video - Grabar video + Grabar vídeo @@ -5567,7 +5567,7 @@ Tamaño de la descarga: %3 &Native - &NAtivo + &Nativo @@ -5608,7 +5608,7 @@ Tamaño de la descarga: %3 WavPack - + WavPack @@ -5701,7 +5701,7 @@ Tamaño de la descarga: %3 %1 Video Logs (*.mvl) - Video-registros de %1 (*.mvl) + Registros de vídeo de %1 (*.mvl) @@ -5764,12 +5764,12 @@ Tamaño de la descarga: %3 Select video log - Seleccionar video-registro + Seleccionar registro de vídeo Video logs (*.mvl) - Video-registros (*.mvl) + Registros de vídeo (*.mvl) @@ -5863,7 +5863,7 @@ Tamaño de la descarga: %3 Save games - Datos de guardado + Datos de guardado @@ -5898,12 +5898,12 @@ Tamaño de la descarga: %3 Recent - Recientes + Cargar reciente Make portable - Hacer "portable" + Crear instalación portable @@ -5913,7 +5913,7 @@ Tamaño de la descarga: %3 Report bug... - Reportar bug... + Reportar error... @@ -5979,17 +5979,17 @@ Tamaño de la descarga: %3 Load alternate save game... - Elegir juego guardado alterno... + Cargar partida guardada alternativa... Load temporary save game... - Elegir juego guardado temporal... + Cargar partida guardada temporal... Convert e-Reader card image to raw... - Convertir imagen de tarjeta e-Reader a raw... + Convertir imagen de tarjeta e-Reader a archivo en bruto... @@ -6040,7 +6040,7 @@ Tamaño de la descarga: %3 Load camera image... - Cargar imagen para la cámara... + Cargar imagen para cámara... @@ -6085,7 +6085,7 @@ Tamaño de la descarga: %3 &Reset - &Reinicializar + &Reiniciar @@ -6095,7 +6095,7 @@ Tamaño de la descarga: %3 Yank game pak - Tirar del cartucho + Sacar cartucho @@ -6180,7 +6180,7 @@ Tamaño de la descarga: %3 Audio/&Video - Audio/&video + Audio/&vídeo @@ -6265,7 +6265,7 @@ Tamaño de la descarga: %3 Video layers - Capas de video + Capas de vídeo @@ -6345,12 +6345,12 @@ Tamaño de la descarga: %3 Scripting... - + Scripts... Game state views - + Estado del juego @@ -6395,12 +6395,12 @@ Tamaño de la descarga: %3 Record debug video log... - Grabar registro de depuración de video... + Grabar registro de depuración de vídeo... Stop debug video log - Detener registro de depuración de video + Detener registro de depuración de vídeo @@ -6450,22 +6450,22 @@ Tamaño de la descarga: %3 Autofire Up - Disparo automático Arriba + Disparo automático arriba Autofire Right - Disparo automático Derecha + Disparo automático derecha Autofire Down - Disparo automático Abajo + Disparo automático abajo Autofire Left - Disparo automático Izquierda + Disparo automático izquierda From 8e5f5ba8acc9810dd3a7a190d6f6004d7d5eed0d Mon Sep 17 00:00:00 2001 From: nivea Date: Wed, 28 Dec 2022 17:25:18 +0000 Subject: [PATCH 157/159] Qt: Update translation (Japanese) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/ja/ --- src/platform/qt/ts/mgba-ja.ts | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/platform/qt/ts/mgba-ja.ts b/src/platform/qt/ts/mgba-ja.ts index fb0cfb04c..6d12001fe 100644 --- a/src/platform/qt/ts/mgba-ja.ts +++ b/src/platform/qt/ts/mgba-ja.ts @@ -41,47 +41,52 @@ Game Boy Advance is a registered trademark of Nintendo Co., Ltd. An update is available - + アップデートが利用可能です An update to %1 is available. - + %1 のアップデートが利用可能です。 + Do you want to download and install it now? You will need to restart the emulator when the download is complete. - + +今すぐダウンロードしてインストールしますか?ダウンロードが完了したら、エミュレータを再起動する必要があります。 Auto-update is not available on this platform. If you wish to update you will need to do it manually. - + +このプラットフォームでは自動アップデートは利用できません。アップデートしたい場合は手動で行う必要があります。 Current version: %1 New version: %2 Download size: %3 - + 現在のバージョン: %1 +新しいバージョン: %2 +ダウンロードサイズ: %3 Downloading update... - + アップデートをダウンロード中... Downloading failed. Please update manually. - + ダウンロードに失敗しました。手動で更新してください。 Downloading done. Press OK to restart %1 and install the update. - + ダウンロード完了。OKで%1 を再起動し、アップデートをインストールします。 @@ -89,12 +94,12 @@ Download size: %3 Stable - + 安定版 Development - + 開発版 @@ -104,7 +109,7 @@ Download size: %3 (None) - + (なし) @@ -170,7 +175,7 @@ Download size: %3 Audio device is missing its core - + オーディオ デバイスにコアがありません From 9a50b6dcd043d9a47740866b7210d5c7ff3aeecb Mon Sep 17 00:00:00 2001 From: Alexander Hedberg Date: Sat, 14 Jan 2023 11:35:20 +0100 Subject: [PATCH 158/159] Qt: Added translation (Swedish) Translation: mGBA/Qt Translate-URL: https://hosted.weblate.org/projects/mgba/mgba-qt/sv/ --- src/platform/qt/ts/mgba-sv.ts | 6523 +++++++++++++++++++++++++++++++++ 1 file changed, 6523 insertions(+) create mode 100644 src/platform/qt/ts/mgba-sv.ts diff --git a/src/platform/qt/ts/mgba-sv.ts b/src/platform/qt/ts/mgba-sv.ts new file mode 100644 index 000000000..954b8ffc2 --- /dev/null +++ b/src/platform/qt/ts/mgba-sv.ts @@ -0,0 +1,6523 @@ + + + + + QGBA::AboutScreen + + + About + Om mGBA + + + + <a href="http://mgba.io/">Website</a> • <a href="https://forums.mgba.io/">Forums / Support</a> • <a href="https://patreon.com/mgba">Donate</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Source</a> + <a href="http://mgba.io/">Hemsida</a> • <a href="https://forums.mgba.io/">Forum / Hjälp</a> • <a href="https://patreon.com/mgba">Stödja mGBA</a> • <a href="https://github.com/mgba-emu/mgba/tree/{gitBranch}">Källkod</a> + + + + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + Branch: <tt>{gitBranch}</tt><br/>Revision: <tt>{gitCommit}</tt> + + + + {projectName} would like to thank the following patrons from Patreon: + {projectName} tackar följande donatorer från Patreon: + + + + © 2013 – {year} Jeffrey Pfau, licensed under the Mozilla Public License, version 2.0 +Game Boy Advance is a registered trademark of Nintendo Co., Ltd. + © 2013 – {year} Jeffrey Pfau, licensierad under Mozilla Public License, version 2.0 +Game Boy Advance är ett registrerat varumärke av Nintendo Co., Ltd. + + + + {projectName} is an open-source Game Boy Advance emulator + {projectName} är en emulator för Game Boy Advance, byggt på öppen källkod + + + + QGBA::ApplicationUpdatePrompt + + + An update is available + En uppdatering är tillgänglig + + + + An update to %1 is available. + + En uppdatering till %1 är tillgänglig. + + + + + +Do you want to download and install it now? You will need to restart the emulator when the download is complete. + +Vill du ladda ner och installera det nu? Programmet måste startas om när nedladdningen är klar. + + + + +Auto-update is not available on this platform. If you wish to update you will need to do it manually. + +Det finns ej automatiska uppdateringar för detta operativ system. Önskar du uppdatera programmet måste du göra det manuellt. + + + + Current version: %1 +New version: %2 +Download size: %3 + Nuvarande version: %1 +Nästa version: %2 +Nedladdningsstorlek: %3 + + + + Downloading update... + Uppdatering laddas ner... + + + + Downloading failed. Please update manually. + Nedladdningen misslyckades. Uppdateringen måste installeras manuellt. + + + + Downloading done. Press OK to restart %1 and install the update. + Nedladdning klar. Välj OK för att starta om %1 och installera uppdateringen. + + + + QGBA::ApplicationUpdater + + + Stable + Standard + + + + Development + Utveckling + + + + Unknown + Okänd + + + + (None) + (Inga) + + + + QGBA::ArchiveInspector + + + Open in archive... + Öppna i arkiveringsprogram... + + + + Loading... + Laddas... + + + + QGBA::AssetTile + + + Tile # + Tile # + + + + Palette # + Palette # + + + + Address + Adress + + + + Red + Röd + + + + Green + Grön + + + + Blue + Blå + + + + + + 0x%0 (%1) + 0x%0 (%1) + + + + QGBA::AudioDevice + + + Can't set format of context-less audio device + Det går inte att ställa in formatet för kontextlös ljudenhet + + + + Audio device is missing its core + Ljudenheten saknar kärnan + + + + Writing data to read-only audio device + Skriver data till skrivskyddad ljudenhet + + + + QGBA::AudioProcessorQt + + + Can't start an audio processor without input + Kan inte starta en ljudprocessor utan ingång + + + + QGBA::AudioProcessorSDL + + + Can't start an audio processor without input + Kan inte starta en ljudprocessor utan ingång + + + + QGBA::BattleChipView + + + BattleChip Gate + BattleChip Gate + + + + Chip name + Chip namn + + + + Insert + Infoga + + + + Save + Spara + + + + Load + Ladda + + + + Add + Lägg till + + + + Remove + Ta bort + + + + Gate type + Gate typ + + + + Inserted + Infogad + + + + Chip ID + Chip ID + + + + Update Chip data + Uppdatera Chip-data + + + + Show advanced + Visa avancerat + + + + BattleChip data missing + BattleChip-data saknas + + + + BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? + BattleChip-data saknas. BattleChip Gates fungerar fortfarande, men viss grafik kommer att saknas. Vill du ladda ner datan nu? + + + + + Select deck file + Välj deck-fil + + + + Incompatible deck + Inkompatibelt deck + + + + The selected deck is not compatible with this Chip Gate + Den valda deck är inte kompatibel med denna Chip Gate + + + + QGBA::CheatsModel + + + (untitled) + (saknar namn) + + + + Failed to open cheats file: %1 + Misslyckades att öppna cheats-filen: %1 + + + + QGBA::CheatsView + + + Cheats + Cheats + + + + Add New Code + Lägg till ny kod + + + + Remove + + + + + Add Lines + + + + + Code type + + + + + Save + + + + + Load + + + + + Enter codes here... + + + + + + Autodetect (recommended) + + + + + + Select cheats file + + + + + Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. + + + + + QGBA::CoreController + + + Reset r%1-%2 %3 + + + + + + Rewinding not currently enabled + + + + + Reset the game? + + + + + Most games will require a reset to load the new save. Do you want to reset now? + + + + + Failed to open save file: %1 + + + + + Failed to open game file: %1 + + + + + Can't yank pack in unexpected platform! + + + + + Failed to open snapshot file for reading: %1 + + + + + Failed to open snapshot file for writing: %1 + + + + + QGBA::CoreManager + + + Failed to open game file: %1 + + + + + Could not load game. Are you sure it's in the correct format? + + + + + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). + + + + + QGBA::DebuggerConsole + + + Debugger + + + + + Enter command (try `help` for more info) + + + + + Break + + + + + QGBA::DebuggerConsoleController + + + Could not open CLI history for writing + + + + + QGBA::DolphinConnector + + + Connect to Dolphin + + + + + Local computer + + + + + IP address + + + + + Connect + + + + + Disconnect + + + + + Close + + + + + Reset on connect + + + + + Couldn't Connect + + + + + Could not connect to Dolphin. + + + + + QGBA::FrameView + + + Inspect frame + + + + + Magnification + + + + + Freeze frame + + + + + Backdrop color + + + + + Disable scanline effects + + + + + Export + + + + + Reset + + + + + Export frame + + + + + Portable Network Graphics (*.png) + + + + + None + + + + + Background + + + + + Window + + + + + Objwin + + + + + Sprite + + + + + Backdrop + + + + + Frame + + + + + %1 %2 + + + + + QGBA::GBAApp + + + Enable Discord Rich Presence + + + + + QGBA::GBAKeyEditor + + + Clear Button + + + + + Clear Analog + + + + + Refresh + + + + + Set all + + + + + QGBA::GDBWindow + + + Server settings + + + + + Local port + + + + + Bind address + + + + + Write watchpoints behavior + + + + + Standard GDB + + + + + Internal change detection + + + + + Break on all writes + + + + + Break + + + + + Stop + + + + + Start + + + + + Crash + + + + + Could not start GDB server + + + + + QGBA::GIFView + + + Record GIF/WebP/APNG + + + + + Loop + + + + + Start + + + + + Stop + + + + + Select File + + + + + APNG + + + + + GIF + + + + + WebP + + + + + Frameskip + + + + + Failed to open output file: %1 + + + + + Select output file + + + + + Graphics Interchange Format (*.gif);;WebP ( *.webp);;Animated Portable Network Graphics (*.png *.apng) + + + + + QGBA::GameBoy + + + + Autodetect + + + + + Game Boy (DMG) + + + + + Game Boy Pocket (MGB) + + + + + Super Game Boy (SGB) + + + + + Super Game Boy 2 (SGB) + + + + + Game Boy Color (CGB) + + + + + Game Boy Advance (AGB) + + + + + Super Game Boy Color (SGB + CGB) + + + + + ROM Only + + + + + MBC1 + + + + + MBC2 + + + + + MBC3 + + + + + MBC3 + RTC + + + + + MBC5 + + + + + MBC5 + Rumble + + + + + MBC6 + + + + + MBC7 (Tilt) + + + + + MMM01 + + + + + HuC-1 + + + + + HuC-3 + + + + + Pocket Cam + + + + + TAMA5 + + + + + Wisdom Tree + + + + + NT (new) + + + + + Pokémon Jade/Diamond + + + + + BBD + + + + + Hitek + + + + + Sachen (MMC1) + + + + + Sachen (MMC2) + + + + + QGBA::IOViewer + + + I/O Viewer + + + + + 0x0000 + + + + + + + B + + + + + Background mode + + + + + Mode 0: 4 tile layers + + + + + Mode 1: 2 tile layers + 1 rotated/scaled tile layer + + + + + Mode 2: 2 rotated/scaled tile layers + + + + + Mode 3: Full 15-bit bitmap + + + + + Mode 4: Full 8-bit bitmap + + + + + Mode 5: Small 15-bit bitmap + + + + + CGB Mode + + + + + Frame select + + + + + Unlocked HBlank + + + + + Linear OBJ tile mapping + + + + + Force blank screen + + + + + Enable background 0 + + + + + Enable background 1 + + + + + Enable background 2 + + + + + Enable background 3 + + + + + Enable OBJ + + + + + Enable Window 0 + + + + + Enable Window 1 + + + + + Enable OBJ Window + + + + + Swap green components + + + + + Currently in VBlank + + + + + Currently in HBlank + + + + + Currently in VCounter + + + + + Enable VBlank IRQ generation + + + + + Enable HBlank IRQ generation + + + + + Enable VCounter IRQ generation + + + + + VCounter scanline + + + + + Current scanline + + + + + + + + Priority + + + + + + + + Tile data base (* 16kB) + + + + + + + + Enable mosaic + + + + + + + + Enable 256-color + + + + + + + + Tile map base (* 2kB) + + + + + + + + Background dimensions + + + + + + Overflow wraps + + + + + + + + + + Horizontal offset + + + + + + + + + + Vertical offset + + + + + + + + + + + + + + + + Fractional part + + + + + + + + + + + + Integer part + + + + + + + + Integer part (low) + + + + + + + + Integer part (high) + + + + + + End x + + + + + + Start x + + + + + + End y + + + + + + Start y + + + + + Window 0 enable BG 0 + + + + + Window 0 enable BG 1 + + + + + Window 0 enable BG 2 + + + + + Window 0 enable BG 3 + + + + + Window 0 enable OBJ + + + + + Window 0 enable blend + + + + + Window 1 enable BG 0 + + + + + Window 1 enable BG 1 + + + + + Window 1 enable BG 2 + + + + + Window 1 enable BG 3 + + + + + Window 1 enable OBJ + + + + + Window 1 enable blend + + + + + Outside window enable BG 0 + + + + + Outside window enable BG 1 + + + + + Outside window enable BG 2 + + + + + Outside window enable BG 3 + + + + + Outside window enable OBJ + + + + + Outside window enable blend + + + + + OBJ window enable BG 0 + + + + + OBJ window enable BG 1 + + + + + OBJ window enable BG 2 + + + + + OBJ window enable BG 3 + + + + + OBJ window enable OBJ + + + + + OBJ window enable blend + + + + + Background mosaic size vertical + + + + + Background mosaic size horizontal + + + + + Object mosaic size vertical + + + + + Object mosaic size horizontal + + + + + BG 0 target 1 + + + + + BG 1 target 1 + + + + + BG 2 target 1 + + + + + BG 3 target 1 + + + + + OBJ target 1 + + + + + Backdrop target 1 + + + + + Blend mode + + + + + Disabled + + + + + Additive blending + + + + + Brighten + + + + + Darken + + + + + BG 0 target 2 + + + + + BG 1 target 2 + + + + + BG 2 target 2 + + + + + BG 3 target 2 + + + + + OBJ target 2 + + + + + Backdrop target 2 + + + + + Blend A (target 1) + + + + + Blend B (target 2) + + + + + Blend Y + + + + + + Sweep shifts + + + + + + Sweep subtract + + + + + + Sweep time (in 1/128s) + + + + + + + + + + + + Sound length + + + + + + + + Duty cycle + + + + + + + + + + Envelope step time + + + + + + + + + + Envelope increase + + + + + + + + + + Initial volume + + + + + + + Sound frequency + + + + + + + + + + + + Timed + + + + + + + + + + + + Reset + + + + + Double-size wave table + + + + + Active wave table + + + + + + Enable channel 3 + + + + + + Volume + + + + + + 0% + + + + + + + 100% + + + + + + + 50% + + + + + + + 25% + + + + + + + + 75% + + + + + + Clock divider + + + + + + Register stages + + + + + + 15 + + + + + + 7 + + + + + + Shifter frequency + + + + + PSG volume right + + + + + PSG volume left + + + + + + Enable channel 1 right + + + + + + Enable channel 2 right + + + + + + Enable channel 3 right + + + + + + Enable channel 4 right + + + + + + Enable channel 1 left + + + + + + Enable channel 2 left + + + + + + Enable channel 3 left + + + + + + Enable channel 4 left + + + + + PSG master volume + + + + + Loud channel A + + + + + Loud channel B + + + + + Enable channel A right + + + + + Enable channel A left + + + + + Channel A timer + + + + + + 0 + + + + + + + + + + + + + 1 + + + + + Channel A reset + + + + + Enable channel B right + + + + + Enable channel B left + + + + + Channel B timer + + + + + Channel B reset + + + + + + Active channel 1 + + + + + + Active channel 2 + + + + + + Active channel 3 + + + + + + Active channel 4 + + + + + + Enable audio + + + + + Bias + + + + + Resolution + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sample + + + + + + + + + + + + Address (low) + + + + + + + + + + + + Address (high) + + + + + + + Sound frequency (low) + + + + + + + Sound frequency (high) + + + + + Source (high) + + + + + Source (low) + + + + + Destination (high) + + + + + Destination (low) + + + + + + Green (low) + + + + + + Green (high) + + + + + + + + Word count + + + + + + + + Destination offset + + + + + + + + + + + + Increment + + + + + + + + + + + + Decrement + + + + + + + + + + + + Fixed + + + + + + + + Increment and reload + + + + + + + + Source offset + + + + + + + + Repeat + + + + + + + + 32-bit + + + + + + + + Start timing + + + + + + + + + Immediate + + + + + + + + + + + + VBlank + + + + + + + + + + + HBlank + + + + + + + + + + + + + IRQ + + + + + + + + + + + + + + + Enable + + + + + + + Audio FIFO + + + + + Video Capture + + + + + DRQ + + + + + + + + + + + + Value + + + + + + + + Scale + + + + + + + + + 1/64 + + + + + + + + + 1/256 + + + + + + + + + 1/1024 + + + + + + + Cascade + + + + + + A + + + + + + Select + + + + + + Start + + + + + + Right + + + + + + Left + + + + + + Up + + + + + + Down + + + + + + R + + + + + + L + + + + + Condition + + + + + SC + + + + + SD + + + + + SI + + + + + SO + + + + + + VCounter + + + + + + Timer 0 + + + + + + Timer 1 + + + + + + Timer 2 + + + + + + Timer 3 + + + + + + SIO + + + + + + DMA 0 + + + + + + DMA 1 + + + + + + DMA 2 + + + + + + DMA 3 + + + + + + Keypad + + + + + + Gamepak + + + + + SRAM wait + + + + + + + + + 4 + + + + + + + + 3 + + + + + + + + + 2 + + + + + + + + + 8 + + + + + Cart 0 non-sequential + + + + + Cart 0 sequential + + + + + Cart 1 non-sequential + + + + + Cart 1 sequential + + + + + Cart 2 non-sequential + + + + + Cart 2 sequential + + + + + PHI terminal + + + + + + Disable + + + + + 4.19MHz + + + + + 8.38MHz + + + + + 16.78MHz + + + + + Gamepak prefetch + + + + + Enable IRQs + + + + + Right/A + + + + + Left/B + + + + + Up/Select + + + + + Down/Start + + + + + Active D-pad + + + + + Active face buttons + + + + + Internal clock + + + + + 32× clocking (CGB only) + + + + + Transfer active + + + + + Divider + + + + + 1/16 + + + + + + LCD STAT + + + + + + Timer + + + + + + Serial + + + + + + Joypad + + + + + Volume right + + + + + Output right + + + + + Volume left + + + + + Output left + + + + + Background enable/priority + + + + + Enable sprites + + + + + Double-height sprites + + + + + Background tile map + + + + + + 0x9800 – 0x9BFF + + + + + + 0x9C00 – 0x9FFF + + + + + Background tile data + + + + + 0x8800 – 0x87FF + + + + + 0x8000 – 0x8FFF + + + + + Enable window + + + + + Window tile map + + + + + Enable LCD + + + + + Mode + + + + + 0: HBlank + + + + + 1: VBlank + + + + + 2: OAM scan + + + + + 3: HDraw + + + + + In LYC + + + + + Enable HBlank (mode 0) IRQ + + + + + Enable VBlank (mode 1) IRQ + + + + + Enable OAM (mode 2) IRQ + + + + + Enable LYC IRQ + + + + + Current Y coordinate + + + + + Comparison Y coordinate + + + + + Start upper byte + + + + + + + Color 0 shade + + + + + + + Color 1 shade + + + + + + + Color 2 shade + + + + + + + Color 3 shade + + + + + Prepare to switch speed + + + + + Double speed + + + + + VRAM bank + + + + + Length + + + + + Timing + + + + + Write bit + + + + + Read bit + + + + + + Unknown + + + + + + Current index + + + + + + Auto-increment + + + + + + Red + + + + + + Blue + + + + + Sprite ordering + + + + + OAM order + + + + + x coordinate sorting + + + + + WRAM bank + + + + + QGBA::KeyEditor + + + + --- + + + + + Super (L) + + + + + Super (R) + + + + + Menu + + + + + QGBA::LibraryTree + + + Name + + + + + Location + + + + + Platform + + + + + Size + + + + + CRC32 + + + + + QGBA::LoadSaveState + + + + %1 State + + + + + + + + + + + + + No Save + + + + + 5 + + + + + 6 + + + + + 8 + + + + + 4 + + + + + 1 + + + + + 3 + + + + + 7 + + + + + 9 + + + + + 2 + + + + + Cancel + + + + + Load State + + + + + Save State + + + + + Empty + + + + + Corrupted + + + + + Slot %1 + + + + + QGBA::LogConfigModel + + + + Default + + + + + Fatal + + + + + Error + + + + + Warning + + + + + Info + + + + + Debug + + + + + Stub + + + + + Game Error + + + + + QGBA::LogController + + + [%1] %2: %3 + + + + + An error occurred + + + + + DEBUG + + + + + STUB + + + + + INFO + + + + + WARN + + + + + ERROR + + + + + FATAL + + + + + GAME ERROR + + + + + QGBA::LogView + + + Logs + + + + + Enabled Levels + + + + + Debug + + + + + Stub + + + + + Info + + + + + Warning + + + + + Error + + + + + Fatal + + + + + Game Error + + + + + Advanced settings + + + + + Clear + + + + + Max Lines + + + + + QGBA::MapView + + + Maps + + + + + Magnification + + + + + Export + + + + + Copy + + + + + Priority + + + + + + Map base + + + + + + Tile base + + + + + Size + + + + + + Offset + + + + + Xform + + + + + Map Addr. + + + + + Mirror + + + + + None + + + + + Both + + + + + Horizontal + + + + + Vertical + + + + + + + N/A + + + + + Export map + + + + + Portable Network Graphics (*.png) + + + + + QGBA::MemoryDump + + + Save Memory Range + + + + + Start Address: + + + + + Byte Count: + + + + + Dump across banks + + + + + Save memory region + + + + + Failed to open output file: %1 + + + + + QGBA::MemoryModel + + + Copy selection + + + + + Save selection + + + + + Paste + + + + + Load + + + + + All + + + + + Load TBL + + + + + Save selected memory + + + + + Failed to open output file: %1 + + + + + Load memory + + + + + Failed to open input file: %1 + + + + + TBL + + + + + ISO-8859-1 + + + + + QGBA::MemorySearch + + + Memory Search + + + + + Address + + + + + Current Value + + + + + + Type + + + + + Value + + + + + Numeric + + + + + Text + + + + + Width + + + + + + Guess + + + + + 1 Byte (8-bit) + + + + + 2 Bytes (16-bit) + + + + + 4 Bytes (32-bit) + + + + + Number type + + + + + Decimal + + + + + Hexadecimal + + + + + Search type + + + + + Equal to value + + + + + Greater than value + + + + + Less than value + + + + + Unknown/changed + + + + + Changed by value + + + + + Unchanged + + + + + Increased + + + + + Decreased + + + + + Search ROM + + + + + New Search + + + + + Search Within + + + + + Open in Memory Viewer + + + + + Refresh + + + + + (%0/%1×) + + + + + (⅟%0×) + + + + + (%0×) + + + + + %1 byte%2 + + + + + QGBA::MemoryView + + + Memory + + + + + Inspect Address: + + + + + Set Alignment: + + + + + &1 Byte + + + + + &2 Bytes + + + + + &4 Bytes + + + + + Unsigned Integer: + + + + + Signed Integer: + + + + + String: + + + + + Load TBL + + + + + Copy Selection + + + + + Paste + + + + + Save Selection + + + + + Save Range + + + + + Load + + + + + QGBA::MessagePainter + + + Frame %1 + + + + + QGBA::ObjView + + + Sprites + + + + + Address + + + + + Copy + + + + + Magnification + + + + + Geometry + + + + + Position + + + + + Dimensions + + + + + Matrix + + + + + Export + + + + + Attributes + + + + + Transform + + + + + + Off + + + + + Palette + + + + + Double Size + + + + + + + Return, Ctrl+R + + + + + Flipped + + + + + H + Short for horizontal + + + + + V + Short for vertical + + + + + Mode + + + + + + Normal + + + + + Mosaic + + + + + Enabled + + + + + Priority + + + + + Tile + + + + + + 0x%0 + + + + + + + + + + + + --- + + + + + Trans + + + + + OBJWIN + + + + + Invalid + + + + + + N/A + + + + + Export sprite + + + + + Portable Network Graphics (*.png) + + + + + QGBA::OverrideView + + + Game Overrides + + + + + Game Boy Advance + + + + + + + + Autodetect + + + + + Realtime clock + + + + + Gyroscope + + + + + Tilt + + + + + Light sensor + + + + + Rumble + + + + + Save type + + + + + None + + + + + SRAM + + + + + Flash 512kb + + + + + Flash 1Mb + + + + + EEPROM 8kB + + + + + EEPROM 512 bytes + + + + + SRAM 64kB (bootlegs only) + + + + + Idle loop + + + + + Game Boy Player features + + + + + VBA bug compatibility mode + + + + + Game Boy + + + + + Game Boy model + + + + + Memory bank controller + + + + + Background Colors + + + + + Sprite Colors 1 + + + + + Sprite Colors 2 + + + + + Palette preset + + + + + Official MBCs + + + + + Licensed MBCs + + + + + Unlicensed MBCs + + + + + QGBA::PaletteView + + + Palette + + + + + Background + + + + + Objects + + + + + Selection + + + + + Red + + + + + Green + + + + + Blue + + + + + 16-bit value + + + + + Hex code + + + + + Palette index + + + + + Export BG + + + + + Export OBJ + + + + + #%0 + + + + + 0x%0 + + + + + + + + 0x%0 (%1) + + + + + Export palette + + + + + Windows PAL (*.pal);;Adobe Color Table (*.act) + + + + + Failed to open output palette file: %1 + + + + + QGBA::PlacementControl + + + Adjust placement + + + + + All + + + + + Offset + + + + + X + + + + + Y + + + + + QGBA::PrinterView + + + Game Boy Printer + + + + + Hurry up! + + + + + Tear off + + + + + Magnification + + + + + Copy + + + + + Save Printout + + + + + Portable Network Graphics (*.png) + + + + + QGBA::ROMInfo + + + + + + (unknown) + + + + + bytes + + + + + (no database present) + + + + + ROM Info + + + + + Game name: + + + + + Internal name: + + + + + Game ID: + + + + + File size: + + + + + CRC32: + + + + + QGBA::ReportView + + + Bug report archive + + + + + ZIP archive (*.zip) + + + + + Generate Bug Report + + + + + <html><head/><body><p>To file a bug report, please first generate a report file to attach to the bug report you're about to file. It is recommended that you include the save files, as these often help with debugging issues. This will collect some information about the version of {projectName} you're running, your configuration, your computer, and the game you currently have open (if any). Once this collection is completed you can review all of the information gathered below and save it to a zip file. The collection will automatically attempt to redact any personal information, such as your username if it's in any of the paths gathered, but just in case you can edit it afterwards. After you have generated and saved it, please click the button below or go to <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> to file the bug report on GitHub. Make sure to attach the report you generated!</p></body></html> + + + + + Generate report + + + + + Save + + + + + Open issue list in browser + + + + + Include save file + + + + + Create and include savestate + + + + + QGBA::SaveConverter + + + Save games and save states (%1) + + + + + Select save game or save state + + + + + Save games (%1) + + + + + Select save game + + + + + Conversion failed + + + + + Failed to convert the save game. This is probably a bug. + + + + + No file selected + + + + + Could not open file + + + + + No valid formats found + + + + + Please select a valid input file + + + + + No valid conversions found + + + + + Cannot convert save games between platforms + + + + + Convert/Extract Save Game + + + + + Input file + + + + + + Browse + + + + + Output file + + + + + %1 %2 save game + + + + + little endian + + + + + big endian + + + + + SRAM + + + + + %1 flash + + + + + %1 EEPROM + + + + + %1 SRAM + RTC + + + + + %1 SRAM + + + + + packed MBC2 + + + + + unpacked MBC2 + + + + + MBC6 flash + + + + + MBC6 combined SRAM + flash + + + + + MBC6 SRAM + + + + + TAMA5 + + + + + %1 (%2) + + + + + %1 save state with embedded %2 save game + + + + + %1 SharkPort %2 save game + + + + + %1 GameShark Advance SP %2 save game + + + + + QGBA::ScriptingTextBuffer + + + Untitled buffer + + + + + QGBA::ScriptingView + + + Scripting + + + + + Run + + + + + File + + + + + Load recent script + + + + + Load script... + + + + + &Reset + + + + + 0 + + + + + Select script to load + + + + + Lua scripts (*.lua) + + + + + All files (*.*) + + + + + QGBA::SensorView + + + Sensors + + + + + Realtime clock + + + + + Fixed time + + + + + System time + + + + + Start time at + + + + + Now + + + + + Offset time + + + + + sec + + + + + MM/dd/yy hh:mm:ss AP + + + + + Light sensor + + + + + Brightness + + + + + Tilt sensor + + + + + + Set Y + + + + + + Set X + + + + + Gyroscope + + + + + Sensitivity + + + + + QGBA::SettingsView + + + + Qt Multimedia + + + + + SDL + + + + + Software (Qt) + + + + + + OpenGL + + + + + OpenGL (force version 1.x) + + + + + None + + + + + None (Still Image) + + + + + Keyboard + + + + + Controllers + + + + + Shortcuts + + + + + + Shaders + + + + + Select BIOS + + + + + Select directory + + + + + (%1×%2) + + + + + Never + + + + + Just now + + + + + Less than an hour ago + + + + + %n hour(s) ago + + + + + + + %n day(s) ago + + + + + + + Settings + + + + + Audio/Video + + + + + Gameplay + + + + + Interface + + + + + Update + + + + + Emulation + + + + + Enhancements + + + + + BIOS + + + + + Paths + + + + + Logging + + + + + Game Boy + + + + + Audio driver: + + + + + Audio buffer: + + + + + + 1536 + + + + + 512 + + + + + 768 + + + + + 1024 + + + + + 2048 + + + + + 3072 + + + + + 4096 + + + + + samples + + + + + Sample rate: + + + + + + 44100 + + + + + 22050 + + + + + 32000 + + + + + 48000 + + + + + Hz + + + + + Volume: + + + + + + + + Mute + + + + + Fast forward volume: + + + + + Audio in multiplayer: + + + + + All windows + + + + + Player 1 window only + + + + + Currently active player window + + + + + Display driver: + + + + + Frameskip: + + + + + Skip every + + + + + + frames + + + + + FPS target: + + + + + frames per second + + + + + Sync: + + + + + + Video + + + + + + Audio + + + + + Lock aspect ratio + + + + + Force integer scaling + + + + + Bilinear filtering + + + + + Show filename instead of ROM name in library view + + + + + + Pause + + + + + When inactive: + + + + + On loading a game: + + + + + Load last state + + + + + Load cheats + + + + + Save entered cheats + + + + + When minimized: + + + + + Current channel: + + + + + Current version: + + + + + Update channel: + + + + + Available version: + + + + + (Unknown) + + + + + Last checked: + + + + + Automatically check on start + + + + + Check now + + + + + Default color palette only + + + + + SGB color palette if available + + + + + GBC color palette if available + + + + + SGB (preferred) or GBC color palette if available + + + + + Game Boy Camera + + + + + Driver: + + + + + Source: + + + + + Native (59.7275) + + + + + Interframe blending + + + + + Language + + + + + Library: + + + + + List view + + + + + Tree view + + + + + Show when no game open + + + + + Clear cache + + + + + Allow opposing input directions + + + + + Suspend screensaver + + + + + Dynamically update window title + + + + + Show filename instead of ROM name in title bar + + + + + Show OSD messages + + + + + Enable Discord Rich Presence + + + + + Periodically autosave state + + + + + Show FPS in title bar + + + + + Show frame count in OSD + + + + + Show emulation info on reset + + + + + Fast forward speed: + + + + + + Unbounded + + + + + Fast forward (held) speed: + + + + + Autofire interval: + + + + + Enable rewind + + + + + Rewind history: + + + + + Idle loops: + + + + + Run all + + + + + Remove known + + + + + Detect and remove + + + + + Preload entire ROM into memory + + + + + Save state extra data: + + + + + + Save game + + + + + Load state extra data: + + + + + Models + + + + + GB only: + + + + + SGB compatible: + + + + + GBC only: + + + + + GBC compatible: + + + + + SGB and GBC compatible: + + + + + Game Boy palette + + + + + Preset: + + + + + + Screenshot + + + + + + Cheat codes + + + + + Enable Game Boy Player features by default + + + + + Enable VBA bug compatibility in ROM hacks + + + + + Video renderer: + + + + + Software + + + + + OpenGL enhancements + + + + + High-resolution scale: + + + + + (240×160) + + + + + XQ GBA audio (experimental) + + + + + GB BIOS file: + + + + + + + + + + + + + Browse + + + + + Use BIOS file if found + + + + + Skip BIOS intro + + + + + GBA BIOS file: + + + + + GBC BIOS file: + + + + + SGB BIOS file: + + + + + Save games + + + + + + + + + Same directory as the ROM + + + + + Save states + + + + + Screenshots + + + + + Patches + + + + + Cheats + + + + + Log to file + + + + + Log to console + + + + + Select Log File + + + + + Default BG colors: + + + + + Default sprite colors 1: + + + + + Default sprite colors 2: + + + + + Super Game Boy borders + + + + + QGBA::ShaderSelector + + + No shader active + + + + + Load shader + + + + + No shader loaded + + + + + by %1 + + + + + Preprocessing + + + + + Pass %1 + + + + + Shaders + + + + + Active Shader: + + + + + Name + + + + + Author + + + + + Description + + + + + Unload Shader + + + + + Load New Shader + + + + + QGBA::ShortcutModel + + + Action + + + + + Keyboard + + + + + Gamepad + + + + + QGBA::ShortcutView + + + Edit Shortcuts + + + + + Keyboard + + + + + Gamepad + + + + + Clear + + + + + QGBA::TileView + + + Export tiles + + + + + + Portable Network Graphics (*.png) + + + + + Export tile + + + + + Tiles + + + + + Export Selected + + + + + Export All + + + + + 256 colors + + + + + Palette + + + + + Magnification + + + + + Tiles per row + + + + + Fit to window + + + + + Displayed tiles + + + + + Only BG tiles + + + + + Only OBJ tiles + + + + + Both + + + + + Copy Selected + + + + + Copy All + + + + + QGBA::VideoView + + + Failed to open output video file: %1 + + + + + Native (%0x%1) + + + + + Select output file + + + + + Record Video + + + + + Start + + + + + Stop + + + + + Select File + + + + + Presets + + + + + High &Quality + + + + + &YouTube + + + + + + WebM + + + + + + MP4 + + + + + &Lossless + + + + + 4K + + + + + &1080p + + + + + &720p + + + + + &480p + + + + + &Native + + + + + Format + + + + + MKV + + + + + AVI + + + + + HEVC + + + + + HEVC (NVENC) + + + + + VP8 + + + + + VP9 + + + + + FFV1 + + + + + + None + + + + + FLAC + + + + + WavPack + + + + + Opus + + + + + Vorbis + + + + + MP3 + + + + + AAC + + + + + Uncompressed + + + + + Bitrate (kbps) + + + + + ABR + + + + + H.264 + + + + + H.264 (NVENC) + + + + + VBR + + + + + CRF + + + + + Dimensions + + + + + Lock aspect ratio + + + + + Show advanced + + + + + QGBA::Window + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + + + Archives (%1) + + + + + + + Select ROM + + + + + Select folder + + + + + + Select save + + + + + Select patch + + + + + Patches (*.ips *.ups *.bps) + + + + + Select e-Reader dotcode + + + + + e-Reader card (*.raw *.bin *.bmp) + + + + + Select image + + + + + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) + + + + + GameShark saves (*.sps *.xps) + + + + + Select video log + + + + + Video logs (*.mvl) + + + + + Crash + + + + + The game has crashed with the following error: + +%1 + + + + + Couldn't Start + + + + + Could not start game. + + + + + Unimplemented BIOS call + + + + + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. + + + + + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. + + + + + Really make portable? + + + + + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? + + + + + Restart needed + + + + + Some changes will not take effect until the emulator is restarted. + + + + + - Player %1 of %2 + + + + + %1 - %2 + + + + + %1 - %2 - %3 + + + + + %1 - %2 (%3 fps) - %4 + + + + + &File + + + + + Load &ROM... + + + + + Load ROM in archive... + + + + + Add folder to library... + + + + + Save games (%1) + + + + + Select save game + + + + + mGBA save state files (%1) + + + + + + Select save state + + + + + Select e-Reader card images + + + + + Image file (*.png *.jpg *.jpeg) + + + + + Conversion finished + + + + + %1 of %2 e-Reader cards converted successfully. + + + + + Load alternate save game... + + + + + Load temporary save game... + + + + + Load &patch... + + + + + Boot BIOS + + + + + Replace ROM... + + + + + Scan e-Reader dotcodes... + + + + + Convert e-Reader card image to raw... + + + + + ROM &info... + + + + + Recent + + + + + Make portable + + + + + &Load state + + + + + Load state file... + + + + + &Save state + + + + + Save state file... + + + + + Quick load + + + + + Quick save + + + + + Load recent + + + + + Save recent + + + + + Undo load state + + + + + Undo save state + + + + + + State &%1 + + + + + Load camera image... + + + + + Convert save game... + + + + + GameShark saves (*.gsv *.sps *.xps) + + + + + Reset needed + + + + + Some changes will not take effect until the game is reset. + + + + + Save games + + + + + Import GameShark Save... + + + + + Export GameShark Save... + + + + + Automatically determine + + + + + Use player %0 save game + + + + + New multiplayer window + + + + + Connect to Dolphin... + + + + + Report bug... + + + + + About... + + + + + E&xit + + + + + &Emulation + + + + + &Reset + + + + + Sh&utdown + + + + + Yank game pak + + + + + &Pause + + + + + &Next frame + + + + + Fast forward (held) + + + + + &Fast forward + + + + + Fast forward speed + + + + + Unbounded + + + + + %0x + + + + + Rewind (held) + + + + + Re&wind + + + + + Step backwards + + + + + Solar sensor + + + + + Increase solar level + + + + + Decrease solar level + + + + + Brightest solar level + + + + + Darkest solar level + + + + + Brightness %1 + + + + + Game Boy Printer... + + + + + BattleChip Gate... + + + + + Audio/&Video + + + + + Frame size + + + + + %1× + + + + + Toggle fullscreen + + + + + Lock aspect ratio + + + + + Force integer scaling + + + + + Interframe blending + + + + + Bilinear filtering + + + + + Frame&skip + + + + + Mute + + + + + FPS target + + + + + Native (59.7275) + + + + + Take &screenshot + + + + + F12 + + + + + Record A/V... + + + + + Record GIF/WebP/APNG... + + + + + Video layers + + + + + Audio channels + + + + + Adjust layer placement... + + + + + &Tools + + + + + View &logs... + + + + + Game &overrides... + + + + + Game Pak sensors... + + + + + &Cheats... + + + + + Settings... + + + + + Open debugger console... + + + + + Start &GDB server... + + + + + Scripting... + + + + + Game state views + + + + + View &palette... + + + + + View &sprites... + + + + + View &tiles... + + + + + View &map... + + + + + &Frame inspector... + + + + + View memory... + + + + + Search memory... + + + + + View &I/O registers... + + + + + Record debug video log... + + + + + Stop debug video log + + + + + Exit fullscreen + + + + + GameShark Button (held) + + + + + Autofire + + + + + Autofire A + + + + + Autofire B + + + + + Autofire L + + + + + Autofire R + + + + + Autofire Start + + + + + Autofire Select + + + + + Autofire Up + + + + + Autofire Right + + + + + Autofire Down + + + + + Autofire Left + + + + + Clear + + + + + QObject + + + %1 byte + + + + + %1 kiB + + + + + %1 MiB + + + + + GBA + + + + + GB + + + + + ? + + + + + QShortcut + + + Shift + + + + + Control + + + + + Alt + + + + + Meta + + + + From a2d12548f52b9b714e12a254267e46e968ca0deb Mon Sep 17 00:00:00 2001 From: Vicki Pfau Date: Sun, 29 Jan 2023 00:20:18 -0800 Subject: [PATCH 159/159] Qt: Update translations --- src/platform/qt/ts/mgba-de.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-en.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-es.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-fr.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-hu.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-it.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-ja.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-ko.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-ms.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-nb_NO.ts | 731 ++++++++++++++++++----------- src/platform/qt/ts/mgba-sv.ts | 769 ++++++++++++++++++++----------- 11 files changed, 5234 insertions(+), 2845 deletions(-) diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index cbd273705..2f90b12d8 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -1,6 +1,29 @@ + + QGBA + + + 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) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Download-Größe: %3 QGBA::ApplicationUpdater - + Stable Stabil - + Development Entwicklung - + Unknown Unbekannt - + (None) (keiner) @@ -400,12 +423,12 @@ Download-Größe: %3 Das GamePak kann nur auf unterstützten Plattformen herausgezogen werden! - + 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 @@ -413,17 +436,17 @@ Download-Größe: %3 QGBA::CoreManager - + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Could not load game. Are you sure it's in the correct format? Konnte das Spiel nicht laden. Bist Du sicher, dass es im korrekten Format vorliegt? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Fehler beim Laden der Spielstand-Datei; Speicherdaten können nicht aktualisiert werden. Bitte stelle sicher, dass das Speicherdaten-Verzeichnis ohne zusätzliche Berechtigungen (z.B. UAC in Windows) beschreibbar ist. @@ -502,6 +525,195 @@ Download-Größe: %3 Verbindung zu Dolphin konnte nicht hergestellt werden. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Hintergrund + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Durchsuchen + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Download-Größe: %3 QGBA::GBAApp - + Enable Discord Rich Presence Discord-Integration aktivieren @@ -601,22 +813,22 @@ Download-Größe: %3 QGBA::GBAKeyEditor - + Clear Button Button löschen - + Clear Analog Analog löschen - + Refresh Aktualisieren - + Set all Alle belegen @@ -750,148 +962,168 @@ Download-Größe: %3 QGBA::GameBoy - - + + Autodetect Automatisch erkennen - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Nur ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + Rumble - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (Tilt) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (neu) - + Pokémon Jade/Diamond Pokémon Jade/Diamond - + BBD BBD - + Hitek Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) Sachen (MMC1) - + Sachen (MMC2) Sachen (MMC2) @@ -2910,7 +3142,7 @@ Download-Größe: %3 Defekt - + Slot %1 Speicherplatz %1 @@ -4070,12 +4302,12 @@ Download-Größe: %3 QGBA::ReportView - + Bug report archive Fehlerbericht speichern - + ZIP archive (*.zip) ZIP-Archiv (*.zip) @@ -4438,95 +4670,95 @@ Download-Größe: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (erzwinge Version 1.x) - + None Keiner - + None (Still Image) Keiner (Standbild) - + Keyboard Tastatur - + Controllers Gamepads - + Shortcuts Tastenkürzel - - + + Shaders Shader - + Select BIOS BIOS auswählen - + Select directory Verzeichnis auswählen - + (%1×%2) (%1×%2) - + Never Nie - + Just now Gerade eben - + Less than an hour ago Vor weniger als einer Stunde - + %n hour(s) ago Vor %n Stunde @@ -4534,7 +4766,7 @@ Download-Größe: %3 - + %n day(s) ago Vor %n Tag @@ -4851,37 +5083,37 @@ Download-Größe: %3 Jetzt überprüfen - + Default color palette only Nur Standard-Farbpalette - + SGB color palette if available SGB-Farbpalette, sofern verfügbar - + GBC color palette if available GBC-Farbpalette, sofern verfügbar - + SGB (preferred) or GBC color palette if available SGB (bevorzugt) oder GBC-Farbpalette, sofern verfügbar - + Game Boy Camera Game Boy Camera - + Driver: Treiber: - + Source: Quelle: @@ -5684,105 +5916,85 @@ Download-Größe: %3 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 - - + + Select save Speicherdatei wählen - + Select patch Patch wählen - + Patches (*.ips *.ups *.bps) Korrekturen (*.ips *.ups *.bps) - + Select e-Reader card images Bilder der Lesegerät-Karte auswählen - + Image file (*.png *.jpg *.jpeg) Bilddatei (*.png *.jpg *.jpeg) - + Conversion finished Konvertierung abgeschlossen - + %1 of %2 e-Reader cards converted successfully. %1 von %2 Lesegerät-Karten erfolgreich konvertiert. - + Select image Bild auswählen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bild-Datei (*.png *.gif *.jpg *.jpeg);;Alle Dateien (*) - + 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 @@ -5791,679 +6003,684 @@ Download-Größe: %3 %1 - + 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. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Es konnte kein geeignetes Ausgabegerät erstellt werden, stattdessen wird Software-Rendering als Rückfalloption genutzt. Spiele laufen möglicherweise langsamer, besonders innerhalb großer Fenster. - + 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... - + Save games Spielstände - + Automatically determine Automatisch erkennen - + Use player %0 save game Verwende Spielstand von Spieler %0 - + Load &patch... &Patch laden... - + Boot BIOS BIOS booten - + Replace ROM... ROM ersetzen... - + Convert e-Reader card image to raw... Lesegerät-Kartenbild in Rohdaten umwandeln … - + ROM &info... ROM-&Informationen... - + Recent Zuletzt verwendet - + Make portable Portablen Modus aktivieren - + &Load state Savestate (aktueller Zustand) &laden - + Load state file... Savestate-Datei laden... - + &Save state Savestate (aktueller Zustand) &speichern - + Save state file... Savestate-Datei speichern... - + Quick load Schnell laden - + Quick save Schnell speichern - + Load recent Lade zuletzt gespeicherten Savestate - + Save recent Speichere aktuellen Zustand - + Undo load state Laden des Savestate rückgängig machen - + Undo save state Speichern des Savestate rückgängig machen - - + + State &%1 Savestate &%1 - + Load camera image... Lade Kamerabild... - + Convert save game... Spielstand konvertieren... - + Reset needed Zurücksetzen erforderlich - + Some changes will not take effect until the game is reset. Einige Änderungen werden erst dann wirksam, wenn das Spiel zurückgesetzt wird. - + New multiplayer window Neues Multiplayer-Fenster - + Connect to Dolphin... Mit Dolphin verbinden... - + Report bug... Fehler melden... - + E&xit &Beenden - + &Emulation &Emulation - + &Reset Zu&rücksetzen - + Sh&utdown Schli&eßen - + Yank game pak Spielmodul herausziehen - + &Pause &Pause - + &Next frame &Nächstes Bild - + Fast forward (held) Schneller Vorlauf (gehalten) - + &Fast forward Schneller &Vorlauf - + Fast forward speed Vorlauf-Geschwindigkeit - + Unbounded Unbegrenzt - + %0x %0x - + Rewind (held) Zurückspulen (gehalten) - + Re&wind Zur&ückspulen - + Step backwards Schrittweiser Rücklauf - + Solar sensor Sonnen-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 - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Bildgröße - + Toggle fullscreen Vollbildmodus umschalten - + Lock aspect ratio Seitenverhältnis korrigieren - + Force integer scaling Pixelgenaue Skalierung (Integer scaling) - + Interframe blending Interframe-Überblendung - + Frame&skip Frame&skip - + Mute Stummschalten - + FPS target Bildwiederholrate - + Take &screenshot &Screenshot erstellen - + F12 F12 - + Scripting... Scripting... - + + Create forwarder... + + + + Game state views Spiel-Zustände ansehen - + Clear Leeren - + Game Boy Printer... Game Boy Printer... - + Video layers Video-Ebenen - + Audio channels Audio-Kanäle - + Adjust layer placement... Lage der Bildebenen anpassen... - + &Tools &Werkzeuge - + View &logs... &Logs ansehen... - + Game &overrides... Spiel-&Überschreibungen... - + &Cheats... &Cheats... - + Open debugger console... Debugger-Konsole öffnen... - + Start &GDB server... &GDB-Server starten... - + Settings... Einstellungen... - + Select folder Ordner auswählen - + Save games (%1) Spielstände (%1) - + Select save game Spielstand auswählen - + mGBA save state files (%1) mGBA-Savestates (%1) - - + + Select save state Savestate auswählen - + Select e-Reader dotcode e-Reader-Code auswählen - + e-Reader card (*.raw *.bin *.bmp) e-Reader-Karte (*.raw *.bin *.bmp) - + GameShark saves (*.gsv *.sps *.xps) GameShark-Spielstände (*.gsv *.sps *.xps) - + Couldn't Start Konnte nicht gestartet werden - + Could not start game. Spiel konnte nicht gestartet werden. - + Add folder to library... Ordner zur Bibliothek hinzufügen... - + Load alternate save game... Alternativen Spielstand laden... - + Load temporary save game... Temporären Spielstand laden... - + Scan e-Reader dotcodes... e-Reader-Code einlesen... - + Import GameShark Save... GameShare-Speicherstand importieren... - + Export GameShark Save... GameShark-Speicherstand exportieren... - + About... Über... - + %1× %1x - + Bilinear filtering Bilineare Filterung - + Native (59.7275) Nativ (59.7275) - + Record A/V... Audio/Video aufzeichnen... - + Record GIF/WebP/APNG... GIF/WebP/APNG aufzeichnen... - + Game Pak sensors... Spielmodul-Sensoren... - + View &palette... &Palette betrachten... - + View &sprites... &Sprites betrachten... - + View &tiles... &Tiles betrachten... - + View &map... &Map betrachten... - + &Frame inspector... &Bildbetrachter... - + View memory... Speicher betrachten... - + Search memory... Speicher durchsuchen... - + View &I/O registers... &I/O-Register betrachten... - + Record debug video log... Video-Protokoll aufzeichnen... - + Stop debug video log Aufzeichnen des Video-Protokolls beenden - + Exit fullscreen Vollbildmodus beenden - + GameShark Button (held) GameShark-Taste (gehalten) - + 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 @@ -6471,32 +6688,32 @@ Download-Größe: %3 QObject - + %1 byte %1 Byte - + %1 kiB %1 KiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-en.ts b/src/platform/qt/ts/mgba-en.ts index ec361c5dc..43a29402c 100644 --- a/src/platform/qt/ts/mgba-en.ts +++ b/src/platform/qt/ts/mgba-en.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -86,22 +109,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown - + (None) @@ -394,12 +417,12 @@ Download size: %3 - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -407,17 +430,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -496,6 +519,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -587,7 +799,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence @@ -595,22 +807,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh - + Set all @@ -744,148 +956,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2904,7 +3136,7 @@ Download size: %3 - + Slot %1 @@ -4064,12 +4296,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -4432,95 +4664,95 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4528,7 +4760,7 @@ Download size: %3 - + %n day(s) ago @@ -4855,37 +5087,37 @@ Download size: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5678,784 +5910,769 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -6463,32 +6680,32 @@ Download size: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-es.ts b/src/platform/qt/ts/mgba-es.ts index c72676dba..994566710 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROMs de Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROMs de Game Boy (%1) + + + + All ROMs (%1) + Todas las ROMs (%1) + + + + %1 Video Logs (*.mvl) + Registros de vídeo de %1 (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Tamaño de descarga: %3 QGBA::ApplicationUpdater - + Stable Estable - + Development Desarrollo - + Unknown Desconocido - + (None) (Ninguno) @@ -400,12 +423,12 @@ Tamaño de descarga: %3 ¡No se puede quitar el cartucho en esta plataforma! - + Failed to open snapshot file for reading: %1 Error al leer del archivo de captura: %1 - + Failed to open snapshot file for writing: %1 Error al escribir al archivo de captura: %1 @@ -413,17 +436,17 @@ Tamaño de descarga: %3 QGBA::CoreManager - + Failed to open game file: %1 Error al abrir el archivo del juego: %1 - + Could not load game. Are you sure it's in the correct format? No se pudo cargar el juego. ¿Estás seguro de que está en el formato correcto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Error al abrir el archivo de guardado; las partidas guardadas no se pueden actualizar. Por favor, asegúrate de que es posible escribir en el directorio de partidas guardadas sin necesidad de privilegios adicionales (por ejemplo, UAC en Windows). @@ -502,6 +525,195 @@ Tamaño de descarga: %3 No se pudo conectar a Dolphin. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Fondo + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Examinar + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Tamaño de descarga: %3 QGBA::GBAApp - + Enable Discord Rich Presence Habilitar Rich Presence en Discord @@ -601,22 +813,22 @@ Tamaño de descarga: %3 QGBA::GBAKeyEditor - + Clear Button Reestablecer botones - + Clear Analog Reestablecer sticks analógicos - + Refresh Actualizar - + Set all Configurar todo @@ -750,148 +962,168 @@ Tamaño de descarga: %3 QGBA::GameBoy - - + + Autodetect Detección automática - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Solo ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + Vibración - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (Ladeado) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (nuevo) - + Pokémon Jade/Diamond Pokémon Jade/Diamante - + BBD BBD - + Hitek Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) Sachen (MMC1) - + Sachen (MMC2) Sachen (MMC2) @@ -2910,7 +3142,7 @@ Tamaño de descarga: %3 Dañado - + Slot %1 Espacio %1 @@ -4070,12 +4302,12 @@ Tamaño de descarga: %3 QGBA::ReportView - + Bug report archive Archivo del informe de errores - + ZIP archive (*.zip) Archivo ZIP (*.zip) @@ -4438,95 +4670,95 @@ Tamaño de descarga: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forzar versión 1.x) - + None Ninguno - + None (Still Image) Nada (imagen estática) - + Keyboard Teclado - + Controllers Mandos - + Shortcuts Atajos de teclado - - + + Shaders Shaders - + Select BIOS Seleccionar BIOS - + Select directory Elegir carpeta - + (%1×%2) - + Never Nunca - + Just now Ahora mismo - + Less than an hour ago Hace menos de una hora - + %n hour(s) ago Hace %n hora @@ -4534,7 +4766,7 @@ Tamaño de descarga: %3 - + %n day(s) ago Hace %n dia @@ -4866,37 +5098,37 @@ Tamaño de descarga: %3 Comprobar ahora - + Default color palette only Sólo paleta de colores predeterminada - + SGB color palette if available Paleta de color SGB si está disponible - + GBC color palette if available Paleta de color GBC si está disponible - + SGB (preferred) or GBC color palette if available Paleta de colores SGB (preferida) o GBC si está disponible - + Game Boy Camera Cámara Game Boy - + Driver: Controlador: - + Source: Fuente: @@ -5684,100 +5916,80 @@ Tamaño de descarga: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROMs de Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROMs de Game Boy (%1) - - - - All ROMs (%1) - Todas las ROMs (%1) - - - - %1 Video Logs (*.mvl) - Registros de vídeo de %1 (*.mvl) - - - + Archives (%1) Contenedores (%1) - - - + + + Select ROM Seleccionar ROM - + Select folder Seleccionar carpeta - - + + Select save Seleccionar guardado - + Select patch Seleccionar parche - + Patches (*.ips *.ups *.bps) Parches (*.ips *.ups *.bps) - + Select e-Reader dotcode Seleccionar dotcode del e-Reader - + e-Reader card (*.raw *.bin *.bmp) Tarjeta e-Reader (*.raw *.bin *.bmp) - + Select image Seleccionar imagen - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Archivo de imagen (*.png *.gif *.jpg *.jpeg);;Todos los archivos (*) - + GameShark saves (*.sps *.xps) Guardados de GameShark (*.sps *.xps) - + Select video log Seleccionar registro de vídeo - + Video logs (*.mvl) Registros de vídeo (*.mvl) - + Crash Cierre inesperado - + The game has crashed with the following error: %1 @@ -5786,684 +5998,689 @@ Tamaño de descarga: %3 %1 - + Unimplemented BIOS call Llamada a BIOS no implementada - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Este juego utiliza una llamada al BIOS que no se ha implementado. Utiliza el BIOS oficial para obtener la mejor experiencia. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. No se pudo crear un dispositivo de pantalla apropiado, recurriendo a software. Los juegos pueden funcionar lentamente, especialmente con ventanas grandes. - + Really make portable? ¿Hacer "portable"? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Esto hará que el emulador cargue su configuración desde el mismo directorio que el ejecutable. ¿Quieres continuar? - + Restart needed Reinicio necesario - + Some changes will not take effect until the emulator is restarted. Algunos cambios no surtirán efecto hasta que se reinicie el emulador. - + - Player %1 of %2 - Jugador %1 de %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Archivo - + Load &ROM... Cargar &ROM... - + Load ROM in archive... Cargar ROM desde contenedor... - + Add folder to library... Agregar carpeta a la biblioteca... - + Save games Datos de guardado - + Automatically determine Determinar automáticamente - + Use player %0 save game Usar la partida guardada del jugador %0 - + Load &patch... Cargar &parche... - + Boot BIOS Arrancar BIOS - + Replace ROM... Reemplazar ROM... - + ROM &info... &Información de la ROM... - + Recent Cargar reciente - + Make portable Crear instalación portable - + &Load state Ca&rgar estado - + Report bug... Reportar error... - + About... Acerca de... - + Game Pak sensors... Sensores del cartucho... - + Clear Limpiar - + Load state file... Cargar archivo de estado... - + Save games (%1) Juegos guardados (%1) - + Select save game Elegir juego guardado - + mGBA save state files (%1) Archivos estados guardados mGBA (%1) - - + + Select save state Elegir estado guardado - + Select e-Reader card images Elegir imágenes de tarjeta e-Reader - + Image file (*.png *.jpg *.jpeg) Archivo de imagen (*.png *.jpg *.jpeg) - + Conversion finished Conversión terminada - + %1 of %2 e-Reader cards converted successfully. %1 de %2 tarjetas e-Reader convertidas con éxito. - + Load alternate save game... Cargar partida guardada alternativa... - + Load temporary save game... Cargar partida guardada temporal... - + Convert e-Reader card image to raw... Convertir imagen de tarjeta e-Reader a archivo en bruto... - + &Save state Guardar e&stado - + Save state file... Guardar archivo de estado... - + Quick load Cargado rápido - + Quick save Guardado rápido - + Load recent Cargar reciente - + Save recent Guardar reciente - + Undo load state Deshacer cargar estado - + Undo save state Deshacer guardar estado - - + + State &%1 Estado &%1 - + Load camera image... Cargar imagen para cámara... - + Convert save game... Convertir juego guardado... - + GameShark saves (*.gsv *.sps *.xps) Partidas guardadas de GameShark (*.gsv *.sps *.xps) - + Reset needed Reinicio necesario - + Some changes will not take effect until the game is reset. Algunos cambios no tendrán efecto hasta que se reinicie el juego. - + New multiplayer window Nueva ventana multijugador - + Connect to Dolphin... Conectar a Dolphin... - + E&xit Salir (&X) - + &Emulation &Emulación - + &Reset &Reiniciar - + Sh&utdown Apagar (&U) - + Yank game pak Sacar cartucho - + &Pause &Pausar - + &Next frame Cuadro siguie&nte - + Fast forward (held) Avance rápido (mantener) - + &Fast forward &Avance rápido - + Fast forward speed Velocidad de avance rápido - + Unbounded Sin límite - + %0x %0x - + Rewind (held) Rebobinar (mantener) - + Re&wind Re&bobinar - + Step backwards Paso hacia atrás - + Solar sensor Sensor solar - + Increase solar level Subir nivel - + Decrease solar level Bajar nivel - + Brightest solar level Más claro - + Darkest solar level Más oscuro - + Brightness %1 Brillo %1 - + Audio/&Video Audio/&vídeo - + Frame size Tamaño del cuadro - + Toggle fullscreen Pantalla completa - + Lock aspect ratio Bloquear proporción de aspecto - + Force integer scaling Forzar escala a enteros - + Bilinear filtering Filtro bilineal - + Frame&skip &Salto de cuadros - + Mute Silenciar - + FPS target Objetivo de FPS - + Native (59.7275) Nativo (59,7275) - + Take &screenshot Tomar pan&tallazo - + F12 F12 - + Game Boy Printer... Game Boy Printer... - + BattleChip Gate... BattleChip Gate... - + %1× %1× - + Interframe blending Mezcla entre cuadros - + Record A/V... Grabar A/V... - + Video layers Capas de vídeo - + Audio channels Canales de audio - + Adjust layer placement... Ajustar ubicación de capas... - + &Tools Herramien&tas - + View &logs... Ver re&gistros... - + Game &overrides... Ajustes específic&os por juego... - + Couldn't Start No se pudo iniciar - + Could not start game. No se pudo iniciar el juego. - + Scan e-Reader dotcodes... Escanear dotcodes del e-Reader... - + Import GameShark Save... Importar desde GameShark... - + Export GameShark Save... Exportar a GameShark... - + Record GIF/WebP/APNG... Grabar GIF/WebP/APNG... - + &Cheats... Tru&cos... - + Settings... Ajustes... - + Open debugger console... Abrir consola de depuración... - + Start &GDB server... Iniciar servidor &GDB... - + Scripting... Scripts... - + + Create forwarder... + + + + Game state views Estado del juego - + View &palette... Ver &paleta... - + View &sprites... Ver &sprites... - + View &tiles... Ver m&osaicos... - + View &map... Ver &mapa... - + &Frame inspector... Inspec&tor de cuadros... - + View memory... Ver memoria... - + Search memory... Buscar memoria... - + View &I/O registers... Ver registros &I/O... - + Record debug video log... Grabar registro de depuración de vídeo... - + Stop debug video log Detener registro de depuración de vídeo - + Exit fullscreen Salir de pantalla completa - + GameShark Button (held) Botón GameShark (mantener) - + Autofire Disparo automático - + Autofire A Disparo automático A - + Autofire B Disparo automático B - + Autofire L Disparo automático L - + Autofire R Disparo automático R - + Autofire Start Disparo automático Start - + Autofire Select Disparo automático Select - + Autofire Up Disparo automático arriba - + Autofire Right Disparo automático derecha - + Autofire Down Disparo automático abajo - + Autofire Left Disparo automático izquierda @@ -6471,32 +6688,32 @@ Tamaño de descarga: %3 QObject - + %1 byte %1 byte - + %1 kiB %1 kiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-fr.ts b/src/platform/qt/ts/mgba-fr.ts index df40af0a7..60d84bbd7 100644 --- a/src/platform/qt/ts/mgba-fr.ts +++ b/src/platform/qt/ts/mgba-fr.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROMs de Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROMs de Game Boy (%1) + + + + All ROMs (%1) + Toutes les ROM (%1) + + + + %1 Video Logs (*.mvl) + %1 Journaux vidéo (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Taille du téléchargement : %3 QGBA::ApplicationUpdater - + Stable Stable - + Development Développement - + Unknown Inconnue - + (None) (Aucune) @@ -401,12 +424,12 @@ Taille du téléchargement : %3 - + Failed to open snapshot file for reading: %1 Échec de l'ouverture de l'instantané pour lire : %1 - + Failed to open snapshot file for writing: %1 Échec de l'ouverture de l'instantané pour écrire : %1 @@ -414,17 +437,17 @@ Taille du téléchargement : %3 QGBA::CoreManager - + Failed to open game file: %1 Échec de l'ouverture du fichier de jeu : %1 - + Could not load game. Are you sure it's in the correct format? Impossible de charger le jeu. Êtes-vous sûr qu'il est dans le bon format ? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Impossible d'ouvrir le fichier de sauvegarde ; les sauvegardes en jeu ne peuvent pas être mises à jour. Veuillez vous assurer que le répertoire de sauvegarde est accessible en écriture sans privilèges supplémentaires (par exemple, UAC sous Windows). @@ -503,6 +526,195 @@ Taille du téléchargement : %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Arrière plan + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Parcourir + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -594,7 +806,7 @@ Taille du téléchargement : %3 QGBA::GBAApp - + Enable Discord Rich Presence Activer intégration avec Discord @@ -602,22 +814,22 @@ Taille du téléchargement : %3 QGBA::GBAKeyEditor - + Clear Button Bouton d'effacement - + Clear Analog Effacer l'analogique - + Refresh Rafraîchir - + Set all Tout définir @@ -751,148 +963,168 @@ Taille du téléchargement : %3 QGBA::GameBoy - - + + Autodetect Détection automatique - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2921,7 +3153,7 @@ Taille du téléchargement : %3 Corrompue - + Slot %1 Emplacement %1 @@ -4088,12 +4320,12 @@ Taille du téléchargement : %3 QGBA::ReportView - + Bug report archive Archive de signalement d'erreur - + ZIP archive (*.zip) Archive ZIP (*.zip) @@ -4457,95 +4689,95 @@ Taille du téléchargement : %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimédia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (version forcée 1.x) - + None Aucun - + None (Still Image) Aucun (Image fixe) - + Keyboard Clavier - + Controllers Contrôleurs - + Shortcuts Raccourcis - - + + Shaders Shaders - + Select BIOS Choisir le BIOS - + Select directory - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4553,7 +4785,7 @@ Taille du téléchargement : %3 - + %n day(s) ago @@ -4951,37 +5183,37 @@ Taille du téléchargement : %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5703,120 +5935,100 @@ Taille du téléchargement : %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROMs de Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROMs de Game Boy (%1) - - - - All ROMs (%1) - Toutes les ROM (%1) - - - - %1 Video Logs (*.mvl) - %1 Journaux vidéo (*.mvl) - - - + Archives (%1) Archives (%1) - - - + + + Select ROM Choisir une ROM - + Select folder Choisir un dossier - - + + Select save Choisir une sauvegarde - + Select patch Sélectionner un correctif - + Patches (*.ips *.ups *.bps) Correctifs/Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode Sélectionnez le numéro de point du e-Reader - + e-Reader card (*.raw *.bin *.bmp) e-Reader carte (*.raw *.bin *.bmp) - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Select image Choisir une image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Image (*.png *.gif *.jpg *.jpeg);;Tous les fichiers (*) - + GameShark saves (*.sps *.xps) Sauvegardes GameShark (*.sps *.xps) - + Select video log Sélectionner un journal vidéo - + Video logs (*.mvl) Journaux vidéo (*.mvl) - + Crash Plantage - + The game has crashed with the following error: %1 @@ -5825,664 +6037,669 @@ Taille du téléchargement : %3 %1 - + Unimplemented BIOS call Requête au BIOS non supporté - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Ce jeu utilise un appel BIOS qui n'est pas implémenté. Veuillez utiliser le BIOS officiel pour une meilleure expérience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Échec de la création d'un périphérique d'affichage approprié, retour à l'affichage du logiciel. Les jeux peuvent fonctionner lentement, en particulier avec des fenêtres plus grandes. - + Really make portable? Vraiment rendre portable ? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Cela amènera l'émulateur à charger sa configuration depuis le même répertoire que l'exécutable. Souhaitez vous continuer ? - + Restart needed Un redémarrage est nécessaire - + Some changes will not take effect until the emulator is restarted. Certains changements ne prendront effet qu'après le redémarrage de l'émulateur. - + - Player %1 of %2 - Joueur %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &Fichier - + Load &ROM... Charger une &ROM… - + Load ROM in archive... Charger la ROM d'une archive… - + Add folder to library... Ajouter un dossier à la bibliothèque… - + Load &patch... Charger un c&orrectif… - + Boot BIOS Démarrer le BIOS - + Replace ROM... Remplacer la ROM… - + + Create forwarder... + + + + Game state views - + Convert e-Reader card image to raw... - + ROM &info... &Infos sur la ROM… - + Recent Récent - + Make portable Rendre portable - + &Load state &Charger un état - + &Save state &Sauvegarder un état - + Quick load Chargement rapide - + Quick save Sauvegarde rapide - + Load recent Charger un fichier récent - + Save recent Sauvegarder un fichier récent - + Undo load state Annuler le chargement de l'état - + Undo save state Annuler la sauvegarde de l'état - - + + State &%1 État &%1 - + Load camera image... Charger une image de la caméra… - + Convert save game... - + New multiplayer window Nouvelle fenêtre multijoueur - + Connect to Dolphin... - + Report bug... Signalement de l'erreur… - + E&xit &Quitter - + &Emulation &Émulation - + &Reset &Réinitialiser - + Sh&utdown Extin&ction - + Yank game pak Yank game pak - + &Pause &Pause - + &Next frame &Image suivante - + Fast forward (held) Avance rapide (maintenir) - + &Fast forward A&vance rapide - + Fast forward speed Vitesse de l'avance rapide - + Unbounded Sans limites - + %0x %0x - + Rewind (held) Rembobiner (maintenir) - + Re&wind Rem&bobiner - + Step backwards Retour en arrière - + Solar sensor Capteur solaire - + Increase solar level Augmenter le niveau solaire - + Decrease solar level Diminuer le niveau solaire - + Brightest solar level Tester le niveau solaire - + Darkest solar level Assombrir le niveau solaire - + Brightness %1 Luminosité %1 - + Audio/&Video Audio/&Vidéo - + Frame size Taille de l'image - + Toggle fullscreen Basculer en plein écran - + Lock aspect ratio Bloquer les proportions - + Force integer scaling Forcer la mise à l'échelle par des nombres entiers - + Bilinear filtering Filtrage bilinèaire - + Frame&skip &Saut d'image - + Mute Muet - + FPS target FPS ciblé - + Take &screenshot Prendre une ca&pture d'écran - + F12 F12 - + Game Boy Printer... Imprimante GameBoy… - + Video layers Couches vidéo - + Audio channels Canaux audio - + Adjust layer placement... Ajuster la disposition… - + &Tools Ou&tils - + View &logs... Voir les &journaux… - + Game &overrides... - + Couldn't Start N'a pas pu démarrer - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + GameShark saves (*.gsv *.sps *.xps) - + Could not start game. Impossible de démarrer le jeu. - + Load alternate save game... - + Load temporary save game... - + Scan e-Reader dotcodes... Scanner les dotcodes e-Reader... - + Load state file... Charger le fichier d'état... - + Save state file... Enregistrer le fichier d'état... - + Import GameShark Save... Importer la sauvegarde de GameShark... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Sauvegarder les jeux - + Export GameShark Save... Exporter la sauvegarde de GameShark... - + Automatically determine - + Use player %0 save game - + About... À propos de… - + BattleChip Gate... - + %1× %1× - + Interframe blending Mélange d'images - + Native (59.7275) Natif (59.7275) - + Record A/V... Enregistrer A/V... - + Record GIF/WebP/APNG... Enregistrer GIF/WebP/APNG... - + Game Pak sensors... Capteurs de la Game Pak... - + &Cheats... &Cheats… - + Settings... Paramètres… - + Open debugger console... Ouvrir la console de débug… - + Start &GDB server... Démarrer le serveur &GDB… - + Scripting... - + View &palette... Voir la &palette… - + View &sprites... Voir les &sprites… - + View &tiles... Voir les &tiles… - + View &map... Voir la &map… - + &Frame inspector... Inspecteur de &frame... - + View memory... Voir la mémoire… - + Search memory... Recherche dans la mémoire… - + View &I/O registers... Voir les registres d'&E/S... - + Record debug video log... Enregistrer le journal vidéo de débogage... - + Stop debug video log Arrêter le journal vidéo de débogage - + Exit fullscreen Quitter le plein écran - + GameShark Button (held) Bouton GameShark (maintenir) - + Autofire Tir automatique - + Autofire A Tir automatique A - + Autofire B Tir automatique B - + Autofire L Tir automatique L - + Autofire R Tir automatique R - + Autofire Start Tir automatique Start - + Autofire Select Tir automatique Select - + Autofire Up Tir automatique Up - + Autofire Right Tir automatique Right - + Autofire Down Tir automatique Down - + Autofire Left Tir automatique Gauche - + Clear Vider @@ -6490,32 +6707,32 @@ Taille du téléchargement : %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-hu.ts b/src/platform/qt/ts/mgba-hu.ts index 38b8bdd70..074576da6 100644 --- a/src/platform/qt/ts/mgba-hu.ts +++ b/src/platform/qt/ts/mgba-hu.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -87,22 +110,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown - + (None) @@ -395,12 +418,12 @@ Download size: %3 A játékkazettát nem lehet kirántani ismeretlen platformon! - + Failed to open snapshot file for reading: %1 A pillanatkép fájljának olvasásra való megnyitása sikertelen: %1 - + Failed to open snapshot file for writing: %1 A pillanatkép fájljának írásra való megnyitása sikertelen: %1 @@ -408,17 +431,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 Nem sikerült a játékfájl megnyitása: %1 - + Could not load game. Are you sure it's in the correct format? A játék betöltése nem sikerült. Biztos vagy benne, hogy a megfelelő formátumú? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -497,6 +520,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Háttér + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -588,7 +800,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence Discord Rich Presence engedélyezése @@ -596,22 +808,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button Gombhozzárendelés törlése - + Clear Analog Analóg hozzárendelések törlése - + Refresh Frissítés - + Set all Mind beállítása @@ -745,148 +957,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect Automatikus észlelés - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2905,7 +3137,7 @@ Download size: %3 - + Slot %1 @@ -4065,12 +4297,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -4433,102 +4665,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None Nincs - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4793,37 +5025,37 @@ Download size: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5677,784 +5909,769 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Napló törlése @@ -6462,32 +6679,32 @@ Download size: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-it.ts b/src/platform/qt/ts/mgba-it.ts index 45092ddac..7be66f1c1 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROM per Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROM per Game Boy (%1) + + + + All ROMs (%1) + Tutte le ROM (%1) + + + + %1 Video Logs (*.mvl) + %1 log Video (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Dimensione del download: %3 QGBA::ApplicationUpdater - + Stable Stabile - + Development Sviluppo - + Unknown Sconosciuto - + (None) (Nessuno) @@ -400,12 +423,12 @@ Dimensione del download: %3 Non riesco a strappare il pacchetto in una piattaforma inaspettata! - + Failed to open snapshot file for reading: %1 Impossibile aprire il file snapshot per la lettura: %1 - + Failed to open snapshot file for writing: %1 Impossibile aprire il file snapshot per la scrittura: %1 @@ -413,17 +436,17 @@ Dimensione del download: %3 QGBA::CoreManager - + Failed to open game file: %1 Impossibile aprire il file di gioco: %1 - + Could not load game. Are you sure it's in the correct format? Impossibile caricare il gioco. Sei sicuro che sia nel formato corretto? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). Impossibile aprire il file di salvataggio; i salvataggi in gioco non possono essere aggiornati. Assicurati che la directory di salvataggio sia scrivibile senza privilegi aggiuntivi (ad esempio UAC su Windows). @@ -502,6 +525,195 @@ Dimensione del download: %3 Impossibile collegarsi a Dolphin. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Sfondo + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Sfoglia + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Dimensione del download: %3 QGBA::GBAApp - + Enable Discord Rich Presence Abilita Discord Rich Presence @@ -601,22 +813,22 @@ Dimensione del download: %3 QGBA::GBAKeyEditor - + Clear Button Svuota i pulsanti - + Clear Analog Svuota Analogici - + Refresh Aggiorna - + Set all Imposta tutti @@ -750,148 +962,168 @@ Dimensione del download: %3 QGBA::GameBoy - - + + Autodetect Rilevamento automatico - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Solo ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + Tremolio - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (Tilt) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (nuovo) - + Pokémon Jade/Diamond Pokemon Giada/Diamante - + BBD BBD - + Hitek Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) Sachen (MMC1) - + Sachen (MMC2) Sachen (MMC2) @@ -2910,7 +3142,7 @@ Dimensione del download: %3 Corrotto - + Slot %1 Slot %1 @@ -4070,12 +4302,12 @@ Dimensione del download: %3 QGBA::ReportView - + Bug report archive Archivio delle segnalazioni di bug - + ZIP archive (*.zip) Archivio ZIP (*.zip) @@ -4438,95 +4670,95 @@ Dimensione del download: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) Software (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (forza la versione 1.x) - + None Nessuno - + None (Still Image) nessuno (immagine fissa) - + Keyboard Tastiera - + Controllers Controller - + Shortcuts Scorciatoie - - + + Shaders Shader - + Select BIOS Seleziona BIOS - + Select directory Seleziona directory - + (%1×%2) (%1×%2) - + Never Mai - + Just now Solo adesso - + Less than an hour ago Meno di un ora fa - + %n hour(s) ago %n ora fa @@ -4534,7 +4766,7 @@ Dimensione del download: %3 - + %n day(s) ago %n giorno fa @@ -4891,37 +5123,37 @@ Dimensione del download: %3 Tavolozza Game Boy - + Default color palette only Solo tavolozza colori predefinita - + SGB color palette if available Tavolozza colori SGB se disponibile - + GBC color palette if available Tavolozza colori GBC se disponibile - + SGB (preferred) or GBC color palette if available Tavolozza colori SGB (preferita) o GBC se disponibile - + Game Boy Camera Videocamera Game Boy - + Driver: Driver: - + Source: Sorgente: @@ -5684,115 +5916,95 @@ Dimensione del download: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROM per Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROM per Game Boy (%1) - - - - All ROMs (%1) - Tutte le ROM (%1) - - - - %1 Video Logs (*.mvl) - %1 log Video (*.mvl) - - - + Archives (%1) Archivi (%1) - - - + + + Select ROM Seleziona ROM - - + + Select save Seleziona salvataggio - + Select patch Seleziona patch - + Patches (*.ips *.ups *.bps) Patch (*.ips *.ups *.bps) - + Select e-Reader dotcode Selezione e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) Scheda e-Reader (*.raw *.bin *.bmp) - + Select e-Reader card images Seleziona immagini carte e-Reader - + Image file (*.png *.jpg *.jpeg) File immagine (*.png *.jpg *.jpeg) - + Conversion finished Conversione terminata - + %1 of %2 e-Reader cards converted successfully. %1 di %2 carte e-Reader convertite con successo. - + Select image Seleziona immagine - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) File immagine (*.png *.gif *.jpg *.jpeg);;Tutti i file (*) - + GameShark saves (*.sps *.xps) Salvataggi GameShark (*.sps *.xps) - + Select video log Seleziona log video - + Video logs (*.mvl) Log video (*.mvl) - + Crash Errore fatale - + The game has crashed with the following error: %1 @@ -5801,669 +6013,674 @@ Dimensione del download: %3 %1 - + Unimplemented BIOS call BIOS non implementato - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Questo gioco utilizza una chiamata BIOS non implementata. Utilizza il BIOS ufficiale per una migliore esperienza. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Impossibile creare un dispositivo di visualizzazione appropriato, tornando alla visualizzazione software. I giochi possono funzionare lentamente, specialmente con finestre più grandi. - + Really make portable? Vuoi davvero rendere portatile l'applicazione? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? In questo modo l'emulatore carica la propria configurazione dalla stessa cartella dell'eseguibile. Vuoi continuare? - + Restart needed È necessario riavviare - + Some changes will not take effect until the emulator is restarted. Alcune modifiche non avranno effetto finché l'emulatore non verrà riavviato. - + - Player %1 of %2 - Giocatore %1 di %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File File - + Load &ROM... Carica ROM... - + Load ROM in archive... Carica la ROM in archivio... - + Load &patch... Carica patch... - + Boot BIOS Avvia BIOS - + Replace ROM... Sostituisci la ROM... - + Scan e-Reader dotcodes... Scansiona e-Reader dotcode... - + Convert e-Reader card image to raw... Converti immagini carte e-Reader in raw... - + ROM &info... Informazioni ROM... - + Recent Recenti - + Make portable Rendi portatile - + &Load state Carica stato - + &Save state Salva stato - + Quick load Caricamento rapido - + Quick save Salvataggio rapido - + Load recent Carica recente - + Save recent Salva recente - + Undo load state Annulla il caricamento dello stato - + Undo save state Annulla salvataggio stato - - + + State &%1 Stato %1 - + Load camera image... Carica immagine fotocamera... - + Convert save game... Converti salvataggi... - + GameShark saves (*.gsv *.sps *.xps) Salvataggi GameShark (*.gsv *.sps *.xps) - + Reset needed Reset necessario - + Some changes will not take effect until the game is reset. Alcune modifiche non si applicheranno finché il gioco non viene resettato. - + New multiplayer window Nuova finestra multigiocatore - + Connect to Dolphin... Connessione a Dolphin... - + Report bug... Segnala bug... - + E&xit Esci (&X) - + &Emulation Emulazione - + &Reset Reset - + Sh&utdown Spegni (&U) - + Yank game pak Stacca game pak - + &Pause Pausa - + &Next frame Salta il prossimo frame (&N) - + Fast forward (held) Avanzamento rapido (tieni premuto) - + &Fast forward Avanzamento rapido (&F) - + Fast forward speed Velocità di avanzamento rapido - + Unbounded Illimitata - + %0x %0x - + Rewind (held) Riavvolgimento (tieni premuto) - + Re&wind Riavvolgimento (&W) - + Step backwards Torna indietro - + Solar sensor Sensore solare - + Increase solar level Incrementa il livello solare - + Decrease solar level Riduci il livello solare - + Brightest solar level Livello solare massimo - + Darkest solar level Livello solare minimo - + Brightness %1 Luminosità %1 - + Audio/&Video Audio/Video - + Frame size Dimensione frame - + Toggle fullscreen Abilita schermo Intero - + Lock aspect ratio Blocca rapporti aspetto - + Frame&skip Salto frame - + Mute Muto - + FPS target FPS finali - + Take &screenshot Acquisisci schermata - + F12 F12 - + Record GIF/WebP/APNG... Registra GIF / WebP / APNG ... - + Video layers Layers video - + Audio channels Canali audio - + &Tools Strumenti - + View &logs... Visualizza registri (&log)... - + Game &overrides... Valore specifico per il gioco... - + &Cheats... Trucchi... - + + Create forwarder... + + + + Open debugger console... Apri console debugger... - + Start &GDB server... Avvia server GDB... - + Settings... Impostazioni... - + Select folder Seleziona cartella - + Couldn't Start Non è stato possibile avviare - + Could not start game. Non è stato possibile avviare il gioco. - + Add folder to library... Aggiungi cartella alla libreria... - + Load state file... Carica stato di salvataggio... - + Save state file... Salva stato di salvataggio... - + Import GameShark Save... Importa Salvataggio GameShark... - + Export GameShark Save... Esporta Salvataggio GameShark... - + About... Informazioni… - + Force integer scaling Forza ridimensionamento a interi - + Bilinear filtering Filtro bilineare - + Game Boy Printer... Stampante Game Boy... - + Save games (%1) Salvataggi (%1) - + Select save game Seleziona salvataggio - + mGBA save state files (%1) file di stati di salvataggio mGBA (%1) - - + + Select save state Seleziona stato di salvataggio - + Save games Salvataggi - + Load alternate save game... Carica stato di salvataggio alternativo... - + Load temporary save game... Carica salvataggio temporaneo... - + Automatically determine Determina automaticamente - + Use player %0 save game Usa il salvataggio del giocatore %0 - + BattleChip Gate... BattleChip Gate... - + %1× %1x - + Interframe blending Miscelazione dei frame - + Native (59.7275) Nativo (59.7) - + Record A/V... Registra A/V... - + Adjust layer placement... Regola posizionamento layer... - + Game Pak sensors... Sensori Game Pak... - + Scripting... Scripting... - + Game state views Viste degli stati del gioco - + View &palette... Mostra palette... - + View &sprites... Mostra sprites... - + View &tiles... Mostra tiles... - + View &map... Mostra mappa... - + &Frame inspector... &Frame inspector... - + View memory... Mostra memoria... - + Search memory... Ricerca memoria... - + View &I/O registers... Mostra registri I/O... - + Record debug video log... Registra video log di debug... - + Stop debug video log Ferma video log di debug - + Exit fullscreen Esci da Schermo Intero - + GameShark Button (held) Pulsante GameShark (tieni premuto) - + Autofire Pulsanti Autofire - + Autofire A Autofire A - + Autofire B Autofire B - + Autofire L Autofire L - + Autofire R Autofire R - + Autofire Start Autofire Start - + Autofire Select Autofire Select - + Autofire Up Autofire Su - + Autofire Right AAutofire Destra - + Autofire Down Autofire Giù - + Autofire Left Autofire Sinistra - + Clear Pulisci @@ -6471,32 +6688,32 @@ Dimensione del download: %3 QObject - + %1 byte %1 byte - + %1 kiB %1 kiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-ja.ts b/src/platform/qt/ts/mgba-ja.ts index 6d12001fe..b3c0cfbc1 100644 --- a/src/platform/qt/ts/mgba-ja.ts +++ b/src/platform/qt/ts/mgba-ja.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ゲームボーイアドバンスファイル (%1) + + + + Game Boy ROMs (%1) + ゲームボーイファイル (%1) + + + + All ROMs (%1) + すべてのファイル (%1) + + + + %1 Video Logs (*.mvl) + %1ビデオログ (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable 安定版 - + Development 開発版 - + Unknown 不明 - + (None) (なし) @@ -400,12 +423,12 @@ Download size: %3 予期しないプラットフォームでパックをヤンクすることはできません! - + Failed to open snapshot file for reading: %1 読み取り用のスナップショットファイルを開けませんでした: %1 - + Failed to open snapshot file for writing: %1 書き込み用のスナップショットファイルを開けませんでした: %1 @@ -413,17 +436,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 ゲームファイルを開けませんでした: %1 - + Could not load game. Are you sure it's in the correct format? ゲームをロードできませんでした。ゲームのフォーマットが正しいことを確認してください。 - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -502,6 +525,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + バックグラウンド + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + 参照 + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence DiscordのRich Presence有効 @@ -601,22 +813,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button ボタンクリア - + Clear Analog アナログクリア - + Refresh 更新 - + Set all すべて設定 @@ -750,148 +962,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect 自動検出 - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2910,7 +3142,7 @@ Download size: %3 破損 - + Slot %1 スロット %1 @@ -4070,12 +4302,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive バグレポートアーカイブ - + ZIP archive (*.zip) ZIPアーカイブ (*.zip) @@ -4438,102 +4670,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia Qt Multimedia - + SDL SDL - + Software (Qt) ソフト(Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL(バージョン1.xを強制) - + None なし - + None (Still Image) なし(静止画) - + Keyboard キーボード - + Controllers コントローラー - + Shortcuts ショートカット - - + + Shaders シェーダー - + Select BIOS BIOSを選択 - + Select directory - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4864,37 +5096,37 @@ Download size: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5682,100 +5914,80 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ゲームボーイアドバンスファイル (%1) - - - - Game Boy ROMs (%1) - ゲームボーイファイル (%1) - - - - All ROMs (%1) - すべてのファイル (%1) - - - - %1 Video Logs (*.mvl) - %1ビデオログ (*.mvl) - - - + Archives (%1) アーカイブファイル (%1) - - - + + + Select ROM ROMを開く - + Select folder フォルダを開く - - + + Select save セーブを開く - + Select patch パッチを開く - + Patches (*.ips *.ups *.bps) パッチファイル (*.ips *.ups *.bps) - + Select e-Reader dotcode カードeを開く - + e-Reader card (*.raw *.bin *.bmp) カードe (*.raw *.bin *.bmp) - + Select image 画像を開く - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 画像ファイル (*.png *.gif *.jpg *.jpeg);;すべてのファイル (*) - + GameShark saves (*.sps *.xps) GameSharkセーブファイル (*.sps *.xps) - + Select video log ビデオログを開く - + Video logs (*.mvl) ビデオログ (*.mvl) - + Crash クラッシュ - + The game has crashed with the following error: %1 @@ -5784,684 +5996,689 @@ Download size: %3 %1 - + Couldn't Start 起動失敗 - + Could not start game. ゲームを起動できませんでした。 - + Unimplemented BIOS call 未実装のBIOS呼び出し - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. このゲームは実装されていないBIOS呼び出しを使用します。最高のエクスペリエンスを得るには公式のBIOSを使用してください。 - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. 適切なディスプレイデバイスの作成に失敗し、ソフトディスプレイにフォールバックしました。特に大きなウィンドウでは、ゲームの実行が遅い場合があります。 - + Really make portable? 本当にポータブルにしますか? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? これによりエミュレータは実行ファイルと同じディレクトリにある設定ファイルをロードします。続けますか? - + Restart needed 再起動が必要 - + Some changes will not take effect until the emulator is restarted. 一部の変更は、エミュレータを再起動するまで有効になりません。 - + - Player %1 of %2 - プレーヤー %1 of %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &ファイル (&F) - + Load &ROM... ROMをロード... - + Load ROM in archive... アーカイブにROMをロード... - + Add folder to library... ライブラリーにフォルダを追加... - + Load &patch... パッチをロード... (&P) - + Boot BIOS BIOSを起動 - + Replace ROM... ROMを交換... - + Scan e-Reader dotcodes... カードeをスキャン... - + ROM &info... ROM情報... (&I) - + Recent 最近開いたROM - + Make portable ポータブル化 - + &Load state ステートをロード (&L) - + Report bug... バグ報告 - + About... バージョン情報... - + Record GIF/WebP/APNG... GIF/WebP/APNGを記録 - + Game Pak sensors... カートリッジセンサー... - + Clear 消去 - + Load state file... ステートファイルをロード... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Convert e-Reader card image to raw... - + &Save state ステートをセーブ (&S) - + Save state file... ステートファイルをセーブ... - + Quick load クイックロード - + Quick save クイックセーブ - + Load recent 最近使ったクイックロットからロード - + Save recent 最近使ったクイックロットにセーブ - + Undo load state クイックロードを元に戻す - + Undo save state クイックセーブを元に戻す - - + + State &%1 クイックスロット &%1 - + Load camera image... カメラ画像をロード... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Import GameShark Save... GameSharkスナップショットを読み込む - + Export GameShark Save... GameSharkスナップショットを書き出す - + New multiplayer window 新しいウィンドウ(マルチプレイ) - + Connect to Dolphin... - + E&xit 終了 (&X) - + &Emulation エミュレーション (&E) - + &Reset リセット (&R) - + Sh&utdown 閉じる (&U) - + Yank game pak ゲームパックをヤンク - + &Pause 一時停止 (&P) - + &Next frame 次のフレーム (&N) - + Fast forward (held) 早送り(押し) - + &Fast forward 早送り (&F) - + Fast forward speed 早送り速度 - + Unbounded 制限なし - + %0x %0x - + Rewind (held) 巻戻し(押し) - + Re&wind 巻戻し (&R) - + Step backwards 1フレーム巻き戻す - + Solar sensor 太陽センサー - + Increase solar level 明るさを上げる - + Decrease solar level 明るさを下げる - + Brightest solar level 明るさ最高 - + Darkest solar level 明るさ最低 - + Brightness %1 明るさ %1 - + Audio/&Video オーディオ/ビデオ (&V) - + Frame size 画面サイズ - + Toggle fullscreen 全画面表示 - + Lock aspect ratio 縦横比を固定 - + Force integer scaling 整数スケーリングを強制 - + Bilinear filtering バイリニアフィルタリング - + Frame&skip フレームスキップ (&S) - + Mute ミュート - + FPS target FPS - + Native (59.7275) ネイティブ(59.7275) - + Take &screenshot スクリーンショット (&S) - + F12 F12 - + Game Boy Printer... ポケットプリンタ... - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games セーブデータ - + Automatically determine - + Use player %0 save game - + BattleChip Gate... チップゲート... - + %1× %1× - + Interframe blending フレーム間混合 - + Record A/V... ビデオ録画... - + Video layers ビデオレイヤー - + Audio channels オーディオチャンネル - + Adjust layer placement... レイヤーの配置を調整... - + &Tools ツール (&T) - + View &logs... ログビューアー... (&L) - + Game &overrides... ゲーム別設定... (&O) - + &Cheats... チート... (&C) - + Settings... 設定... - + Open debugger console... デバッガコンソールを開く... - + Start &GDB server... GDBサーバを起動... (&G) - + Scripting... - + + Create forwarder... + + + + Game state views - + View &palette... パレットビューアー... (&P) - + View &sprites... スプライトビューアー... (&S) - + View &tiles... タイルビューアー... (&T) - + View &map... マップビューアー... (&M) - + &Frame inspector... フレームインスペクタ... (&F) - + View memory... メモリビューアー... - + Search memory... メモリ検索... - + View &I/O registers... IOビューアー... (&I) - + Record debug video log... デバッグビデオログ... - + Stop debug video log デバッグビデオログを停止 - + Exit fullscreen 全画面表示を終了 - + GameShark Button (held) GameSharkボタン(押し) - + Autofire 連打 - + Autofire A 連打 A - + Autofire B 連打 B - + Autofire L 連打 L - + Autofire R 連打 R - + Autofire Start 連打 Start - + Autofire Select 連打 Select - + Autofire Up 連打 上 - + Autofire Right 連打 右 - + Autofire Down 連打 下 - + Autofire Left 連打 左 @@ -6469,32 +6686,32 @@ Download size: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-ko.ts b/src/platform/qt/ts/mgba-ko.ts index b34ac9e87..567688861 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + 게임 보이 어드밴스 롬 (%1) + + + + Game Boy ROMs (%1) + 게임 보이 (%1) + + + + All ROMs (%1) + 모든 롬 (%1) + + + + %1 Video Logs (*.mvl) + %1 비디오 로그 (*.mvl) + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable 안정적인 - + Development 개발 - + Unknown 미확인 - + (None) (없음) @@ -400,12 +423,12 @@ Download size: %3 예기치 않은 플랫폼에서 팩을 잡아당길 수 없습니다! - + Failed to open snapshot file for reading: %1 읽기 용 스냅샷 파일을 열지 못했습니다: %1 - + Failed to open snapshot file for writing: %1 쓰기 용 스냅샷 파일을 열지 못했습니다: %1 @@ -413,17 +436,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 게임 파일을 열지 못했습니다: %1 - + Could not load game. Are you sure it's in the correct format? 게임을 로드할 수 없습니다. 올바른 형식인지 확인했나요? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). 저장 파일을 열지 못했습니다. 게임 내 저장은 업데이트할 수 없습니다. 저장 디렉토리가 추가 권한 없이 쓰기 가능한지 확인하세요 (예: 윈도우즈의 UAC). @@ -502,6 +525,195 @@ Download size: %3 돌핀에 연결할 수 없습니다. + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + 배경 + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + 브라우저 + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence 디스코드 Rich Presence 활성화 @@ -601,22 +813,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button 버튼 정리 - + Clear Analog 아날로그 정리 - + Refresh 새로 고침 - + Set all 모두 설정 @@ -750,148 +962,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect 자동 감지 - + Game Boy (DMG) 게임 보이 (DMG) - + Game Boy Pocket (MGB) 게임 보이 포켓 (MGB) - + Super Game Boy (SGB) 슈퍼 게임 보이 (SGB) - + Super Game Boy 2 (SGB) 슈퍼 게임 보이 2 (SGB) - + Game Boy Color (CGB) 게임 보이 컬러 (CGB) - + Game Boy Advance (AGB) 게임 보이 어드밴스 (AGB) - + Super Game Boy Color (SGB + CGB) 슈퍼 게임 보이 컬러 (SGB + CGB) - + ROM Only 롬 전용 - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble MBC5 + 진동 - + MBC6 MBC6 - + MBC7 (Tilt) MBC7 (틸트) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam 포켓 캠 - + TAMA5 TAMA5 - + Wisdom Tree 지혜의 나무 - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) NT (신규) - + Pokémon Jade/Diamond 포켓몬 제이드/다이아몬드 - + BBD BBD - + Hitek 하이텍 - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) 사첸 (MMC1) - + Sachen (MMC2) 사첸 (MMC2) @@ -2910,7 +3142,7 @@ Download size: %3 손상 - + Slot %1 슬롯 %1 @@ -4070,12 +4302,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive 버그 보고서 파일 - + ZIP archive (*.zip) ZIP 파일 (*.zip) @@ -4438,102 +4670,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia Qt 멀티미디어 - + SDL SDL - + Software (Qt) 소프트웨어 (Qt) - + OpenGL 오픈GL - + OpenGL (force version 1.x) 오픈GL (버전 1.x 강제) - + None 없음 - + None (Still Image) 없음 (정지 이미지) - + Keyboard 키보드 - + Controllers 컨트롤러 - + Shortcuts 단축키 - - + + Shaders 쉐이더 - + Select BIOS 바이오스 선택 - + Select directory 디렉토리 선택 - + (%1×%2) (%1×%2) - + Never 절대 - + Just now 방금 - + Less than an hour ago 1시간 미만 전 - + %n hour(s) ago %n 시간 전 - + %n day(s) ago %n 일 전 @@ -4844,37 +5076,37 @@ Download size: %3 지금 확인 - + Default color palette only 기본 색상 팔레트만 - + SGB color palette if available 가능한 경우 SGB 색상 팔레트 - + GBC color palette if available 가능한 경우 GBC 색상 팔레트 - + SGB (preferred) or GBC color palette if available 사용 가능한 경우 SGB (선호) 또는 GBC 색상 팔레트 - + Game Boy Camera 게임 보이 카메라 - + Driver: 드라이버: - + Source: 소스: @@ -5682,115 +5914,95 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - 게임 보이 어드밴스 롬 (%1) - - - - Game Boy ROMs (%1) - 게임 보이 (%1) - - - - All ROMs (%1) - 모든 롬 (%1) - - - - %1 Video Logs (*.mvl) - %1 비디오 로그 (*.mvl) - - - + Archives (%1) 아카이브 (%1) - - - + + + Select ROM 롬 선택 - - + + Select save 저장 파일 선택 - + Select patch 패치 선택 - + Patches (*.ips *.ups *.bps) 패치 (*.ips *.ups *.bps) - + Select e-Reader dotcode e-리더 도트코드 선택 - + e-Reader card (*.raw *.bin *.bmp) e-리더 카드 (*.raw *.bin *.bmp) - + Select e-Reader card images e-리더 카드 이미지 선택 - + Image file (*.png *.jpg *.jpeg) 이미지 파일 (*.png *.jpg *.jpeg) - + Conversion finished 변환 완료 - + %1 of %2 e-Reader cards converted successfully. %2 e-리더 카드 중 %1이(가) 성공적으로 변환되었습니다. - + Select image 이미지 선택 - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) 이미지 파일 (*.png *.gif *.jpg *.jpeg);;모든 파일 (*) - + GameShark saves (*.sps *.xps) 게임샤크 저장 파일 (*.sps *.xps) - + Select video log 비디오 로그 선택 - + Video logs (*.mvl) 비디오 로그 (*.mvl) - + Crash 치명적인 오류 - + The game has crashed with the following error: %1 @@ -5799,669 +6011,674 @@ Download size: %3 %1 - + Unimplemented BIOS call 구현되지 않은 바이오스 호출 - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. 이 게임은 구현되지 않은 바이오스 호출을 사용합니다. 최상의 성능을 얻으려면 공식 바이오스를 사용하십시오. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. 적절한 디스플레이 장치를 생성하지 못했습니다. 소프트웨어 디스플레이로 돌아갑니다. 특히 큰 창에서는 게임이 느리게 실행될 수 있습니다. - + Really make portable? 정말로 휴대용을 만듭니까? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? 이렇게하면 에뮬레이터가 실행 파일과 동일한 디렉토리에서 구성을 로드하게됩니다. 계속 하시겠습니까? - + Restart needed 재시작 필요 - + Some changes will not take effect until the emulator is restarted. 일부 변경 사항은 에뮬레이터가 다시 시작될 때까지 적용되지 않습니다. - + Reset needed 재설정 필요 - + Some changes will not take effect until the game is reset. 일부 변경 사항은 게임이 재설정될 때까지 적용되지 않습니다. - + - Player %1 of %2 - 플레이어 %1 의 %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &파일 - + Load &ROM... 로드 &롬... - + Load ROM in archive... 롬을 아카이브에 로드... - + Save games 게임 저장 - + Automatically determine 자동으로 결정 - + Use player %0 save game 플레이어 %0 저장 게임 사용 - + Load &patch... 로드 &패치... - + Boot BIOS BIOS 부팅 - + Replace ROM... 롬 교체... - + Scan e-Reader dotcodes... e-리더 도트코드 스캔... - + Game state views 게임 상태 보기 - + Convert e-Reader card image to raw... e-리더 카드 이미지를 원시 데이터로 변환... - + ROM &info... 롬 &정보... - + Recent 최근 실행 - + Make portable 휴대용 만들기 - + &Load state &로드 파일 상태 - + &Save state &저장 파일 상태 - + Quick load 빠른 로드 - + Quick save 빠른 저장 - + Load recent 최근 실행 로드 - + Save recent 최근 실행 저장 - + Undo load state 로드 파일 상태 복원 - + Undo save state 저장 파일 상태 복원 - - + + State &%1 상태 &%1 - + Load camera image... 카메라 이미지 로드... - + Convert save game... 저장 게임 변환... - + GameShark saves (*.gsv *.sps *.xps) 게임샤크 저장 (*.gsv *.sps *.xps) - + New multiplayer window 새로운 멀티 플레이어 창 - + Connect to Dolphin... 돌핀에 연결... - + E&xit 종&료 - + &Emulation &에뮬레이션 - + &Reset &재설정 - + Sh&utdown 종&료 - + Yank game pak 양키 게임 팩 - + &Pause &정지 - + &Next frame &다음 프레임 - + Fast forward (held) 빨리 감기 (누름) - + &Fast forward &빨리 감기 - + Fast forward speed 빨리 감기 속도 - + Unbounded 무제한 - + %0x %0x - + Rewind (held) 되김기 (누름) - + Re&wind 리&와인드 - + Step backwards 돌아가기 - + Brightest solar level 가장 밝은 태양 수준 - + Darkest solar level 가장 어두운 태양 수준 - + Brightness %1 밝기 %1 - + Audio/&Video 오디오/&비디오 - + Frame size 프레임 크기 - + Toggle fullscreen 전체화면 전환 - + Lock aspect ratio 화면비 잠금 - + Frame&skip 프레임&건너뛰기 - + Mute 무음 - + FPS target FPS 대상 - + Take &screenshot 스크린샷 &찍기 - + F12 F12 - + Video layers 비디오 레이어 - + Audio channels 오디오 채널 - + &Tools &도구 - + View &logs... 로그 &보기... - + Game &overrides... 게임 &오버라이드... - + &Cheats... &치트.. - + + Create forwarder... + + + + Open debugger console... 디버거 콘솔 열기... - + Start &GDB server... GDB 서버 &시작... - + Settings... 설정... - + Select folder 폴더 선택 - + Couldn't Start 시작할 수 없음 - + Could not start game. 게임을 시작할 수 없습니다. - + Add folder to library... 라이브러리에 폴더 추가... - + Load state file... 상태 파일 로드... - + Save state file... 상태 파일 저장... - + Import GameShark Save... 게임샤크 저장 가져오기... - + Export GameShark Save... 게임샤크 저장 내보내기... - + Report bug... 버그를 제보하기... - + About... 정보... - + Solar sensor 태양광 센서 - + Increase solar level 태양광 레벨 증가 - + Decrease solar level 태양광 레벨 감소 - + Force integer scaling 정수 스케일링 강제 수행 - + Bilinear filtering 이중선형 필터링 - + Game Boy Printer... 게임 보이 프린터... - + Save games (%1) 게임 (%1) 저장 - + Select save game 저장 게임 선택 - + mGBA save state files (%1) mGBA 저장 상태 파일 (%1) - - + + Select save state 저장 상태 선택 - + Load alternate save game... 대체 저장 게임 로드... - + Load temporary save game... 임시 저장 게임 로드... - + BattleChip Gate... 배틀칩 게이트... - + %1× %1× - + Interframe blending 프레임간 조합 - + Native (59.7275) 실기 (59.7275) - + Record A/V... A/V 녹화... - + Record GIF/WebP/APNG... GIF/WebP/APNG 녹화... - + Adjust layer placement... 레이어 배치 조정... - + Game Pak sensors... 게임 팩 센서... - + Scripting... 스크립팅... - + View &palette... 팔레트 &보기... - + View &sprites... 스프라이트 &보기... - + View &tiles... 타일 &보기... - + View &map... 지도 &보기... - + &Frame inspector... 프레임 검사기 (&F)... - + View memory... 메모리 보기... - + Search memory... 메모리 검색... - + View &I/O registers... I/O 레지스터 &보기... - + Record debug video log... 디버그 비디오 로그 녹화... - + Stop debug video log 디버그 비디오 로그 중지 - + Exit fullscreen 전체화면 종료 - + GameShark Button (held) 게임샤크 버튼 (누름) - + Autofire 연사 - + Autofire A 연사 A - + Autofire B 연사 B - + Autofire L 연사 L - + Autofire R 연사 R - + Autofire Start 연사 시작 - + Autofire Select 연사 선택 - + Clear 지움 - + Autofire Up 연사 위쪽 - + Autofire Right 연사 오른쪽 - + Autofire Down 연사 아래쪽 - + Autofire Left 연사 왼쪽 @@ -6469,32 +6686,32 @@ Download size: %3 QObject - + %1 byte %1 바이트 - + %1 kiB %1 키비바이트 - + %1 MiB %1 메비바이트 - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-ms.ts b/src/platform/qt/ts/mgba-ms.ts index 54a273a64..2e46c1d08 100644 --- a/src/platform/qt/ts/mgba-ms.ts +++ b/src/platform/qt/ts/mgba-ms.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + ROM Game Boy Advance (%1) + + + + Game Boy ROMs (%1) + ROM Game Boy (%1) + + + + All ROMs (%1) + Semua ROM (%1) + + + + %1 Video Logs (*.mvl) + %1 Log Video (*.mvl) + + QGBA::AboutScreen @@ -86,22 +109,22 @@ Download size: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown - + (None) @@ -394,12 +417,12 @@ Download size: %3 - + Failed to open snapshot file for reading: %1 Gagal membuka fail snapshot untuk baca: %1 - + Failed to open snapshot file for writing: %1 Gagal membuka fail snapshot untuk menulis: %1 @@ -407,17 +430,17 @@ Download size: %3 QGBA::CoreManager - + Failed to open game file: %1 Gagal membuka fail permainan: %1 - + Could not load game. Are you sure it's in the correct format? Gagal memuat permainan. Adakah ia dalam format betul? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -496,6 +519,195 @@ Download size: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Latar belakang + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + Pilih fail + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -587,7 +799,7 @@ Download size: %3 QGBA::GBAApp - + Enable Discord Rich Presence Dayakan Discord Rich Presence @@ -595,22 +807,22 @@ Download size: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh Segar Semula - + Set all @@ -744,148 +956,168 @@ Download size: %3 QGBA::GameBoy - - + + Autodetect Autokesan - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2904,7 +3136,7 @@ Download size: %3 Rosak - + Slot %1 Slot %1 @@ -4064,12 +4296,12 @@ Download size: %3 QGBA::ReportView - + Bug report archive Arkib laporan pepijat - + ZIP archive (*.zip) Arkib ZIP (*.zip) @@ -4432,102 +4664,102 @@ Download size: %3 QGBA::SettingsView - - + + Qt Multimedia Multimedia Qt - + SDL SDL - + Software (Qt) Perisian (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) OpenGL (paksa versi 1.x) - + None Tiada - + None (Still Image) Tiada (Gambar Tenang) - + Keyboard Papan Kekunci - + Controllers Pengawal - + Shortcuts Pintas - - + + Shaders - + Select BIOS Pilih BIOS - + Select directory Pilih direktori - + (%1×%2) (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago - + %n day(s) ago @@ -4853,37 +5085,37 @@ Download size: %3 - + Default color palette only Palet warna piawai sahaja - + SGB color palette if available Palet warna SGB jika ada - + GBC color palette if available Palet warna GBC jika ada - + SGB (preferred) or GBC color palette if available SGB (pilihan utama) atau palet warna GBC jika ada - + Game Boy Camera Game Boy Camera - + Driver: Pemacu: - + Source: Sumber: @@ -5676,100 +5908,80 @@ Download size: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - ROM Game Boy Advance (%1) - - - - Game Boy ROMs (%1) - ROM Game Boy (%1) - - - - All ROMs (%1) - Semua ROM (%1) - - - - %1 Video Logs (*.mvl) - %1 Log Video (*.mvl) - - - + Archives (%1) Arkib (%1) - - - + + + Select ROM Pilih ROM - + Select folder Pilih folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image Pilih gambar - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Fail gambar (*.png *.gif *.jpg *.jpeg);;Semua fail (*) - + GameShark saves (*.sps *.xps) Simpanan GameShark (*.sps *.xps) - + Select video log Pilih log video - + Video logs (*.mvl) Log video (*.mvl) - + Crash Nahas - + The game has crashed with the following error: %1 @@ -5778,684 +5990,689 @@ Download size: %3 %1 - + Couldn't Start Tidak dapat memula - + Could not start game. Permainan tidak dapat bermula. - + Unimplemented BIOS call Panggilan BIOS yg belum dilaksanakan - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. Permainan ini menggunakan panggilan BIOS yang belum dilaksanakan. Sila pakai BIOS rasmi untuk pengalaman yang lebih baik. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. Gagal mencipta peranti paparan yang sesuai, berbalik ke paparan perisian. Permainan mungkin menjadi lembap, terutamanya dengan tetingkap besar. - + Really make portable? Betulkah mahu buat jadi mudah alih? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? Ini akan menetapkan pelagak untuk muat konfigurasi dari direktori yang sama dengan fail bolehlakunya. Teruskan? - + Restart needed Mula semula diperlukan - + Some changes will not take effect until the emulator is restarted. Beberapa perubahan tidak akan dilaksanakan sehingga pelagak dimula semula. - + - Player %1 of %2 - Pemain %1 dari %2 - + %1 - %2 %1 - %2 - + %1 - %2 - %3 %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 %1 - %2 (%3 fps) - %4 - + &File &File - + Load &ROM... Muat %ROM... - + Load ROM in archive... Muat ROM daripada arkib... - + Add folder to library... Tambah folder ke perpustakaan... - + Save games (%1) Simpanan permainan (%1) - + Select save game Pilih simpanan permainan - + mGBA save state files (%1) mGBA fail keadaan tersimpan (%1) - - + + Select save state Pilih keadaan tersimpan - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) Fail gambar (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... Muat simpanan permainan alternatif... - + Load temporary save game... Muat simpanan permainan sementara... - + Load &patch... - + Boot BIOS But BIOS - + Replace ROM... Ganti ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... &Perihal ROM... - + Recent Terkini - + Make portable Buat jadi mudah alih - + &Load state &Muat keadaan - + Load state file... Muat fail keadaan... - + &Save state &Simpan keadaan - + Save state file... Simpan fail keadaan... - + Quick load - + Quick save - + Load recent Muat terkini - + Save recent Simpan terkini - + Undo load state Buat asal keadaan termuat - + Undo save state Buat asal keadaan tersimpan - - + + State &%1 Keadaan &%1 - + Load camera image... Muat gambar kamera... - + Convert save game... Tukar simpanan permainan... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games Simpanan permainan - + Import GameShark Save... Import Simpanan GameShark... - + Export GameShark Save... Eksport Simpanan GameShark... - + Automatically determine - + Use player %0 save game - + New multiplayer window Tetingkap multipemain baru - + Connect to Dolphin... Sambung ke Dolphin... - + Report bug... Laporkan pepijat... - + About... Perihal... - + E&xit &Keluar - + &Emulation Pe&lagak - + &Reset - + Sh&utdown &Matikan - + Yank game pak Alih keluar Game Pak - + &Pause &Jeda - + &Next frame Bingkai se&terusnya - + Fast forward (held) Mundar laju (pegang) - + &Fast forward Mundar &laju - + Fast forward speed Kelajuan mundar laju - + Unbounded Tidak terbatas - + %0x %0x - + Rewind (held) Putar balik (pegang) - + Re&wind Ma&ndir - + Step backwards Langkah belakang - + Solar sensor Pengesan suria - + Increase solar level Meningkatkan aras suria - + Decrease solar level Mengurangkan aras suria - + Brightest solar level Aras suria paling terang - + Darkest solar level Aras suria paling gelap - + Brightness %1 Kecerahan %1 - + Game Boy Printer... Pencetak Game Boy... - + BattleChip Gate... BattleChip Gate... - + Audio/&Video Audio/&Video - + Frame size Saiz bingkai - + %1× %1× - + Toggle fullscreen Togol skrinpenuh - + Lock aspect ratio Kekalkan nisbah aspek - + Force integer scaling Paksa skala integer - + Interframe blending Persebatian antarabingkai - + Bilinear filtering Penapisan bilinear - + Frame&skip Langkauan &bingkai - + Mute Senyap - + FPS target Sasaran FPS - + Native (59.7275) Asal (59.7275) - + Take &screenshot Ambil &cekupan skrin - + F12 F12 - + Record A/V... Rakam A/V... - + Record GIF/WebP/APNG... Rakam GIF/WebP/APNG... - + Video layers Lapisan video - + Audio channels Saluran audio - + Adjust layer placement... Melaras peletakan lapisan... - + &Tools &Alat - + View &logs... Lihat &log... - + Game &overrides... - + Game Pak sensors... Pengesan Game Pak... - + &Cheats... &Tipu... - + + Create forwarder... + + + + Settings... Tetapan... - + Open debugger console... Buka konsol penyahpepijat... - + Start &GDB server... Mula pelayan &GDB... - + Scripting... - + Game state views - + View &palette... Pelihat &palet... - + View &sprites... - + View &tiles... Pelihat &jubin... - + View &map... Pelihat pe&ta... - + &Frame inspector... Periksa &bingkai... - + View memory... Lihat ingatan... - + Search memory... Cari ingatan... - + View &I/O registers... Lihat daftar &I/O... - + Record debug video log... Rakam log video nyahpepijat... - + Stop debug video log Henti log video nyahpepijat - + Exit fullscreen Keluar skrinpenuh - + GameShark Button (held) Butang GameShark (pegang) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Kosongkan @@ -6463,32 +6680,32 @@ Download size: %3 QObject - + %1 byte %1 bait - + %1 kiB %1 kiB - + %1 MiB %1 MiB - + GBA GBA - + GB GB - + ? ? diff --git a/src/platform/qt/ts/mgba-nb_NO.ts b/src/platform/qt/ts/mgba-nb_NO.ts index 05bea134c..169a4bab3 100644 --- a/src/platform/qt/ts/mgba-nb_NO.ts +++ b/src/platform/qt/ts/mgba-nb_NO.ts @@ -1,6 +1,29 @@ + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -89,22 +112,22 @@ Nedlastningsstørrelse: %3 QGBA::ApplicationUpdater - + Stable - + Development - + Unknown Ukjent - + (None) @@ -397,12 +420,12 @@ Nedlastningsstørrelse: %3 - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -410,17 +433,17 @@ Nedlastningsstørrelse: %3 QGBA::CoreManager - + Failed to open game file: %1 Klarte ikke å åpne spillfil: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -499,6 +522,195 @@ Nedlastningsstørrelse: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + Bakgrunn + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -590,7 +802,7 @@ Nedlastningsstørrelse: %3 QGBA::GBAApp - + Enable Discord Rich Presence @@ -598,22 +810,22 @@ Nedlastningsstørrelse: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh Gjenoppfrisk - + Set all @@ -747,148 +959,168 @@ Nedlastningsstørrelse: %3 QGBA::GameBoy - - + + Autodetect - + Game Boy (DMG) Game Boy (DMG) - + Game Boy Pocket (MGB) Game Boy Pocket (MGB) - + Super Game Boy (SGB) Super Game Boy (SGB) - + Super Game Boy 2 (SGB) Super Game Boy 2 (SGB) - + Game Boy Color (CGB) Game Boy Color (CGB) - + Game Boy Advance (AGB) Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) Super Game Boy Color (SGB + CGB) - + ROM Only Kun ROM - + MBC1 MBC1 - + MBC2 MBC2 - + MBC3 MBC3 - + MBC3 + RTC MBC3 + RTC - + MBC5 MBC5 - + MBC5 + Rumble - + MBC6 MBC6 - + MBC7 (Tilt) - + MMM01 MMM01 - + HuC-1 HuC-1 - + HuC-3 HuC-3 - + Pocket Cam - + TAMA5 TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2907,7 +3139,7 @@ Nedlastningsstørrelse: %3 Skadet - + Slot %1 Plass %1 @@ -4067,12 +4299,12 @@ Nedlastningsstørrelse: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) ZIP-arkiv (*.zip) @@ -4435,95 +4667,95 @@ Nedlastningsstørrelse: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL SDL - + Software (Qt) Programvare (Qt) - + OpenGL OpenGL - + OpenGL (force version 1.x) - + None Ingen - + None (Still Image) - + Keyboard Tastatur - + Controllers Kontrollere - + Shortcuts Snarveier - - + + Shaders Skyggeleggere - + Select BIOS Velg BIOS - + Select directory Velg mappe - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago @@ -4531,7 +4763,7 @@ Nedlastningsstørrelse: %3 - + %n day(s) ago @@ -4858,37 +5090,37 @@ Nedlastningsstørrelse: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5681,784 +5913,769 @@ Nedlastningsstørrelse: %3 QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) Arkiv (%1) - - - + + + Select ROM Velg ROM - + Select folder Velg mappe - - + + Select save - + Select patch Velg feilfiks - + Patches (*.ips *.ups *.bps) Feilfikser (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image Velg bilde - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) Bildefil (*.png *.gif *.jpg *.jpeg);;All filer (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash Krasj - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear Tøm @@ -6466,32 +6683,32 @@ Nedlastningsstørrelse: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ? diff --git a/src/platform/qt/ts/mgba-sv.ts b/src/platform/qt/ts/mgba-sv.ts index 954b8ffc2..88d08dee1 100644 --- a/src/platform/qt/ts/mgba-sv.ts +++ b/src/platform/qt/ts/mgba-sv.ts @@ -1,6 +1,29 @@ - + + + QGBA + + + Game Boy Advance ROMs (%1) + + + + + Game Boy ROMs (%1) + + + + + All ROMs (%1) + + + + + %1 Video Logs (*.mvl) + + + QGBA::AboutScreen @@ -92,22 +115,22 @@ Nedladdningsstorlek: %3 QGBA::ApplicationUpdater - + Stable Standard - + Development Utveckling - + Unknown Okänd - + (None) (Inga) @@ -316,7 +339,7 @@ Nedladdningsstorlek: %3 Remove - + Ta bort @@ -331,12 +354,12 @@ Nedladdningsstorlek: %3 Save - + Spara Load - + Ladda @@ -400,12 +423,12 @@ Nedladdningsstorlek: %3 - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -413,17 +436,17 @@ Nedladdningsstorlek: %3 QGBA::CoreManager - + Failed to open game file: %1 - + Could not load game. Are you sure it's in the correct format? - + Failed to open save file; in-game saves cannot be updated. Please ensure the save directory is writable without additional privileges (e.g. UAC on Windows). @@ -502,6 +525,195 @@ Nedladdningsstorlek: %3 + + QGBA::ForwarderGenerator + + + 3DS + + + + + Vita + + + + + QGBA::ForwarderGenerator3DS + + + Icon + + + + + Banner + + + + + QGBA::ForwarderGeneratorVita + + + Bubble + + + + + Background + + + + + Startup + + + + + QGBA::ForwarderView + + + Create forwarder + + + + + Files + + + + + ROM file: + + + + + + + Browse + + + + + Output filename: + + + + + Forwarder base: + + + + + Latest stable version + + + + + Latest development build + + + + + Specific file + + + + + Base file: + + + + + System + + + + + 3DS + + + + + Vita + + + + + Presentation + + + + + Title: + + + + + Images: + + + + + Use default image + + + + + Preferred size: + + + + + Select image file + + + + + Select ROM file + + + + + Select output filename + + + + + Select base file + + + + + Build finished + + + + + Forwarder finished building + + + + + Build failed + + + + + Failed to build forwarder + + + + + %1 installable package (*.%2) + + + + + Select an image + + + QGBA::FrameView @@ -593,7 +805,7 @@ Nedladdningsstorlek: %3 QGBA::GBAApp - + Enable Discord Rich Presence @@ -601,22 +813,22 @@ Nedladdningsstorlek: %3 QGBA::GBAKeyEditor - + Clear Button - + Clear Analog - + Refresh - + Set all @@ -750,148 +962,168 @@ Nedladdningsstorlek: %3 QGBA::GameBoy - - + + Autodetect - + Game Boy (DMG) - + Game Boy Pocket (MGB) - + Super Game Boy (SGB) - + Super Game Boy 2 (SGB) - + Game Boy Color (CGB) - + Game Boy Advance (AGB) - + Super Game Boy Color (SGB + CGB) - + ROM Only - + MBC1 - + MBC2 - + MBC3 - + MBC3 + RTC - + MBC5 - + MBC5 + Rumble - + MBC6 - + MBC7 (Tilt) - + MMM01 - + HuC-1 - + HuC-3 - + Pocket Cam - + TAMA5 - + Wisdom Tree - + + NT (old 1) + + + + + NT (old 2) + + + + NT (new) - + Pokémon Jade/Diamond - + BBD - + Hitek - + + GGB-81 + + + + + Li Cheng + + + + Sachen (MMC1) - + Sachen (MMC2) @@ -2719,7 +2951,7 @@ Nedladdningsstorlek: %3 Unknown - + Okänd @@ -2737,13 +2969,13 @@ Nedladdningsstorlek: %3 Red - + Röd Blue - + Blå @@ -2910,7 +3142,7 @@ Nedladdningsstorlek: %3 - + Slot %1 @@ -3226,7 +3458,7 @@ Nedladdningsstorlek: %3 Load - + Ladda @@ -3279,7 +3511,7 @@ Nedladdningsstorlek: %3 Address - + Adress @@ -3514,7 +3746,7 @@ Nedladdningsstorlek: %3 Load - + Ladda @@ -3535,7 +3767,7 @@ Nedladdningsstorlek: %3 Address - + Adress @@ -3879,17 +4111,17 @@ Nedladdningsstorlek: %3 Red - + Röd Green - + Grön Blue - + Blå @@ -3932,7 +4164,7 @@ Nedladdningsstorlek: %3 0x%0 (%1) - + 0x%0 (%1) @@ -4070,12 +4302,12 @@ Nedladdningsstorlek: %3 QGBA::ReportView - + Bug report archive - + ZIP archive (*.zip) @@ -4097,7 +4329,7 @@ Nedladdningsstorlek: %3 Save - + Spara @@ -4438,105 +4670,107 @@ Nedladdningsstorlek: %3 QGBA::SettingsView - - + + Qt Multimedia - + SDL - + Software (Qt) - + OpenGL - + OpenGL (force version 1.x) - + None - + None (Still Image) - + Keyboard - + Controllers - + Shortcuts - - + + Shaders - + Select BIOS - + Select directory - + (%1×%2) - + Never - + Just now - + Less than an hour ago - + %n hour(s) ago + - + %n day(s) ago + @@ -4859,37 +5093,37 @@ Nedladdningsstorlek: %3 - + Default color palette only - + SGB color palette if available - + GBC color palette if available - + SGB (preferred) or GBC color palette if available - + Game Boy Camera - + Driver: - + Source: @@ -5222,7 +5456,7 @@ Nedladdningsstorlek: %3 Cheats - + Cheats @@ -5676,790 +5910,775 @@ Nedladdningsstorlek: %3 Show advanced - + Visa avancerat QGBA::Window - - Game Boy Advance ROMs (%1) - - - - - Game Boy ROMs (%1) - - - - - All ROMs (%1) - - - - - %1 Video Logs (*.mvl) - - - - + Archives (%1) - - - + + + Select ROM - + Select folder - - + + Select save - + Select patch - + Patches (*.ips *.ups *.bps) - + Select e-Reader dotcode - + e-Reader card (*.raw *.bin *.bmp) - + Select image - + Image file (*.png *.gif *.jpg *.jpeg);;All files (*) - + GameShark saves (*.sps *.xps) - + Select video log - + Video logs (*.mvl) - + Crash - + The game has crashed with the following error: %1 - + Couldn't Start - + Could not start game. - + Unimplemented BIOS call - + This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Really make portable? - + This will make the emulator load its configuration from the same directory as the executable. Do you want to continue? - + Restart needed - + Some changes will not take effect until the emulator is restarted. - + - Player %1 of %2 - + %1 - %2 - + %1 - %2 - %3 - + %1 - %2 (%3 fps) - %4 - + &File - + Load &ROM... - + Load ROM in archive... - + Add folder to library... - + Save games (%1) - + Select save game - + mGBA save state files (%1) - - + + Select save state - + Select e-Reader card images - + Image file (*.png *.jpg *.jpeg) - + Conversion finished - + %1 of %2 e-Reader cards converted successfully. - + Load alternate save game... - + Load temporary save game... - + Load &patch... - + Boot BIOS - + Replace ROM... - + Scan e-Reader dotcodes... - + Convert e-Reader card image to raw... - + ROM &info... - + Recent - + Make portable - + &Load state - + Load state file... - + &Save state - + Save state file... - + Quick load - + Quick save - + Load recent - + Save recent - + Undo load state - + Undo save state - - + + State &%1 - + Load camera image... - + Convert save game... - + GameShark saves (*.gsv *.sps *.xps) - + Reset needed - + Some changes will not take effect until the game is reset. - + Save games - + Import GameShark Save... - + Export GameShark Save... - + Automatically determine - + Use player %0 save game - + New multiplayer window - + Connect to Dolphin... - + Report bug... - + About... - + E&xit - + &Emulation - + &Reset - + Sh&utdown - + Yank game pak - + &Pause - + &Next frame - + Fast forward (held) - + &Fast forward - + Fast forward speed - + Unbounded - + %0x - + Rewind (held) - + Re&wind - + Step backwards - + Solar sensor - + Increase solar level - + Decrease solar level - + Brightest solar level - + Darkest solar level - + Brightness %1 - + Game Boy Printer... - + BattleChip Gate... - + Audio/&Video - + Frame size - + %1× - + Toggle fullscreen - + Lock aspect ratio - + Force integer scaling - + Interframe blending - + Bilinear filtering - + Frame&skip - + Mute - + FPS target - + Native (59.7275) - + Take &screenshot - + F12 - + Record A/V... - + Record GIF/WebP/APNG... - + Video layers - + Audio channels - + Adjust layer placement... - + &Tools - + View &logs... - + Game &overrides... - + Game Pak sensors... - + &Cheats... - + + Create forwarder... + + + + Settings... - + Open debugger console... - + Start &GDB server... - + Scripting... - + Game state views - + View &palette... - + View &sprites... - + View &tiles... - + View &map... - + &Frame inspector... - + View memory... - + Search memory... - + View &I/O registers... - + Record debug video log... - + Stop debug video log - + Exit fullscreen - + GameShark Button (held) - + Autofire - + Autofire A - + Autofire B - + Autofire L - + Autofire R - + Autofire Start - + Autofire Select - + Autofire Up - + Autofire Right - + Autofire Down - + Autofire Left - + Clear @@ -6467,32 +6686,32 @@ Nedladdningsstorlek: %3 QObject - + %1 byte - + %1 kiB - + %1 MiB - + GBA - + GB - + ?