diff --git a/CHANGES b/CHANGES index b87501580..a4057d775 100644 --- a/CHANGES +++ b/CHANGES @@ -1,11 +1,17 @@ 0.11.0: (Future) Features: + - Forwarder support for 3DS and Vita + - Custom border support - New option to lock the maximum frame size + - Memory access and information logging - Scripting: New `input` API for getting raw keyboard/mouse/controller state - Scripting: New `storage` API for saving data for a script, e.g. settings + - Scripting: New `image` and `canvas` APIs for drawing images and displaying on-screen - Scripting: Debugger integration to allow for breakpoints and watchpoints - New unlicensed GB mappers: NT (older types 1 and 2), Li Cheng, GGB-81 + - Initial support for bootleg GBA multicarts - Debugger: Add range watchpoints + - "Headless" frontend for running tests, automation, etc. Emulation fixes: - ARM: Add framework for coprocessor support - GB Serialize: Add missing Pocket Cam state to savestates @@ -26,10 +32,12 @@ Other fixes: - mGUI: Load parent directory if last used directory is missing (fixes mgba.io/i/3379) - Qt: Fix savestate preview sizes with different scales (fixes mgba.io/i/2560) - Qt: Fix potential crash when configuring shortcuts + - Qt: Fix regression where loading BIOS creates a save file (fixes mgba.io/i/3359) Misc: - Core: Handle relative paths for saves, screenshots, etc consistently (fixes mgba.io/i/2826) - Core: Improve rumble emulation by averaging state over entire frame (fixes mgba.io/i/3232) - Core: Add MD5 hashing for ROMs + - Core: Add support for specifying an arbitrary portable directory - GB: Prevent incompatible BIOSes from being used on differing models - GB Serialize: Add missing savestate support for MBC6 and NT (newer) - GBA: Improve detection of valid ELF ROMs @@ -41,12 +49,18 @@ Misc: - Libretro: Add Super Game Boy Color support (closes mgba.io/i/3188) - mGUI: Enable auto-softpatching (closes mgba.io/i/2899) - mGUI: Persist fast forwarding after closing menu (fixes mgba.io/i/2414) + - mGUI: Wrap around menu cursor when navigating past end (closes mgba.io/i/3356) - Qt: Handle multiple save game files for disparate games separately (fixes mgba.io/i/2887) - Qt: Remove maligned double-click-to-fullscreen shortcut (closes mgba.io/i/2632) - Qt: Pass logging context through to video proxy thread (fixes mgba.io/i/3095) - Qt: Show maker code and game version in ROM info - Qt: Show a dummy shader settings tab if shaders aren't supported + - Qt: Allow passing multiple games on command line for multiplayer (closes mgba.io/i/3061) + - Qt: Support building against Qt 6 + - Qt: Add shortcuts to increment fast forward speed (mgba.io/i/2903) + - Res: Port hq2x and OmniScale shaders from SameBoy - Res: Port NSO-gba-colors shader (closes mgba.io/i/2834) + - Res: Update gba-colors shader (closes mgba.io/i/2976) - Scripting: Add `callbacks:oneshot` for single-call callbacks - Updater: Fix rewriting folders and files on Windows (fixes mgba.io/i/3384) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f4737fdd..ad464bab0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ endif() include(FindFeature) include(FindFunction) +include(DebugStrip) # Version information add_custom_target(${BINARY_NAME}-version-info ALL @@ -240,6 +241,10 @@ endif() if(APPLE) execute_process(COMMAND xcrun --show-sdk-version OUTPUT_VARIABLE MACOSX_SDK) + if(NOT MACOSX_SDK) + message(WARNING "Could not detect SDK version; defaulting to system version. Is SDKROOT set?") + set(MACOSX_SDK ${CMAKE_SYSTEM_VERSION}) + endif() add_definitions(-D_DARWIN_C_SOURCE) list(APPEND OS_LIB "-framework Foundation") @@ -948,6 +953,14 @@ if(NOT SKIP_LIBRARY) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/mgba-util) + if(BUILD_STATIC) + # Some versions of CMake are remarkably bad about deduplicating this list, + # leading to issues with static linking. Let's do it manually. + list(REVERSE DEPENDENCY_LIB) + list(REMOVE_DUPLICATES DEPENDENCY_LIB) + list(REVERSE DEPENDENCY_LIB) + endif() + target_link_libraries(${BINARY_NAME} ${DEBUGGER_LIB} ${DEPENDENCY_LIB} ${OS_LIB}) install(TARGETS ${BINARY_NAME} LIBRARY DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME} NAMELINK_SKIP ARCHIVE DESTINATION ${LIBDIR} RUNTIME DESTINATION ${LIBDIR} COMPONENT lib${BINARY_NAME}) if(BUILD_SHARED) @@ -1039,7 +1052,8 @@ endif() if(BUILD_HEADLESS) add_executable(${BINARY_NAME}-headless ${CMAKE_CURRENT_SOURCE_DIR}/src/platform/headless-main.c) - target_link_libraries(${BINARY_NAME}-headless ${BINARY_NAME}) + target_link_libraries(${BINARY_NAME}-headless ${PLATFORM_LIBRARY} ${BINARY_NAME}) + debug_strip(${BINARY_NAME}-headless) target_compile_definitions(${BINARY_NAME}-headless PRIVATE "${OS_DEFINES};${FEATURE_DEFINES};${FUNCTION_DEFINES}") install(TARGETS ${BINARY_NAME}-headless DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-headless) endif() @@ -1220,7 +1234,6 @@ elseif(BUILD_STATIC) cpack_add_component(lib${BINARY_NAME} GROUP dev) endif() cpack_add_component(${BINARY_NAME}-dev GROUP dev) -cpack_add_component(${BINARY_NAME}-headless GROUP dev) if(3DS) cpack_add_component(${BINARY_NAME}-3ds GROUP base) @@ -1242,6 +1255,11 @@ if(SDL_FOUND) cpack_add_component(${BINARY_NAME}-sdl GROUP sdl) endif() +if(BUILD_HEADLESS) + cpack_add_component_group(headless PARENT_GROUP base) + cpack_add_component(${BINARY_NAME}-headless GROUP headless) +endif() + if(DISTBUILD) cpack_add_component_group(debug PARENT_GROUP dev) if(BUILD_SHARED AND NOT IS_EMBEDDED) @@ -1256,6 +1274,9 @@ if(DISTBUILD) if(SDL_FOUND) cpack_add_component(${BINARY_NAME}-sdl-dbg GROUP debug) endif() + if(BUILD_HEADLESS) + cpack_add_component(${BINARY_NAME}-headless-dbg GROUP debug) + endif() if(WIN32) cpack_add_component_group(installer PARENT_GROUP base) endif() diff --git a/README_JP.md b/README_JP.md new file mode 100644 index 000000000..41bca336b --- /dev/null +++ b/README_JP.md @@ -0,0 +1,268 @@ +mGBA +==== + +mGBAは、ゲームボーイアドバンスのゲームを実行するためのエミュレーターです。mGBAの目標は、既存の多くのゲームボーイアドバンスエミュレーターよりも高速かつ正確であり、他のエミュレーターにはない機能を追加することです。また、ゲームボーイおよびゲームボーイカラーのゲームもサポートしています。 + +最新のニュースとダウンロードは、[mgba.io](https://mgba.io/)で見つけることができます。 + +[![Build status](https://buildbot.mgba.io/badges/build-win32.svg)](https://buildbot.mgba.io) +[![Translation status](https://hosted.weblate.org/widgets/mgba/-/svg-badge.svg)](https://hosted.weblate.org/engage/mgba) + +特徴 +-------- + +- 高精度なゲームボーイアドバンスハードウェアのサポート[[1]](#missing)。 +- ゲームボーイ/ゲームボーイカラーのハードウェアサポート。 +- 高速なエミュレーション。ネットブックなどの低スペックハードウェアでもフルスピードで動作することが知られています。 +- 重量級と軽量級のフロントエンドのためのQtおよびSDLポート。 +- ローカル(同じコンピュータ)リンクケーブルのサポート。 +- フラッシュメモリサイズを含む保存タイプの検出[[2]](#flashdetect)。 +- モーションセンサーと振動機能を備えたカートリッジのサポート(ゲームコントローラーでのみ使用可能)。 +- 設定なしでもリアルタイムクロックのサポート。 +- ボクタイゲームのためのソーラーセンサーのサポート。 +- ゲームボーイカメラとゲームボーイプリンターのサポート。 +- 内蔵BIOS実装と外部BIOSファイルの読み込み機能。 +- Luaを使用したスクリプトサポート。 +- Tabキーを押し続けることでターボ/早送りサポート。 +- バッククォートを押し続けることで巻き戻し。 +- 最大10まで設定可能なフレームスキップ。 +- スクリーンショットのサポート。 +- チートコードのサポート。 +- 9つのセーブステートスロット。セーブステートはスクリーンショットとしても表示可能。 +- ビデオ、GIF、WebP、およびAPNGの録画。 +- e-Readerのサポート。 +- キーボードとゲームパッドのリマップ可能なコントロール。 +- ZIPおよび7zファイルからの読み込み。 +- IPS、UPS、およびBPSパッチのサポート。 +- コマンドラインインターフェースとGDBリモートサポートを介したゲームデバッグ、GhidraおよびIDA Proと互換性あり。 +- 設定可能なエミュレーションの巻き戻し。 +- GameSharkおよびAction Replayスナップショットの読み込みおよびエクスポートのサポート。 +- RetroArch/LibretroおよびOpenEmu用のコア。 +- [Weblate](https://hosted.weblate.org/engage/mgba)を介した複数の言語のコミュニティ提供の翻訳。 +- その他、多くの小さな機能。 + +#### ゲームボーイマッパー + +以下のマッパーが完全にサポートされています: + +- MBC1 +- MBC1M +- MBC2 +- MBC3 +- MBC3+RTC +- MBC30 +- MBC5 +- MBC5+Rumble +- MBC7 +- Wisdom Tree(非公式) +- NT "old type" 1 and 2(非公式マルチカート) +- NT "new type"(非公式MBC5類似) +- Pokémon Jade/Diamond(非公式) +- Sachen MMC1(非公式) + +以下のマッパーが部分的にサポートされています: + +- MBC6(フラッシュメモリ書き込みサポートなし) +- MMM01 +- Pocket Cam +- TAMA5(RTCサポート不完全) +- HuC-1(IRサポートなし) +- HuC-3(IRサポートなし) +- Sachen MMC2(代替配線サポートなし) +- BBD(ロゴ切り替えなし) +- Hitek(ロゴ切り替えなし) +- GGB-81(ロゴ切り替えなし) +- Li Cheng(ロゴ切り替えなし) + +### 計画されている機能 + +- ネットワーク対応のマルチプレイヤーリンクケーブルサポート。 +- Dolphin/JOYバスリンクケーブルサポート。 +- MP2kオーディオミキシング、ハードウェアより高品質のサウンド。 +- ツールアシストランのための再録サポート。 +- 包括的なデバッグスイート。 +- ワイヤレスアダプターのサポート。 + +サポートされているプラットフォーム +------------------- + +- Windows 7以降 +- OS X 10.9(Mavericks)[[3]](#osxver)以降 +- Linux +- FreeBSD +- Nintendo 3DS +- Nintendo Switch +- Wii +- PlayStation Vita + +他のUnix系プラットフォーム(OpenBSDなど)も動作することが知られていますが、テストされておらず、完全にはサポートされていません。 + +### システム要件 + +要件は最小限です。Windows Vista以降を実行できるコンピュータであれば、エミュレーションを処理できるはずです。OpenGL 1.1以降のサポートも必要であり、シェーダーや高度な機能にはOpenGL 3.2以降が必要です。 + +ダウンロード +--------- + +ダウンロードは公式ウェブサイトの[ダウンロード][downloads]セクションで見つけることができます。ソースコードは[GitHub][source]で見つけることができます。 + +コントロール +-------- + +コントロールは設定メニューで設定可能です。多くのゲームコントローラーはデフォルトで自動的にマッピングされるはずです。デフォルトのキーボードコントロールは次のとおりです: + +- **A**:X +- **B**:Z +- **L**:A +- **R**:S +- **Start**:Enter +- **Select**:Backspace + +コンパイル +--------- + +コンパイルにはCMake 3.1以降の使用が必要です。GCC、Clang、およびVisual Studio 2019はmGBAのコンパイルに使用できることが知られています。 + +#### Dockerビルド + +ほとんどのプラットフォームでのビルドにはDockerを使用することをお勧めします。いくつかのプラットフォームでmGBAをビルドするために必要なツールチェーンと依存関係を含むいくつかのDockerイメージが提供されています。 + +注意:Windows 10以前の古いWindowsシステムを使用している場合、DockerがVirtualBox共有フォルダーを使用して現在の`mgba`チェックアウトディレクトリをDockerイメージの作業ディレクトリに正しくマッピングするように構成する必要がある場合があります。(詳細については、issue [#1985](https://mgba.io/i/1985)を参照してください。) + +Dockerイメージを使用してmGBAをビルドするには、mGBAのチェックアウトのルートで次のコマンドを実行します: + + docker run --rm -it -v ${PWD}:/home/mgba/src mgba/windows:w32 + +Dockerコンテナを起動した後、ビルド成果物を含む`build-win32`ディレクトリが生成されます。他のプラットフォーム用のDockerイメージに置き換えると、対応する他のディレクトリが生成されます。Docker Hubで利用可能なDockerイメージは次のとおりです: + +- mgba/3ds +- mgba/switch +- mgba/ubuntu:xenial +- mgba/ubuntu:bionic +- mgba/ubuntu:focal +- mgba/ubuntu:groovy +- mgba/vita +- mgba/wii +- mgba/windows:w32 +- mgba/windows:w64 + +ビルドプロセスを高速化したい場合は、`-e MAKEFLAGS=-jN`フラグを追加して、`N`個のCPUコアでmGBAの並列ビルドを行うことを検討してください。 + +#### *nixビルド + +UnixベースのシステムでCMakeを使用してビルドするには、次のコマンドを実行することをお勧めします: + + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .. + make + sudo make install + +これにより、mGBAがビルドされ、`/usr/bin`および`/usr/lib`にインストールされます。インストールされている依存関係は自動的に検出され、依存関係が見つからない場合に無効になる機能は、`cmake`コマンドを実行した後に警告として表示されます。 + +macOSを使用している場合、手順は少し異なります。homebrewパッケージマネージャーを使用していると仮定すると、依存関係を取得してビルドするための推奨コマンドは次のとおりです: + + brew install cmake ffmpeg libzip qt5 sdl2 libedit lua pkg-config + mkdir build + cd build + cmake -DCMAKE_PREFIX_PATH=`brew --prefix qt5` .. + make + +macOSでは`make install`を実行しないでください。正しく動作しないためです。 + +#### Windows開発者ビルド + +##### MSYS2 + +Windowsでの開発用ビルドにはMSYS2を使用することをお勧めします。MSYS2の[ウェブサイト](https://msys2.github.io)に記載されているインストール手順に従ってください。32ビットバージョン(「MSYS2 MinGW 32-bit」)を実行していることを確認してください(x86_64用にビルドする場合は64ビットバージョン「MSYS2 MinGW 64-bit」を実行してください)。必要な依存関係をインストールするために次の追加コマンド(中括弧を含む)を実行します(このコマンドは1100MiB以上のパッケージをダウンロードするため、長時間かかることに注意してください): + + pacman -Sy --needed base-devel git ${MINGW_PACKAGE_PREFIX}-{cmake,ffmpeg,gcc,gdb,libelf,libepoxy,libzip,lua,pkgconf,qt5,SDL2,ntldd-git} + +次のコマンドを実行してソースコードをチェックアウトします: + + git clone https://github.com/mgba-emu/mgba.git + +最後に、次のコマンドを実行してビルドします: + + mkdir -p mgba/build + cd mgba/build + cmake .. -G "MSYS Makefiles" + make -j$(nproc --ignore=1) + +このWindows用mGBAビルドは、実行に必要なDLLが分散しているため、配布には適していないことに注意してください。ただし、開発には最適です。ただし、そのようなビルドを配布する必要がある場合(たとえば、MSYS2環境がインストールされていないマシンでのテスト用)、`cpack -G ZIP`を実行すると、必要なDLLをすべて含むzipファイルが準備されます。 + +##### Visual Studio + +Visual Studioを使用してビルドするには、同様に複雑なセットアップが必要です。まず、[vcpkg](https://github.com/Microsoft/vcpkg)をインストールする必要があります。vcpkgをインストールした後、いくつかの追加パッケージをインストールする必要があります: + + vcpkg install ffmpeg[vpx,x264] libepoxy libpng libzip lua sdl2 sqlite3 + +このインストールでは、Nvidiaハードウェアでのハードウェアアクセラレーションビデオエンコーディングはサポートされません。これが重要な場合は、事前にCUDAをインストールし、前のコマンドに`ffmpeg[vpx,x264,nvcodec]`を置き換えます。 + +Qtもインストールする必要があります。ただし、Qtは合理的な組織ではなく、困窮している会社によって所有および運営されているため、最新バージョンのオフラインオープンソースエディションインストーラーは存在しないため、[旧バージョンのインストーラー](https://download.qt.io/official_releases/qt/5.12/5.12.9/qt-opensource-windows-x86-5.12.9.exe)に戻る必要があります(これには無用なアカウントの作成が必要ですが、一時的に無効なプロキシを設定するか、ネットワークを無効にすることで回避できます)、オンラインインストーラーを使用する(いずれにしてもアカウントが必要です)、またはvcpkgを使用してビルドする(遅い)。これらはすべて良い選択肢ではありません。インストーラーを使用する場合は、適用可能なMSVCバージョンをインストールする必要があります。オフラインインストーラーはMSVC 2019をサポートしていないことに注意してください。vcpkgを使用する場合、次のようにインストールする必要があります。特にクアッドコア以下のコンピュータではかなりの時間がかかります: + + vcpkg install qt5-base qt5-multimedia + +次に、Visual Studioを開き、「リポジトリのクローンを作成」を選択し、`https://github.com/mgba-emu/mgba.git`を入力します。Visual Studioがクローンを完了したら、「ファイル」>「CMake」に移動し、チェックアウトされたリポジトリのルートにあるCMakeLists.txtファイルを開きます。そこから、他のVisual Studio CMakeプロジェクトと同様にVisual StudioでmGBAを開発できます。 + +#### ツールチェーンビルド + +devkitARM(3DS用)、devkitPPC(Wii用)、devkitA64(Switch用)、またはvitasdk(PS Vita用)を持っている場合は、次のコマンドを使用してビルドできます: + + mkdir build + cd build + cmake -DCMAKE_TOOLCHAIN_FILE=../src/platform/3ds/CMakeToolchain.txt .. + make + +次のプラットフォーム用に`-DCMAKE_TOOLCHAIN_FILE`パラメータを置き換えます: + +- 3DS:`../src/platform/3ds/CMakeToolchain.txt` +- Switch:`../src/platform/switch/CMakeToolchain.txt` +- Vita:`../src/platform/psp2/CMakeToolchain.vitasdk` +- Wii:`../src/platform/wii/CMakeToolchain.txt` + +### 依存関係 + +mGBAには厳密な依存関係はありませんが、特定の機能には次のオプションの依存関係が必要です。依存関係が見つからない場合、これらの機能は無効になります。 + +- Qt 5:GUIフロントエンド用。オーディオにはQt MultimediaまたはSDLが必要です。 +- SDL:より基本的なフロントエンドおよびQtフロントエンドでのゲームパッドサポート用。SDL 2が推奨されますが、1.2もサポートされています。 +- zlibおよびlibpng:スクリーンショットサポートおよびPNG内セーブステートサポート用。 +- libedit:コマンドラインデバッガーサポート用。 +- ffmpegまたはlibav:ビデオ、GIF、WebP、およびAPNGの録画用。 +- libzipまたはzlib:zipファイルに保存されたROMの読み込み用。 +- SQLite3:ゲームデータベース用。 +- libelf:ELF読み込み用。 +- Lua:スクリプト用。 +- json-c:スクリプトの`storage` API用。 + +SQLite3、libpng、およびzlibはエミュレーターに含まれているため、最初に外部でコンパイルする必要はありません。 + +脚注 +--------- + +[1] 現在欠けている機能は次のとおりです + +- モード3、4、および5のOBJウィンドウ([バグ#5](http://mgba.io/b/5)) + +[2] フラッシュメモリサイズの検出は一部のケースで機能しません。これらは実行時に構成できますが、そのようなケースに遭遇した場合はバグを報告することをお勧めします。 + +[3] 10.9はQtポートにのみ必要です。10.7またはそれ以前のバージョンでQtポートをビルドまたは実行することは可能かもしれませんが、公式にはサポートされていません。SDLポートは10.5で動作することが知られており、古いバージョンでも動作する可能性があります。 + +[downloads]: http://mgba.io/downloads.html +[source]: https://github.com/mgba-emu/mgba/ + +著作権 +--------- + +mGBAの著作権は© 2013 – 2023 Jeffrey Pfauに帰属します。これは[Mozilla Public License version 2.0](https://www.mozilla.org/MPL/2.0/)の下で配布されています。配布されたLICENSEファイルにライセンスのコピーが含まれています。 + +mGBAには次のサードパーティライブラリが含まれています: + +- [inih](https://github.com/benhoyt/inih)、著作権© 2009 – 2020 Ben Hoyt、BSD 3-clauseライセンスの下で使用。 +- [LZMA SDK](http://www.7-zip.org/sdk.html)、パブリックドメイン。 +- [MurmurHash3](https://github.com/aappleby/smhasher)、Austin Applebyによる実装、パブリックドメイン。 +- [getopt for MSVC](https://github.com/skandhurkat/Getopt-for-Visual-Studio/)、パブリックドメイン。 +- [SQLite3](https://www.sqlite.org)、パブリックドメイン。 + +ゲームパブリッシャーであり、商業利用のためにmGBAのライセンスを取得したい場合は、[licensing@mgba.io](mailto:licensing@mgba.io)までメールでお問い合わせください。 diff --git a/include/mgba/core/cpu.h b/include/mgba/core/cpu.h index 71f3398c2..e538f4a59 100644 --- a/include/mgba/core/cpu.h +++ b/include/mgba/core/cpu.h @@ -20,6 +20,15 @@ enum mCPUComponentType { CPU_COMPONENT_MAX }; +enum mMemoryAccessSource { + mACCESS_UNKNOWN = 0, + mACCESS_PROGRAM, + mACCESS_DMA, + mACCESS_SYSTEM, + mACCESS_DECOMPRESS, + mACCESS_COPY, +}; + struct mCPUComponent { uint32_t id; void (*init)(void* cpu, struct mCPUComponent* component); diff --git a/include/mgba/debugger/debugger.h b/include/mgba/debugger/debugger.h index eb69faaf4..230a95564 100644 --- a/include/mgba/debugger/debugger.h +++ b/include/mgba/debugger/debugger.h @@ -111,6 +111,7 @@ struct mDebuggerEntryInfo { uint32_t newValue; enum mWatchpointType watchType; enum mWatchpointType accessType; + enum mMemoryAccessSource accessSource; } wp; struct { @@ -256,6 +257,7 @@ bool mDebuggerIsShutdown(const struct mDebugger*); struct mDebuggerModule* mDebuggerCreateModule(enum mDebuggerType type, struct mCore*); void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule*); +void mDebuggerModuleClearNeedsCallback(struct mDebuggerModule*); bool mDebuggerLookupIdentifier(struct mDebugger* debugger, const char* name, int32_t* value, int* segment); diff --git a/include/mgba/feature/commandline.h b/include/mgba/feature/commandline.h index 17af655fe..9ce118fd2 100644 --- a/include/mgba/feature/commandline.h +++ b/include/mgba/feature/commandline.h @@ -44,6 +44,7 @@ struct mSubParser { bool (*parse)(struct mSubParser* parser, int option, const char* arg); bool (*parseLong)(struct mSubParser* parser, const char* option, const char* arg); void (*apply)(struct mSubParser* parser, struct mCoreConfig* config); + bool (*handleExtraArg)(struct mSubParser* parser, const char* arg); const char* extraOptions; const struct mOption* longOptions; void* opts; diff --git a/include/mgba/internal/arm/arm.h b/include/mgba/internal/arm/arm.h index 5f25acf90..5ef2da965 100644 --- a/include/mgba/internal/arm/arm.h +++ b/include/mgba/internal/arm/arm.h @@ -132,6 +132,8 @@ struct ARMMemory { uint32_t activeNonseqCycles16; int32_t (*stall)(struct ARMCore*, int32_t wait); void (*setActiveRegion)(struct ARMCore*, uint32_t address); + + enum mMemoryAccessSource accessSource; }; struct ARMCoprocessor { diff --git a/include/mgba/internal/debugger/access-logger.h b/include/mgba/internal/debugger/access-logger.h index 26186ac20..180168f3a 100644 --- a/include/mgba/internal/debugger/access-logger.h +++ b/include/mgba/internal/debugger/access-logger.h @@ -23,6 +23,7 @@ struct mDebuggerAccessLogRegion { uint32_t segmentStart; mDebuggerAccessLogFlags* block; mDebuggerAccessLogFlagsEx* blockEx; + ssize_t watchpoint; }; DECLARE_VECTOR(mDebuggerAccessLogRegionList, struct mDebuggerAccessLogRegion); @@ -41,11 +42,16 @@ void mDebuggerAccessLoggerDeinit(struct mDebuggerAccessLogger*); bool mDebuggerAccessLoggerOpen(struct mDebuggerAccessLogger*, struct VFile*, int mode); bool mDebuggerAccessLoggerClose(struct mDebuggerAccessLogger*); +void mDebuggerAccessLoggerStart(struct mDebuggerAccessLogger*); +void mDebuggerAccessLoggerStop(struct mDebuggerAccessLogger*); + int mDebuggerAccessLoggerWatchMemoryBlockId(struct mDebuggerAccessLogger*, size_t id, mDebuggerAccessLogRegionFlags); int mDebuggerAccessLoggerWatchMemoryBlockName(struct mDebuggerAccessLogger*, const char* internalName, mDebuggerAccessLogRegionFlags); bool mDebuggerAccessLoggerCreateShadowFile(struct mDebuggerAccessLogger*, int region, struct VFile*, uint8_t fill); +struct mDebuggerAccessLogRegion* mDebuggerAccessLoggerGetRegion(struct mDebuggerAccessLogger*, uint32_t address, int segment, size_t* offset); + CXX_GUARD_END #endif diff --git a/include/mgba/internal/gba/savedata.h b/include/mgba/internal/gba/savedata.h index e69d07834..7fec242c4 100644 --- a/include/mgba/internal/gba/savedata.h +++ b/include/mgba/internal/gba/savedata.h @@ -45,9 +45,13 @@ enum FlashStateMachine { FLASH_STATE_CONTINUE = 2, }; -enum FlashManufacturer { - FLASH_MFG_PANASONIC = 0x1B32, - FLASH_MFG_SANYO = 0x1362 +enum FlashId { + FLASH_ATMEL_AT29LV512 = 0x3D1F, // 512k + FLASH_MACRONIX_MX29L512 = 0x1CC2, // 512k, unused + FLASH_MACRONIX_MX29L010 = 0x09C2, // 1M + FLASH_PANASONIC_MN63F805MNP = 0x1B32, // 512k, unused + FLASH_SANYO_LE26FV10N1TS = 0x1362, // 1M + FLASH_SST_39LVF512 = 0xD4BF, // 512k }; enum { diff --git a/include/mgba/internal/sm83/sm83.h b/include/mgba/internal/sm83/sm83.h index 86b4d374b..7f0fabd7a 100644 --- a/include/mgba/internal/sm83/sm83.h +++ b/include/mgba/internal/sm83/sm83.h @@ -61,6 +61,8 @@ struct SM83Memory { uint16_t activeMask; uint16_t activeRegionEnd; void (*setActiveRegion)(struct SM83Core*, uint16_t address); + + enum mMemoryAccessSource accessSource; }; struct SM83InterruptHandler { diff --git a/src/arm/debugger/memory-debugger.c b/src/arm/debugger/memory-debugger.c index 7dce87795..b0b48423b 100644 --- a/src/arm/debugger/memory-debugger.c +++ b/src/arm/debugger/memory-debugger.c @@ -121,6 +121,7 @@ static void _checkWatchpoints(struct ARMDebugger* debugger, uint32_t address, en info.type.wp.newValue = newValue; info.type.wp.watchType = watchpoint->type; info.type.wp.accessType = type; + info.type.wp.accessSource = debugger->cpu->memory.accessSource; info.address = address; info.segment = 0; info.width = width; diff --git a/src/core/thread.c b/src/core/thread.c index 988ce0848..92d134120 100644 --- a/src/core/thread.c +++ b/src/core/thread.c @@ -507,6 +507,10 @@ void mCoreThreadClearCrashed(struct mCoreThread* threadContext) { void mCoreThreadEnd(struct mCoreThread* threadContext) { MutexLock(&threadContext->impl->stateMutex); + if (threadContext->impl->state == mTHREAD_SHUTDOWN) { + MutexUnlock(&threadContext->impl->stateMutex); + return; + } _waitOnInterrupt(threadContext->impl); threadContext->impl->state = mTHREAD_EXITING; ConditionWake(&threadContext->impl->stateOnThreadCond); diff --git a/src/debugger/access-logger.c b/src/debugger/access-logger.c index 41c780c4e..b9e8222d1 100644 --- a/src/debugger/access-logger.c +++ b/src/debugger/access-logger.c @@ -62,74 +62,102 @@ static void _mDebuggerAccessLoggerEntered(struct mDebuggerModule* debugger, enum break; } - size_t i; - for (i = 0; i < mDebuggerAccessLogRegionListSize(&logger->regions); ++i) { - struct mDebuggerAccessLogRegion* region = mDebuggerAccessLogRegionListGetPointer(&logger->regions, i); - if (info->address < region->start || info->address >= region->end) { - continue; - } - size_t offset = info->address - region->start; - if (info->segment > 0) { - uint32_t segmentSize = region->end - region->segmentStart; - offset %= segmentSize; - offset += segmentSize * info->segment; - } + size_t offset; + struct mDebuggerAccessLogRegion* region = mDebuggerAccessLoggerGetRegion(logger, info->address, info->segment, &offset); + if (!region) { + return; + } + offset &= -info->width; - if (offset >= region->size) { - continue; - } - - offset &= -info->width; - - int j; - switch (reason) { - case DEBUGGER_ENTER_WATCHPOINT: - for (j = 0; j < info->width; ++j) { - if (info->type.wp.accessType & WATCHPOINT_WRITE) { - region->block[offset + j] = mDebuggerAccessLogFlagsFillWrite(region->block[offset + j]); - } - if (info->type.wp.accessType & WATCHPOINT_READ) { - region->block[offset + j] = mDebuggerAccessLogFlagsFillRead(region->block[offset + j]); - } - } - switch (info->width) { - case 1: - region->block[offset] = mDebuggerAccessLogFlagsFillAccess8(region->block[offset]); - break; - case 2: - region->block[offset] = mDebuggerAccessLogFlagsFillAccess16(region->block[offset]); - region->block[offset + 1] = mDebuggerAccessLogFlagsFillAccess16(region->block[offset + 1]); - break; - case 4: - region->block[offset] = mDebuggerAccessLogFlagsFillAccess32(region->block[offset]); - region->block[offset + 1] = mDebuggerAccessLogFlagsFillAccess32(region->block[offset + 1]); - region->block[offset + 2] = mDebuggerAccessLogFlagsFillAccess32(region->block[offset + 2]); - region->block[offset + 3] = mDebuggerAccessLogFlagsFillAccess32(region->block[offset + 3]); - break; - case 8: - region->block[offset] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset]); - region->block[offset + 1] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 1]); - region->block[offset + 2] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 2]); - region->block[offset + 3] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 3]); - region->block[offset + 4] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 4]); - region->block[offset + 5] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 5]); - region->block[offset + 6] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 6]); - region->block[offset + 7] = mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 7]); - break; - } + mDebuggerAccessLogFlags flags = 0; + mDebuggerAccessLogFlagsEx flagsEx = 0; + switch (reason) { + case DEBUGGER_ENTER_WATCHPOINT: + switch (info->type.wp.accessSource) { + case mACCESS_PROGRAM: + flagsEx = mDebuggerAccessLogFlagsExFillAccessProgram(flagsEx); break; - case DEBUGGER_ENTER_ILLEGAL_OP: - region->block[offset] = mDebuggerAccessLogFlagsFillExecute(region->block[offset]); + case mACCESS_DMA: + flagsEx = mDebuggerAccessLogFlagsExFillAccessDMA(flagsEx); + break; + case mACCESS_SYSTEM: + flagsEx = mDebuggerAccessLogFlagsExFillAccessSystem(flagsEx); + break; + case mACCESS_DECOMPRESS: + flagsEx = mDebuggerAccessLogFlagsExFillAccessDecompress(flagsEx); + break; + case mACCESS_COPY: + flagsEx = mDebuggerAccessLogFlagsExFillAccessCopy(flagsEx); + break; + case mACCESS_UNKNOWN: + break; + } + if (info->type.wp.accessType & WATCHPOINT_WRITE) { + flags = mDebuggerAccessLogFlagsFillWrite(flags); + } + if (info->type.wp.accessType & WATCHPOINT_READ) { + flags = mDebuggerAccessLogFlagsFillRead(flags); + } + switch (info->width) { + case 1: + region->block[offset] = flags | mDebuggerAccessLogFlagsFillAccess8(region->block[offset]); if (region->blockEx) { - uint16_t ex; - LOAD_16LE(ex, 0, ®ion->blockEx[offset]); - ex = mDebuggerAccessLogFlagsExFillErrorIllegalOpcode(ex); - STORE_16LE(ex, 0, ®ion->blockEx[offset]); + region->blockEx[offset] |= flagsEx; } break; - default: + case 2: + region->block[offset] = flags | mDebuggerAccessLogFlagsFillAccess16(region->block[offset]); + region->block[offset + 1] = flags | mDebuggerAccessLogFlagsFillAccess16(region->block[offset + 1]); + if (region->blockEx) { + region->blockEx[offset] |= flagsEx; + region->blockEx[offset + 1] |= flagsEx; + } + break; + case 4: + region->block[offset] = flags | mDebuggerAccessLogFlagsFillAccess32(region->block[offset]); + region->block[offset + 1] = flags | mDebuggerAccessLogFlagsFillAccess32(region->block[offset + 1]); + region->block[offset + 2] = flags | mDebuggerAccessLogFlagsFillAccess32(region->block[offset + 2]); + region->block[offset + 3] = flags | mDebuggerAccessLogFlagsFillAccess32(region->block[offset + 3]); + if (region->blockEx) { + region->blockEx[offset] |= flagsEx; + region->blockEx[offset + 1] |= flagsEx; + region->blockEx[offset + 2] |= flagsEx; + region->blockEx[offset + 3] |= flagsEx; + } + break; + case 8: + region->block[offset] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset]); + region->block[offset + 1] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 1]); + region->block[offset + 2] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 2]); + region->block[offset + 3] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 3]); + region->block[offset + 4] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 4]); + region->block[offset + 5] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 5]); + region->block[offset + 6] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 6]); + region->block[offset + 7] = flags | mDebuggerAccessLogFlagsFillAccess64(region->block[offset + 7]); + if (region->blockEx) { + region->blockEx[offset] |= flagsEx; + region->blockEx[offset + 1] |= flagsEx; + region->blockEx[offset + 2] |= flagsEx; + region->blockEx[offset + 3] |= flagsEx; + region->blockEx[offset + 4] |= flagsEx; + region->blockEx[offset + 5] |= flagsEx; + region->blockEx[offset + 6] |= flagsEx; + region->blockEx[offset + 7] |= flagsEx; + } break; } + break; + case DEBUGGER_ENTER_ILLEGAL_OP: + region->block[offset] = mDebuggerAccessLogFlagsFillExecute(region->block[offset]); + if (region->blockEx) { + uint16_t ex; + LOAD_16LE(ex, 0, ®ion->blockEx[offset]); + ex = mDebuggerAccessLogFlagsExFillErrorIllegalOpcode(ex); + STORE_16LE(ex, 0, ®ion->blockEx[offset]); + } + break; + default: + break; } } @@ -139,34 +167,22 @@ static void _mDebuggerAccessLoggerCallback(struct mDebuggerModule* debugger) { struct mDebuggerInstructionInfo info; logger->d.p->platform->nextInstructionInfo(logger->d.p->platform, &info); + size_t offset; + struct mDebuggerAccessLogRegion* region = mDebuggerAccessLoggerGetRegion(logger, info.address, info.segment, &offset); + if (!region) { + return; + } + size_t i; - for (i = 0; i < mDebuggerAccessLogRegionListSize(&logger->regions); ++i) { - struct mDebuggerAccessLogRegion* region = mDebuggerAccessLogRegionListGetPointer(&logger->regions, i); - if (info.address < region->start || info.address >= region->end) { - continue; - } - size_t offset = info.address - region->start; - if (info.segment > 0) { - uint32_t segmentSize = region->end - region->segmentStart; - offset %= segmentSize; - offset += segmentSize * info.segment; - } + for (i = 0; i < info.width; ++i) { + uint16_t ex = 0; + region->block[offset + i] = mDebuggerAccessLogFlagsFillExecute(region->block[offset + i]); + region->block[offset + i] |= info.flags[i]; - if (offset >= region->size) { - continue; - } - - size_t j; - for (j = 0; j < info.width; ++j) { - uint16_t ex = 0; - region->block[offset + j] = mDebuggerAccessLogFlagsFillExecute(region->block[offset + j]); - region->block[offset + j] |= info.flags[j]; - - if (region->blockEx) { - LOAD_16LE(ex, 0, ®ion->blockEx[offset + j]); - ex |= info.flagsEx[j]; - STORE_16LE(ex, 0, ®ion->blockEx[offset + j]); - } + if (region->blockEx) { + LOAD_16LE(ex, 0, ®ion->blockEx[offset + i]); + ex |= info.flagsEx[i]; + STORE_16LE(ex, 0, ®ion->blockEx[offset + i]); } } } @@ -240,13 +256,18 @@ static bool _setupRegion(struct mDebuggerAccessLogger* logger, struct mDebuggerA return false; } - struct mWatchpoint wp = { - .segment = -1, - .minAddress = region->start, - .maxAddress = region->end, - .type = WATCHPOINT_RW, - }; - logger->d.p->platform->setWatchpoint(logger->d.p->platform, &logger->d, &wp); + if (region->watchpoint < 0) { + struct mWatchpoint wp = { + .segment = -1, + .minAddress = region->start, + .maxAddress = region->end, + .type = WATCHPOINT_RW, + }; + region->watchpoint = logger->d.p->platform->setWatchpoint(logger->d.p->platform, &logger->d, &wp); + } + if (region->watchpoint < 0) { + return false; + } mDebuggerModuleSetNeedsCallback(&logger->d); return true; } @@ -290,7 +311,7 @@ static bool mDebuggerAccessLoggerLoad(struct mDebuggerAccessLogger* logger) { LOAD_32LE(region->end, 0, &info->end); LOAD_32LE(region->size, 0, &info->size); LOAD_32LE(region->segmentStart, 0, &info->segmentStart); - if (!_setupRegion(logger, region, info)) { + if (!_mapRegion(logger, region, info)) { mDebuggerAccessLogRegionListClear(&logger->regions); return false; } @@ -335,6 +356,30 @@ bool mDebuggerAccessLoggerOpen(struct mDebuggerAccessLogger* logger, struct VFil return loaded; } +void mDebuggerAccessLoggerStart(struct mDebuggerAccessLogger* logger) { + size_t i; + for (i = 0; i < logger->mapped->header.nRegions; ++i) { + struct mDebuggerAccessLogRegionInfo* info = &logger->mapped->regionInfo[i]; + struct mDebuggerAccessLogRegion* region = mDebuggerAccessLogRegionListGetPointer(&logger->regions, i); + if (!_setupRegion(logger, region, info)) { + return; + } + } +} + +void mDebuggerAccessLoggerStop(struct mDebuggerAccessLogger* logger) { + size_t i; + for (i = 0; i < logger->mapped->header.nRegions; ++i) { + struct mDebuggerAccessLogRegion* region = mDebuggerAccessLogRegionListGetPointer(&logger->regions, i); + if (region->watchpoint < 0) { + continue; + } + logger->d.p->platform->clearBreakpoint(logger->d.p->platform, region->watchpoint); + region->watchpoint = -1; + } + mDebuggerModuleClearNeedsCallback(&logger->d); +} + static int _mDebuggerAccessLoggerWatchMemoryBlock(struct mDebuggerAccessLogger* logger, const struct mCoreMemoryBlock* block, mDebuggerAccessLogRegionFlags flags) { if (mDebuggerAccessLogRegionListSize(&logger->regions) >= logger->mapped->header.regionCapacity) { return -1; @@ -406,6 +451,7 @@ static int _mDebuggerAccessLoggerWatchMemoryBlock(struct mDebuggerAccessLogger* region->size = block->size; region->segmentStart = block->segmentStart; region->block = (mDebuggerAccessLogFlags*) ((uintptr_t) logger->backing + fileEnd); + region->watchpoint = -1; struct mDebuggerAccessLogRegionInfo* info = &logger->mapped->regionInfo[id]; STORE_32LE(region->start, 0, &info->start); @@ -434,6 +480,7 @@ bool mDebuggerAccessLoggerClose(struct mDebuggerAccessLogger* logger) { if (!logger->backing) { return true; } + mDebuggerAccessLoggerStop(logger); mDebuggerAccessLogRegionListClear(&logger->regions); logger->backing->unmap(logger->backing, logger->mapped, logger->backing->size(logger->backing)); logger->mapped = NULL; @@ -518,3 +565,28 @@ bool mDebuggerAccessLoggerCreateShadowFile(struct mDebuggerAccessLogger* logger, } return true; } + +struct mDebuggerAccessLogRegion* mDebuggerAccessLoggerGetRegion(struct mDebuggerAccessLogger* logger, uint32_t address, int segment, size_t* offsetOut) { + size_t i; + for (i = 0; i < mDebuggerAccessLogRegionListSize(&logger->regions); ++i) { + struct mDebuggerAccessLogRegion* region = mDebuggerAccessLogRegionListGetPointer(&logger->regions, i); + if (address < region->start || address >= region->end) { + continue; + } + size_t offset = address - region->start; + if (segment > 0) { + uint32_t segmentSize = region->end - region->segmentStart; + offset %= segmentSize; + offset += segmentSize * segment; + } + + if (offset >= region->size) { + continue; + } + if (offsetOut) { + *offsetOut = offset; + } + return region; + } + return NULL; +} diff --git a/src/debugger/debugger.c b/src/debugger/debugger.c index a03f106d9..995a28099 100644 --- a/src/debugger/debugger.c +++ b/src/debugger/debugger.c @@ -320,3 +320,8 @@ void mDebuggerModuleSetNeedsCallback(struct mDebuggerModule* debugger) { debugger->needsCallback = true; mDebuggerUpdatePaused(debugger->p); } + +void mDebuggerModuleClearNeedsCallback(struct mDebuggerModule* debugger) { + debugger->needsCallback = false; + mDebuggerUpdatePaused(debugger->p); +} diff --git a/src/feature/commandline.c b/src/feature/commandline.c index fca5c7747..1e22a4f2c 100644 --- a/src/feature/commandline.c +++ b/src/feature/commandline.c @@ -187,7 +187,21 @@ bool mArgumentsParse(struct mArguments* args, int argc, char* const* argv, struc argc -= optind; argv += optind; if (argc > 1) { - return false; + for (j = 0; j < argc; ++j) { + bool handled = false; + for (i = 0; i < nSubparsers; ++i) { + if (!subparsers[i].handleExtraArg) { + continue; + } + handled = subparsers[i].handleExtraArg(&subparsers[i], argv[j]); + if (handled) { + break; + } + } + if (!handled) { + return false; + } + } } else if (argc == 1) { args->fname = strdup(argv[0]); } else { @@ -303,6 +317,7 @@ void mSubParserGraphicsInit(struct mSubParser* parser, struct mGraphicsOpts* opt parser->apply = _applyGraphicsArgs; parser->extraOptions = GRAPHICS_OPTIONS; parser->longOptions = _graphicsLongOpts; + parser->handleExtraArg = NULL; opts->multiplier = 0; opts->fullscreen = false; } diff --git a/src/gb/memory.c b/src/gb/memory.c index dccd91e08..fdacc1a9b 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -148,6 +148,7 @@ void GBMemoryInit(struct GB* gb) { cpu->memory.store8 = GBStore8; cpu->memory.currentSegment = GBCurrentSegment; cpu->memory.setActiveRegion = GBSetActiveRegion; + cpu->memory.accessSource = mACCESS_UNKNOWN; gb->memory.wram = 0; gb->memory.wramBank = 0; @@ -205,6 +206,7 @@ void GBMemoryReset(struct GB* gb) { gb->memory.hdmaDest = 0; gb->memory.isHdma = false; + gb->cpu->memory.accessSource = mACCESS_UNKNOWN; gb->memory.dmaEvent.context = gb; gb->memory.dmaEvent.name = "GB DMA"; @@ -576,10 +578,13 @@ void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesL struct GB* gb = context; int dmaRemaining = gb->memory.dmaRemaining; gb->memory.dmaRemaining = 0; + enum mMemoryAccessSource oldAccess = gb->cpu->memory.accessSource; + gb->cpu->memory.accessSource = mACCESS_DMA; uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource); // TODO: Can DMA write OAM during modes 2-3? gb->video.oam.raw[gb->memory.dmaDest] = b; gb->video.renderer->writeOAM(gb->video.renderer, gb->memory.dmaDest); + gb->cpu->memory.accessSource = oldAccess; ++gb->memory.dmaSource; ++gb->memory.dmaDest; gb->memory.dmaRemaining = dmaRemaining - 1; @@ -591,8 +596,11 @@ void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesL void _GBMemoryHDMAService(struct mTiming* timing, void* context, uint32_t cyclesLate) { struct GB* gb = context; gb->cpuBlocked = true; + enum mMemoryAccessSource oldAccess = gb->cpu->memory.accessSource; + gb->cpu->memory.accessSource = mACCESS_DMA; uint8_t b = gb->cpu->memory.load8(gb->cpu, gb->memory.hdmaSource); gb->cpu->memory.store8(gb->cpu, gb->memory.hdmaDest, b); + gb->cpu->memory.accessSource = oldAccess; ++gb->memory.hdmaSource; ++gb->memory.hdmaDest; --gb->memory.hdmaRemaining; diff --git a/src/gba/bios.c b/src/gba/bios.c index 1ff5004d3..c7b963a40 100644 --- a/src/gba/bios.c +++ b/src/gba/bios.c @@ -174,6 +174,8 @@ static void _BgAffineSet(struct GBA* gba) { int destination = cpu->gprs[1]; float a, b, c, d; float rx, ry; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_SYSTEM; while (i--) { // [ sx 0 0 ] [ cos(theta) -sin(theta) 0 ] [ 1 0 cx - ox ] [ A B rx ] // [ 0 sy 0 ] * [ sin(theta) cos(theta) 0 ] * [ 0 1 cy - oy ] = [ C D ry ] @@ -205,6 +207,7 @@ static void _BgAffineSet(struct GBA* gba) { cpu->memory.store32(cpu, destination + 12, ry * 256, 0); destination += 16; } + cpu->memory.accessSource = oldAccess; } static void _ObjAffineSet(struct GBA* gba) { @@ -216,6 +219,8 @@ static void _ObjAffineSet(struct GBA* gba) { int destination = cpu->gprs[1]; int diff = cpu->gprs[3]; float a, b, c, d; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_SYSTEM; while (i--) { // [ sx 0 ] [ cos(theta) -sin(theta) ] [ A B ] // [ 0 sy ] * [ sin(theta) cos(theta) ] = [ C D ] @@ -237,6 +242,7 @@ static void _ObjAffineSet(struct GBA* gba) { cpu->memory.store16(cpu, destination + diff * 3, d * 256, 0); destination += diff * 4; } + cpu->memory.accessSource = oldAccess; } static void _MidiKey2Freq(struct GBA* gba) { @@ -244,7 +250,10 @@ static void _MidiKey2Freq(struct GBA* gba) { int oldRegion = gba->memory.activeRegion; gba->memory.activeRegion = GBA_REGION_BIOS; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_SYSTEM; uint32_t key = cpu->memory.load32(cpu, cpu->gprs[0] + 4, 0); + cpu->memory.accessSource = oldAccess; gba->memory.activeRegion = oldRegion; cpu->gprs[0] = key / exp2f((180.f - cpu->gprs[1] - cpu->gprs[2] / 256.f) / 12.f); @@ -624,6 +633,8 @@ static void _unLz77(struct GBA* gba, int width) { uint32_t source = cpu->gprs[0]; uint32_t dest = cpu->gprs[1]; int cycles = 20; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_DECOMPRESS; int remaining = (cpu->memory.load32(cpu, source, &cycles) & 0xFFFFFF00) >> 8; // We assume the signature byte (0x10) is correct int blockheader = 0; // Some compilers warn if this isn't set, even though it's trivially provably always set @@ -698,6 +709,7 @@ static void _unLz77(struct GBA* gba, int width) { blocksRemaining = 8; } } + cpu->memory.accessSource = oldAccess; cpu->gprs[0] = source; cpu->gprs[1] = dest; cpu->gprs[3] = 0; @@ -713,6 +725,8 @@ static void _unHuffman(struct GBA* gba) { struct ARMCore* cpu = gba->cpu; uint32_t source = cpu->gprs[0] & 0xFFFFFFFC; uint32_t dest = cpu->gprs[1]; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_DECOMPRESS; uint32_t header = cpu->memory.load32(cpu, source, 0); int remaining = header >> 8; unsigned bits = header & 0xF; @@ -722,6 +736,7 @@ static void _unHuffman(struct GBA* gba) { } if (32 % bits || bits == 1) { mLOG(GBA_BIOS, STUB, "Unimplemented unaligned Huffman"); + cpu->memory.accessSource = oldAccess; return; } // We assume the signature byte (0x20) is correct @@ -773,6 +788,7 @@ static void _unHuffman(struct GBA* gba) { } } } + cpu->memory.accessSource = oldAccess; cpu->gprs[0] = source; cpu->gprs[1] = dest; } @@ -780,6 +796,8 @@ static void _unHuffman(struct GBA* gba) { static void _unRl(struct GBA* gba, int width) { struct ARMCore* cpu = gba->cpu; uint32_t source = cpu->gprs[0]; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_DECOMPRESS; int remaining = (cpu->memory.load32(cpu, source & 0xFFFFFFFC, 0) & 0xFFFFFF00) >> 8; int padding = (4 - remaining) & 0x3; // We assume the signature byte (0x30) is correct @@ -846,6 +864,7 @@ static void _unRl(struct GBA* gba, int width) { ++dest; } } + cpu->memory.accessSource = oldAccess; cpu->gprs[0] = source; cpu->gprs[1] = dest; } @@ -854,6 +873,8 @@ static void _unFilter(struct GBA* gba, int inwidth, int outwidth) { struct ARMCore* cpu = gba->cpu; uint32_t source = cpu->gprs[0] & 0xFFFFFFFC; uint32_t dest = cpu->gprs[1]; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_DECOMPRESS; uint32_t header = cpu->memory.load32(cpu, source, 0); int remaining = header >> 8; // We assume the signature nybble (0x8) is correct @@ -888,6 +909,7 @@ static void _unFilter(struct GBA* gba, int inwidth, int outwidth) { old = new; source += inwidth; } + cpu->memory.accessSource = oldAccess; cpu->gprs[0] = source; cpu->gprs[1] = dest; } @@ -897,6 +919,8 @@ static void _unBitPack(struct GBA* gba) { uint32_t source = cpu->gprs[0]; uint32_t dest = cpu->gprs[1]; uint32_t info = cpu->gprs[2]; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; + cpu->memory.accessSource = mACCESS_DECOMPRESS; unsigned sourceLen = cpu->memory.load16(cpu, info, 0); unsigned sourceWidth = cpu->memory.load8(cpu, info + 2, 0); unsigned destWidth = cpu->memory.load8(cpu, info + 3, 0); @@ -908,6 +932,7 @@ static void _unBitPack(struct GBA* gba) { break; default: mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack source width: %u", sourceWidth); + cpu->memory.accessSource = oldAccess; return; } switch (destWidth) { @@ -920,6 +945,7 @@ static void _unBitPack(struct GBA* gba) { break; default: mLOG(GBA_BIOS, GAME_ERROR, "Bad BitUnPack destination width: %u", destWidth); + cpu->memory.accessSource = oldAccess; return; } uint32_t bias = cpu->memory.load32(cpu, info + 4, 0); @@ -949,6 +975,7 @@ static void _unBitPack(struct GBA* gba) { dest += 4; } } + cpu->memory.accessSource = oldAccess; cpu->gprs[0] = source; cpu->gprs[1] = dest; } diff --git a/src/gba/dma.c b/src/gba/dma.c index e9044d26a..3e3e8222d 100644 --- a/src/gba/dma.c +++ b/src/gba/dma.c @@ -248,10 +248,12 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { uint32_t dest = info->nextDest; uint32_t sourceRegion = source >> BASE_OFFSET; uint32_t destRegion = dest >> BASE_OFFSET; + enum mMemoryAccessSource oldAccess = cpu->memory.accessSource; int32_t cycles = 2; gba->cpuBlocked = true; gba->performingDMA = 1 | (number << 1); + cpu->memory.accessSource = mACCESS_DMA; if (info->count == info->nextCount) { if (width == 4) { @@ -315,6 +317,7 @@ void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { --info->nextCount; gba->performingDMA = 0; + cpu->memory.accessSource = oldAccess; int i; for (i = 0; i < 4; ++i) { diff --git a/src/gba/memory.c b/src/gba/memory.c index 4f074657a..27e289c8e 100644 --- a/src/gba/memory.c +++ b/src/gba/memory.c @@ -82,6 +82,7 @@ void GBAMemoryInit(struct GBA* gba) { cpu->memory.activeSeqCycles16 = 0; cpu->memory.activeNonseqCycles32 = 0; cpu->memory.activeNonseqCycles16 = 0; + cpu->memory.accessSource = mACCESS_UNKNOWN; gba->memory.biosPrefetch = 0; gba->memory.agbPrintProtect = 0; @@ -132,6 +133,7 @@ void GBAMemoryReset(struct GBA* gba) { gba->memory.prefetch = false; gba->memory.lastPrefetchedPc = 0; + gba->cpu->memory.accessSource = mACCESS_UNKNOWN; if (!gba->memory.wram || !gba->memory.iwram) { GBAMemoryDeinit(gba); @@ -299,22 +301,27 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { memory->activeRegion = newRegion; switch (newRegion) { case GBA_REGION_BIOS: + cpu->memory.accessSource = mACCESS_SYSTEM; cpu->memory.activeRegion = memory->bios; cpu->memory.activeMask = GBA_SIZE_BIOS - 1; break; case GBA_REGION_EWRAM: + cpu->memory.accessSource = mACCESS_PROGRAM; cpu->memory.activeRegion = memory->wram; cpu->memory.activeMask = GBA_SIZE_EWRAM - 1; break; case GBA_REGION_IWRAM: + cpu->memory.accessSource = mACCESS_PROGRAM; cpu->memory.activeRegion = memory->iwram; cpu->memory.activeMask = GBA_SIZE_IWRAM - 1; break; case GBA_REGION_PALETTE_RAM: + cpu->memory.accessSource = mACCESS_PROGRAM; cpu->memory.activeRegion = (uint32_t*) gba->video.palette; cpu->memory.activeMask = GBA_SIZE_PALETTE_RAM - 1; break; case GBA_REGION_VRAM: + cpu->memory.accessSource = mACCESS_PROGRAM; if (address & 0x10000) { cpu->memory.activeRegion = (uint32_t*) &gba->video.vram[0x8000]; cpu->memory.activeMask = 0x00007FFF; @@ -324,6 +331,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } break; case GBA_REGION_OAM: + cpu->memory.accessSource = mACCESS_PROGRAM; cpu->memory.activeRegion = (uint32_t*) gba->video.oam.raw; cpu->memory.activeMask = GBA_SIZE_OAM - 1; break; @@ -333,6 +341,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { case GBA_REGION_ROM1_EX: case GBA_REGION_ROM2: case GBA_REGION_ROM2_EX: + cpu->memory.accessSource = mACCESS_PROGRAM; cpu->memory.activeRegion = memory->rom; cpu->memory.activeMask = memory->romMask; if ((address & (GBA_SIZE_ROM0 - 1)) < memory->romSize) { @@ -345,6 +354,7 @@ static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t address) { } // Fall through default: + cpu->memory.accessSource = mACCESS_UNKNOWN; memory->activeRegion = -1; cpu->memory.activeRegion = (uint32_t*) _deadbeef; cpu->memory.activeMask = 0; diff --git a/src/gba/savedata.c b/src/gba/savedata.c index 50d2be381..13204cbb3 100644 --- a/src/gba/savedata.c +++ b/src/gba/savedata.c @@ -372,11 +372,11 @@ uint8_t GBASavedataReadFlash(struct GBASavedata* savedata, uint16_t address) { if (savedata->command == FLASH_COMMAND_ID) { if (savedata->type == GBA_SAVEDATA_FLASH512) { if (address < 2) { - return FLASH_MFG_PANASONIC >> (address * 8); + return FLASH_PANASONIC_MN63F805MNP >> (address * 8); } } else if (savedata->type == GBA_SAVEDATA_FLASH1M) { if (address < 2) { - return FLASH_MFG_SANYO >> (address * 8); + return FLASH_SANYO_LE26FV10N1TS >> (address * 8); } } } diff --git a/src/platform/cmake/DebugStrip.cmake b/src/platform/cmake/DebugStrip.cmake new file mode 100644 index 000000000..7e2b842a4 --- /dev/null +++ b/src/platform/cmake/DebugStrip.cmake @@ -0,0 +1,12 @@ +function(debug_strip TARGET) + if(DISTBUILD AND NOT APPLE) + if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$" "$.debug") + add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND "${STRIP}" "$") + add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND "${OBJCOPY}" --add-gnu-debuglink "$.debug" "$") + install(FILES "$.debug" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${TARGET}-dbg) + elseif(BUILD_STATIC AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")) + add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND "${STRIP}" "$") + endif() + endif() +endfunction() diff --git a/src/platform/headless-main.c b/src/platform/headless-main.c index f501e52b2..61110b1e8 100644 --- a/src/platform/headless-main.c +++ b/src/platform/headless-main.c @@ -75,6 +75,7 @@ int main(int argc, char * argv[]) { bool cleanExit = false; int uncleanExit = 1; + size_t i; struct HeadlessOpts headlessOpts = { 3, NULL }; StringListInit(&headlessOpts.scripts, 0); @@ -206,7 +207,6 @@ int main(int argc, char * argv[]) { mScriptContextAttachCore(&scriptContext, core); - size_t i; for (i = 0; i < StringListSize(&headlessOpts.scripts); ++i) { if (!mScriptContextLoadFile(&scriptContext, *StringListGetPointer(&headlessOpts.scripts, i))) { mLOG(STATUS, ERROR, "Failed to load script \"%s\"", *StringListGetPointer(&headlessOpts.scripts, i)); @@ -253,7 +253,6 @@ loadError: } argsExit: - size_t i; for (i = 0; i < StringListSize(&headlessOpts.scripts); ++i) { free(*StringListGetPointer(&headlessOpts.scripts, i)); } diff --git a/src/platform/python/mgba/gba.py b/src/platform/python/mgba/gba.py index 0c249cec5..91ac3254e 100644 --- a/src/platform/python/mgba/gba.py +++ b/src/platform/python/mgba/gba.py @@ -54,7 +54,7 @@ class GBA(Core): class GBAMemory(Memory): - def __init__(self, core, romSize=lib.SIZE_CART0): + def __init__(self, core, romSize=lib.GBA_SIZE_ROM0): super(GBAMemory, self).__init__(core, 0x100000000) self.bios = Memory(core, lib.GBA_SIZE_BIOS, lib.GBA_BASE_BIOS) @@ -64,11 +64,10 @@ class GBAMemory(Memory): 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.rom0 = Memory(core, romSize, lib.GBA_BASE_ROM0) + self.rom1 = Memory(core, romSize, lib.GBA_BASE_ROM1) + self.rom2 = Memory(core, romSize, lib.GBA_BASE_ROM2) + self.rom = self.rom0 self.sram = Memory(core, lib.GBA_SIZE_SRAM, lib.GBA_BASE_SRAM) diff --git a/src/platform/qt/CMakeLists.txt b/src/platform/qt/CMakeLists.txt index a5fe5b8a8..9858fddb0 100644 --- a/src/platform/qt/CMakeLists.txt +++ b/src/platform/qt/CMakeLists.txt @@ -261,6 +261,8 @@ if(ENABLE_DEBUGGERS) DebuggerController.cpp DebuggerConsole.cpp DebuggerConsoleController.cpp + MemoryAccessLogController.cpp + MemoryAccessLogModel.cpp MemoryAccessLogView.cpp) endif() @@ -536,17 +538,7 @@ elseif(WIN32) endif() endif() -if(DISTBUILD AND NOT APPLE) - if(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$" "$.debug") - add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${STRIP}" "$") - add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${OBJCOPY}" --add-gnu-debuglink "$.debug" "$") - install(FILES "$.debug" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt-dbg) - elseif(BUILD_STATIC AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")) - add_custom_command(TARGET ${BINARY_NAME}-qt POST_BUILD COMMAND "${STRIP}" "$") - endif() -endif() - +debug_strip(${BINARY_NAME}-qt) install(TARGETS ${BINARY_NAME}-qt RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-qt BUNDLE DESTINATION ${APPDIR} COMPONENT ${BINARY_NAME}-qt) diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index ed000ab3b..a12768709 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -196,6 +196,14 @@ ConfigController::ConfigController(QObject* parent) m_subparsers[1].extraOptions = nullptr; m_subparsers[1].longOptions = s_frontendOptions; m_subparsers[1].opts = this; + m_subparsers[1].handleExtraArg = [](struct mSubParser* parser, const char* arg) { + ConfigController* self = static_cast(parser->opts); + if (self->m_fnames.count() >= MAX_GBAS) { + return false; + } + self->m_fnames.append(QString::fromUtf8(arg)); + return true; + }; } ConfigController::~ConfigController() { diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 4325a46ba..5aa9b7ac2 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -102,6 +102,7 @@ public: const mArguments* args() const { return &m_args; } const mGraphicsOpts* graphicsOpts() const { return &m_graphicsOpts; } + QStringList fileNames() const { return m_fnames; } void usage(const char* arg0) const; static const QString& configDir(); @@ -129,6 +130,7 @@ private: mArguments m_args{}; mGraphicsOpts m_graphicsOpts{}; std::array m_subparsers; + QStringList m_fnames; bool m_parsed = false; QHash m_argvOptions; diff --git a/src/platform/qt/CoreController.cpp b/src/platform/qt/CoreController.cpp index 2496f8039..53d5b3892 100644 --- a/src/platform/qt/CoreController.cpp +++ b/src/platform/qt/CoreController.cpp @@ -8,6 +8,7 @@ #include "ConfigController.h" #include "InputController.h" #include "LogController.h" +#include "MemoryAccessLogController.h" #include "MultiplayerController.h" #include "Override.h" @@ -459,6 +460,15 @@ void CoreController::setLogger(LogController* logger) { connect(this, &CoreController::logPosted, m_log, &LogController::postLog); } +#ifdef ENABLE_DEBUGGERS +std::weak_ptr CoreController::memoryAccessLogController() { + if (!m_malController) { + m_malController = std::make_shared(this); + } + return m_malController; +} +#endif + void CoreController::start() { QSize size(screenDimensions()); m_activeBuffer.resize(size.width() * size.height() * sizeof(mColor)); @@ -479,6 +489,10 @@ void CoreController::start() { void CoreController::stop() { setSync(false); #ifdef ENABLE_DEBUGGERS + if (m_malController) { + m_malController->stop(); + } + detachDebugger(); #endif setPaused(false); @@ -1253,6 +1267,9 @@ void CoreController::finishFrame() { } void CoreController::updatePlayerSave() { + if (m_saveBlocked) { + return; + } int savePlayerId = m_multiplayer->saveId(this); QString saveSuffix; diff --git a/src/platform/qt/CoreController.h b/src/platform/qt/CoreController.h index 57eb9ffe0..4de454d4b 100644 --- a/src/platform/qt/CoreController.h +++ b/src/platform/qt/CoreController.h @@ -40,6 +40,7 @@ namespace QGBA { class ConfigController; class InputController; class LogController; +class MemoryAccessLogController; class MultiplayerController; class Override; @@ -113,6 +114,8 @@ public: void detachDebugger(); void attachDebuggerModule(mDebuggerModule*, bool interrupt = true); void detachDebuggerModule(mDebuggerModule*); + + std::weak_ptr memoryAccessLogController(); #endif void setMultiplayerController(MultiplayerController*); @@ -173,6 +176,7 @@ public slots: void scanCards(const QStringList&); void replaceGame(const QString&); void yankPak(); + void blockSave() { m_saveBlocked = true; } void addKey(int key); void clearKey(int key); @@ -260,6 +264,7 @@ private: bool m_patched = false; bool m_preload = false; + bool m_saveBlocked = false; uint32_t m_crc32; QString m_internalTitle; @@ -326,6 +331,10 @@ private: GBASIODolphin m_dolphin; #endif +#ifdef ENABLE_DEBUGGERS + std::shared_ptr m_malController; +#endif + mVideoLogContext* m_vl = nullptr; VFile* m_vlVf = nullptr; diff --git a/src/platform/qt/CoreManager.cpp b/src/platform/qt/CoreManager.cpp index b2f7cabf0..385b3440b 100644 --- a/src/platform/qt/CoreManager.cpp +++ b/src/platform/qt/CoreManager.cpp @@ -169,6 +169,7 @@ CoreController* CoreManager::loadBIOS(int platform, const QString& path) { mDirectorySetAttachBase(&core->dirs, VDirOpen(bytes.constData())); CoreController* cc = new CoreController(core); + cc->blockSave(); if (m_multiplayer) { cc->setMultiplayerController(m_multiplayer); } diff --git a/src/platform/qt/GBAApp.cpp b/src/platform/qt/GBAApp.cpp index 90fc89170..258df9f0c 100644 --- a/src/platform/qt/GBAApp.cpp +++ b/src/platform/qt/GBAApp.cpp @@ -397,6 +397,26 @@ void GBAApp::finishJob(qint64 jobId) { m_workerJobCallbacks.remove(jobId); } +void GBAApp::initMultiplayer() { + QStringList fnames = m_configController->fileNames(); + if (fnames.count() < 2) { + return; + } + + Window* w = m_windows[0]; + for (const auto& fname : fnames) { + if (!w) { + w = newWindow(); + } + if (!w) { + break; + } + CoreController* core = m_manager.loadGame(fname); + w->setController(core, fname); + w = nullptr; + } +} + GBAApp::WorkerJob::WorkerJob(qint64 id, std::function&& job, GBAApp* owner) : m_id(id) , m_job(std::move(job)) diff --git a/src/platform/qt/GBAApp.h b/src/platform/qt/GBAApp.h index 2388bc297..43848b183 100644 --- a/src/platform/qt/GBAApp.h +++ b/src/platform/qt/GBAApp.h @@ -82,6 +82,8 @@ public: ApplicationUpdater* updater() { return &m_updater; } QString invokeOnExit() { return m_invokeOnExit; } + void initMultiplayer(); + public slots: void restartForUpdate(); Window* newWindow(); diff --git a/src/platform/qt/MemoryAccessLogController.cpp b/src/platform/qt/MemoryAccessLogController.cpp new file mode 100644 index 000000000..d62b2f8a5 --- /dev/null +++ b/src/platform/qt/MemoryAccessLogController.cpp @@ -0,0 +1,189 @@ +/* Copyright (c) 2013-2025 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 "MemoryAccessLogController.h" + +#include "GBAApp.h" +#include "LogController.h" +#include "utils.h" +#include "VFileDevice.h" + +#include + +using namespace QGBA; + +int MemoryAccessLogController::Flags::count() const { + return popcount32(flags) + popcount32(flagsEx); +} + +MemoryAccessLogController::MemoryAccessLogController(CoreController* controller, QObject* parent) + : QObject(parent) + , m_controller(controller) +{ + mCore* core = m_controller->thread()->core; + const mCoreMemoryBlock* info; + size_t nBlocks = core->listMemoryBlocks(core, &info); + + for (size_t i = 0; i < nBlocks; ++i) { + if (!(info[i].flags & mCORE_MEMORY_MAPPED)) { + continue; + } + m_regions.append({ + QString::fromUtf8(info[i].longName), + QString::fromUtf8(info[i].internalName) + }); + } +} + +MemoryAccessLogController::~MemoryAccessLogController() { + stop(); +} + +bool MemoryAccessLogController::canExport() const { + return m_regionMapping.contains("cart0"); +} + +MemoryAccessLogController::Flags MemoryAccessLogController::flagsForAddress(uint32_t addresss, int segment) { + uint32_t offset = cacheRegion(addresss, segment); + if (!m_cachedRegion) { + return { 0, 0 }; + } + return { + m_cachedRegion->blockEx ? m_cachedRegion->blockEx[offset] : mDebuggerAccessLogFlagsEx{}, + m_cachedRegion->block ? m_cachedRegion->block[offset] : mDebuggerAccessLogFlags{}, + }; +} + +void MemoryAccessLogController::updateRegion(const QString& internalName, bool checked) { + if (checked) { + m_watchedRegions += internalName; + } else { + m_watchedRegions -= internalName; + } + if (!m_active) { + return; + } + if (checked && !m_regionMapping.contains(internalName)) { + m_regionMapping[internalName] = mDebuggerAccessLoggerWatchMemoryBlockName(&m_logger, internalName.toUtf8().constData(), activeFlags()); + } + emit regionMappingChanged(internalName, checked); +} + +void MemoryAccessLogController::setFile(const QString& path) { + m_path = path; +} + +void MemoryAccessLogController::start(bool loadExisting, bool logExtra) { + if (!m_loaded) { + load(loadExisting); + } + if (!m_loaded) { + return; + } + CoreController::Interrupter interrupter(m_controller); + mDebuggerAccessLoggerStart(&m_logger); + m_logExtra = logExtra; + + m_active = true; + for (const auto& region : m_watchedRegions) { + m_regionMapping[region] = mDebuggerAccessLoggerWatchMemoryBlockName(&m_logger, region.toUtf8().constData(), activeFlags()); + } + emit loggingChanged(true); +} + +void MemoryAccessLogController::stop() { + if (!m_active) { + return; + } + CoreController::Interrupter interrupter(m_controller); + mDebuggerAccessLoggerStop(&m_logger); + emit loggingChanged(false); + interrupter.resume(); + m_active = false; +} + +void MemoryAccessLogController::load(bool loadExisting) { + if (m_loaded) { + return; + } + int flags = O_CREAT | O_RDWR; + if (!loadExisting) { + flags |= O_TRUNC; + } + VFile* vf = VFileDevice::open(m_path, flags); + if (!vf) { + LOG(QT, ERROR) << tr("Failed to open memory log file"); + return; + } + + mDebuggerAccessLoggerInit(&m_logger); + CoreController::Interrupter interrupter(m_controller); + m_controller->attachDebuggerModule(&m_logger.d); + if (!mDebuggerAccessLoggerOpen(&m_logger, vf, flags)) { + mDebuggerAccessLoggerDeinit(&m_logger); + LOG(QT, ERROR) << tr("Failed to open memory log file"); + return; + } + emit loaded(true); + m_loaded = true; +} + +void MemoryAccessLogController::unload() { + if (m_active) { + stop(); + } + if (m_active) { + return; + } + CoreController::Interrupter interrupter(m_controller); + m_controller->detachDebuggerModule(&m_logger.d); + mDebuggerAccessLoggerDeinit(&m_logger); + emit loaded(false); + m_loaded = false; +} + +mDebuggerAccessLogRegionFlags MemoryAccessLogController::activeFlags() const { + mDebuggerAccessLogRegionFlags loggerFlags = 0; + if (m_logExtra) { + loggerFlags = mDebuggerAccessLogRegionFlagsFillHasExBlock(loggerFlags); + } + return loggerFlags; +} + +void MemoryAccessLogController::exportFile(const QString& filename) { + VFile* vf = VFileDevice::open(filename, O_CREAT | O_TRUNC | O_WRONLY); + if (!vf) { + // log error + return; + } + + CoreController::Interrupter interrupter(m_controller); + mDebuggerAccessLoggerCreateShadowFile(&m_logger, m_regionMapping[QString("cart0")], vf, 0); + vf->close(vf); +} + +uint32_t MemoryAccessLogController::cacheRegion(uint32_t address, int segment) { + if (m_cachedRegion && (address < m_cachedRegion->start || address >= m_cachedRegion->end)) { + m_cachedRegion = nullptr; + } + if (!m_cachedRegion) { + m_cachedRegion = mDebuggerAccessLoggerGetRegion(&m_logger, address, segment, nullptr); + } + if (!m_cachedRegion) { + return 0; + } + + size_t offset = address - m_cachedRegion->start; + if (segment > 0) { + uint32_t segmentSize = m_cachedRegion->end - m_cachedRegion->segmentStart; + offset %= segmentSize; + offset += segmentSize * segment; + } + if (offset >= m_cachedRegion->size) { + m_cachedRegion = nullptr; + return cacheRegion(address, segment); + } + return offset; +} diff --git a/src/platform/qt/MemoryAccessLogController.h b/src/platform/qt/MemoryAccessLogController.h new file mode 100644 index 000000000..b2df55214 --- /dev/null +++ b/src/platform/qt/MemoryAccessLogController.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2013-2025 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 "CoreController.h" + +#include +#include + +namespace QGBA { + +class MemoryAccessLogController : public QObject { +Q_OBJECT + +public: + struct Region { + QString longName; + QString internalName; + }; + + struct Flags { + mDebuggerAccessLogFlagsEx flagsEx; + mDebuggerAccessLogFlags flags; + + int count() const; + bool operator==(const Flags& other) const { return flags == other.flags && flagsEx == other.flagsEx; } + bool operator!=(const Flags& other) const { return flags != other.flags || flagsEx != other.flagsEx; } + operator bool() const { return flags || flagsEx; } + }; + + MemoryAccessLogController(CoreController* controller, QObject* parent = nullptr); + ~MemoryAccessLogController(); + + QVector listRegions() const { return m_regions; } + QSet watchedRegions() const { return m_watchedRegions; } + + bool canExport() const; + mPlatform platform() const { return m_controller->platform(); } + + Flags flagsForAddress(uint32_t address, int segment = -1); + + QString file() const { return m_path; } + bool active() const { return m_active; } + bool isLoaded() const { return m_loaded; } + +public slots: + void updateRegion(const QString& internalName, bool enable); + void setFile(const QString& path); + + void start(bool loadExisting, bool logExtra); + void stop(); + + void load(bool loadExisting); + void unload(); + + void exportFile(const QString& filename); + +signals: + void loaded(bool loaded); + void loggingChanged(bool active); + void regionMappingChanged(const QString& internalName, bool active); + +private: + bool m_logExtra = false; + QString m_path; + CoreController* m_controller; + QSet m_watchedRegions; + QHash m_regionMapping; + QVector m_regions; + struct mDebuggerAccessLogger m_logger{}; + bool m_loaded = false; + bool m_active = false; + mDebuggerAccessLogRegion* m_cachedRegion = nullptr; + + mDebuggerAccessLogRegionFlags activeFlags() const; + uint32_t cacheRegion(uint32_t address, int segment); +}; + +} diff --git a/src/platform/qt/MemoryAccessLogModel.cpp b/src/platform/qt/MemoryAccessLogModel.cpp new file mode 100644 index 000000000..6fa8592d0 --- /dev/null +++ b/src/platform/qt/MemoryAccessLogModel.cpp @@ -0,0 +1,301 @@ +/* Copyright (c) 2013-2025 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 "MemoryAccessLogModel.h" + +#include + +using namespace QGBA; + +MemoryAccessLogModel::MemoryAccessLogModel(std::weak_ptr controller, mPlatform platform) + : m_controller(controller) + , m_platform(platform) +{ +} + +QVariant MemoryAccessLogModel::data(const QModelIndex& index, int role) const { + if (role != Qt::DisplayRole) { + return {}; + } + if (index.column() != 0) { + return {}; + } + int blockIndex = -1; + int flagIndex = -1; + QModelIndex parent = index.parent(); + if (!parent.isValid()) { + blockIndex = index.row(); + } else { + blockIndex = parent.row(); + flagIndex = index.row(); + } + + if (blockIndex < 0 || blockIndex >= m_cachedBlocks.count()) { + return {}; + } + + const Block& block = m_cachedBlocks[blockIndex]; + + if (flagIndex < 0) { + if (m_platform == mPLATFORM_GB) { + if (m_segment < 0) { + return QString("$%1 – $%2") + .arg(QString("%0").arg(block.region.first, 4, 16, QChar('0')).toUpper()) + .arg(QString("%0").arg(block.region.second, 4, 16, QChar('0')).toUpper()); + } else { + return QString("$%1:%3 – $%2:%4") + .arg(QString("%0").arg(m_segment, 2, 16, QChar('0')).toUpper()) + .arg(QString("%0").arg(m_segment, 2, 16, QChar('0')).toUpper()) + .arg(QString("%0").arg(block.region.first, 4, 16, QChar('0')).toUpper()) + .arg(QString("%0").arg(block.region.second, 4, 16, QChar('0')).toUpper()); + } + } else { + return QString("0x%1 – 0x%2") + .arg(QString("%0").arg(block.region.first, 8, 16, QChar('0')).toUpper()) + .arg(QString("%0").arg(block.region.second, 8, 16, QChar('0')).toUpper()); + } + } + for (int i = 0; i < 8; ++i) { + if (!(block.flags.flags & (1 << i))) { + continue; + } + if (flagIndex == 0) { + switch (i) { + case 0: + return tr("Data read"); + case 1: + return tr("Data written"); + case 2: + return tr("Code executed"); + case 3: + return tr("Code aborted"); + case 4: + return tr("8-bit access"); + case 5: + return tr("16-bit access"); + case 6: + return tr("32-bit access"); + case 7: + return tr("64-bit access"); + default: + Q_UNREACHABLE(); + } + } + --flagIndex; + } + for (int i = 0; i < 16; ++i) { + if (!(block.flags.flagsEx & (1 << i))) { + continue; + } + if (flagIndex == 0) { + switch (i) { + case 0: + return tr("Accessed by instruction"); + case 1: + return tr("Accessed by DMA"); + case 2: + return tr("Accessed by BIOS"); + case 3: + return tr("Compressed data"); + case 4: + return tr("Accessed by memory copy"); + case 5: + return tr("(Unknown extra bit 5)"); + case 6: + return tr("(Unknown extra bit 6)"); + case 7: + return tr("(Unknown extra bit 7)"); + case 8: + return tr("Invalid instruction"); + case 9: + return tr("Invalid read"); + case 10: + return tr("Invalid write"); + case 11: + return tr("Invalid executable address"); + case 12: + return tr("(Private bit 0)"); + case 13: + return tr("(Private bit 1)"); + case 14: + switch (m_platform) { + case mPLATFORM_GBA: + return tr("ARM code"); + case mPLATFORM_GB: + return tr("Instruction opcode"); + default: + return tr("(Private bit 2)"); + } + case 15: + switch (m_platform) { + case mPLATFORM_GBA: + return tr("Thumb code"); + case mPLATFORM_GB: + return tr("Instruction operand"); + default: + return tr("(Private bit 3)"); + } + default: + Q_UNREACHABLE(); + } + } + --flagIndex; + } + return tr("(Unknown)"); +} + +QModelIndex MemoryAccessLogModel::index(int row, int column, const QModelIndex& parent) const { + if (column != 0) { + return {}; + } + if (parent.isValid()) { + return createIndex(row, 0, parent.row()); + } + return createIndex(row, 0, std::numeric_limits::max()); +} + +QModelIndex MemoryAccessLogModel::parent(const QModelIndex& index) const { + if (!index.isValid()) { + return {}; + } + quintptr row = index.internalId(); + if (row >= std::numeric_limits::max()) { + return {}; + } + return createIndex(row, 0, std::numeric_limits::max()); +} + +int MemoryAccessLogModel::rowCount(const QModelIndex& parent) const { + int blockIndex = -1; + if (!parent.isValid()) { + return m_cachedBlocks.count(); + } else if (parent.column() != 0) { + return 0; + } else if (parent.parent().isValid()) { + return 0; + } else { + blockIndex = parent.row(); + } + + if (blockIndex < 0 || blockIndex >= m_cachedBlocks.count()) { + return 0; + } + + const Block& block = m_cachedBlocks[blockIndex]; + return block.flags.count(); +} + +void MemoryAccessLogModel::updateSelection(uint32_t start, uint32_t end) { + std::shared_ptr controller = m_controller.lock(); + if (!controller) { + return; + } + QVector newBlocks; + uint32_t lastStart = start; + auto lastFlags = controller->flagsForAddress(m_base + start, m_segment); + + for (uint32_t address = start; address < end; ++address) { + auto flags = controller->flagsForAddress(m_base + address, m_segment); + if (flags == lastFlags) { + continue; + } + if (lastFlags) { + newBlocks.append({ lastFlags, qMakePair(lastStart, address) }); + } + lastFlags = flags; + lastStart = address; + } + if (lastFlags) { + newBlocks.append({ lastFlags, qMakePair(lastStart, end) }); + } + + if (m_cachedBlocks.count() == 0 || newBlocks.count() == 0) { + beginResetModel(); + m_cachedBlocks = newBlocks; + endResetModel(); + return; + } + + QPair changed{ -1, -1 }; + for (int i = 0; i < m_cachedBlocks.count() && i < newBlocks.count(); ++i) { + const Block& oldBlock = m_cachedBlocks.at(i); + const Block& newBlock = newBlocks.at(i); + + if (oldBlock != newBlock) { + changed = qMakePair(i, m_cachedBlocks.count()); + break; + } + } + + if (m_cachedBlocks.count() > newBlocks.count()) { + beginRemoveRows({}, newBlocks.count(), m_cachedBlocks.count()); + m_cachedBlocks.resize(newBlocks.count()); + endRemoveRows(); + changed.second = newBlocks.count(); + } + + if (m_cachedBlocks.count() < newBlocks.count()) { + beginInsertRows({}, m_cachedBlocks.count(), newBlocks.count()); + if (changed.first < 0) { + // Only new rows + m_cachedBlocks = newBlocks; + endInsertRows(); + return; + } + } + + if (changed.first < 0) { + // No changed rows, though some might have been removed + return; + } + + for (int i = 0; i < m_cachedBlocks.count() && i < newBlocks.count(); ++i) { + const Block& oldBlock = m_cachedBlocks.at(i); + const Block& newBlock = newBlocks.at(i); + if (oldBlock.flags != newBlock.flags) { + int oldFlags = oldBlock.flags.count(); + int newFlags = newBlock.flags.count(); + if (oldFlags > newFlags) { + beginRemoveRows(createIndex(i, 0, std::numeric_limits::max()), newFlags, oldFlags); + } else if (oldFlags < newFlags) { + beginInsertRows(createIndex(i, 0, std::numeric_limits::max()), oldFlags, newFlags); + } + m_cachedBlocks[i] = newBlock; + emit dataChanged(createIndex(0, 0, i), createIndex(std::min(oldFlags, newFlags), 0, i)); + if (oldFlags > newFlags) { + endRemoveRows(); + } else if (oldFlags < newFlags) { + endInsertRows(); + } + } + } + emit dataChanged(createIndex(changed.first, 0, std::numeric_limits::max()), + createIndex(changed.second, 0, std::numeric_limits::max())); + + if (m_cachedBlocks.count() < newBlocks.count()) { + m_cachedBlocks = newBlocks; + endInsertRows(); + } +} + +void MemoryAccessLogModel::setSegment(int segment) { + if (m_segment == segment) { + return; + } + beginResetModel(); + m_segment = segment; + m_cachedBlocks.clear(); + endResetModel(); +} + +void MemoryAccessLogModel::setRegion(uint32_t base, uint32_t, bool useSegments) { + if (m_base == base) { + return; + } + beginResetModel(); + m_segment = useSegments ? 0 : -1; + m_cachedBlocks.clear(); + endResetModel(); +} diff --git a/src/platform/qt/MemoryAccessLogModel.h b/src/platform/qt/MemoryAccessLogModel.h new file mode 100644 index 000000000..b18f0dee5 --- /dev/null +++ b/src/platform/qt/MemoryAccessLogModel.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2013-2025 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 "MemoryAccessLogController.h" + +struct mCheatDevice; +struct mCheatSet; + +namespace QGBA { + +class MemoryAccessLogModel : public QAbstractItemModel { +Q_OBJECT + +public: + MemoryAccessLogModel(std::weak_ptr controller, mPlatform platform); + + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override; + virtual QModelIndex parent(const QModelIndex& index) const override; + + virtual int columnCount(const QModelIndex& = QModelIndex()) const override { return 1; } + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + +public slots: + void updateSelection(uint32_t start, uint32_t end); + void setSegment(int segment); + void setRegion(uint32_t base, uint32_t segmentSize, bool useSegments); + +private: + struct Block { + MemoryAccessLogController::Flags flags; + QPair region; + + bool operator==(const Block& other) const { return flags == other.flags && region == other.region; } + bool operator!=(const Block& other) const { return flags != other.flags || region != other.region; } + }; + + int flagCount(int index) const; + + std::weak_ptr m_controller; + mPlatform m_platform; + uint32_t m_base = 0; + int m_segment = -1; + QVector m_cachedBlocks; +}; + +} diff --git a/src/platform/qt/MemoryAccessLogView.cpp b/src/platform/qt/MemoryAccessLogView.cpp index 3c846b440..a06c649ae 100644 --- a/src/platform/qt/MemoryAccessLogView.cpp +++ b/src/platform/qt/MemoryAccessLogView.cpp @@ -9,107 +9,87 @@ #include "GBAApp.h" #include "LogController.h" +#include "MemoryAccessLogController.h" #include "utils.h" #include "VFileDevice.h" using namespace QGBA; -MemoryAccessLogView::MemoryAccessLogView(std::shared_ptr controller, QWidget* parent) +MemoryAccessLogView::MemoryAccessLogView(std::weak_ptr controller, QWidget* parent) : QWidget(parent) - , m_controller(std::move(controller)) + , m_controller(controller) { m_ui.setupUi(this); + std::shared_ptr controllerPtr = m_controller.lock(); connect(m_ui.browse, &QAbstractButton::clicked, this, &MemoryAccessLogView::selectFile); connect(m_ui.exportButton, &QAbstractButton::clicked, this, &MemoryAccessLogView::exportFile); - connect(this, &MemoryAccessLogView::loggingChanged, m_ui.start, &QWidget::setDisabled); - connect(this, &MemoryAccessLogView::loggingChanged, m_ui.stop, &QWidget::setEnabled); - connect(this, &MemoryAccessLogView::loggingChanged, m_ui.filename, &QWidget::setDisabled); - connect(this, &MemoryAccessLogView::loggingChanged, m_ui.browse, &QWidget::setDisabled); + connect(controllerPtr.get(), &MemoryAccessLogController::regionMappingChanged, this, &MemoryAccessLogView::updateRegion); + connect(controllerPtr.get(), &MemoryAccessLogController::loggingChanged, this, &MemoryAccessLogView::handleStartStop); + connect(controllerPtr.get(), &MemoryAccessLogController::loaded, this, &MemoryAccessLogView::handleLoadUnload); - mCore* core = m_controller->thread()->core; - const mCoreMemoryBlock* info; - size_t nBlocks = core->listMemoryBlocks(core, &info); + bool active = controllerPtr->active(); + bool loaded = controllerPtr->isLoaded(); + auto watchedRegions = controllerPtr->watchedRegions(); QVBoxLayout* regionBox = static_cast(m_ui.regionBox->layout()); - for (size_t i = 0; i < nBlocks; ++i) { - if (!(info[i].flags & mCORE_MEMORY_MAPPED)) { - continue; - } - QCheckBox* region = new QCheckBox(QString::fromUtf8(info[i].longName)); + for (const auto& info : controllerPtr->listRegions()) { + QCheckBox* region = new QCheckBox(info.longName); regionBox->addWidget(region); - QString name(QString::fromUtf8(info[i].internalName)); + QString name(info.internalName); m_regionBoxes[name] = region; connect(region, &QAbstractButton::toggled, this, [this, name](bool checked) { - updateRegion(name, checked); + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { + return; + } + controllerPtr->updateRegion(name, checked); }); } -} -MemoryAccessLogView::~MemoryAccessLogView() { - stop(); + handleLoadUnload(loaded); + handleStartStop(active); } void MemoryAccessLogView::updateRegion(const QString& internalName, bool checked) { if (checked) { - m_watchedRegions += internalName; - } else { - m_watchedRegions -= internalName; + m_regionBoxes[internalName]->setEnabled(false); } - if (!m_active) { - return; - } - m_regionBoxes[internalName]->setEnabled(false); - m_regionMapping[internalName] = mDebuggerAccessLoggerWatchMemoryBlockName(&m_logger, internalName.toUtf8().constData(), activeFlags()); } void MemoryAccessLogView::start() { - int flags = O_CREAT | O_RDWR; - if (!m_ui.loadExisting->isChecked()) { - flags |= O_TRUNC; - } - VFile* vf = VFileDevice::open(m_ui.filename->text(), flags); - if (!vf) { - // log error + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { return; } - mDebuggerAccessLoggerInit(&m_logger); - CoreController::Interrupter interrupter(m_controller); - m_controller->attachDebuggerModule(&m_logger.d); - if (!mDebuggerAccessLoggerOpen(&m_logger, vf, flags)) { - mDebuggerAccessLoggerDeinit(&m_logger); - LOG(QT, ERROR) << tr("Failed to open memory log file"); - return; - } - - m_active = true; - emit loggingChanged(true); - for (const auto& region : m_watchedRegions) { - m_regionBoxes[region]->setEnabled(false); - m_regionMapping[region] = mDebuggerAccessLoggerWatchMemoryBlockName(&m_logger, region.toUtf8().constData(), activeFlags()); - } - interrupter.resume(); - - if (m_watchedRegions.contains(QString("cart0"))) { - m_ui.exportButton->setEnabled(true); - } + controllerPtr->setFile(m_ui.filename->text()); + controllerPtr->start(m_ui.loadExisting->isChecked(), m_ui.logExtra->isChecked()); } void MemoryAccessLogView::stop() { - if (!m_active) { + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { return; } - CoreController::Interrupter interrupter(m_controller); - m_controller->detachDebuggerModule(&m_logger.d); - mDebuggerAccessLoggerDeinit(&m_logger); - emit loggingChanged(false); - interrupter.resume(); + controllerPtr->stop(); +} - for (const auto& region : m_watchedRegions) { - m_regionBoxes[region]->setEnabled(true); +void MemoryAccessLogView::load() { + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { + return; } - m_ui.exportButton->setEnabled(false); + controllerPtr->setFile(m_ui.filename->text()); + controllerPtr->load(m_ui.loadExisting->isChecked()); +} + +void MemoryAccessLogView::unload() { + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { + return; + } + controllerPtr->unload(); } void MemoryAccessLogView::selectFile() { @@ -119,30 +99,55 @@ void MemoryAccessLogView::selectFile() { } } -mDebuggerAccessLogRegionFlags MemoryAccessLogView::activeFlags() const { - mDebuggerAccessLogRegionFlags loggerFlags = 0; - if (m_ui.logExtra->isChecked()) { - loggerFlags = mDebuggerAccessLogRegionFlagsFillHasExBlock(loggerFlags); - } - return loggerFlags; -} - void MemoryAccessLogView::exportFile() { - if (!m_regionMapping.contains("cart0")) { + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { + return; + } + if (!controllerPtr->canExport()) { return; } - QString filename = GBAApp::app()->getSaveFileName(this, tr("Select access log file"), romFilters(false, m_controller->platform(), true)); + QString filename = GBAApp::app()->getSaveFileName(this, tr("Select access log file"), romFilters(false, controllerPtr->platform(), true)); if (filename.isEmpty()) { return; } - VFile* vf = VFileDevice::open(filename, O_CREAT | O_TRUNC | O_WRONLY); - if (!vf) { - // log error + controllerPtr->exportFile(filename); +} + +void MemoryAccessLogView::handleStartStop(bool start) { + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { return; } + m_ui.filename->setText(controllerPtr->file()); - CoreController::Interrupter interrupter(m_controller); - mDebuggerAccessLoggerCreateShadowFile(&m_logger, m_regionMapping[QString("cart0")], vf, 0); - vf->close(vf); + auto watchedRegions = controllerPtr->watchedRegions(); + for (const auto& region : watchedRegions) { + m_regionBoxes[region]->setDisabled(start); + m_regionBoxes[region]->setChecked(true); + } + + m_ui.start->setDisabled(start); + m_ui.stop->setEnabled(start); + m_ui.unload->setDisabled(start || !controllerPtr->isLoaded()); +} + +void MemoryAccessLogView::handleLoadUnload(bool load) { + std::shared_ptr controllerPtr = m_controller.lock(); + if (!controllerPtr) { + return; + } + m_ui.filename->setText(controllerPtr->file()); + + if (load && controllerPtr->canExport()) { + m_ui.exportButton->setEnabled(true); + } else if (!load) { + m_ui.exportButton->setEnabled(false); + } + + m_ui.load->setDisabled(load); + m_ui.unload->setEnabled(load); + m_ui.filename->setDisabled(load); + m_ui.browse->setDisabled(load); } diff --git a/src/platform/qt/MemoryAccessLogView.h b/src/platform/qt/MemoryAccessLogView.h index ffe31cc43..f1f7c0388 100644 --- a/src/platform/qt/MemoryAccessLogView.h +++ b/src/platform/qt/MemoryAccessLogView.h @@ -18,12 +18,14 @@ namespace QGBA { +class MemoryAccessLogController; + class MemoryAccessLogView : public QWidget { Q_OBJECT public: - MemoryAccessLogView(std::shared_ptr controller, QWidget* parent = nullptr); - ~MemoryAccessLogView(); + MemoryAccessLogView(std::weak_ptr controller, QWidget* parent = nullptr); + ~MemoryAccessLogView() = default; private slots: void updateRegion(const QString& internalName, bool enable); @@ -32,22 +34,19 @@ private slots: void start(); void stop(); + void load(); + void unload(); + void exportFile(); -signals: - void loggingChanged(bool active); + void handleStartStop(bool start); + void handleLoadUnload(bool load); private: Ui::MemoryAccessLogView m_ui; - std::shared_ptr m_controller; - QSet m_watchedRegions; + std::weak_ptr m_controller; QHash m_regionBoxes; - QHash m_regionMapping; - struct mDebuggerAccessLogger m_logger{}; - bool m_active = false; - - mDebuggerAccessLogRegionFlags activeFlags() const; }; } diff --git a/src/platform/qt/MemoryAccessLogView.ui b/src/platform/qt/MemoryAccessLogView.ui index fe9450f53..9fadd0870 100644 --- a/src/platform/qt/MemoryAccessLogView.ui +++ b/src/platform/qt/MemoryAccessLogView.ui @@ -6,8 +6,8 @@ 0 0 - 385 - 380 + 311 + 387 @@ -22,25 +22,25 @@ Log file - - + + - + Browse - + Log additional information (uses 3× space) - + Load existing file if present @@ -50,6 +50,23 @@ + + + + Load + + + + + + + false + + + Unload + + + @@ -124,9 +141,43 @@ + + load + clicked() + QGBA::MemoryAccessLogView + load() + + + 81 + 152 + + + 192 + 189 + + + + + unload + clicked() + QGBA::MemoryAccessLogView + unload() + + + 226 + 152 + + + 192 + 189 + + + start() stop() + load() + unload() diff --git a/src/platform/qt/MemoryView.cpp b/src/platform/qt/MemoryView.cpp index 4383867e0..dd0bc18a3 100644 --- a/src/platform/qt/MemoryView.cpp +++ b/src/platform/qt/MemoryView.cpp @@ -7,6 +7,7 @@ #include "MemoryView.h" #include "CoreController.h" +#include "MemoryAccessLogView.h" #include "MemoryDump.h" #include @@ -107,6 +108,9 @@ QValidator::State IntValidator::validate(QString& input, int&) const { MemoryView::MemoryView(std::shared_ptr controller, QWidget* parent) : QWidget(parent) , m_controller(controller) +#ifdef ENABLE_DEBUGGERS + , m_malModel(controller->memoryAccessLogController(), controller->platform()) +#endif { m_ui.setupUi(this); @@ -133,20 +137,10 @@ MemoryView::MemoryView(std::shared_ptr controller, QWidget* pare } } - connect(m_ui.width8, &QAbstractButton::clicked, [this]() { - m_ui.hexfield->setAlignment(1); - m_sintValidator.setWidth(1); - m_uintValidator.setWidth(1); - }); - connect(m_ui.width16, &QAbstractButton::clicked, [this]() { - m_ui.hexfield->setAlignment(2); - m_sintValidator.setWidth(2); - m_uintValidator.setWidth(2); - }); - connect(m_ui.width32, &QAbstractButton::clicked, [this]() { - m_ui.hexfield->setAlignment(4); - m_sintValidator.setWidth(4); - m_uintValidator.setWidth(4); + connect(m_ui.width, static_cast(&QComboBox::currentIndexChanged), [this](int index) { + m_ui.hexfield->setAlignment(1 << index); + m_sintValidator.setWidth(1 << index); + m_uintValidator.setWidth(1 << index); }); connect(m_ui.setAddress, static_cast(&QSpinBox::valueChanged), this, static_cast(&MemoryView::jumpToAddress)); @@ -199,6 +193,22 @@ MemoryView::MemoryView(std::shared_ptr controller, QWidget* pare } update(); }); + +#ifdef ENABLE_DEBUGGERS + connect(m_ui.hexfield, &MemoryModel::selectionChanged, &m_malModel, &MemoryAccessLogModel::updateSelection); + connect(m_ui.segments, static_cast(&QSpinBox::valueChanged), + &m_malModel, &MemoryAccessLogModel::setSegment); + connect(m_ui.accessLoggerButton, &QAbstractButton::clicked, this, [this]() { + std::weak_ptr controller = m_controller->memoryAccessLogController(); + MemoryAccessLogView* view = new MemoryAccessLogView(controller); + connect(m_controller.get(), &CoreController::stopping, view, &QWidget::close); + view->setAttribute(Qt::WA_DeleteOnClose); + view->show(); + }); + m_ui.accessLog->setModel(&m_malModel); +#else + m_ui.accessLog->hide(); +#endif } void MemoryView::setIndex(int index) { @@ -216,6 +226,10 @@ void MemoryView::setIndex(int index) { m_ui.segmentColon->setVisible(info.maxSegment > 0); m_ui.segments->setMaximum(info.maxSegment); m_ui.hexfield->setRegion(info.start, info.end - info.start, info.shortName); + +#ifdef ENABLE_DEBUGGERS + m_malModel.setRegion(info.start, info.segmentStart - info.start, info.maxSegment > 0); +#endif } void MemoryView::setSegment(int segment) { @@ -258,7 +272,7 @@ void MemoryView::updateStatus() { mCore* core = m_controller->thread()->core; QByteArray selection(m_ui.hexfield->serialize()); QString text(m_ui.hexfield->decodeText(selection)); - m_ui.stringVal->setText(text); + m_ui.stringVal->setPlainText(text); if (m_selection.first & (align - 1) || m_selection.second - m_selection.first != align) { m_ui.sintVal->clear(); diff --git a/src/platform/qt/MemoryView.h b/src/platform/qt/MemoryView.h index f68052a06..d6a5c6f93 100644 --- a/src/platform/qt/MemoryView.h +++ b/src/platform/qt/MemoryView.h @@ -8,6 +8,7 @@ #include #include "MemoryModel.h" +#include "MemoryAccessLogModel.h" #include "ui_MemoryView.h" @@ -54,6 +55,10 @@ private: std::shared_ptr m_controller; QPair m_region; QPair m_selection; + +#ifdef ENABLE_DEBUGGERS + MemoryAccessLogModel m_malModel; +#endif }; } diff --git a/src/platform/qt/MemoryView.ui b/src/platform/qt/MemoryView.ui index 45879db88..73407fdd1 100644 --- a/src/platform/qt/MemoryView.ui +++ b/src/platform/qt/MemoryView.ui @@ -6,167 +6,14 @@ 0 0 - 874 - 900 + 708 + 549 Memory - - - - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - Inspect Address: - - - - - - - -1 - - - 0 - - - 16 - - - - - - - : - - - - - - - true - - - 0x - - - 268435455 - - - 16 - - - 16 - - - - - - - - - - - Set Alignment: - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - &1 Byte - - - true - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - &2 Bytes - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - - - &4 Bytes - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - - + @@ -175,127 +22,296 @@ 0 + + + 200 + 0 + + - - - + + + + + + - + - Unsigned Integer: + Address: - - - 10 + + + Qt::Horizontal - + + + 0 + 0 + + + + + + + + -1 + + + 0 + + + 16 + + + + + + + : + + + + + + + + 100 + 0 + + + true + + 0x + + + 268435455 + + + 16 + + + 16 + - - + + - + - Signed Integer: + Alignment: - - - 11 + + + + 0 + 0 + - - true + + + 1 Byte + + + + + 2 Bytes + + + + + 4 Bytes + + + + + + + + + + + 0 + 0 + + + + + + + + + + Signed: + + + + + + + 11 + + + true + + + + + + + + + + Unsigned: + + + + + + + 10 + + + true + + + + + + + + + + String: + + + + + + + + 0 + 0 + + + + Load TBL + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + Copy Selection + + + + + + + Save Selection + + + + + + + Paste + + + + + + + Save Range + + + + + + + + 0 + 0 + + + + Load - - - - - - String: - - - - - - - true - - - - - - - Load TBL - - - - - - - - - - - - Copy Selection + + + + 0 + 0 + - - - - - - Paste - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Save Selection - - - - - - - Save Range - - - - - - - Load + + Selected address accesses + + + + + + 0 + 0 + + + + Qt::ElideNone + + + false + + + + + + + Logging configuration + + + + @@ -310,23 +326,6 @@ 1 - - regions - segments - setAddress - width8 - width16 - width32 - sintVal - uintVal - stringVal - loadTBL - copy - paste - save - saveRange - load - diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index bc956fcc4..a224c3ba4 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -972,7 +972,12 @@ void Window::gameStopped() { #endif } - m_controller.reset(); + std::shared_ptr controller; + m_controller.swap(controller); + QTimer::singleShot(0, this, [controller]() { + // Destroy the controller after everything else has cleaned up + Q_UNUSED(controller); + }); detachWidget(); updateTitle(); @@ -1767,7 +1772,12 @@ void Window::setupMenu(QMenuBar* menubar) { addGameAction(tr("View &I/O registers..."), "ioViewer", openControllerTView(), "stateViews"); #ifdef ENABLE_DEBUGGERS - addGameAction(tr("Log memory &accesses..."), "memoryAccessView", openControllerTView(), "tools"); + addGameAction(tr("Log memory &accesses..."), "memoryAccessView", [this]() { + std::weak_ptr controller = m_controller->memoryAccessLogController(); + MemoryAccessLogView* view = new MemoryAccessLogView(controller); + connect(m_controller.get(), &CoreController::stopping, view, &QWidget::close); + openView(view); + }, "tools"); #endif #if defined(USE_FFMPEG) && defined(M_CORE_GBA) @@ -2150,11 +2160,6 @@ void Window::setController(CoreController* controller, const QString& fname) { connect(m_controller.get(), &CoreController::started, this, &Window::gameStarted); 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, GBAApp::app(), &GBAApp::resumeScreensaver); connect(m_controller.get(), &CoreController::paused, this, &Window::updateFrame); diff --git a/src/platform/qt/main.cpp b/src/platform/qt/main.cpp index f98d2cd88..48f1984f7 100644 --- a/src/platform/qt/main.cpp +++ b/src/platform/qt/main.cpp @@ -131,10 +131,9 @@ int main(int argc, char* argv[]) { } Window* w = application.newWindow(); - w->loadConfig(); w->argumentsPassed(); - w->show(); + application.initMultiplayer(); int ret = application.exec(); if (ret != 0) { diff --git a/src/platform/qt/ts/mgba-de.ts b/src/platform/qt/ts/mgba-de.ts index b430e2758..726f511d6 100644 --- a/src/platform/qt/ts/mgba-de.ts +++ b/src/platform/qt/ts/mgba-de.ts @@ -387,48 +387,48 @@ Download-Größe: %3 QGBA::CoreController - + Reset r%1-%2 %3 r%1-%2 %3 zurücksetzen - - + + Rewinding not currently enabled Zurückspulen ist derzeit nicht aktiviert - + Reset the game? Spiel zurücksetzen? - + Most games will require a reset to load the new save. Do you want to reset now? Die meisten Spiele müssen zurückgesetzt werden, um einen neuen Spielstand zu laden. Möchtest Du das Spiel jetzt zurücksetzen? - + Failed to open save file: %1 Fehler beim Öffnen der Speicherdatei: %1 - + Failed to open game file: %1 Fehler beim Öffnen der Spieldatei: %1 - + Can't yank pack in unexpected platform! 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 @@ -3403,6 +3403,15 @@ Download-Größe: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Download-Größe: %3 Stopp - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6187,7 +6191,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may ROM ersetzen... - + Convert e-Reader card image to raw... Lesegerät-Kartenbild in Rohdaten umwandeln … @@ -6498,7 +6502,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Clear Leeren @@ -6714,77 +6718,77 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may &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 316c5d2c3..20b0fe65b 100644 --- a/src/platform/qt/ts/mgba-en.ts +++ b/src/platform/qt/ts/mgba-en.ts @@ -381,48 +381,48 @@ Download size: %3 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 @@ -3397,6 +3397,15 @@ Download size: %3 + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3445,18 +3454,13 @@ Download size: %3 - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6230,7 +6234,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... @@ -6701,82 +6705,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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 65223278e..6aa86cb78 100644 --- a/src/platform/qt/ts/mgba-es.ts +++ b/src/platform/qt/ts/mgba-es.ts @@ -387,48 +387,48 @@ Tamaño de descarga: %3 QGBA::CoreController - + Reset r%1-%2 %3 Reiniciar r%1-%2 %3 - - + + Rewinding not currently enabled Rebobinado desactivado actualmente - + Reset the game? ¿Reiniciar el juego? - + Most games will require a reset to load the new save. Do you want to reset now? La mayoría de juegos requieren reiniciar para cargar la nueva partida guardada. ¿Quieres reiniciar ahora? - + Failed to open save file: %1 Error al abrir el archivo de guardado: %1 - + Failed to open game file: %1 Error al abrir el archivo del juego: %1 - + Can't yank pack in unexpected platform! ¡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 @@ -3403,6 +3403,15 @@ Tamaño de descarga: %3 Gráficos de red portátiles (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + Error al abrir el archivo de registro de memoria + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Tamaño de descarga: %3 Detener - - Failed to open memory log file - Error al abrir el archivo de registro de memoria - - - - + + Select access log file Seleccionar archivo de registro de acceso - + Memory access logs (*.mal) Archivos de registro de memoria (*.mal) @@ -3829,12 +3833,12 @@ Tamaño de descarga: %3 Clearing invalid save ID - + Borrando identificador de guardado inválido Clearing invalid preferred ID - + Borrando identificador inválido preferido @@ -4367,7 +4371,7 @@ Tamaño de descarga: %3 File information - + Información de archivo @@ -4377,12 +4381,12 @@ Tamaño de descarga: %3 MD5 - + MD5 ROM header - + Cabecera de ROM @@ -4857,7 +4861,9 @@ Tamaño de descarga: %3 Shaders are not supported when the display driver is not OpenGL. If it is set to OpenGL and you still see this, your graphics card or drivers may be too old. - + Los shader no están disponibles cuando el controlador de pantalla no es OpenGL. + +Si está configurado como OpenGL y estás viendo esto, tu tarjeta gráfica o controladores son demasiado antiguos. @@ -5666,17 +5672,17 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may mGBA Shaders - + Shaders de mGBA Error loading shader - + Error al cargar shader The shader "%1" could not be loaded successfully. - + El shader "%1" falló al cargarse. @@ -6222,7 +6228,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Sensores del cartucho... - + Clear Limpiar @@ -6283,7 +6289,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Cargar partida guardada temporal... - + Convert e-Reader card image to raw... Convertir imagen de tarjeta e-Reader a archivo en bruto... @@ -6714,77 +6720,77 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Registrar &accesoa la memoria… - + 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 diff --git a/src/platform/qt/ts/mgba-fr.ts b/src/platform/qt/ts/mgba-fr.ts index 04411e5c3..628941e50 100644 --- a/src/platform/qt/ts/mgba-fr.ts +++ b/src/platform/qt/ts/mgba-fr.ts @@ -388,48 +388,48 @@ Taille du téléchargement : %3 QGBA::CoreController - + Reset r%1-%2 %3 Réinitialiser r%1-%2 %3 - - + + Rewinding not currently enabled Le rembobinage n'est pas actuellement activé - + Reset the game? Réinitialiser le jeu ? - + Most games will require a reset to load the new save. Do you want to reset now? La plupart des jeux nécessitent une réinitialisation pour charger la nouvelle sauvegarde. Voulez-vous réinitialiser maintenant ? - + Failed to open save file: %1 Échec de l'ouverture du fichier de sauvegarde : %1 - + Failed to open game file: %1 Échec de l'ouverture du fichier de jeu : %1 - + Can't yank pack in unexpected platform! - + 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 @@ -3421,6 +3421,15 @@ Taille du téléchargement : %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3469,18 +3478,13 @@ Taille du téléchargement : %3 Arrêter - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6241,7 +6245,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... @@ -6728,82 +6732,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may 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 b7da40d8c..95f654c8a 100644 --- a/src/platform/qt/ts/mgba-hu.ts +++ b/src/platform/qt/ts/mgba-hu.ts @@ -388,48 +388,48 @@ Letöltendő adat: %3 QGBA::CoreController - + Reset r%1-%2 %3 - - + + Rewinding not currently enabled Visszatekerés jelenleg nem engedélyezett - + 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 Nem sikerült a mentésfájl megnyitása: %1 - + Failed to open game file: %1 Nem sikerült a játékfájl megnyitása: %1 - + Can't yank pack in unexpected platform! 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 @@ -3404,6 +3404,15 @@ Letöltendő adat: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3452,18 +3461,13 @@ Letöltendő adat: %3 - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6235,7 +6239,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... @@ -6706,82 +6710,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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 6aaf5d809..f12794bb1 100644 --- a/src/platform/qt/ts/mgba-it.ts +++ b/src/platform/qt/ts/mgba-it.ts @@ -387,48 +387,48 @@ Dimensione del download: %3 QGBA::CoreController - + Reset r%1-%2 %3 Reset r%1-%2 %3 - - + + Rewinding not currently enabled La funzione 'riavvolgi' non è attualmente abilitata - + Reset the game? Riavviare il gioco? - + Most games will require a reset to load the new save. Do you want to reset now? La maggior parte dei giochi richiede un riavvio per caricare il nuovo salvataggio. Vuoi riavviare ora? - + Failed to open save file: %1 Impossibile aprire il file di salvataggio: %1 - + Failed to open game file: %1 Impossibile aprire il file di gioco: %1 - + Can't yank pack in unexpected platform! 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 @@ -3403,6 +3403,15 @@ Dimensione del download: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + Impossibile aprire il file di registro della memoria + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Dimensione del download: %3 Ferma - - Failed to open memory log file - Impossibile aprire il file di registro della memoria - - - - + + Select access log file Seleziona il file di registro di accesso - + Memory access logs (*.mal) Registri degli accessi alla memoria (*.mal) @@ -6187,7 +6191,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Scansiona e-Reader dotcode... - + Convert e-Reader card image to raw... Converti immagini carte e-Reader in raw... @@ -6709,82 +6713,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Registra memoria &accessi... - + Record debug video log... Salva registro video di debug... - + Stop debug video log Ferma registro video 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 8a2425753..5d4fa2a78 100644 --- a/src/platform/qt/ts/mgba-ja.ts +++ b/src/platform/qt/ts/mgba-ja.ts @@ -387,48 +387,48 @@ Download size: %3 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 セーブファイルを開けませんでした: %1 - + Failed to open game file: %1 ゲームファイルを開けませんでした: %1 - + Can't yank pack in unexpected platform! 予期しないプラットフォームでパックをヤンクすることはできません! - + Failed to open snapshot file for reading: %1 読み取り用のスナップショットファイルを開けませんでした: %1 - + Failed to open snapshot file for writing: %1 書き込み用のスナップショットファイルを開けませんでした: %1 @@ -3403,6 +3403,15 @@ Download size: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Download size: %3 停止 - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6225,7 +6229,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may カートリッジセンサー... - + Clear 消去 @@ -6286,7 +6290,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... @@ -6712,77 +6716,77 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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 e6363dd49..18fc7ea44 100644 --- a/src/platform/qt/ts/mgba-ko.ts +++ b/src/platform/qt/ts/mgba-ko.ts @@ -387,48 +387,48 @@ Download size: %3 QGBA::CoreController - + Reset r%1-%2 %3 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 저장 파일을 열지 못했습니다: %1 - + Failed to open game file: %1 게임 파일을 열지 못했습니다: %1 - + Can't yank pack in unexpected platform! 예기치 않은 플랫폼에서 팩을 잡아당길 수 없습니다! - + Failed to open snapshot file for reading: %1 읽기 용 스냅샷 파일을 열지 못했습니다: %1 - + Failed to open snapshot file for writing: %1 쓰기 용 스냅샷 파일을 열지 못했습니다: %1 @@ -3403,6 +3403,15 @@ Download size: %3 휴대용 네트워크 그래픽 (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + 메모리 로그 파일을 열 수 없음 + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Download size: %3 정지 - - Failed to open memory log file - 메모리 로그 파일을 열 수 없음 - - - - + + Select access log file 접속 파일 선택 - + Memory access logs (*.mal) 메모리 접속 로그 (*.mal) @@ -6215,7 +6219,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may 게임 상태 보기 - + Convert e-Reader card image to raw... e-리더 카드 이미지를 원시 데이터로 변환... @@ -6707,82 +6711,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may 메모리 및 접속 기록... - + 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 d8b2f9b09..077aad1e6 100644 --- a/src/platform/qt/ts/mgba-ms.ts +++ b/src/platform/qt/ts/mgba-ms.ts @@ -381,48 +381,48 @@ Download size: %3 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 Gagal membuka fail tersimpan: %1 - + Failed to open game file: %1 Gagal membuka fail permainan: %1 - + Can't yank pack in unexpected platform! - + 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 @@ -3397,6 +3397,15 @@ Download size: %3 Grafik Rangkaian Mudah Alih (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3445,18 +3454,13 @@ Download size: %3 Henti - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6230,7 +6234,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... @@ -6701,82 +6705,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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 f62f3793a..3042bccf2 100644 --- a/src/platform/qt/ts/mgba-nb_NO.ts +++ b/src/platform/qt/ts/mgba-nb_NO.ts @@ -385,48 +385,48 @@ Nedlastningsstørrelse: %3 QGBA::CoreController - + Reset r%1-%2 %3 - - + + Rewinding not currently enabled - + Reset the game? Vil du starte spillet på nytt? - + 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 Klarte ikke å åpne spillfil: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 - + Failed to open snapshot file for writing: %1 @@ -3401,6 +3401,15 @@ Nedlastningsstørrelse: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3449,18 +3458,13 @@ Nedlastningsstørrelse: %3 Stopp - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6234,7 +6238,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Skann e-Reader-punktkoder... - + Convert e-Reader card image to raw... @@ -6705,82 +6709,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Record debug video log... - + Stop debug video log - + Exit fullscreen Gå ut av fullskjerm - + 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 e4b191608..15b04da90 100644 --- a/src/platform/qt/ts/mgba-pl.ts +++ b/src/platform/qt/ts/mgba-pl.ts @@ -387,48 +387,48 @@ Rozmiar pobierania: %3 QGBA::CoreController - + Reset r%1-%2 %3 Reset r%1-%2 %3 - - + + Rewinding not currently enabled Przewijanie nie jest obecnie włączone - + Reset the game? Zresetować grę? - + Most games will require a reset to load the new save. Do you want to reset now? Większość gier wymaga zresetowania, aby wczytać nowy zapis. Czy chcesz teraz zresetować? - + Failed to open save file: %1 Nie udało się otworzyć pliku zapisu: %1 - + Failed to open game file: %1 Nie udało się otworzyć pliku gry: %1 - + Can't yank pack in unexpected platform! Nie można wyciągnąć pack na nieoczekiwanej platformie! - + Failed to open snapshot file for reading: %1 Nie udało się otworzyć pliku snapshot do odczytu: %1 - + Failed to open snapshot file for writing: %1 Nie udało się otworzyć pliku snapshot do zapisu: %1 @@ -3403,17 +3403,26 @@ Rozmiar pobierania: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + Błąd odczytu pliku logów pamięci + + QGBA::MemoryAccessLogView Memory access logging - + Logowanie dostępu do pamięci Log file - + Plik logów @@ -3423,7 +3432,7 @@ Rozmiar pobierania: %3 Log additional information (uses 3× space) - + Loguj dodatkowe informacje (używa 3× miejsca) @@ -3433,12 +3442,12 @@ Rozmiar pobierania: %3 Regions - + Regiony Export ROM snapshot - + Eksportuj zrzut ROMu @@ -3451,18 +3460,13 @@ Rozmiar pobierania: %3 Stop - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -4367,7 +4371,7 @@ Rozmiar pobierania: %3 File information - + Informacje o pliku @@ -4377,12 +4381,12 @@ Rozmiar pobierania: %3 MD5 - + MD5 ROM header - + Nagłówek ROMu @@ -4402,7 +4406,7 @@ Rozmiar pobierania: %3 Revision: - + Rewizja: @@ -5668,12 +5672,12 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may mGBA Shaders - + Shadery mGBA Error loading shader - + Błąd wczytywania shaderu @@ -6240,7 +6244,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Skanuj kody kropkowe czytnika e-Reader... - + Convert e-Reader card image to raw... Konwertuj obraz karty czytnika e-Reader na surowy... @@ -6711,82 +6715,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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 e132eaeb3..dbe2b3b46 100644 --- a/src/platform/qt/ts/mgba-pt_BR.ts +++ b/src/platform/qt/ts/mgba-pt_BR.ts @@ -387,48 +387,48 @@ Tamanho do download: %3 QGBA::CoreController - + Reset r%1-%2 %3 Resetar r%1-%2 %3 - - + + Rewinding not currently enabled O rebobinamento não está ativado atualmente - + Reset the game? Resetar o jogo? - + Most games will require a reset to load the new save. Do you want to reset now? A maioria dos jogos requerirão um reset pra carregar o novo save. Você quer resetar agora? - + Failed to open save file: %1 Falhou em abrir o arquivo do save: %1 - + Failed to open game file: %1 Falhou em abrir o arquivo do jogo: %1 - + Can't yank pack in unexpected platform! Não pode arrancar o pacote numa plataforma inesperada! - + Failed to open snapshot file for reading: %1 Falhou em abrir o arquivo do snapshot pra leitura: %1 - + Failed to open snapshot file for writing: %1 Falhou em abrir o arquivo do snapshot pra gravação: %1 @@ -3262,7 +3262,7 @@ Tamanho do download: %3 Info - Informações + Info @@ -3403,6 +3403,15 @@ Tamanho do download: %3 Gráficos Portáteis da Rede (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + Falhou em abrir o arquivo do registro da memória + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Tamanho do download: %3 Parar - - Failed to open memory log file - Falhou em abrir o arquivo do registro da memória - - - - + + Select access log file Selecionar o arquivo de registro do acesso - + Memory access logs (*.mal) Registros de acesso a memória (*.mal) @@ -3829,12 +3833,12 @@ Tamanho do download: %3 Clearing invalid save ID - + Limpando a ID inválida do save Clearing invalid preferred ID - + Limpando a ID inválida preferida @@ -4367,7 +4371,7 @@ Tamanho do download: %3 File information - + Informação do arquivo @@ -4377,12 +4381,12 @@ Tamanho do download: %3 MD5 - + MD5 ROM header - + Cabeçalho da ROM @@ -4857,7 +4861,9 @@ Tamanho do download: %3 Shaders are not supported when the display driver is not OpenGL. If it is set to OpenGL and you still see this, your graphics card or drivers may be too old. - + Os shaders não são suportados quando o driver de exibição não é OpenGL. + +Se ele está definido como OpenGL e você ainda ver isto sua placa gráfica ou drivers podem ser muito antigos. @@ -5666,17 +5672,17 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may mGBA Shaders - + Shaders do mGBA Error loading shader - + Erro ao carregar o shader The shader "%1" could not be loaded successfully. - + O shader "%1" não pôde ser carregado com sucesso. @@ -6222,7 +6228,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Sensores do Game Pak... - + Clear Limpar @@ -6283,7 +6289,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Carregar save temporário do jogo... - + Convert e-Reader card image to raw... Converter a imagem do cartão do e-Reader pro natural... @@ -6714,77 +6720,77 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Registrar os acessos a &memória... - + 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 (pressionado) - + 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-pt_PT.ts b/src/platform/qt/ts/mgba-pt_PT.ts index 5f4a2e76f..7953a6d35 100644 --- a/src/platform/qt/ts/mgba-pt_PT.ts +++ b/src/platform/qt/ts/mgba-pt_PT.ts @@ -387,48 +387,48 @@ Tamanho da descarga: %3 QGBA::CoreController - + Reset r%1-%2 %3 Resetar r%1-%2 %3 - - + + Rewinding not currently enabled O rebobinamento não está ativado atualmente - + Reset the game? Resetar o jogo? - + Most games will require a reset to load the new save. Do you want to reset now? A maioria dos jogos requerirão um reset para carregar o novo save. Quer resetar agora? - + Failed to open save file: %1 Falha ao abrir o ficheiro dde gravação: %1 - + Failed to open game file: %1 Falha ao abrir o ficheiro do jogo: %1 - + Can't yank pack in unexpected platform! Não pode arrancar o pacote numa plataforma inesperada! - + Failed to open snapshot file for reading: %1 Falha ao abrir o ficheiro do snapshot para leitura: %1 - + Failed to open snapshot file for writing: %1 Falha ao abrir o ficheiro do snapshot para gravação: %1 @@ -3403,6 +3403,15 @@ Tamanho da descarga: %3 Gráficos Portáteis da Rede (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Tamanho da descarga: %3 Parar - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6238,7 +6242,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Escanear dotcodes do e-Reader... - + Convert e-Reader card image to raw... Converter imagem do cartão do e-Reader para natural... @@ -6709,82 +6713,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Record debug video log... Gravar registo do vídeo de debug... - + Stop debug video log Parar o registo do vídeo de debug - + Exit fullscreen Sair do ecrã inteiro - + 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 Para Cima - + Autofire Right Auto-disparar Direita - + Autofire Down Auto-disparar Para Baixo - + Autofire Left Auto-disparar Esquerda - + Clear Limpar diff --git a/src/platform/qt/ts/mgba-ru.ts b/src/platform/qt/ts/mgba-ru.ts index 35b3f0ecb..40cd31946 100644 --- a/src/platform/qt/ts/mgba-ru.ts +++ b/src/platform/qt/ts/mgba-ru.ts @@ -387,48 +387,48 @@ Download size: %3 QGBA::CoreController - + Reset r%1-%2 %3 Сброс 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 Не удалось открыть файл сохранения: %1 - + Failed to open game file: %1 Не удалось открыть файл игры: %1 - + Can't yank pack in unexpected platform! Невозможно пнуть картридж на неожиданной платформе! - + Failed to open snapshot file for reading: %1 Не удалось открыть файл изображения для считывания: %1 - + Failed to open snapshot file for writing: %1 Не удалось открыть файл изображения для записи: %1 @@ -3403,6 +3403,15 @@ Download size: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Download size: %3 - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6240,7 +6244,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Сканировать dot-коды e-Reader... - + Convert e-Reader card image to raw... Конвертировать карту e-Reader в raw... @@ -6711,82 +6715,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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-sv.ts b/src/platform/qt/ts/mgba-sv.ts index b0ddef6a8..49fdcb432 100644 --- a/src/platform/qt/ts/mgba-sv.ts +++ b/src/platform/qt/ts/mgba-sv.ts @@ -387,48 +387,48 @@ Hämtningsstorlek: %3 QGBA::CoreController - + Reset r%1-%2 %3 Starta om r%1-%2 %3 - - + + Rewinding not currently enabled - + Reset the game? Starta om spelet? - + Most games will require a reset to load the new save. Do you want to reset now? De flesta spel kommer att kräva en omstart för att läsa in ny sparning. Vill du starta om nu? - + Failed to open save file: %1 Misslyckades med att öppna sparad fil: %1 - + Failed to open game file: %1 Misslyckades med att öppna spelfil: %1 - + Can't yank pack in unexpected platform! - + Failed to open snapshot file for reading: %1 Misslyckades med att öppna ögonblicksfil för läsning: %1 - + Failed to open snapshot file for writing: %1 Misslyckades med att öppna ögonblicksfil för skrivning: %1 @@ -1158,7 +1158,7 @@ Hämtningsstorlek: %3 B - + B @@ -2742,13 +2742,13 @@ Hämtningsstorlek: %3 Serial - + Serienummer Joypad - + Joypad @@ -2902,28 +2902,28 @@ Hämtningsstorlek: %3 Color 0 shade - + Färg 0-skugga Color 1 shade - + Färg 1-skugga Color 2 shade - + Färg 2-skugga Color 3 shade - + Färg 3-skugga @@ -2938,7 +2938,7 @@ Hämtningsstorlek: %3 VRAM bank - + VRAM-bank @@ -3348,7 +3348,7 @@ Hämtningsstorlek: %3 Offset - Förskjut + Marginal @@ -3403,6 +3403,15 @@ Hämtningsstorlek: %3 Portable Network Graphics (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + Misslyckades med att öppna minnesloggfil + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Hämtningsstorlek: %3 Stoppa - - Failed to open memory log file - Misslyckades med att öppna minnesloggfil - - - - + + Select access log file Välj åtkomstloggfil - + Memory access logs (*.mal) Minnesåtkomstloggar (*.mal) @@ -3482,7 +3486,7 @@ Hämtningsstorlek: %3 Byte Count: - + Antal byte: @@ -4030,7 +4034,7 @@ Hämtningsstorlek: %3 Game Overrides - + Spelförbättringar @@ -4118,7 +4122,7 @@ Hämtningsstorlek: %3 Game Boy Player features - + Game Boy Player-funktioner @@ -4138,7 +4142,7 @@ Hämtningsstorlek: %3 Memory bank controller - + Minnesbankskontroller @@ -4148,12 +4152,12 @@ Hämtningsstorlek: %3 Sprite Colors 1 - + Spritefärger 1 Sprite Colors 2 - + Spritefärger 2 @@ -4367,7 +4371,7 @@ Hämtningsstorlek: %3 File information - + Filinformation @@ -4377,12 +4381,12 @@ Hämtningsstorlek: %3 MD5 - + MD5 ROM header - + ROM-header @@ -4440,7 +4444,7 @@ Hämtningsstorlek: %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>För att rapportera en bugg eller fel så måste du generera en rapportfil att lägga till i felrapporten som du håller på att skicka in. Det rekommenderas att du inkluderar sparade filer eftersom dessa hjälper till att felsöka problem. Detta kommer att samla in viss information om versionen av {projectName} som du använder, din konfiguration, din dator samt spelet som du för närvarande har öppet (om något). När detta har samlats in så kan du granska all information nedan och spara det till en zip-fil. Personlig information kommer att undantas från den samlade informationen om det går, så som ditt användarnamn om det finns i någon sökväg, men du kan även redigera det i efterhand. Efter att du har genererat och sparat det, klicka på knappen nedan eller gå till <a href="https://mgba.io/i/"><span style=" text-decoration: underline; color:#2980b9;">mgba.io/i</span></a> för att skicka in felrapporten på GitHub. Försäkra dig om att lägga till rapporten som du genererat!</p></body></html> @@ -4779,13 +4783,13 @@ Hämtningsstorlek: %3 Set Y - + Ställ in Y Set X - + Ställ in X @@ -5122,7 +5126,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may FPS target: - + Bilder/s: @@ -5350,7 +5354,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Periodically autosave state - + Autospara tillstånd periodiskt @@ -5360,7 +5364,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Show frame count in OSD - + Visa bilder/s i OSD @@ -5370,7 +5374,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Custom border: - + Anpassad ram: @@ -5381,32 +5385,32 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Unbounded - Obunden + Obunden Fast forward (held) speed: - + Hastighet för snabbspolning (håll): Autofire interval: - + Intervall för autofire: Enable rewind - + Aktivera tillbakaspolning Rewind history: - + Historik för tillbakaspolning: Rewind speed: - + Hastighet för tillbakaspolning: @@ -5436,7 +5440,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Save state extra data: - + Extradata för sparade tillstånd: @@ -5447,7 +5451,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Load state extra data: - + Extradata för inlästa tillstånd: @@ -5647,7 +5651,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Super Game Boy borders - + Super Game Boy-ramar @@ -5666,17 +5670,17 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may mGBA Shaders - + mGBA-shaders Error loading shader - + Fel vid inläsning av shader The shader "%1" could not be loaded successfully. - + Shadern "%1" kunde inte läsas in korrekt. @@ -5726,7 +5730,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Unload Shader - + Läs ur shader @@ -6035,7 +6039,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Select e-Reader dotcode - + Välj e-Reader-punktkod @@ -6094,17 +6098,17 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Unimplemented BIOS call - + Ej implementerat BIOS-anrop This game uses a BIOS call that is not implemented. Please use the official BIOS for best experience. - + Detta spel använder ett BIOS-anrop som inte är implementerat. Använd ett officiellt BIOS för bästa upplevelse. Failed to create an appropriate display device, falling back to software display. Games may run slowly, especially with larger windows. - + Misslyckades med att skapa en lämplig visningsenhet, faller tillbaka på programvaruvisning. Spel kan bli långsamma, speciellt med större fönster. @@ -6238,7 +6242,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Skanna e-Reader-punktkoder... - + Convert e-Reader card image to raw... Konvertera e-Reader-kortavbilder till raw... @@ -6556,7 +6560,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Frame&skip - + Hoppa över &bildrutor @@ -6566,7 +6570,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may FPS target - + Bilder/s @@ -6621,7 +6625,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Game &overrides... - + Spel&förbättringar... @@ -6636,7 +6640,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Create forwarder... - + Skapa forwarder... @@ -6661,7 +6665,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Game state views - + Visa speltillstånd @@ -6709,82 +6713,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Logga minneså&tkomster... - + Record debug video log... Spela in felsökningsvideologg... - + Stop debug video log Stoppa felsökningsvideologg - + Exit fullscreen Avsluta helskärm - + GameShark Button (held) - + GameShark-knapp (håll) - + Autofire - - - - - Autofire A - + Autofire - Autofire B - + Autofire A + Autofire A - Autofire L - + Autofire B + Autofire B - Autofire R - + Autofire L + Autofire L - Autofire Start - + Autofire R + Autofire R - Autofire Select - + Autofire Start + Autofire Start - Autofire Up - + Autofire Select + Autofire Select - Autofire Right - + Autofire Up + Autofire upp - Autofire Down - + Autofire Right + Autofire höger - Autofire Left - + Autofire Down + Autofire ner - + + Autofire Left + Autofire vänster + + + Clear Töm diff --git a/src/platform/qt/ts/mgba-template.ts b/src/platform/qt/ts/mgba-template.ts index 0155d9c06..77294e94d 100644 --- a/src/platform/qt/ts/mgba-template.ts +++ b/src/platform/qt/ts/mgba-template.ts @@ -381,48 +381,48 @@ Download size: %3 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 @@ -3397,6 +3397,15 @@ Download size: %3 + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3445,18 +3454,13 @@ Download size: %3 - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6228,7 +6232,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... @@ -6699,82 +6703,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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 aaafaf988..6db0c47a6 100644 --- a/src/platform/qt/ts/mgba-tr.ts +++ b/src/platform/qt/ts/mgba-tr.ts @@ -387,48 +387,48 @@ Yeni sürüm: %2 QGBA::CoreController - + Reset r%1-%2 %3 - - + + Rewinding not currently enabled - + Reset the game? Oyun sıfırlansım mı? - + Most games will require a reset to load the new save. Do you want to reset now? - + Failed to open save file: %1 Kayıt dosyası açılamadı: %1 - + Failed to open game file: %1 Oyun dosyası açılamadı: %1 - + Can't yank pack in unexpected platform! Beklenmedik bir platformda kartı çıkaramazsın! - + Failed to open snapshot file for reading: %1 Anlık görüntü dosyası okuma için açılamadı: %1 - + Failed to open snapshot file for writing: %1 Anlık görüntü dosyası yazma için açılamadı: %1 @@ -3403,6 +3403,15 @@ Yeni sürüm: %2 + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Yeni sürüm: %2 Durdur - - Failed to open memory log file - - - - - + + Select access log file - + Memory access logs (*.mal) @@ -6220,7 +6224,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + Convert e-Reader card image to raw... e-Okuyucu kart resimlerini rawa dönüştür... @@ -6707,82 +6711,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may &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 a051d76fb..0518b256b 100644 --- a/src/platform/qt/ts/mgba-zh_CN.ts +++ b/src/platform/qt/ts/mgba-zh_CN.ts @@ -387,48 +387,48 @@ Download size: %3 QGBA::CoreController - + Reset r%1-%2 %3 重置 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 打开存档失败: %1 - + Failed to open game file: %1 打开游戏文件失败: %1 - + Can't yank pack in unexpected platform! 无法在意外平台上抽出卡带! - + Failed to open snapshot file for reading: %1 读取快照文件失败: %1 - + Failed to open snapshot file for writing: %1 写入快照文件失败: %1 @@ -3403,6 +3403,15 @@ Download size: %3 便携式网络图形 (*.png) + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + 打开内存日志文件失败 + + QGBA::MemoryAccessLogView @@ -3451,18 +3460,13 @@ Download size: %3 停止 - - Failed to open memory log file - 打开内存日志文件失败 - - - - + + Select access log file 选择访问日志文件 - + Memory access logs (*.mal) 内存访问日志(*.mal) @@ -3829,17 +3833,17 @@ Download size: %3 Clearing invalid save ID - + 清理非法存档ID Clearing invalid preferred ID - + 清理非法优先ID Trying to get player ID for a multiplayer player that's not attached - 尝试获取未连接的多人玩家的 ID + 尝试获取多人联机中未连接上的玩家ID @@ -6236,7 +6240,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may 扫描 e-Reader 点码... - + Convert e-Reader card image to raw... 将 e-Reader 卡片图像转换为原始数据... @@ -6707,82 +6711,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may 记录内存访问(&a)... - + 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-zh_Hant.ts b/src/platform/qt/ts/mgba-zh_Hant.ts index 8cbb231b5..e764abb92 100644 --- a/src/platform/qt/ts/mgba-zh_Hant.ts +++ b/src/platform/qt/ts/mgba-zh_Hant.ts @@ -6,22 +6,22 @@ Game Boy Advance ROMs (%1) - Game Boy Advance ROMs (%1) + Game Boy Advance ROMs (%1) Game Boy ROMs (%1) - Game Boy ROMs (%1) + Game Boy ROMs (%1) All ROMs (%1) - 所有 ROMs (%1) + 所有 ROMs (%1) %1 Video Logs (*.mvl) - %1 影片記錄檔 (*.mvl) + %1 影片記錄檔 (*.mvl) @@ -29,7 +29,7 @@ About - 關於 + 關於 @@ -193,17 +193,17 @@ Download size: %3 Can't set format of context-less audio device - + 無法設定無上下文音訊設備的格式 Audio device is missing its core - + 音訊設備缺少其核心 Writing data to read-only audio device - + 將資料寫入唯讀音訊設備 @@ -211,7 +211,7 @@ Download size: %3 Can't start an audio processor without input - + 無輸入時無法啟動音訊處理器 @@ -219,7 +219,7 @@ Download size: %3 Can't start an audio processor without input - + 無輸入時無法啟動音訊處理器 @@ -242,12 +242,12 @@ Download size: %3 Save - + 儲存 Load - + 載入 @@ -287,28 +287,28 @@ Download size: %3 BattleChip data missing - + 缺少 BattleChip 數據 BattleChip data is missing. BattleChip Gates will still work, but some graphics will be missing. Would you like to download the data now? - + 缺少 BattleChip 數據。 BattleChip Gate 仍然可以使用,但一些圖形將會遺失。您要立即下載資料嗎? Select deck file - + 選擇卡座文件 Incompatible deck - + 不相容的卡座 The selected deck is not compatible with this Chip Gate - + 所選卡座與此 Chip Gate 不相容 @@ -341,7 +341,7 @@ Download size: %3 Some cheats could not be added. Please ensure they're formatted correctly and/or try other cheat types. - + 無法新增某些作弊碼。請確認它們的格式是否正確並且/或嘗試其它的作弊類型。 @@ -356,12 +356,12 @@ Download size: %3 Load - + 載入 Save - + 儲存 @@ -387,48 +387,48 @@ Download size: %3 QGBA::CoreController - + Reset r%1-%2 %3 - + 重置 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 存檔開啟失敗: %1 - + Failed to open game file: %1 遊戲開啟失敗: %1 - + Can't yank pack in unexpected platform! 無法在預料外的平台拔除卡帶! - + Failed to open snapshot file for reading: %1 讀取快照失敗: %1 - + Failed to open snapshot file for writing: %1 寫入快照失敗: %1 @@ -474,7 +474,7 @@ Download size: %3 Could not open CLI history for writing - + 無法開啟用於寫入的 CLI 歷史 @@ -482,7 +482,7 @@ Download size: %3 Failed to create an OpenGL 3 context, trying old-style... - + 建立OpenGL3環境失敗,請嘗試舊形式... @@ -525,12 +525,12 @@ Download size: %3 Couldn't Connect - + 無法連接 Could not connect to Dolphin. - + 無法連線到 Dolphin。 @@ -538,12 +538,12 @@ Download size: %3 3DS - + 3DS Vita - + Vita @@ -551,12 +551,12 @@ Download size: %3 Icon - + 圖示 Banner - + 橫幅 @@ -564,7 +564,7 @@ Download size: %3 Bubble - + 氣泡 @@ -574,7 +574,7 @@ Download size: %3 Startup - + 啟動 @@ -582,17 +582,17 @@ Download size: %3 Create forwarder - + 建立轉發器 Files - + 文件 ROM file: - + ROM 文件: @@ -604,127 +604,127 @@ Download size: %3 Output filename: - + 輸出檔名: Forwarder base: - + 轉發器基礎: Latest stable version - + 最新穩定版本 Latest development build - + 最新開發版本 Specific file - + 指定文件 Base file: - + 基礎文件: System - + 系統 3DS - + 3DS Vita - + Vita Presentation - + 介紹 Title: - + 標題: Images: - + 圖像: Use default image - + 使用預設影像 Preferred size: - + 首選大小: Select image file - + 選擇圖像文件 Select ROM file - + 選擇ROM文件 Select output filename - + 選擇輸出檔名 Select base file - + 選擇基礎文件 Build finished - + 已完成建置 Forwarder finished building - + 已完成建置轉發器 Build failed - + 建置失敗 Failed to build forwarder - + 建立轉發器失敗 %1 installable package (*.%2) - + %1 個可安裝套件 (*.%2) Select an image - + 選擇一個影像 Image files (*.png *.jpg *.bmp) - + 圖片檔案 (*.png *.jpg *.bmp) @@ -866,22 +866,22 @@ Download size: %3 Write watchpoints behavior - + 編寫觀察點行為 Standard GDB - + 標準 GDB Internal change detection - + 內部變化檢測 Break on all writes - + 中斷所有寫入操作 @@ -939,7 +939,7 @@ Download size: %3 Start - + 開始 @@ -983,102 +983,102 @@ Download size: %3 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 - + 僅 ROM MBC1 - + MBC1 MBC2 - + MBC2 MBC3 - + MBC3 MBC3 + RTC - + MBC3 + 實時時鐘 MBC5 - + MBC5 MBC5 + Rumble - + MBC5 + 震動 MBC6 - + MBC6 MBC7 (Tilt) - + MBC7 (Tilt) MMM01 - + MMM01 HuC-1 - + HuC-1 HuC-3 - + HuC-3 Pocket Cam - + 袖珍攝影機 @@ -1088,57 +1088,57 @@ Download size: %3 Wisdom Tree - + Wisdom Tree NT (old 1) - + NT (舊 1) NT (old 2) - + NT (舊 2) NT (new) - + NT(新) Pokémon Jade/Diamond - + 寶可夢翡翠/鑽石 BBD - + BBD Hitek - + Hitek GGB-81 - + GGB-81 Li Cheng - + Li Cheng Sachen (MMC1) - + Sachen (MMC1) Sachen (MMC2) - + Sachen (MMC2) @@ -3338,7 +3338,7 @@ Download size: %3 Map Addr. - 映射位址 + 映射位址。 @@ -3403,17 +3403,26 @@ Download size: %3 複製 + + QGBA::MemoryAccessLogController + + + + Failed to open memory log file + 打開記憶體日誌檔案失敗 + + QGBA::MemoryAccessLogView Memory access logging - + 記憶體存取日誌 Log file - + 紀錄檔案 @@ -3423,27 +3432,27 @@ Download size: %3 Log additional information (uses 3× space) - + 記錄額外資訊(使用 3x 空間) Load existing file if present - + 存在的情況下載入已有文件 Regions - + 區域 Export ROM snapshot - + 匯出ROM快照 Start - + 啟動 @@ -3451,20 +3460,15 @@ Download size: %3 停止 - - Failed to open memory log file - - - - - + + Select access log file - + 選擇訪問日誌文件 - + Memory access logs (*.mal) - + 記憶體存取日誌(*.mal) @@ -3808,7 +3812,7 @@ Download size: %3 Load - + 載入 @@ -3816,7 +3820,7 @@ Download size: %3 Frame %1 - + 帧 %1 @@ -3824,17 +3828,17 @@ Download size: %3 Trying to detach a multiplayer player that's not attached - + 嘗試斷開與未連接的多人玩家的連接 Clearing invalid save ID - + 清理非法存檔ID Clearing invalid preferred ID - + 清理非法優先ID @@ -4407,7 +4411,7 @@ Download size: %3 Game ID: - 遊戲 ID + 遊戲 ID: @@ -4450,7 +4454,7 @@ Download size: %3 Save - + 儲存 @@ -5506,12 +5510,12 @@ Download size: %3 Default sprite colors 1: - 預設角色圖顏色組 1 + 預設角色圖顏色組 1: Default sprite colors 2: - 預設角色圖顏色組 2 + 預設角色圖顏色組 2: @@ -6213,7 +6217,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may %1 of %2 e-Reader cards converted successfully. - %1 張 e-Reader 卡片轉換成功。 (共 %2 張) + %2 張電子閱讀器卡中的 %1 張已成功轉換。 @@ -6243,17 +6247,17 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Scan e-Reader dotcodes... - 掃描 e-Reader 點碼 + 掃描 e-Reader 點碼... - + Convert e-Reader card image to raw... - 轉換 e-Reader 卡片圖檔為原始資料 + 轉換 e-Reader 卡片圖檔為原始資料... ROM &info... - ROM 資訊 (&I) + ROM 資訊 (&I)... @@ -6344,12 +6348,12 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Import GameShark Save... - 匯入 GameShark 存檔 + 匯入 GameShark 存檔... Export GameShark Save... - 匯出 GameShark 存檔 + 匯出 GameShark 存檔... @@ -6614,12 +6618,12 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may View &logs... - 查看記錄檔 (&L) + 查看記錄檔 (&L)... Game &overrides... - 遊戲替代 (&O) + 遊戲替代 (&O)... @@ -6629,7 +6633,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may &Cheats... - 金手指 (&C) + 金手指 (&C)... @@ -6654,7 +6658,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may Start &GDB server... - 啟動 GDB 伺服器 (&G) + 啟動 GDB 伺服器 (&G)... @@ -6664,27 +6668,27 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may View &palette... - 檢視調色盤 (&P) + 檢視調色盤 (&P)... View &sprites... - 檢視角色圖 (&S) + 檢視角色圖 (&S)... View &tiles... - 檢視圖塊 (&T) + 檢視圖塊 (&T)... View &map... - 檢視映射 (&M) + 檢視映射 (&M)... &Frame inspector... - 畫格檢視器 (&F) + 畫格檢視器 (&F)... @@ -6699,7 +6703,7 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may View &I/O registers... - 檢視 I/O 暫存器 (&I) + 檢視 I/O 暫存器 (&I)... @@ -6707,82 +6711,82 @@ If it is set to OpenGL and you still see this, your graphics card or drivers may - + 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/sdl/CMakeLists.txt b/src/platform/sdl/CMakeLists.txt index 5246f959b..2399dc599 100644 --- a/src/platform/sdl/CMakeLists.txt +++ b/src/platform/sdl/CMakeLists.txt @@ -115,11 +115,4 @@ if(UNIX) install(FILES ${PROJECT_SOURCE_DIR}/doc/mgba.6 DESTINATION ${MANDIR}/man6 COMPONENT ${BINARY_NAME}-sdl) endif() -if(DISTBUILD AND CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - if(NOT APPLE) - add_custom_command(TARGET ${BINARY_NAME}-sdl POST_BUILD COMMAND "${OBJCOPY}" --only-keep-debug "$" "$.debug") - add_custom_command(TARGET ${BINARY_NAME}-sdl POST_BUILD COMMAND "${STRIP}" "$") - add_custom_command(TARGET ${BINARY_NAME}-sdl POST_BUILD COMMAND "${OBJCOPY}" --add-gnu-debuglink "$.debug" "$") - install(FILES "$.debug" DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${BINARY_NAME}-sdl-dbg) - endif() -endif() +debug_strip(${BINARY_NAME}-sdl) diff --git a/src/platform/test/fuzz-main.c b/src/platform/test/fuzz-main.c index 143edaba1..5fdfff8a3 100644 --- a/src/platform/test/fuzz-main.c +++ b/src/platform/test/fuzz-main.c @@ -130,6 +130,7 @@ int main(int argc, char** argv) { mDebuggerAccessLoggerInit(&accessLog); mDebuggerAttachModule(&debugger, &accessLog.d); mDebuggerAccessLoggerOpen(&accessLog, vf, O_RDWR); + mDebuggerAccessLoggerStart(&accessLog); hasDebugger = true; } diff --git a/src/sm83/debugger/memory-debugger.c b/src/sm83/debugger/memory-debugger.c index a35c2afeb..345dc50bf 100644 --- a/src/sm83/debugger/memory-debugger.c +++ b/src/sm83/debugger/memory-debugger.c @@ -61,6 +61,7 @@ static void _checkWatchpoints(struct SM83Debugger* debugger, uint16_t address, e info.type.wp.newValue = newValue; info.type.wp.watchType = watchpoint->type; info.type.wp.accessType = type; + info.type.wp.accessSource = debugger->cpu->memory.accessSource; info.address = address; info.segment = debugger->originalMemory.currentSegment(debugger->cpu, address); info.width = 1; diff --git a/src/util/gui/menu.c b/src/util/gui/menu.c index 2109bf8e1..3aeff4533 100644 --- a/src/util/gui/menu.c +++ b/src/util/gui/menu.c @@ -119,12 +119,6 @@ static enum GUIMenuExitReason GUIMenuPollInput(struct GUIParams* params, struct state->cursor = GUIPollCursor(params, &state->cx, &state->cy); // Check for new direction presses - if (newInput & (1 << GUI_INPUT_UP) && menu->index > 0) { - --menu->index; - } - if (newInput & (1 << GUI_INPUT_DOWN) && menu->index < GUIMenuItemListSize(&menu->items) - 1) { - ++menu->index; - } if (newInput & (1 << GUI_INPUT_LEFT)) { struct GUIMenuItem* item = GUIMenuItemListGetPointer(&menu->items, menu->index); if (item->validStates && !item->readonly) { @@ -145,6 +139,20 @@ static enum GUIMenuExitReason GUIMenuPollInput(struct GUIParams* params, struct menu->index = GUIMenuItemListSize(&menu->items) - 1; } } + if (newInput & (1 << GUI_INPUT_UP)) { + if (menu->index > 0) { + --menu->index; + } else { + menu->index = GUIMenuItemListSize(&menu->items) - 1; + } + } + if (newInput & (1 << GUI_INPUT_DOWN)) { + if (menu->index < GUIMenuItemListSize(&menu->items) - 1) { + ++menu->index; + } else { + menu->index = 0; + } + } // Handle cursor movement if (state->cursor != GUI_CURSOR_NOT_PRESENT) {