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'