diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 00000000..8a4b741f
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,23 @@
+# Security Policy
+
+melonDS DS (including the underlying emulator)
+is intended for recreational purposes
+and should not be used in security-critical environments.
+
+Only the latest release is supported;
+_security fixes will not be backported to older releases_.
+
+melonDS DS is only intended to execute code for the hardware it emulates;
+any bug that allows it to execute arbitrary code on the host
+is a vulnerability and should be reported.
+
+If you discover such a bug, please submit a private vulnerability report
+(**not** a public bug)
+with a homebrew ROM that demonstrates the issue.
+
+I will share this information with the maintainers of upstream melonDS,
+as such a vulnerability would most likely affect them as well.
+
+If you are able to provide a fix,
+please do so in the form of a pull request or a patch file;
+I will merge and release it as soon as possible.
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 91dea2a4..833eb7d4 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -95,6 +95,7 @@ jobs:
build-type:
- Debug
- Release
+ - RelWithDebInfo
runs-on: ${{ inputs.runs-on }}
defaults:
run:
@@ -171,8 +172,16 @@ jobs:
run: |
pip install -v -r "${{ github.workspace }}/test/requirements.txt"
- - name: Download Test Files
+ - name: Use Cached Test Files
if: ${{ inputs.test-suite }}
+ id: cache-test-files
+ uses: actions/cache@v4
+ with:
+ path: "${{ github.workspace }}/testfiles"
+ key: "testfiles"
+
+ - name: Download Test Files
+ if: ${{ inputs.test-suite && steps.cache-test-files.outputs.cache-hit != 'true' }}
uses: actions/checkout@v4
with:
repository: "${{ secrets.TESTFILE_REPO }}"
@@ -180,12 +189,12 @@ jobs:
path: "testfiles"
- name: Prepare Test Files
- if: ${{ inputs.test-suite }}
+ if: ${{ inputs.test-suite && steps.cache-test-files.outputs.cache-hit != 'true' }}
working-directory: "${{ github.workspace }}/testfiles"
shell: bash
run: 7z x "${{ secrets.DSI_NAND_ARCHIVE }}"
- - name: Create build environment
+ - name: Create Build Directory
run: mkdir -vp "${{ env.BUILD_DIR }}"
- name: Configure
@@ -194,6 +203,7 @@ jobs:
run: |
cmake "${{ github.workspace }}" \
-DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" \
+ -DTRACY_ENABLE="${{ matrix.build-type == 'RelWithDebInfo' && 'ON' || 'OFF' }}" \
${{ inputs.cmake-args }}
- name: Configure (With Test Suite)
@@ -202,6 +212,7 @@ jobs:
run: |
cmake "${{ github.workspace }}" \
-DCMAKE_BUILD_TYPE="${{ matrix.build-type }}" \
+ -DTRACY_ENABLE="${{ matrix.build-type == 'RelWithDebInfo' && 'ON' || 'OFF' }}" \
-DBUILD_TESTING=ON \
-DARM7_BIOS="${{ env.TESTFILE_DIR }}/${{ secrets.ARM7_BIOS }}" \
-DARM9_BIOS="${{ env.TESTFILE_DIR }}/${{ secrets.ARM9_BIOS }}" \
@@ -242,6 +253,9 @@ jobs:
working-directory: "${{ env.BUILD_DIR }}"
env:
PYTHONUNBUFFERED: 1
+ # Defining TRACY_NO_INVARIANT_CHECK fixes test suite failures on the macos-13 runner;
+ # this is fine because we're not taking traces on the CI anyway.
+ TRACY_NO_INVARIANT_CHECK: 1
run: ctest --exclude-regex example --output-on-failure
- name: Run Test Suite (Linux)
diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml
index fde2a3b7..de798269 100644
--- a/.github/workflows/main.yaml
+++ b/.github/workflows/main.yaml
@@ -166,83 +166,3 @@ jobs:
lib-ext: dylib
cmake-args: --toolchain ./cmake/toolchain/ios.toolchain.cmake -DPLATFORM=TVOS -DDEPLOYMENT_TARGET=14
# Disabled OpenGL on tvOS due to https://github.com/JesseTG/melonds-ds/issues/23
-
- create-release:
- name: Create Release
- needs: [ windows, macos-x86_64, macos-arm64, linux-x86_64, linux-aarch64, android, ios, tvos ]
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- runs-on: ubuntu-latest
- steps:
- - name: Check Out Source
- uses: actions/checkout@v4
- with:
- fetch-depth: 0 # To ensure we have all tags
- - name: Get Latest Changelog Version
- id: changelog
- uses: release-flow/keep-a-changelog-action@v3
- with:
- command: query
- version: latest
- - name: Get the Newest Tag
- id: newest-tag
- run: |
- echo "version=`git tag --list "v[0-9]*.[0-9]*.[0-9]*" --sort=-v:refname | head -n1 | cut -c2-`" >> "$GITHUB_OUTPUT"
- - name: Download Artifacts
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- uses: actions/download-artifact@v4
- with:
- path: artifact
- - name: Zip Release Artifacts
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- shell: bash
- working-directory: artifact
- run: |
- for file in melondsds_libretro-*-Release; do
- zip -r "${file}.zip" "$file"
- done
- - name: Upload .info File Artifact
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- uses: actions/upload-artifact@v4
- with:
- name: melondsds_libretro.info
- path: artifact/melondsds_libretro-linux-x86_64-Release/cores/melondsds_libretro.info
- # The .info file doesn't vary by platform, so we only need to upload one
- - name: Create Release
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- uses: softprops/action-gh-release@v2
- with:
- token: ${{ secrets.RELEASE_TOKEN }}
- tag_name: "v${{ steps.changelog.outputs.version }}"
- body: "${{ steps.changelog.outputs.release-notes }}"
- files: |
- artifact/melondsds_libretro-win32-x86_64-Release.zip
- artifact/melondsds_libretro-macos-x86_64-Release.zip
- artifact/melondsds_libretro-macos-arm64-Release.zip
- artifact/melondsds_libretro-linux-x86_64-Release.zip
- artifact/melondsds_libretro-linux-arm64-Release.zip
- artifact/melondsds_libretro-android-Release.zip
- artifact/melondsds_libretro-ios-Release.zip
- artifact/melondsds_libretro-tvos-Release.zip
-
- - name: Checkout libretro-super
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- uses: actions/checkout@v4
- with:
- repository: "libretro/libretro-super"
- path: libretro-super
-
- - name: Add Updated .info File
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- run: |
- cp -f "${{ github.workspace }}/artifact/melondsds_libretro-linux-x86_64-Release/cores/melondsds_libretro.info" libretro-super/dist/info/melondsds_libretro.info
-
- - name: Open Pull Request
- if: "${{ steps.changelog.outputs.version != steps.newest-tag.outputs.version }}"
- uses: peter-evans/create-pull-request@v6
- with:
- token: ${{ secrets.RELEASE_TOKEN }}
- path: libretro-super
- commit-message: 'Submit melondsds_libretro.info for melonDS DS release v${{ steps.changelog.outputs.version }} on behalf of @${{ github.triggering_actor }}'
- title: "Update melonDS DS to ${{ steps.changelog.outputs.version }}"
- branch: "melondsds-v${{ steps.changelog.outputs.version }}"
- push-to-fork: "${{ github.triggering_actor }}/libretro-super"
\ No newline at end of file
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 00000000..fa2d44f9
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,94 @@
+name: Release New Version
+on:
+ workflow_run:
+ # Run this workflow right after the build succeeds and test pass on the main branch
+ workflows: ["Build Artifacts"]
+ types: [completed]
+ branches:
+ - main
+jobs:
+ check-release-version:
+ name: Check Release Version
+ if: github.ref == 'refs/heads/main' && github.event.workflow_run.conclusion == 'success'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check Out Source
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # To ensure we have all tags
+ - name: Get Latest Changelog Version
+ id: changelog
+ uses: release-flow/keep-a-changelog-action@v3
+ with:
+ command: query
+ version: latest
+ - name: Get the Newest Tag
+ id: newest-tag
+ run: |
+ echo "newest-tag=`git tag --list "v[0-9]*.[0-9]*.[0-9]*" --sort=-v:refname | head -n1 | cut -c2-`" >> "$GITHUB_OUTPUT"
+ outputs:
+ newest-version: ${{ steps.changelog.outputs.version }}
+ newest-tag: ${{ steps.newest-tag.outputs.version }}
+ release-notes: ${{ steps.changelog.outputs.release-notes}}
+
+ create-release:
+ name: Create Release
+ needs: check-release-version
+ if: "${{ needs.check-release-version.outputs.newest-tag != needs.check-release-version.outputs.newest-version }}"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download Artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifact
+
+ - name: Zip Release Artifacts
+ shell: bash
+ working-directory: artifact
+ run: |
+ for file in melondsds_libretro-*-Release; do
+ zip -r "${file}.zip" "$file"
+ done
+
+ - name: Upload .info File Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: melondsds_libretro.info
+ path: artifact/melondsds_libretro-linux-x86_64-Release/cores/melondsds_libretro.info
+ # The .info file doesn't vary by platform, so we only need to upload one
+
+ - name: Create Release
+ uses: softprops/action-gh-release@v2
+ with:
+ token: ${{ secrets.RELEASE_TOKEN }}
+ tag_name: "v${{ needs.check-release-version.outputs.newest-version }}"
+ body: "${{ needs.check-release-version.outputs.release-notes }}"
+ files: |
+ artifact/melondsds_libretro-win32-x86_64-Release.zip
+ artifact/melondsds_libretro-macos-x86_64-Release.zip
+ artifact/melondsds_libretro-macos-arm64-Release.zip
+ artifact/melondsds_libretro-linux-x86_64-Release.zip
+ artifact/melondsds_libretro-linux-arm64-Release.zip
+ artifact/melondsds_libretro-android-Release.zip
+ artifact/melondsds_libretro-ios-Release.zip
+ artifact/melondsds_libretro-tvos-Release.zip
+
+ - name: Checkout libretro-super
+ uses: actions/checkout@v4
+ with:
+ repository: "libretro/libretro-super"
+ path: libretro-super
+
+ - name: Add Updated .info File
+ run: |
+ cp -f "${{ github.workspace }}/artifact/melondsds_libretro-linux-x86_64-Release/cores/melondsds_libretro.info" libretro-super/dist/info/melondsds_libretro.info
+
+ - name: Open Pull Request
+ uses: peter-evans/create-pull-request@v6
+ with:
+ token: ${{ secrets.RELEASE_TOKEN }}
+ path: libretro-super
+ commit-message: 'Submit melondsds_libretro.info for melonDS DS release v${{ needs.check-release-version.outputs.newest-version }} on behalf of @${{ github.triggering_actor }}'
+ title: "Update melonDS DS to ${{ needs.check-release-version.outputs.newest-version }}"
+ branch: "melondsds-v${{ needs.check-release-version.outputs.newest-version }}"
+ push-to-fork: "${{ github.triggering_actor }}/libretro-super"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 7b21fae4..4d1fad6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
+CMakeUserPresets.json
Testing
Makefile
cmake_install.cmake
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 19c916fd..ef2c122a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,29 @@ New features will increment the minor version.
Breaking changes (**except for savestates**) will increment the major version;
a design goal is to avoid a 2.x release for as long as possible.
+## [Unreleased]
+
+### Added
+
+- Added `RelWithDebInfo` builds that include Tracy support.
+ These will be distributed on GitHub for all supported platforms,
+ starting with this release.
+- Added a contributor's guide at `CONTRIBUTING.md`.
+ [#107](https://github.com/JesseTG/melonds-ds/issues/107)
+
+### Changed
+
+- Moved build instructions from `README.md` to the new `CONTRIBUTING.md`.
+
+### Fixed
+
+- Fixed encrypted NDS ROMs failing to load without any feedback;
+ loading one without using the native BIOS will now display an error message.
+ [#228](https://github.com/JesseTG/melonds-ds/issues/228)
+- Fixed Blow mode for emulated microphone input not being implemented
+ despite being available in the core options.
+ [#187](https://github.com/JesseTG/melonds-ds/issues/187)
+
## [1.1.7] - 2024-08-20
### Fixed
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..959e2d98
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,353 @@
+# Contributing to melonDS DS
+
+Thanks for your interest in contributing to melonDS DS!
+There are many ways you can help improve the project,
+even if you're not a coder.
+
+## Contributing Upstream
+
+First and foremost, you can help improve melonDS DS
+by contributing to the [underlying emulator][melonds-contributing]
+that this project is based on;
+since parity with standalone melonDS is a priority for melonDS DS,
+most improvements to the original emulator
+will eventually make their way here.
+
+## Reporting Issues
+
+Found a bug? Have a feature request?
+You can open a ticket by going [here][melondsds-issues]
+and following the instructions --
+or if your issue is something I already know about,
+you can comment on an existing ticket
+with details about how it affects you.
+
+Having a record of bugs or feature requests helps me keep my backlog organized
+and plan long-term improvements to the project,
+so don't be shy!
+
+When reporting a bug,
+you'll usually want to include supporting information.
+Here are some common artifacts that I may ask for:
+
+### Logs
+
+Usually, providing a log when reporting a bug or asking for help
+can eliminate a lot of blind guesswork.
+When in doubt, include a log.
+
+See [here](https://docs.libretro.com/guides/generating-retroarch-logs)
+for guidance on generating a log with RetroArch.
+Instructions may vary for other libretro frontends.
+
+### Traces
+
+melonDS DS supports the [Tracy][tracy] frame profiler,
+which is a great way to diagnose performance issues.
+Having a Tracy capture can help me learn more about:
+
+You can take a trace with the following steps:
+
+1. Download a Tracy-enabled build of melonDS DS for your platform
+ from the [Releases][melondsds-releases] or a GitHub Actions artifact.
+ These builds have `RelWithDebInfo` in the name.
+2. Install the Tracy-enabled build of melonDS DS in place of your normal build.
+3. Download, install, and launch [Tracy][tracy].
+4. If you're running RetroArch on a different device than you're running Tracy,
+ enter the device's IP address in the "client address" field.
+5. Launch RetroArch with the Tracy-enabled core.
+ The trace will start automatically,
+ and the Tracy window should start updating immediately.
+6. Perform the actions that you want to profile
+ (i.e. do the thing that's causing the slowdown).
+7. Close RetroArch to stop the trace.
+8. Save the trace to a file,
+ then attach it to the relevant ticket.
+
+> [!NOTE]
+> For most platforms, running RetroArch (or any Tracy-instrumented app)
+> with admin privileges will allow Tracy to capture extra data.
+> However, the resulting trace **will contain personal information**
+> about everything else that's running on your system.
+>
+> If you capture a trace with elevated privileges,
+> don't post it publicly.
+> Send it to me privately instead.
+
+## Translating Text
+
+melonDS DS is only available in English right now,
+but support for other languages is planned.
+Once the infrastructure for that is in place,
+you'll be able to help translate melonDS DS
+through libretro's [Crowdin][libretro-crowdin] project.
+
+## Sponsorship
+
+Spare change burning a hole in your bank account?
+[Sponsoring me through GitHub Sponsors][sponsor] is a great way to say thanks!
+melonDS DS is a passion project that will always be free to use
+(just like the original emulator),
+but I'm always grateful for financial support!
+The fine people behind [melonDS](https://melonds.kuribo64.net/donate.php)
+and [RetroArch](https://www.retroarch.com/index.php?page=donate)
+certainly wouldn't mind, either.
+
+> [!NOTE]
+> Right now I can't guarantee specific benefits for sponsors,
+> but I will reevaluate that stance if I see a sustained desire for it.
+
+## Spreading the Word
+
+If you enjoy using melonDS DS,
+you can help me out by spreading the good word!
+Tweet it, stream it, invite me on your podcast, book a television ad --
+whatever you think will help.
+The more people who use the project,
+the more likely one of them will want to contribute
+(and the more joy it'll bring to players)!
+
+## Contributing Code
+
+Submitting improvements to melonDS DS is a great way to help out,
+but it also requires the most attention and coordination.
+I don't want to see your hard work go to waste,
+so if there's something specific you want to work on
+then I _strongly_ recommend you run it by me beforehand.
+
+### Installing Dependencies
+
+You will need to install the following beforehand:
+
+- CMake 3.19 or later
+- Git
+- A C++17 compiler (MSVC is not supported)
+
+Most other dependencies are fetched automatically by CMake.
+
+#### Windows
+
+1. Install [MSYS2](https://www.msys2.org).
+2. Open the MSYS2 MinGW 64-bit terminal from the Start Menu.
+3. Install dependencies like so:
+
+ ```sh
+ pacman -Syu # update the package database
+ pacman -S git mingw-w64-x86_64-{cmake,toolchain} # install dependencies
+ ```
+4. Proceed to [Compilation](#compilation).
+ You may need to remain in the MSYS2 terminal.
+
+#### macOS
+
+1. Install [Homebrew](https://brew.sh).
+2. Install dependencies like so:
+
+ ```sh
+ brew install cmake git pkg-config cmake
+ ```
+3. Install Xcode and the Xcode command-line tools.
+4. Proceed to [Compilation](#compilation).
+
+> [!NOTE]
+> macOS builds exclude OpenGL by default,
+> as the OpenGL renderer [doesn't currently work on the platform](https://github.com/JesseTG/melonds-ds/issues/12).
+> To enable it anyway, pass `-DENABLE_OPENGL=ON` to CMake.
+
+#### Linux
+
+1. Install dependencies like so:
+
+ ```sh
+ sudo apt install cmake git pkg-config # Ubuntu/Debian
+ sudo pacman -S base-devel cmake extra-cmake-modules git # Arch Linux
+ ```
+2. Proceed to [Compilation](#compilation).
+
+#### Android
+
+1. Install the Android SDK and [NDK](https://developer.android.com/ndk/downloads).
+ The simplest way to do this is through [Android Studio](https://developer.android.com/studio).
+2. Proceed to [Compilation](#compilation).
+
+#### iOS
+
+These steps can only be done on macOS.
+
+1. Install Xcode and the Xcode command-line tools.
+2. Proceed to [Compilation](#compilation).
+
+### Compilation
+
+Once you've installed the dependencies,
+the process for building melonDS DS is mostly the same on all platforms:
+
+```sh
+git clone https://github.com/JesseTG/melonds-ds
+cd melonds-ds
+cmake -B build # Generate the build system, and add any -D or --toolchain flags here
+cmake --build build # Build the project
+```
+
+However, some platforms or features need you to add some extra flags to the first `cmake` command:
+
+#### macOS
+
+If building for the macOS architecture that your device uses,
+no extra flags are required.
+To produce a build for a specific architecture,
+pass `-DCMAKE_OSX_ARCHITECTURES:STRING=$ARCH` to the initial `cmake` command,
+where `$ARCH` is one of the following:
+
+- `x86_64` for x86_64 builds.
+- `arm64` for Apple Silicon builds.
+- `x86_64;arm64` for universal builds.
+
+> [!WARNING]
+> Universal builds of melonDS DS are not supported,
+> as [there is a history](https://github.com/JesseTG/melonds-ds/issues/131)
+> of them not working reliably.
+
+#### Android
+
+You'll need to add the following flags to build for Android.
+
+- `--toolchain=...`:
+ The path to the `android.toolchain.cmake` file in your NDK installation.
+ The location varies depending on how you installed the NDK;
+ it will most likely be in `$ANDROID_NDK/build/cmake`.
+- `-DANDROID_ABI=...`:
+ The ABI to build for.
+ This should be `arm64-v8a` or `x86_64`.
+ If in doubt, use `arm64-v8a`.
+- `-DANDROID_PLATFORM=...`:
+ The Android API level to target.
+ The minimum level supported by melonDS DS is 24.
+
+You should also use the version of `cmake` that the NDK includes.
+
+Here's an example configure step for `cmake` on Windows.
+This command uses the NDK-bundled toolchain
+to prepare a 64-bit ARM build for Android API level 24.
+
+```pwsh
+PS C:\Users\Jesse\Projects\melonds-ds> $Env:ANDROID_SDK_ROOT\cmake\3.22.1\bin\cmake.exe `
+ -DANDROID_ABI=arm64-v8a `
+ -DANDROID_PLATFORM=24 `
+ -DCMAKE_TOOLCHAIN_FILE=$Env:ANDROID_NDK\build\cmake\android.toolchain.cmake
+```
+
+The command will be more or less the same on other platforms,
+but the paths will be different.
+
+See [here](https://developer.android.com/ndk/guides/cmake#variables) for more information
+about these and other Android-specific CMake variables.
+
+#### iOS/tvOS
+
+You will need to add the following flags to build for iOS or tvOS:
+
+- `--toolchain=./cmake/toolchain/ios.toolchain.cmake`:
+ The path to the `ios.toolchain.cmake` that's bundled with melonDS DS.
+- `-DPLATFORM=...`:
+ The target platform to build for.
+ Use `OS64` for iOS and `TVOS` for tvOS.
+ See `cmake/toolchain/ios.toolchain.cmake` for more information
+ about the available CMake variables that this toolchain defines.
+- `-DDEPLOYMENT_TARGET=...`:
+ The minimum SDK version to target.
+ The minimum level supported by melonDS DS is 14.
+
+#### Tracy Integration
+
+melonDS DS supports the [Tracy](https://github.com/wolfpld/tracy) frame profiler.
+To enable it, add `-DTRACY_ENABLE=ON` to the initial `cmake` command.
+For best results, build with the `RelWithDebInfo` configuration
+by adding `-DCMAKE_BUILD_TYPE=RelWithDebInfo` when running `cmake`.
+
+### Customizing the Build
+
+These are some of the most important CMake variables
+that can be used to configure the build.
+To see the rest, run `cmake -LH` in the build directory.
+
+| Variable | Description |
+|-----------------------------------|------------------------------------------------------------------------------------------------------------------------|
+| `ENABLE_OPENGL` | Whether to build the OpenGL renderer. Defaults to `ON` on Windows and Linux, `OFF` on other platforms. |
+| `TRACY_ENABLE` | Enables the Tracy frame profiler. |
+| `MELONDS_REPOSITORY_URL` | The Git repo from which melonDS will be cloned. Set this to use a fork. |
+| `MELONDS_REPOSITORY_TAG` | The melonDS commit to use in the build. |
+| `FETCHCONTENT_SOURCE_DIR_MELONDS` | Path to a copy of the melonDS repo on your system. Set this to use a local branch _instead_ of cloning. |
+| `LIBRETRO_COMMON_REPOSITORY_URL` | The Git repo from which `libretro-common` will be cloned. Set this to use a fork. |
+| `LIBRETRO_COMMON_REPOSITORY_TAG` | The `libretro-common` commit to use in the build. |
+
+See [here](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html)
+and [here](https://cmake.org/cmake/help/latest/module/FetchContent.html#id8)
+for more information about the variables that CMake and its modules define;
+these can also be used to customize the build.
+
+### Getting Your Patch Merged
+
+Remember, I ultimately have to maintain whatever changes you submit.
+Submissions that complicate this would harm melonDS DS in the long run,
+so I will be picky about what gets merged.
+
+That said, I _want_ you to succeed!
+If you come to me in advance with your idea,
+I can guide you towards a solution that everyone can be happy with --
+or at least prevent you from wasting time on a non-starter.
+
+Here are some rules and guidelines you'll need to follow
+as you implement your contribution:
+
+#### Tests
+
+melonDS DS has a suite of tests to ensure that
+most of the core works as expected.
+The tests will automatically be run on the CI pipeline
+when new commits are pushed.
+
+> [!TIP]
+> You are _strongly_ encouraged to run the test suite locally,
+> as doing so will help you catch issues early and speed up iteration.
+> See [here][melondsds-tests] for instructions on doing so.
+
+**All builds must succeed and all test cases must pass
+for your contribution to be merged,
+barring exceptional circumstances.**
+
+You encouraged to write new tests
+if doing so makes sense for your contribution
+(and [libretro.py][libretro.py] supports it) --
+I may even ask you to do so as a condition of merging.
+
+Note that tests are only run on GitHub Actions --
+they are not run on libretro's build infrastructure.
+
+#### Style
+
+There isn't currently a style guide for the codebase (C++ or otherwise).
+But I do have some rules you'll need to follow:
+
+- Do not introduce new dependencies unless absolutely necessary.
+- If you _do_ need to introduce a new dependency,
+ then fetch it at configure-time with `FetchContent` instead of vendoring it.
+ See [here](cmake/FetchDependencies.cmake) for more details.
+- All C++ code (including dependencies) *must* be built as C++17.
+- All text should use [Semantic Line Breaks][sembr] (aka SemBr),
+ including comments and string literals in the code.
+ It helps with readability and version control.
+- Please update documentation and comments as you work,
+ if relevant to your changes.
+
+[libretro.py]: https://github.com/JesseTG/libretro.py
+[libretro-crowdin]: https://docs.libretro.com/development/retroarch/new-translations-crowdin
+[melonds]: https://github.com/melonDS-emu/melonDS
+[melonds-contributing]: https://github.com/melonDS-emu/melonDS/blob/master/CONTRIBUTING.md
+[melondsds-actions]: https://github.com/JesseTG/melonds-ds/actions
+[melondsds-issues]: https://github.com/JesseTG/melonds-ds/issues/new/choose
+[melondsds-releases]: https://github.com/JesseTG/melonds-ds/releases
+[melondsds-tests]: test/README.md
+[sembr]: https://sembr.org
+[sponsor]: https://github.com/sponsors/JesseTG
+[tracy]: https://github.com/wolfpld/tracy
\ No newline at end of file
diff --git a/README.md b/README.md
index 39367e19..65c31574 100644
--- a/README.md
+++ b/README.md
@@ -267,176 +267,7 @@ _but melonDS DS will not support these platforms unless there's enough demand_.
# Building
-melonDS DS is built with CMake.
-
-## Dependencies
-
-You will need to install the following beforehand:
-
-- CMake 3.19 or later
-- Git
-- A C++17 compiler (MSVC is not supported)
-
-Most other dependencies are fetched automatically by CMake.
-
-### Windows
-
-1. Install [MSYS2](https://www.msys2.org).
-2. Open the MSYS2 MinGW 64-bit terminal from the Start Menu.
-3. Install dependencies like so:
-
- ```sh
- pacman -Syu # update the package database
- pacman -S git mingw-w64-x86_64-{cmake,toolchain} # install dependencies
- ```
-4. Proceed to [Compilation](#compilation).
- You may need to remain in the MSYS2 terminal.
-
-### macOS
-
-1. Install [Homebrew](https://brew.sh).
-2. Install dependencies like so:
-
- ```sh
- brew install cmake git pkg-config cmake
- ```
-3. Install Xcode and the Xcode command-line tools.
-4. Proceed to [Compilation](#compilation).
-
-> [!NOTE]
-> macOS builds exclude OpenGL by default,
-> as the OpenGL renderer [doesn't currently work on the platform](https://github.com/JesseTG/melonds-ds/issues/12).
-> To enable it anyway, pass `-DENABLE_OPENGL=ON` to CMake.
-
-### Linux
-
-1. Install dependencies like so:
-
- ```sh
- sudo apt install cmake git pkg-config # Ubuntu/Debian
- sudo pacman -S base-devel cmake extra-cmake-modules git # Arch Linux
- ```
-2. Proceed to [Compilation](#compilation).
-
-### Android
-
-1. Install the Android SDK and [NDK](https://developer.android.com/ndk/downloads).
- The simplest way to do this is through [Android Studio](https://developer.android.com/studio).
-2. Proceed to [Compilation](#compilation).
-
-### iOS
-
-These steps can only be done on macOS.
-
-1. Install Xcode and the Xcode command-line tools.
-2. Proceed to [Compilation](#compilation).
-
-## Compilation
-
-Once you've installed the dependencies,
-the process for building melonDS DS is mostly the same on all platforms:
-
-```sh
-git clone https://github.com/JesseTG/melonds-ds
-cd melonds-ds
-cmake -B build # Generate the build system, and add any -D or --toolchain flags here
-cmake --build build # Build the project
-```
-
-However, some platforms or features need you to add some extra flags to the first `cmake` command:
-
-### macOS
-
-If building for the macOS architecture that your device uses,
-no extra flags are required.
-To produce a build for a specific arhitecture,
-pass `-DCMAKE_OSX_ARCHITECTURES:STRING=$ARCH` to the initial `cmake` command,
-where `$ARCH` is one of the following:
-
-- `x86_64` for x86_64 builds.
-- `arm64` for Apple Silicon builds.
-- `x86_64;arm64` for universal builds.
-
-> [!WARNING]
-> Universal builds of melonDS DS are not supported,
-> as [there is a history](https://github.com/JesseTG/melonds-ds/issues/131)
-> of them not working reliably.
-
-### Android
-
-You'll need to add the following flags to build for Android.
-
-- `--toolchain=...`:
- The path to the `android.toolchain.cmake` file in your NDK installation.
- The location varies depending on how you installed the NDK;
- it will most likely be in `$ANDROID_NDK/build/cmake`.
-- `-DANDROID_ABI=...`:
- The ABI to build for.
- This should be `arm64-v8a` or `x86_64`.
- If in doubt, use `arm64-v8a`.
-- `-DANDROID_PLATFORM=...`:
- The Android API level to target.
- The minimum level supported by melonDS DS is 24.
-
-You should also use the version of `cmake` that the NDK includes.
-
-Here's an example configure step for `cmake` on Windows.
-This command uses the NDK-bundled toolchain
-to prepare a 64-bit ARM build for Android API level 24.
-
-```pwsh
-PS C:\Users\Jesse\Projects\melonds-ds> $Env:ANDROID_SDK_ROOT\cmake\3.22.1\bin\cmake.exe `
- -DANDROID_ABI=arm64-v8a `
- -DANDROID_PLATFORM=24 `
- -DCMAKE_TOOLCHAIN_FILE=$Env:ANDROID_NDK\build\cmake\android.toolchain.cmake
-```
-
-The command will be more or less the same on other platforms,
-but the paths will be different.
-
-See [here](https://developer.android.com/ndk/guides/cmake#variables) for more information
-about these and other Android-specific CMake variables.
-
-### iOS/tvOS
-
-You will need to add the following flags to build for iOS or tvOS:
-
-- `--toolchain=./cmake/toolchain/ios.toolchain.cmake`:
- The path to the `ios.toolchain.cmake` that's bundled with melonDS DS.
-- `-DPLATFORM=...`:
- The target platform to build for.
- Use `OS64` for iOS and `TVOS` for tvOS.
- See `cmake/toolchain/ios.toolchain.cmake` for more information
- about the available CMake variables that this toolchain defines.
-- `-DDEPLOYMENT_TARGET=...`:
- The minimum SDK version to target.
- The minimum level supported by melonDS DS is 14.
-
-### Tracy Integration
-
-melonDS DS supports the [Tracy](https://github.com/wolfpld/tracy) frame profiler.
-To enable it, add `-DTRACY_ENABLE=ON` to the initial `cmake` command.
-
-## CMake Variables
-
-These are some of the most important CMake variables
-that can be used to configure the build.
-To see the rest, run `cmake -LH` in the build directory.
-
-| Variable | Description |
-|-----------------------------------|------------------------------------------------------------------------------------------------------------------------|
-| `ENABLE_OPENGL` | Whether to build the OpenGL renderer. Defaults to `ON` on Windows and Linux, `OFF` on other platforms. |
-| `TRACY_ENABLE` | Enables the Tracy frame profiler. |
-| `MELONDS_REPOSITORY_URL` | The Git repo from which melonDS will be cloned. Set this to use a fork. |
-| `MELONDS_REPOSITORY_TAG` | The melonDS commit to use in the build. |
-| `FETCHCONTENT_SOURCE_DIR_MELONDS` | Path to a copy of the melonDS repo on your system. Set this to use a local branch _instead_ of cloning. |
-| `LIBRETRO_COMMON_REPOSITORY_URL` | The Git repo from which `libretro-common` will be cloned. Set this to use a fork. |
-| `LIBRETRO_COMMON_REPOSITORY_TAG` | The `libretro-common` commit to use in the build. |
-
-See [here](https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html)
-and [here](https://cmake.org/cmake/help/latest/module/FetchContent.html#id8)
-for more information about the variables that CMake and its modules define;
-these can also be used to customize the build.
+See the [contributor's guide](CONTRIBUTING.md) for instructions on building melonDS DS.
# About the Name
@@ -453,6 +284,9 @@ What do these games have in common?
I see this core as an enhanced remake of the [legacy melonDS core][melonds-libretro],
so I wanted to embody that in the name.
+Put differently,
+if I had given [the DeSmuME core](https://github.com/libretro/desmume) a similar treatment
+then I would've named it "DeSmuME DS"!
# Special Thanks
diff --git a/cmake/FetchDependencies.cmake b/cmake/FetchDependencies.cmake
index 7c1f691e..68a196e4 100644
--- a/cmake/FetchDependencies.cmake
+++ b/cmake/FetchDependencies.cmake
@@ -53,7 +53,7 @@ fetch_dependency(date "https://github.com/HowardHinnant/date" "1ead671")
fetch_dependency(zlib "https://github.com/madler/zlib" "v1.3.1")
if (TRACY_ENABLE)
- fetch_dependency(tracy "https://github.com/wolfpld/tracy.git" "v0.10")
+ fetch_dependency(tracy "https://github.com/wolfpld/tracy" "v0.11.1")
endif()
set(CMAKE_MODULE_PATH "${FETCHCONTENT_BASE_DIR}/melonds-src/cmake" "${FETCHCONTENT_BASE_DIR}/embed-binaries-src/cmake" "${CMAKE_MODULE_PATH}")
@@ -70,6 +70,7 @@ if (TRACY_ENABLE)
set(BUILD_SHARED_LIBS OFF)
option(TRACY_DELAYED_INIT "" ON)
option(TRACY_MANUAL_LIFETIME "" ON)
+ option(TRACY_ON_DEMAND "" ON)
option(TRACY_STATIC "" ON)
FetchContent_MakeAvailable(tracy)
endif()
diff --git a/src/libretro/CMakeLists.txt b/src/libretro/CMakeLists.txt
index d96a6634..cfe70cb6 100644
--- a/src/libretro/CMakeLists.txt
+++ b/src/libretro/CMakeLists.txt
@@ -96,6 +96,8 @@ add_library(melondsds_libretro ${LIBRARY_TYPE}
sram.cpp
sram.hpp
tracy.hpp
+ tracy/client.hpp
+ tracy/opengl.hpp
utils.cpp
utils.hpp
../pntr/pntr.c
@@ -175,7 +177,11 @@ if (HAVE_NETWORKING)
endif ()
if (TRACY_ENABLE)
- target_sources(melondsds_libretro PRIVATE tracy.cpp)
+ target_sources(melondsds_libretro PRIVATE tracy/memory.cpp)
+
+ if (HAVE_OPENGL OR HAVE_OPENGLES)
+ target_sources(melondsds_libretro PRIVATE tracy/opengl.cpp)
+ endif()
endif ()
if (HAVE_OPENGL OR HAVE_OPENGLES)
diff --git a/src/libretro/config/console.cpp b/src/libretro/config/console.cpp
index b0484218..37a62f7d 100644
--- a/src/libretro/config/console.cpp
+++ b/src/libretro/config/console.cpp
@@ -251,6 +251,14 @@ static melonDS::NDSArgs MelonDsDs::GetNdsArgs(
if (ndsInfo) {
ndsargs.NDSROM = LoadNdsCart(config, *ndsInfo);
+ const uint8_t* romdata = ndsargs.NDSROM->GetROM();
+ const NDSHeader &header = ndsargs.NDSROM->GetHeader();
+
+ bool romDecrypted = (*(uint32_t*)&romdata[header.ARM9ROMOffset] == 0xE7FFDEFF && *(uint32_t*)&romdata[header.ARM9ROMOffset + 0x10] != 0xE7FFDEFF);
+ if (!header.IsHomebrew() && !romDecrypted && !(bios7Loaded && bios9Loaded)) {
+ // If this is an encrypted retail ROM but we aren't using the native BIOS...
+ throw encrypted_rom_exception();
+ }
}
if (gbaInfo) {
diff --git a/src/libretro/config/parse.hpp b/src/libretro/config/parse.hpp
index 45e01ef5..2d9a8902 100644
--- a/src/libretro/config/parse.hpp
+++ b/src/libretro/config/parse.hpp
@@ -162,6 +162,7 @@ namespace MelonDsDs {
constexpr std::optional ParseMicInputMode(std::string_view value) noexcept {
if (value == config::values::MICROPHONE) return MicInputMode::HostMic;
if (value == config::values::NOISE) return MicInputMode::WhiteNoise;
+ if (value == config::values::BLOW) return MicInputMode::Blow;
if (value == config::values::SILENCE) return MicInputMode::None;
return std::nullopt;
diff --git a/src/libretro/config/types.hpp b/src/libretro/config/types.hpp
index 534caf28..bdfac2db 100644
--- a/src/libretro/config/types.hpp
+++ b/src/libretro/config/types.hpp
@@ -54,6 +54,7 @@ namespace MelonDsDs {
enum class MicInputMode {
None,
+ Blow,
HostMic,
WhiteNoise,
};
diff --git a/src/libretro/environment.cpp b/src/libretro/environment.cpp
index 7101136f..0faa5711 100644
--- a/src/libretro/environment.cpp
+++ b/src/libretro/environment.cpp
@@ -814,26 +814,21 @@ void retro::NormalizePath(std::span buffer, size_t& pathLength) noexcept {
}
PUBLIC_SYMBOL void retro_set_video_refresh(retro_video_refresh_t video_refresh) {
- ZoneScopedN(TracyFunction);
retro::_video_refresh = video_refresh;
}
PUBLIC_SYMBOL void retro_set_audio_sample(retro_audio_sample_t) {
- ZoneScopedN(TracyFunction);
// Noop, we don't use this callback
}
PUBLIC_SYMBOL void retro_set_audio_sample_batch(retro_audio_sample_batch_t audio_sample_batch) {
- ZoneScopedN(TracyFunction);
retro::_audio_sample_batch = audio_sample_batch;
}
PUBLIC_SYMBOL void retro_set_input_poll(retro_input_poll_t input_poll) {
- ZoneScopedN(TracyFunction);
retro::_input_poll = input_poll;
}
PUBLIC_SYMBOL void retro_set_input_state(retro_input_state_t input_state) {
- ZoneScopedN(TracyFunction);
retro::_input_state = input_state;
}
\ No newline at end of file
diff --git a/src/libretro/exceptions.cpp b/src/libretro/exceptions.cpp
index 7d3a408c..ce2b3438 100644
--- a/src/libretro/exceptions.cpp
+++ b/src/libretro/exceptions.cpp
@@ -73,6 +73,14 @@ MelonDsDs::wrong_firmware_type_exception::wrong_firmware_type_exception(
) {
}
+MelonDsDs::encrypted_rom_exception::encrypted_rom_exception() noexcept : bios_exception(
+ "The loaded ROM is encrypted and needs native NDS BIOS files, "
+ "but they're not loaded.",
+ "Ensure that you have the required files in your frontend's system folder. "
+ "Select Native in the BIOS/Firmware Mode core option, then restart the core."
+) {
+}
+
MelonDsDs::dsi_region_mismatch_exception::dsi_region_mismatch_exception(
string_view nandName,
diff --git a/src/libretro/exceptions.hpp b/src/libretro/exceptions.hpp
index c13ac2dd..529ddc24 100644
--- a/src/libretro/exceptions.hpp
+++ b/src/libretro/exceptions.hpp
@@ -70,6 +70,11 @@ namespace MelonDsDs
using config_exception::config_exception;
};
+ class encrypted_rom_exception : public bios_exception {
+ public:
+ encrypted_rom_exception() noexcept;
+ };
+
class dsi_region_mismatch_exception : public config_exception {
public:
dsi_region_mismatch_exception(std::string_view nandName, melonDS::DSi_NAND::ConsoleRegion nandRegion, melonDS::RegionMask gameRegionMask) noexcept;
diff --git a/src/libretro/microphone.cpp b/src/libretro/microphone.cpp
index 2c00f7c1..9d7f414d 100644
--- a/src/libretro/microphone.cpp
+++ b/src/libretro/microphone.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include
#include "config/config.hpp"
#include "environment.hpp"
@@ -122,7 +123,19 @@ void MelonDsDs::MicrophoneState::Read(std::span buffer) noexcept {
switch (_micInputMode) {
case MicInputMode::WhiteNoise: {
for (short& i : buffer)
- i = rand() & 0xFFFF;
+ i = _random(_randomEngine);
+
+ break;
+ }
+ case MicInputMode::Blow: {
+ constexpr size_t MIC_BLOW_LENGTH = sizeof(mic_blow) / sizeof(mic_blow[0]);
+
+ // builtin sample is 16-bit signed PCM
+ // sample rate is 44.1KHz
+ for (int i = 0; i < buffer.size(); ++i) {
+ buffer[i] = static_cast(mic_blow[_blowSampleOffset] ^ 0x8000);
+ _blowSampleOffset = (_blowSampleOffset + 1) % MIC_BLOW_LENGTH;
+ }
break;
}
diff --git a/src/libretro/microphone.hpp b/src/libretro/microphone.hpp
index 3608f691..6b68251b 100644
--- a/src/libretro/microphone.hpp
+++ b/src/libretro/microphone.hpp
@@ -18,7 +18,9 @@
#define MELONDS_DS_MICROPHONE_HPP
#include
+#include
#include
+#include
#include "config/types.hpp"
#include "retro/microphone.hpp"
@@ -51,6 +53,9 @@ namespace MelonDsDs {
std::optional _microphone {};
MicInputMode _micInputMode = MicInputMode::None;
MicButtonMode _micButtonMode = MicButtonMode::Hold;
+ size_t _blowSampleOffset = 0;
+ std::default_random_engine _randomEngine;
+ std::uniform_int_distribution _random {std::numeric_limits::min(), std::numeric_limits::max()};
bool _micButtonDown = false;
bool _prevMicButtonDown = false;
bool _shouldCaptureAudio = false;
diff --git a/src/libretro/render/opengl.cpp b/src/libretro/render/opengl.cpp
index fefe976f..d717c1c8 100644
--- a/src/libretro/render/opengl.cpp
+++ b/src/libretro/render/opengl.cpp
@@ -174,9 +174,6 @@ MelonDsDs::OpenGLRenderState::OpenGLRenderState() {
retro_assert(hw_render.debug_context);
#endif
- uintptr_t framebuffer = hw_render.get_current_framebuffer();
- retro::debug("OpenGL context requested. Current framebuffer: {}", framebuffer);
-
gl_query_core_context_set(hw_render.context_type == RETRO_HW_CONTEXT_OPENGL_CORE);
}
@@ -191,6 +188,10 @@ MelonDsDs::OpenGLRenderState::~OpenGLRenderState() noexcept {
glDeleteBuffers(1, &vbo);
glDeleteProgram(_screenProgram);
glsm_ctl(GLSM_CTL_STATE_UNBIND, nullptr);
+
+#ifdef HAVE_TRACY
+ _tracyCapture = std::nullopt;
+#endif
}
glsm_ctl(GLSM_CTL_STATE_CONTEXT_DESTROY, nullptr);
gl_query_core_context_unset();
@@ -262,6 +263,14 @@ void MelonDsDs::OpenGLRenderState::ContextReset(melonDS::NDS& nds, const CoreCon
glsm_ctl(GLSM_CTL_STATE_UNBIND, nullptr); // Always succeeds
retro::debug("Unbound GL state");
+#ifdef HAVE_TRACY
+ if (tracy::ProfilerAvailable()) {
+ // If we're using Tracy...
+ retro::debug("Using Tracy, will capture OpenGL calls");
+ _tracyCapture.emplace(_openGlDebugAvailable); // ...then get ready to capture OpenGL calls
+ }
+#endif
+
retro::debug("OpenGL context reset successfully.");
}
@@ -299,6 +308,7 @@ void MelonDsDs::OpenGLRenderState::SetUpCoreOpenGlState(const CoreConfig& config
throw shader_compilation_failed_exception("Failed to compile and link melonDS DS screen shader program.");
if (_openGlDebugAvailable) {
+ // TODO: Fall back to glLabelObjectEXT if glObjectLabel isn't available
glObjectLabel(GL_PROGRAM, _screenProgram, -1, SHADER_PROGRAM_NAME);
}
@@ -364,8 +374,9 @@ void MelonDsDs::OpenGLRenderState::Render(
glsm_ctl(GLSM_CTL_STATE_BIND, nullptr);
+ GLuint current_fbo = glsm_get_current_framebuffer();
// Tell OpenGL that we want to draw to (and read from) the screen framebuffer
- glBindFramebuffer(GL_FRAMEBUFFER, glsm_get_current_framebuffer());
+ glBindFramebuffer(GL_FRAMEBUFFER, current_fbo);
melonDS::GLRenderer& renderer = static_cast(nds.GetRenderer3D());
@@ -429,6 +440,13 @@ void MelonDsDs::OpenGLRenderState::Render(
glsm_ctl(GLSM_CTL_STATE_UNBIND, nullptr);
+#ifdef HAVE_TRACY
+ if (_tracyCapture) {
+ // TODO: Expose the FBO that the emulator's GLRenderer uses for rendering, then pass it here
+ _tracyCapture->CaptureFrame(current_fbo, config.ScaleFactor());
+ }
+#endif
+
retro::video_refresh(
RETRO_HW_FRAME_BUFFER_VALID,
screenLayout.BufferWidth(),
@@ -454,6 +472,12 @@ void MelonDsDs::OpenGLRenderState::ContextDestroyed() {
vbo = 0;
GL_ShaderConfig = {};
ubo = 0;
+ // TODO: Delete these objects, since the context hasn't been destroyed yet
+ // (just in case it's not really destroyed afterwards)
+
+#ifdef HAVE_TRACY
+ _tracyCapture = std::nullopt;
+#endif
}
void MelonDsDs::OpenGLRenderState::InitFrameState(melonDS::NDS& nds, const CoreConfig& config, const ScreenLayoutData& screenLayout) noexcept {
diff --git a/src/libretro/render/opengl.hpp b/src/libretro/render/opengl.hpp
index 75606214..8afe9e16 100644
--- a/src/libretro/render/opengl.hpp
+++ b/src/libretro/render/opengl.hpp
@@ -28,6 +28,11 @@
#include
#include
+#ifdef HAVE_TRACY
+#include "tracy.hpp"
+#include "tracy/opengl.hpp"
+#endif
+
namespace MelonDsDs {
using glm::vec2;
using glm::vec4;
@@ -85,6 +90,10 @@ namespace MelonDsDs {
} GL_ShaderConfig {};
GLuint ubo = 0;
+
+#ifdef HAVE_TRACY
+ std::optional _tracyCapture;
+#endif
};
}
diff --git a/src/libretro/tracy.hpp b/src/libretro/tracy.hpp
index bece76ad..291a524e 100644
--- a/src/libretro/tracy.hpp
+++ b/src/libretro/tracy.hpp
@@ -17,131 +17,9 @@
#ifndef MELONDS_DS_TRACY_HPP
#define MELONDS_DS_TRACY_HPP
-#if defined(__clang__) || defined(__GNUC__)
-# define TracyFunction __PRETTY_FUNCTION__
-#elif defined(_MSC_VER)
-# define TracyFunction __FUNCSIG__
-#endif
-
-#ifdef HAVE_TRACY
-
-#include
-#else
-#define ZoneNamed(x,y)
-#define ZoneNamedN(x,y,z)
-#define ZoneNamedC(x,y,z)
-#define ZoneNamedNC(x,y,z,w)
-
-#define ZoneTransient(x,y)
-#define ZoneTransientN(x,y,z)
-
-#define ZoneScoped
-#define ZoneScopedN(x)
-#define ZoneScopedC(x)
-#define ZoneScopedNC(x,y)
-
-#define ZoneText(x,y)
-#define ZoneTextV(x,y,z)
-#define ZoneName(x,y)
-#define ZoneNameV(x,y,z)
-#define ZoneColor(x)
-#define ZoneColorV(x,y)
-#define ZoneValue(x)
-#define ZoneValueV(x,y)
-#define ZoneIsActive false
-#define ZoneIsActiveV(x) false
-
-#define FrameMark
-#define FrameMarkNamed(x)
-#define FrameMarkStart(x)
-#define FrameMarkEnd(x)
-
-#define FrameImage(x,y,z,w,a)
-
-#define TracyLockable( type, varname ) type varname
-#define TracyLockableN( type, varname, desc ) type varname
-#define TracySharedLockable( type, varname ) type varname
-#define TracySharedLockableN( type, varname, desc ) type varname
-#define LockableBase( type ) type
-#define SharedLockableBase( type ) type
-#define LockMark(x) (void)x
-#define LockableName(x,y,z)
-
-#define TracyPlot(x,y)
-#define TracyPlotConfig(x,y,z,w,a)
-
-#define TracyMessage(x,y)
-#define TracyMessageL(x)
-#define TracyMessageC(x,y,z)
-#define TracyMessageLC(x,y)
-#define TracyAppInfo(x,y)
-
-#define TracyAlloc(x,y)
-#define TracyFree(x)
-#define TracySecureAlloc(x,y)
-#define TracySecureFree(x)
-
-#define TracyAllocN(x,y,z)
-#define TracyFreeN(x,y)
-#define TracySecureAllocN(x,y,z)
-#define TracySecureFreeN(x,y)
-
-#define ZoneNamedS(x,y,z)
-#define ZoneNamedNS(x,y,z,w)
-#define ZoneNamedCS(x,y,z,w)
-#define ZoneNamedNCS(x,y,z,w,a)
-
-#define ZoneTransientS(x,y,z)
-#define ZoneTransientNS(x,y,z,w)
-
-#define ZoneScopedS(x)
-#define ZoneScopedNS(x,y)
-#define ZoneScopedCS(x,y)
-#define ZoneScopedNCS(x,y,z)
-
-#define TracyAllocS(x,y,z)
-#define TracyFreeS(x,y)
-#define TracySecureAllocS(x,y,z)
-#define TracySecureFreeS(x,y)
-
-#define TracyAllocNS(x,y,z,w)
-#define TracyFreeNS(x,y,z)
-#define TracySecureAllocNS(x,y,z,w)
-#define TracySecureFreeNS(x,y,z)
-
-#define TracyMessageS(x,y,z)
-#define TracyMessageLS(x,y)
-#define TracyMessageCS(x,y,z,w)
-#define TracyMessageLCS(x,y,z)
-
-#define TracySourceCallbackRegister(x,y)
-#define TracyParameterRegister(x,y)
-#define TracyParameterSetup(x,y,z,w)
-#define TracyIsConnected false
-#define TracySetProgramName(x)
-
-#define TracyFiberEnter(x)
-#define TracyFiberLeave
-#endif
-
-#if defined(HAVE_TRACY) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGLES))
-#include "PlatformOGLPrivate.h"
-#include
-#else
-#define TracyGpuContext
-#define TracyGpuContextName(x,y)
-#define TracyGpuNamedZone(x,y,z)
-#define TracyGpuNamedZoneC(x,y,z,w)
-#define TracyGpuZone(x)
-#define TracyGpuZoneC(x,y)
-#define TracyGpuZoneTransient(x,y,z)
-#define TracyGpuCollect
-
-#define TracyGpuNamedZoneS(x,y,z,w)
-#define TracyGpuNamedZoneCS(x,y,z,w,a)
-#define TracyGpuZoneS(x,y)
-#define TracyGpuZoneCS(x,y,z)
-#define TracyGpuZoneTransientS(x,y,z,w)
-#endif
+#include "tracy/client.hpp"
+// All Tracy-related declarations were originally in this header,
+// but I moved them to a new directory to keep the codebase clean.
+// This file still exists to keep the diff smaller.
#endif //MELONDS_DS_TRACY_HPP
diff --git a/src/libretro/tracy/client.hpp b/src/libretro/tracy/client.hpp
new file mode 100644
index 00000000..5832cef6
--- /dev/null
+++ b/src/libretro/tracy/client.hpp
@@ -0,0 +1,144 @@
+/*
+ Copyright 2024 Jesse Talavera
+
+ melonDS DS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS DS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS DS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#pragma once
+
+
+#if defined(__clang__) || defined(__GNUC__)
+# define TracyFunction __PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+# define TracyFunction __FUNCSIG__
+#endif
+
+#ifdef HAVE_TRACY
+#include
+#else
+#define ZoneNamed(x,y)
+#define ZoneNamedN(x,y,z)
+#define ZoneNamedC(x,y,z)
+#define ZoneNamedNC(x,y,z,w)
+
+#define ZoneTransient(x,y)
+#define ZoneTransientN(x,y,z)
+
+#define ZoneScoped
+#define ZoneScopedN(x)
+#define ZoneScopedC(x)
+#define ZoneScopedNC(x,y)
+
+#define ZoneText(x,y)
+#define ZoneTextV(x,y,z)
+#define ZoneName(x,y)
+#define ZoneNameV(x,y,z)
+#define ZoneColor(x)
+#define ZoneColorV(x,y)
+#define ZoneValue(x)
+#define ZoneValueV(x,y)
+#define ZoneIsActive false
+#define ZoneIsActiveV(x) false
+
+#define FrameMark
+#define FrameMarkNamed(x)
+#define FrameMarkStart(x)
+#define FrameMarkEnd(x)
+
+#define FrameImage(x,y,z,w,a)
+
+#define TracyLockable( type, varname ) type varname
+#define TracyLockableN( type, varname, desc ) type varname
+#define TracySharedLockable( type, varname ) type varname
+#define TracySharedLockableN( type, varname, desc ) type varname
+#define LockableBase( type ) type
+#define SharedLockableBase( type ) type
+#define LockMark(x) (void)x
+#define LockableName(x,y,z)
+
+#define TracyPlot(x,y)
+#define TracyPlotConfig(x,y,z,w,a)
+
+#define TracyMessage(x,y)
+#define TracyMessageL(x)
+#define TracyMessageC(x,y,z)
+#define TracyMessageLC(x,y)
+#define TracyAppInfo(x,y)
+
+#define TracyAlloc(x,y)
+#define TracyFree(x)
+#define TracySecureAlloc(x,y)
+#define TracySecureFree(x)
+
+#define TracyAllocN(x,y,z)
+#define TracyFreeN(x,y)
+#define TracySecureAllocN(x,y,z)
+#define TracySecureFreeN(x,y)
+
+#define ZoneNamedS(x,y,z)
+#define ZoneNamedNS(x,y,z,w)
+#define ZoneNamedCS(x,y,z,w)
+#define ZoneNamedNCS(x,y,z,w,a)
+
+#define ZoneTransientS(x,y,z)
+#define ZoneTransientNS(x,y,z,w)
+
+#define ZoneScopedS(x)
+#define ZoneScopedNS(x,y)
+#define ZoneScopedCS(x,y)
+#define ZoneScopedNCS(x,y,z)
+
+#define TracyAllocS(x,y,z)
+#define TracyFreeS(x,y)
+#define TracySecureAllocS(x,y,z)
+#define TracySecureFreeS(x,y)
+
+#define TracyAllocNS(x,y,z,w)
+#define TracyFreeNS(x,y,z)
+#define TracySecureAllocNS(x,y,z,w)
+#define TracySecureFreeNS(x,y,z)
+
+#define TracyMessageS(x,y,z)
+#define TracyMessageLS(x,y)
+#define TracyMessageCS(x,y,z,w)
+#define TracyMessageLCS(x,y,z)
+
+#define TracySourceCallbackRegister(x,y)
+#define TracyParameterRegister(x,y)
+#define TracyParameterSetup(x,y,z,w)
+#define TracyIsConnected false
+#define TracySetProgramName(x)
+
+#define TracyFiberEnter(x)
+#define TracyFiberLeave
+#endif
+
+#if defined(HAVE_TRACY) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGLES))
+#include "PlatformOGLPrivate.h"
+#include
+#else
+#define TracyGpuContext
+#define TracyGpuContextName(x,y)
+#define TracyGpuNamedZone(x,y,z)
+#define TracyGpuNamedZoneC(x,y,z,w)
+#define TracyGpuZone(x)
+#define TracyGpuZoneC(x,y)
+#define TracyGpuZoneTransient(x,y,z)
+#define TracyGpuCollect
+
+#define TracyGpuNamedZoneS(x,y,z,w)
+#define TracyGpuNamedZoneCS(x,y,z,w,a)
+#define TracyGpuZoneS(x,y)
+#define TracyGpuZoneCS(x,y,z)
+#define TracyGpuZoneTransientS(x,y,z,w)
+#endif
\ No newline at end of file
diff --git a/src/libretro/tracy.cpp b/src/libretro/tracy/memory.cpp
similarity index 84%
rename from src/libretro/tracy.cpp
rename to src/libretro/tracy/memory.cpp
index a00011a3..ea60cc56 100644
--- a/src/libretro/tracy.cpp
+++ b/src/libretro/tracy/memory.cpp
@@ -1,5 +1,5 @@
/*
- Copyright 2023 Jesse Talavera-Greenberg
+ Copyright 2024 Jesse Talavera
melonDS DS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
@@ -14,7 +14,11 @@
with melonDS DS. If not, see http://www.gnu.org/licenses/.
*/
-#include "tracy.hpp"
+#include
+
+// Defining these functions in the global scope
+// overrides operator new and operator delete
+// for all linked translation units.
void* operator new(std::size_t count)
{
diff --git a/src/libretro/tracy/opengl.cpp b/src/libretro/tracy/opengl.cpp
new file mode 100644
index 00000000..f00b0646
--- /dev/null
+++ b/src/libretro/tracy/opengl.cpp
@@ -0,0 +1,186 @@
+/*
+ Copyright 2024 Jesse Talavera
+
+ melonDS DS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS DS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS DS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#include "opengl.hpp"
+
+#include
+#include
+
+#include "screenlayout.hpp"
+
+using std::string;
+
+MelonDsDs::OpenGlTracyCapture::OpenGlTracyCapture(bool debug) : _debug(debug) {
+ if (!tracy::ProfilerAvailable()) {
+ throw std::runtime_error("Tracy not available");
+ }
+
+ // We're going to send the OpenGL-rendered image to tracy, but for performance reasons:
+ // - We want to scale it down to the DS's native size (if necessary)
+ // - We want to do this asynchronously, so we don't block the CPU
+ // - The rendering can run ahead of the GPU by a few frames
+
+ ZoneScopedN(TracyFunction);
+ TracyGpuZone(TracyFunction);
+
+ // Allocate the textures for the resized image
+ glGenTextures(FRAME_LAG, _tracyTextures.data());
+
+ // Create some FBOs to let us write to the textures
+ glGenFramebuffers(FRAME_LAG, _tracyFbos.data());
+
+ // Create some PBOs to let the CPU read from the textures
+ glGenBuffers(FRAME_LAG, _tracyPbos.data());
+
+ if (_debug) {
+ assert(glObjectLabel != nullptr);
+ for (int i = 0; i < FRAME_LAG; ++i) {
+ fmt::basic_memory_buffer label_buffer;
+ fmt::format_to(std::back_inserter(label_buffer), "Tracy Capture Texture #{}", i);
+ glObjectLabel(GL_TEXTURE, _tracyTextures[i], -1, label_buffer.data());
+ fmt::format_to(std::back_inserter(label_buffer), "Tracy Capture FBO #{}", i);
+ glObjectLabel(GL_FRAMEBUFFER, _tracyFbos[i], -1, label_buffer.data());
+ fmt::format_to(std::back_inserter(label_buffer), "Tracy Capture PBO #{}", i);
+ glObjectLabel(GL_BUFFER, _tracyPbos[i], -1, label_buffer.data());
+ }
+ }
+
+ for (int i = 0; i < FRAME_LAG; i++) {
+ // Let's configure one texture at a time...
+ glBindTexture(GL_TEXTURE_2D, _tracyTextures[i]);
+
+ // We'll use nearest-neighbor interpolation to avoid blurring
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ // And we want our texture to be 2D, in RGBA format, big enough to hold a pair of NDS screens without mipmaps,
+ // and with each component being an unsigned byte.
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, NDS_SCREEN_WIDTH, NDS_SCREEN_HEIGHT * 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ // Now we'll configure the FBO used to draw to this texture...
+ glBindFramebuffer(GL_FRAMEBUFFER, _tracyFbos[i]);
+
+ // ...we'll attach a texture to the new FBO.
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tracyTextures[i], 0);
+
+ // And we'll create a new PBO so we can read from the texture.
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, _tracyPbos[i]);
+
+ // And the PBO has to be big enough to hold two NDS screens.
+ glBufferData(GL_PIXEL_PACK_BUFFER, NDS_SCREEN_AREA * 2 * 4, nullptr, GL_STREAM_READ);
+ }
+
+ retro::debug("Initialized OpenGL Tracy capture");
+}
+
+MelonDsDs::OpenGlTracyCapture::~OpenGlTracyCapture() noexcept {
+ ZoneScopedN(TracyFunction);
+ TracyGpuZone(TracyFunction);
+
+ // Clean up the textures
+ glDeleteTextures(4, _tracyTextures.data());
+
+ // Clean up the FBOs
+ glDeleteFramebuffers(4, _tracyFbos.data());
+
+ // Clean up the PBOs
+ glDeleteBuffers(4, _tracyPbos.data());
+
+ // Clean up the fences
+ for (int i = 0; i < 4; i++) {
+ glDeleteSync(_tracyFences[i]);
+ }
+}
+
+void MelonDsDs::OpenGlTracyCapture::CaptureFrame(GLuint current_fbo, float scale) noexcept {
+ if (!tracy::ProfilerAvailable()) {
+ return;
+ }
+
+ ZoneScopedN(TracyFunction);
+ TracyGpuZone(TracyFunction);
+
+ // TODO: Capture the OpenGL renderer's buffer, not the RetroArch framebuffer
+ while (!_tracyQueue.empty()) {
+ // Until we've checked all the capture fences...
+
+ // Pull the oldest capture fence from the queue
+ const auto fiIdx = _tracyQueue.front();
+
+ // Check this fence, but don't wait for it
+ // If the fence hasn't gone off yet, then stop checking
+ // (none of the newer fences will have been signaled yet)
+ if (glClientWaitSync(_tracyFences[fiIdx], 0, 0) == GL_TIMEOUT_EXPIRED) break;
+
+ // The fence has been signaled!
+ // That means the capture we want is ready to send to Tracy
+
+ // Thanks for your hard work, fence; you're no longer needed
+ glDeleteSync(_tracyFences[fiIdx]);
+ _tracyFences[fiIdx] = nullptr;
+
+ // Get the capture PBO ready to read its contents out...
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, _tracyPbos[fiIdx]);
+
+ // Expose the capture PBO's contents to RAM
+ auto ptr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, NDS_SCREEN_AREA * 2 * 4, GL_MAP_READ_BIT);
+
+ // Send the frame to Tracy
+ FrameImage(ptr, NDS_SCREEN_WIDTH, NDS_SCREEN_HEIGHT * 2, _tracyQueue.size(), true);
+
+ // We're done with the capture PBO
+ glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
+ _tracyQueue.pop();
+ }
+
+ // TODO: Only downscale if playing at a scale factor other than 1
+ assert(_tracyQueue.empty() || _tracyQueue.front() != _tracyIndex); // check for buffer overrun
+
+ // Get the capture FBO ready to receive the screen(s)...
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _tracyFbos[_tracyIndex]);
+
+ // Copy the active framebuffer's contents to the capture FBO, downscaling along the way
+ glBlitFramebuffer(0, 0, NDS_SCREEN_WIDTH * scale, NDS_SCREEN_HEIGHT * 2 * scale, 0, 0, NDS_SCREEN_WIDTH, NDS_SCREEN_HEIGHT * 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+
+ // Okay, we're done downscaling the screen
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
+
+ // Get the capture FBO ready to read its contents out...
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, _tracyFbos[_tracyIndex]);
+
+ // Get the PBO ready to receive the downscaled screen(s)...
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, _tracyPbos[_tracyIndex]);
+
+ // Actually read the screen into the PBO
+ // (nullptr means to read data into the bound PBO, not to the CPU)
+ glReadPixels(0, 0, NDS_SCREEN_WIDTH, NDS_SCREEN_HEIGHT * 2, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ // Okay, now we're done with the capture FBO; you can have the current FBO back
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, current_fbo);
+
+ // Create a new fence that'll go off when every OpenGL command that came before it finishes
+ // (No other acceptable arguments are currently defined for glFenceSync)
+ _tracyFences[_tracyIndex] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ if (_debug) {
+ fmt::basic_memory_buffer label_buffer;
+ fmt::format_to(std::back_inserter(label_buffer), "Tracy Capture Fence Slot #{}", _tracyIndex);
+ glObjectPtrLabel(_tracyFences[_tracyIndex], -1, label_buffer.data());
+ }
+
+ // "Hang onto this flag for now, we'll check it again next frame."
+ _tracyQueue.push(_tracyIndex);
+ _tracyIndex = (_tracyIndex + 1) % 4;
+}
\ No newline at end of file
diff --git a/src/libretro/tracy/opengl.hpp b/src/libretro/tracy/opengl.hpp
new file mode 100644
index 00000000..de6fbe98
--- /dev/null
+++ b/src/libretro/tracy/opengl.hpp
@@ -0,0 +1,51 @@
+/*
+ Copyright 2024 Jesse Talavera
+
+ melonDS DS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS DS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS DS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#pragma once
+
+#if defined(HAVE_TRACY) && (defined(HAVE_OPENGL) || defined(HAVE_OPENGLES))
+#include
+#include
+
+#include "PlatformOGLPrivate.h"
+#include
+
+namespace MelonDsDs {
+ /// \brief Class for capturing OpenGL frames for Tracy.
+ /// Suitable for both OpenGL renderers.
+ class OpenGlTracyCapture {
+ public:
+ OpenGlTracyCapture(bool debug);
+ ~OpenGlTracyCapture() noexcept;
+
+ // Copying the OpenGL objects is too much of a hassle.
+ OpenGlTracyCapture(const OpenGlTracyCapture&) = delete;
+ OpenGlTracyCapture& operator=(const OpenGlTracyCapture&) = delete;
+ OpenGlTracyCapture(OpenGlTracyCapture&&) = delete;
+ OpenGlTracyCapture& operator=(OpenGlTracyCapture&&) = delete;
+ void CaptureFrame(GLuint current_fbo, float scale) noexcept;
+ private:
+ static constexpr int FRAME_LAG = 4;
+ std::array _tracyTextures;
+ std::array _tracyFbos;
+ std::array _tracyPbos;
+ std::array _tracyFences;
+ int _tracyIndex = 0;
+ std::queue _tracyQueue;
+ bool _debug;
+ };
+}
+#endif
\ No newline at end of file
diff --git a/test/cmake/Basics.cmake b/test/cmake/Basics.cmake
index a75b430e..b1c93c44 100644
--- a/test/cmake/Basics.cmake
+++ b/test/cmake/Basics.cmake
@@ -324,6 +324,14 @@ add_python_test(
CORE_OPTION melonds_boot_mode=direct
)
+add_python_test(
+ NAME "Core accepts microphone input with Blow mode"
+ TEST_MODULE basics.core_accepts_microphone_input
+ CONTENT "${MICRECORD_NDS}"
+ CORE_OPTION melonds_boot_mode=direct
+ CORE_OPTION melonds_mic_input=blow
+)
+
add_python_test(
NAME "Core queries device power state"
TEST_MODULE basics.core_gets_power_state
diff --git a/test/python/basics/core_defines_controller_info.py b/test/python/basics/core_defines_controller_info.py
index 22c94187..ff92d8fd 100644
--- a/test/python/basics/core_defines_controller_info.py
+++ b/test/python/basics/core_defines_controller_info.py
@@ -7,7 +7,7 @@
assert info is not None
assert len(info) > 0
- assert all(len(i) for i in info)
+ assert all(i.desc for i in info)
# Testing this _after_ unloading the core to ensure the data is still valid
diff --git a/test/python/basics/core_rotates_screen.py b/test/python/basics/core_rotates_screen.py
index 61401166..2746e9be 100644
--- a/test/python/basics/core_rotates_screen.py
+++ b/test/python/basics/core_rotates_screen.py
@@ -34,7 +34,7 @@ def generate_input():
layout1 = screen_layout()
assert layout1 == 0, f"Expected screen layout 0 (TopBottom), got {layout1}"
- frame1 = session.video.screenshot()
+ frame1 = session.video.screenshot(False)
geometry1 = session.video.geometry
assert frame1 is not None
@@ -54,7 +54,7 @@ def generate_input():
layout2 = screen_layout()
assert layout2 == 8, f"Expected screen layout 8 (TurnLeft), got {layout2}"
- frame2 = session.video.screenshot()
+ frame2 = session.video.screenshot(False)
geometry2 = session.video.geometry
assert frame2 is not None
diff --git a/test/python/firmware/core_rejects_invalid_nds_firmware_id.py b/test/python/firmware/core_rejects_invalid_nds_firmware_id.py
index 1403b554..3ecaafdf 100644
--- a/test/python/firmware/core_rejects_invalid_nds_firmware_id.py
+++ b/test/python/firmware/core_rejects_invalid_nds_firmware_id.py
@@ -9,7 +9,7 @@
assert len(firmware) == 262144
firmware[0x8:0xC] = b"NNNN"
-badfirmwarepath = os.path.join(prelude.core_system_dir, "badfirmware.bin")
+badfirmwarepath = os.path.join(prelude.core_system_dir, b"badfirmware.bin")
with open(badfirmwarepath, "wb") as f:
f.write(firmware)
diff --git a/test/python/firmware/firmware_not_overwritten_when_switching_console_mode.py b/test/python/firmware/firmware_not_overwritten_when_switching_console_mode.py
index 2bb71154..d14d746b 100644
--- a/test/python/firmware/firmware_not_overwritten_when_switching_console_mode.py
+++ b/test/python/firmware/firmware_not_overwritten_when_switching_console_mode.py
@@ -5,9 +5,9 @@
import prelude
nds_firmware_path = os.environ["NDS_FIRMWARE"]
-nds_firmware_basename = os.path.basename(nds_firmware_path)
+nds_firmware_basename = os.path.basename(nds_firmware_path).encode()
dsi_firmware_path = os.environ["DSI_FIRMWARE"]
-dsi_firmware_basename = os.path.basename(dsi_firmware_path)
+dsi_firmware_basename = os.path.basename(dsi_firmware_path).encode()
with open(nds_firmware_path, "rb") as firmware_file:
nds_firmware = firmware_file.read()
diff --git a/test/python/firmware/nds_firmware_not_overwritten.py b/test/python/firmware/nds_firmware_not_overwritten.py
index 9bbe24af..e41007ad 100644
--- a/test/python/firmware/nds_firmware_not_overwritten.py
+++ b/test/python/firmware/nds_firmware_not_overwritten.py
@@ -6,7 +6,7 @@
nds_firmware_path = os.environ["NDS_FIRMWARE"]
nds_firmware_basename = os.path.basename(nds_firmware_path)
-test_nds_firmware_path = os.path.join(prelude.core_system_dir, nds_firmware_basename)
+test_nds_firmware_path = os.path.join(prelude.core_system_dir, nds_firmware_basename.encode())
original_nds_firmware_size = os.stat(nds_firmware_path).st_size
assert original_nds_firmware_size > 0, f"{nds_firmware_path} is empty"
diff --git a/test/python/prelude.py b/test/python/prelude.py
index d28a7a35..adc5c3d9 100644
--- a/test/python/prelude.py
+++ b/test/python/prelude.py
@@ -1,29 +1,30 @@
import os
import shutil
import sys
-import tempfile
import libretro
-from libretro import SubsystemContent, SessionBuilder, DefaultPathDriver
+from libretro import SubsystemContent, SessionBuilder, TempDirPathDriver
if not __debug__:
raise RuntimeError("The melonDS DS test suite should not be run with -O")
SYSTEM_FILES = ("ARM7_BIOS", "ARM9_BIOS", "ARM7_DSI_BIOS", "ARM9_DSI_BIOS", "NDS_FIRMWARE", "DSI_FIRMWARE", "DSI_NAND")
-testdir = tempfile.TemporaryDirectory(".libretro")
-system_dir = os.path.join(testdir.name, "system")
-assets_dir = os.path.join(testdir.name, "assets")
-playlist_dir = os.path.join(testdir.name, "playlist")
-save_dir = os.path.join(testdir.name, "savefiles")
+core_path = sys.argv[1]
+path_driver = TempDirPathDriver(core_path, ".libretro")
+testdir = path_driver.root_dir
+system_dir = path_driver.system_dir
+assets_dir = path_driver.core_assets_dir
+playlist_dir = path_driver.playlist_dir
+save_dir = path_driver.save_dir
save_directory = save_dir
-savestate_directory = os.path.join(testdir.name, "states")
-core_system_dir = os.path.join(system_dir, "melonDS DS")
-core_save_dir = os.path.join(save_dir, "melonDS DS")
-wfcsettings_path = os.path.join(core_system_dir, "wfcsettings.bin")
-dldi_sd_card_path = os.path.join(core_save_dir, "dldi_sd_card.bin")
-dldi_sd_card_sync_path = os.path.join(core_save_dir, "dldi_sd_card")
-
-print("Test dir:", testdir.name)
+savestate_directory = os.path.join(testdir, b"states")
+core_system_dir = os.path.join(system_dir, b"melonDS DS")
+core_save_dir = os.path.join(save_dir, b"melonDS DS")
+wfcsettings_path = os.path.join(core_system_dir, b"wfcsettings.bin")
+dldi_sd_card_path = os.path.join(core_save_dir, b"dldi_sd_card.bin")
+dldi_sd_card_sync_path = os.path.join(core_save_dir, b"dldi_sd_card")
+
+print("Test dir:", testdir)
print("System dir:", system_dir)
print("Save dir:", save_dir)
print("Savestate dir:", savestate_directory)
@@ -35,13 +36,12 @@
for _f in SYSTEM_FILES:
if _f in os.environ:
basename = os.path.basename(os.environ[_f])
- targetpath = os.path.join(core_system_dir, basename)
+ targetpath = os.path.join(core_system_dir, basename.encode())
print(f"Copying {os.environ[_f]} to {targetpath}")
shutil.copyfile(os.environ[_f], targetpath)
options_string = os.getenv("RETRO_CORE_OPTIONS")
subsystem = os.getenv("SUBSYSTEM")
-core_path = sys.argv[1]
content_path = sys.argv[2] if len(sys.argv) > 2 and sys.argv[2] else None
content_paths = tuple(s for s in sys.argv[2:] if s) if len(sys.argv) > 2 else ()
@@ -73,15 +73,7 @@ def builder(**kwargs) -> SessionBuilder:
libretro
.defaults(core_path)
.with_content(content)
- .with_paths(
- DefaultPathDriver(
- corepath=core_path,
- system=system_dir,
- assets=assets_dir,
- save=save_dir,
- playlist=playlist_dir,
- )
- )
+ .with_paths(path_driver)
.with_options(options)
)
diff --git a/test/requirements.txt b/test/requirements.txt
index 1e1806d2..76fde4dc 100644
--- a/test/requirements.txt
+++ b/test/requirements.txt
@@ -1,6 +1,6 @@
# melonDS DS doesn't support OpenGL on macOS,
# so there's no need to install OpenGL dependencies
-libretro.py==0.1.11
-libretro.py[opengl]==0.1.11 ; sys_platform != "darwin"
+libretro.py==0.3.1
+libretro.py[opengl]==0.3.1 ; sys_platform != "darwin"
Pillow==10.3.0
more-itertools==10.2.* ; python_version < '3.12'