update
This commit is contained in:
commit
9f248f10dd
|
@ -9,6 +9,13 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
MELONDS_VERSION_SUFFIX: " RC"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-macos:
|
build-macos:
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -28,12 +35,13 @@ jobs:
|
||||||
- name: Set up vcpkg
|
- name: Set up vcpkg
|
||||||
uses: lukka/run-vcpkg@v11
|
uses: lukka/run-vcpkg@v11
|
||||||
with:
|
with:
|
||||||
vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: lukka/run-cmake@v10
|
uses: lukka/run-cmake@v10
|
||||||
with:
|
with:
|
||||||
configurePreset: release-mac-${{ matrix.arch }}
|
configurePreset: release-mac-${{ matrix.arch }}
|
||||||
buildPreset: release-mac-${{ matrix.arch }}
|
buildPreset: release-mac-${{ matrix.arch }}
|
||||||
|
configurePresetAdditionalArgs: "['-DMELONDS_EMBED_BUILD_INFO=ON']"
|
||||||
- name: Compress app bundle
|
- name: Compress app bundle
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -4,82 +4,60 @@ on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
- ci/*
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
MELONDS_VERSION_SUFFIX: " RC"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-x86_64:
|
build:
|
||||||
|
continue-on-error: true
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- runner: ubuntu-22.04
|
||||||
name: x86_64
|
name: x86_64
|
||||||
runs-on: ubuntu-22.04
|
- runner: ubuntu-22.04-arm
|
||||||
|
name: aarch64
|
||||||
|
|
||||||
|
name: ${{ matrix.arch.name }}
|
||||||
|
runs-on: ${{ matrix.arch.runner }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
name: Check out sources
|
name: Check out sources
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list
|
|
||||||
sudo apt update
|
sudo apt update
|
||||||
sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \
|
sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev libenet-dev \
|
||||||
qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2
|
qt6-{base,base-private,multimedia}-dev libqt6svg6-dev libarchive-dev libzstd-dev libfuse2
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr
|
run: cmake -B build -G Ninja -DCMAKE_INSTALL_PREFIX=/usr -DMELONDS_EMBED_BUILD_INFO=ON
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cmake --build build
|
cmake --build build
|
||||||
DESTDIR=AppDir cmake --install build
|
DESTDIR=AppDir cmake --install build
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: melonDS-ubuntu-x86_64
|
name: melonDS-ubuntu-${{ matrix.arch.name }}
|
||||||
path: AppDir/usr/bin/melonDS
|
path: AppDir/usr/bin/melonDS
|
||||||
- name: Fetch AppImage tools
|
- name: Fetch AppImage tools
|
||||||
run: |
|
run: |
|
||||||
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.arch.name }}.AppImage
|
||||||
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-${{ matrix.arch.name }}.AppImage
|
||||||
chmod a+x linuxdeploy-*.AppImage
|
chmod a+x linuxdeploy-*.AppImage
|
||||||
- name: Build the AppImage
|
- name: Build the AppImage
|
||||||
env:
|
env:
|
||||||
QMAKE: /usr/lib/qt6/bin/qmake
|
QMAKE: /usr/lib/qt6/bin/qmake
|
||||||
run: |
|
run: |
|
||||||
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage
|
./linuxdeploy-${{ matrix.arch.name }}.AppImage --appdir AppDir --plugin qt --output appimage
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: melonDS-appimage-x86_64
|
name: melonDS-appimage-${{ matrix.arch.name }}
|
||||||
path: melonDS*.AppImage
|
path: melonDS*.AppImage
|
||||||
|
|
||||||
build-aarch64:
|
|
||||||
name: aarch64
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: ubuntu:22.04
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Prepare system
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
dpkg --add-architecture arm64
|
|
||||||
sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new"
|
|
||||||
rm /etc/apt/sources.list
|
|
||||||
mv /etc/apt/sources.list{.new,}
|
|
||||||
apt update
|
|
||||||
apt -y full-upgrade
|
|
||||||
apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \
|
|
||||||
{libsdl2,qt6-{base,base-private,multimedia},libqt6svg6,libarchive,libzstd,libenet}-dev:arm64 \
|
|
||||||
pkg-config dpkg-dev
|
|
||||||
- name: Check out source
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Configure
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cmake -B build -G Ninja \
|
|
||||||
-DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \
|
|
||||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
|
|
||||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
|
|
||||||
-DUSE_QT6=ON
|
|
||||||
- name: Build
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cmake --build build
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: melonDS-ubuntu-aarch64
|
|
||||||
path: build/melonDS
|
|
||||||
|
|
|
@ -9,6 +9,13 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
VCPKG_COMMIT: 6f29f12e82a8293156836ad81cc9bf5af41fe836
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
MELONDS_VERSION_SUFFIX: " RC"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
@ -27,9 +34,9 @@ jobs:
|
||||||
- name: Set up vcpkg
|
- name: Set up vcpkg
|
||||||
uses: lukka/run-vcpkg@v11
|
uses: lukka/run-vcpkg@v11
|
||||||
with:
|
with:
|
||||||
vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake --preset=release-mingw-x86_64
|
run: cmake --preset=release-mingw-x86_64 -DMELONDS_EMBED_BUILD_INFO=ON
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cmake --build --preset=release-mingw-x86_64
|
run: cmake --build --preset=release-mingw-x86_64
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
# Building melonDS
|
||||||
|
|
||||||
|
* [Linux](#linux)
|
||||||
|
* [Windows](#windows)
|
||||||
|
* [macOS](#macos)
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
1. Install dependencies:
|
||||||
|
* Ubuntu:
|
||||||
|
* All versions: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libarchive-dev libenet-dev libzstd-dev`
|
||||||
|
* 24.04: `sudo apt install qt6-{base,base-private,multimedia,svg}-dev`
|
||||||
|
* 22.04: `sudo apt install qtbase6-dev qtbase6-private-dev qtmultimedia6-dev libqt6svg6-dev`
|
||||||
|
* Older versions: `sudo apt install qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev`
|
||||||
|
Also add `-DUSE_QT6=OFF` to the first CMake command below.
|
||||||
|
* Fedora: `sudo dnf install gcc-c++ cmake extra-cmake-modules SDL2-devel libarchive-devel enet-devel libzstd-devel qt6-{qtbase,qtbase-private,qtmultimedia,qtsvg}-devel wayland-devel`
|
||||||
|
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt6-{base,multimedia,svg} libarchive enet zstd`
|
||||||
|
2. Download the melonDS repository and prepare:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/melonDS-emu/melonDS
|
||||||
|
cd melonDS
|
||||||
|
```
|
||||||
|
3. Compile:
|
||||||
|
```bash
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build -j$(nproc --all)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
1. Install [MSYS2](https://www.msys2.org/)
|
||||||
|
2. Open the MSYS2 terminal from the Start menu:
|
||||||
|
* For x64 systems (most common), use **MSYS2 UCRT64**
|
||||||
|
* For ARM64 systems, use **MSYS2 CLANGARM64**
|
||||||
|
3. Update the packages using `pacman -Syu` and reopen the same terminal if it asks you to
|
||||||
|
4. Install git and clone the repository
|
||||||
|
```bash
|
||||||
|
pacman -S git
|
||||||
|
git clone https://github.com/melonDS-emu/melonDS
|
||||||
|
cd melonDS
|
||||||
|
```
|
||||||
|
5. Install dependencies:
|
||||||
|
Replace `<prefix>` below with `mingw-w64-ucrt-x86_64` on x64 systems, or `mingw-w64-clang-aarch64` on ARM64 systems.
|
||||||
|
```bash
|
||||||
|
pacman -S <prefix>-{toolchain,cmake,SDL2,libarchive,enet,zstd}
|
||||||
|
```
|
||||||
|
6. Install Qt and configure the build directory
|
||||||
|
* Dynamic builds (with DLLs)
|
||||||
|
1. Install Qt: `pacman -S <prefix>-{qt6-base,qt6-svg,qt6-multimedia,qt6-svg,qt6-tools}`
|
||||||
|
2. Set up the build directory with `cmake -B build`
|
||||||
|
* Static builds (without DLLs, standalone executable)
|
||||||
|
1. Install Qt: `pacman -S <prefix>-qt5-static`
|
||||||
|
(Note: As of writing, the `qt6-static` package does not work.)
|
||||||
|
2. Set up the build directory with `cmake -B build -DBUILD_STATIC=ON -DUSE_QT6=OFF -DCMAKE_PREFIX_PATH=$MSYSTEM_PREFIX/qt5-static`
|
||||||
|
7. Compile: `cmake --build build`
|
||||||
|
|
||||||
|
If everything went well, melonDS should now be in the `build` folder. For dynamic builds, you may need to run melonDS from the MSYS2 terminal in order for it to find the required DLLs.
|
||||||
|
|
||||||
|
## macOS
|
||||||
|
1. Install the [Homebrew Package Manager](https://brew.sh)
|
||||||
|
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd`
|
||||||
|
3. Download the melonDS repository and prepare:
|
||||||
|
```zsh
|
||||||
|
git clone https://github.com/melonDS-emu/melonDS
|
||||||
|
cd melonDS
|
||||||
|
```
|
||||||
|
4. Compile:
|
||||||
|
```zsh
|
||||||
|
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)"
|
||||||
|
cmake --build build -j$(sysctl -n hw.logicalcpu)
|
||||||
|
```
|
||||||
|
If everything went well, melonDS.app should now be in the `build` directory.
|
||||||
|
|
||||||
|
### Self-contained app bundle
|
||||||
|
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
|
||||||
|
../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
|
||||||
|
|
||||||
|
## Nix (macOS/Linux)
|
||||||
|
|
||||||
|
melonDS provides a Nix flake with support for both macOS and Linux. The [Nix package manager](https://nixos.org) needs to be installed to use it.
|
||||||
|
|
||||||
|
* To run melonDS, just type `nix run github:melonDS-emu/melonDS`.
|
||||||
|
* To get a shell for development, clone the melonDS repository and type `nix develop` in its directory.
|
|
@ -9,6 +9,7 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
|
||||||
|
|
||||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
|
||||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
|
set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_CURRENT_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake")
|
||||||
|
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||||
|
|
||||||
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
|
option(USE_VCPKG "Use vcpkg for dependency packages" OFF)
|
||||||
if (USE_VCPKG)
|
if (USE_VCPKG)
|
||||||
|
@ -16,7 +17,7 @@ if (USE_VCPKG)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(melonDS
|
project(melonDS
|
||||||
VERSION 0.9.5
|
VERSION 1.0
|
||||||
DESCRIPTION "DS emulator, sorta"
|
DESCRIPTION "DS emulator, sorta"
|
||||||
HOMEPAGE_URL "https://melonds.kuribo64.net"
|
HOMEPAGE_URL "https://melonds.kuribo64.net"
|
||||||
LANGUAGES C CXX)
|
LANGUAGES C CXX)
|
||||||
|
@ -27,8 +28,7 @@ include(CMakeDependentOption)
|
||||||
include(CheckIPOSupported)
|
include(CheckIPOSupported)
|
||||||
|
|
||||||
include(SetupCCache)
|
include(SetupCCache)
|
||||||
|
include(Sanitizers)
|
||||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
|
|
|
@ -27,10 +27,6 @@
|
||||||
"binaryDir": "${sourceDir}/build/release-mingw-x86_64",
|
"binaryDir": "${sourceDir}/build/release-mingw-x86_64",
|
||||||
"generator": "Ninja",
|
"generator": "Ninja",
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"USE_QT6": {
|
|
||||||
"type": "BOOL",
|
|
||||||
"value": "ON"
|
|
||||||
},
|
|
||||||
"BUILD_STATIC": {
|
"BUILD_STATIC": {
|
||||||
"type": "BOOL",
|
"type": "BOOL",
|
||||||
"value": "ON"
|
"value": "ON"
|
||||||
|
|
70
README.md
70
README.md
|
@ -32,75 +32,7 @@ DS BIOS dumps from a DSi or 3DS can be used with no compatibility issues. DSi BI
|
||||||
As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though!
|
As for the rest, the interface should be pretty straightforward. If you have a question, don't hesitate to ask, though!
|
||||||
|
|
||||||
## How to build
|
## How to build
|
||||||
|
See [BUILD.md](./BUILD.md) for build instructions.
|
||||||
### Linux
|
|
||||||
1. Install dependencies:
|
|
||||||
* Ubuntu 22.04: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev`
|
|
||||||
* Older Ubuntu: `sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libqt5svg5-dev libarchive-dev libenet-dev libzstd-dev`
|
|
||||||
* Arch Linux: `sudo pacman -S base-devel cmake extra-cmake-modules git libpcap sdl2 qt5-base qt5-multimedia qt5-svg libarchive enet zstd`
|
|
||||||
3. Download the melonDS repository and prepare:
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/melonDS-emu/melonDS
|
|
||||||
cd melonDS
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Compile:
|
|
||||||
```bash
|
|
||||||
cmake -B build
|
|
||||||
cmake --build build -j$(nproc --all)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
1. Install [MSYS2](https://www.msys2.org/)
|
|
||||||
2. Open the **MSYS2 MinGW 64-bit** terminal
|
|
||||||
3. Update the packages using `pacman -Syu` and reopen the terminal if it asks you to
|
|
||||||
4. Install git to clone the repository
|
|
||||||
```bash
|
|
||||||
pacman -S git
|
|
||||||
```
|
|
||||||
5. Download the melonDS repository and prepare:
|
|
||||||
```bash
|
|
||||||
git clone https://github.com/melonDS-emu/melonDS
|
|
||||||
cd melonDS
|
|
||||||
```
|
|
||||||
#### Dynamic builds (with DLLs)
|
|
||||||
5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-base,qt5-svg,qt5-multimedia,qt5-svg,qt5-tools,libarchive,enet,zstd}`
|
|
||||||
6. Compile:
|
|
||||||
```bash
|
|
||||||
cmake -B build
|
|
||||||
cmake --build build
|
|
||||||
cd build
|
|
||||||
../tools/msys-dist.sh
|
|
||||||
```
|
|
||||||
If everything went well, melonDS and the libraries it needs should now be in the `dist` folder.
|
|
||||||
|
|
||||||
#### Static builds (without DLLs, standalone executable)
|
|
||||||
5. Install dependencies: `pacman -S mingw-w64-x86_64-{cmake,SDL2,toolchain,qt5-static,libarchive,enet,zstd}`
|
|
||||||
6. Compile:
|
|
||||||
```bash
|
|
||||||
cmake -B build -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static
|
|
||||||
cmake --build build
|
|
||||||
```
|
|
||||||
If everything went well, melonDS should now be in the `build` folder.
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
1. Install the [Homebrew Package Manager](https://brew.sh)
|
|
||||||
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libarchive enet zstd`
|
|
||||||
3. Download the melonDS repository and prepare:
|
|
||||||
```zsh
|
|
||||||
git clone https://github.com/melonDS-emu/melonDS
|
|
||||||
cd melonDS
|
|
||||||
```
|
|
||||||
4. Compile:
|
|
||||||
```zsh
|
|
||||||
cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)"
|
|
||||||
cmake --build build -j$(sysctl -n hw.logicalcpu)
|
|
||||||
```
|
|
||||||
If everything went well, melonDS.app should now be in the `build` directory.
|
|
||||||
|
|
||||||
#### Self-contained app bundle
|
|
||||||
If you want an app bundle that can be distributed to other computers without needing to install dependencies through Homebrew, you can additionally run `
|
|
||||||
../tools/mac-libs.rb .` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
|
|
||||||
|
|
||||||
## TODO LIST
|
## TODO LIST
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
|
||||||
endif()
|
endif()
|
||||||
FetchContent_Declare(vcpkg
|
FetchContent_Declare(vcpkg
|
||||||
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
||||||
GIT_TAG 2024.08.23
|
GIT_TAG 2025.01.13
|
||||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||||
FetchContent_MakeAvailable(vcpkg)
|
FetchContent_MakeAvailable(vcpkg)
|
||||||
endif()
|
endif()
|
||||||
|
@ -19,11 +19,7 @@ set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets")
|
||||||
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
|
option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON)
|
||||||
|
|
||||||
# Duplicated here because it needs to be set before project()
|
# Duplicated here because it needs to be set before project()
|
||||||
if (NOT WIN32)
|
option(USE_QT6 "Use Qt 6 instead of Qt 5" ON)
|
||||||
option(USE_QT6 "Build using Qt 6 instead of 5" ON)
|
|
||||||
else()
|
|
||||||
option(USE_QT6 "Build using Qt 6 instead of 5" OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Since the Linux build pulls in glib anyway, we can just use upstream libslirp
|
# Since the Linux build pulls in glib anyway, we can just use upstream libslirp
|
||||||
if (UNIX AND NOT APPLE)
|
if (UNIX AND NOT APPLE)
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
set(SANITIZE "" CACHE STRING "Sanitizers to enable.")
|
||||||
|
|
||||||
|
string(REGEX MATCHALL "[^,]+" ENABLED_SANITIZERS "${SANITIZE}")
|
||||||
|
|
||||||
|
foreach(SANITIZER ${ENABLED_SANITIZERS})
|
||||||
|
add_compile_options("-fsanitize=${SANITIZER}")
|
||||||
|
add_link_options("-fsanitize=${SANITIZER}")
|
||||||
|
endforeach()
|
12
flake.lock
12
flake.lock
|
@ -5,11 +5,11 @@
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710146030,
|
"lastModified": 1731533236,
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -20,11 +20,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1725432240,
|
"lastModified": 1739020877,
|
||||||
"narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
|
"narHash": "sha256-mIvECo/NNdJJ/bXjNqIh8yeoSjVLAuDuTUzAo7dzs8Y=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ad416d066ca1222956472ab7d0555a6946746a80",
|
"rev": "a79cfe0ebd24952b580b1cf08cd906354996d547",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
37
flake.nix
37
flake.nix
|
@ -12,42 +12,50 @@
|
||||||
inherit (pkgs.lib) cmakeBool optionals makeLibraryPath;
|
inherit (pkgs.lib) cmakeBool optionals makeLibraryPath;
|
||||||
inherit (pkgs.stdenv) isLinux isDarwin;
|
inherit (pkgs.stdenv) isLinux isDarwin;
|
||||||
|
|
||||||
versionSuffix = with self; if sourceInfo?dirtyShortRev
|
revision = with self; if sourceInfo?dirtyRev
|
||||||
|
then sourceInfo.dirtyRev
|
||||||
|
else sourceInfo.rev;
|
||||||
|
shortRevision = with self; if sourceInfo?dirtyShortRev
|
||||||
then sourceInfo.dirtyShortRev
|
then sourceInfo.dirtyShortRev
|
||||||
else sourceInfo.shortRev;
|
else sourceInfo.shortRev;
|
||||||
|
|
||||||
melonDS = pkgs.stdenv.mkDerivation {
|
melonDS = pkgs.stdenv.mkDerivation {
|
||||||
pname = "melonDS";
|
pname = "melonDS";
|
||||||
version = "0.9.5-${versionSuffix}";
|
version = "1.0-${shortRevision}";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
cmake
|
cmake
|
||||||
ninja
|
ninja
|
||||||
pkg-config
|
pkg-config
|
||||||
kdePackages.wrapQtAppsHook
|
qt6.wrapQtAppsHook
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = (with pkgs; [
|
buildInputs = (with pkgs; [
|
||||||
kdePackages.qtbase
|
qt6.qtbase
|
||||||
kdePackages.qtmultimedia
|
qt6.qtmultimedia
|
||||||
extra-cmake-modules
|
|
||||||
SDL2
|
SDL2
|
||||||
zstd
|
zstd
|
||||||
libarchive
|
libarchive
|
||||||
libGL
|
libGL
|
||||||
libslirp
|
libslirp
|
||||||
enet
|
enet
|
||||||
]) ++ optionals isLinux [
|
]) ++ optionals (!isDarwin) (with pkgs; [
|
||||||
pkgs.wayland
|
kdePackages.extra-cmake-modules
|
||||||
pkgs.kdePackages.qtwayland
|
qt6.qtwayland
|
||||||
];
|
wayland
|
||||||
|
]);
|
||||||
|
|
||||||
cmakeFlags = [
|
cmakeFlags = [
|
||||||
(cmakeBool "USE_QT6" true)
|
(cmakeBool "USE_QT6" true)
|
||||||
(cmakeBool "USE_SYSTEM_LIBSLIRP" true)
|
(cmakeBool "USE_SYSTEM_LIBSLIRP" true)
|
||||||
|
(cmakeBool "MELONDS_EMBED_BUILD_INFO" true)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
env.MELONDS_GIT_HASH = revision;
|
||||||
|
env.MELONDS_GIT_BRANCH = "(unknown)";
|
||||||
|
env.MELONDS_BUILD_PROVIDER = "Nix";
|
||||||
|
|
||||||
qtWrapperArgs = optionals isLinux [
|
qtWrapperArgs = optionals isLinux [
|
||||||
"--prefix LD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap pkgs.wayland ]}"
|
"--prefix LD_LIBRARY_PATH : ${makeLibraryPath [ pkgs.libpcap pkgs.wayland ]}"
|
||||||
] ++ optionals isDarwin [
|
] ++ optionals isDarwin [
|
||||||
|
@ -68,6 +76,9 @@
|
||||||
devShells = {
|
devShells = {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
inputsFrom = [ self.packages.${system}.default ];
|
inputsFrom = [ self.packages.${system}.default ];
|
||||||
|
packages = with pkgs; [
|
||||||
|
qt6.qttools
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Shell for building static melonDS release builds with vcpkg
|
# Shell for building static melonDS release builds with vcpkg
|
||||||
|
@ -84,7 +95,13 @@
|
||||||
libtool
|
libtool
|
||||||
ninja
|
ninja
|
||||||
pkg-config
|
pkg-config
|
||||||
|
python3
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Undo the SDK setup done by nixpkgs so we can use AppleClang
|
||||||
|
shellHook = ''
|
||||||
|
unset DEVELOPER_DIR SDKROOT MACOSX_DEPLOYMENT_TARGET
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
|
@ -2,5 +2,6 @@
|
||||||
<RCC version="1.0">
|
<RCC version="1.0">
|
||||||
<qresource>
|
<qresource>
|
||||||
<file alias="melon-icon">icon/melon_256x256.png</file>
|
<file alias="melon-icon">icon/melon_256x256.png</file>
|
||||||
|
<file alias="melon-logo">melon384.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
//include version information in .exe, modify these values to match your needs
|
//include version information in .exe, modify these values to match your needs
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
|
FILEVERSION ${MELON_RC_VERSION}
|
||||||
PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
|
PRODUCTVERSION ${MELON_RC_VERSION}
|
||||||
FILETYPE VFT_APP
|
FILETYPE VFT_APP
|
||||||
{
|
{
|
||||||
BLOCK "StringFileInfo"
|
BLOCK "StringFileInfo"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
27
src/ARM.cpp
27
src/ARM.cpp
|
@ -109,20 +109,13 @@ const u32 ARM::ConditionTable[16] =
|
||||||
|
|
||||||
ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) :
|
ARM::ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, melonDS::NDS& nds) :
|
||||||
#ifdef GDBSTUB_ENABLED
|
#ifdef GDBSTUB_ENABLED
|
||||||
GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
|
GdbStub(this),
|
||||||
|
BreakOnStartup(false),
|
||||||
#endif
|
#endif
|
||||||
Num(num), // well uh
|
Num(num), // well uh
|
||||||
NDS(nds)
|
NDS(nds)
|
||||||
{
|
{
|
||||||
#ifdef GDBSTUB_ENABLED
|
SetGdbArgs(jit ? std::nullopt : gdb);
|
||||||
if (gdb
|
|
||||||
#ifdef JIT_ENABLED
|
|
||||||
&& !jit // TODO: Should we support toggling the GdbStub without destroying the ARM?
|
|
||||||
#endif
|
|
||||||
)
|
|
||||||
GdbStub.Init();
|
|
||||||
IsSingleStep = false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ARM::~ARM()
|
ARM::~ARM()
|
||||||
|
@ -147,6 +140,20 @@ ARMv5::~ARMv5()
|
||||||
// DTCM is owned by Memory, not going to delete it
|
// DTCM is owned by Memory, not going to delete it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ARM::SetGdbArgs(std::optional<GDBArgs> gdb)
|
||||||
|
{
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
GdbStub.Close();
|
||||||
|
if (gdb)
|
||||||
|
{
|
||||||
|
int port = Num ? gdb->PortARM7 : gdb->PortARM9;
|
||||||
|
GdbStub.Init(port);
|
||||||
|
BreakOnStartup = Num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup;
|
||||||
|
}
|
||||||
|
IsSingleStep = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void ARM::Reset()
|
void ARM::Reset()
|
||||||
{
|
{
|
||||||
Cycles = 0;
|
Cycles = 0;
|
||||||
|
|
|
@ -68,6 +68,8 @@ public:
|
||||||
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
|
ARM(u32 num, bool jit, std::optional<GDBArgs> gdb, NDS& nds);
|
||||||
virtual ~ARM(); // destroy shit
|
virtual ~ARM(); // destroy shit
|
||||||
|
|
||||||
|
void SetGdbArgs(std::optional<GDBArgs> gdb);
|
||||||
|
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
|
|
||||||
virtual void DoSavestate(Savestate* file);
|
virtual void DoSavestate(Savestate* file);
|
||||||
|
@ -323,6 +325,7 @@ public:
|
||||||
u32 CP15Control;
|
u32 CP15Control;
|
||||||
|
|
||||||
u32 RNGSeed;
|
u32 RNGSeed;
|
||||||
|
u32 TraceProcessID;
|
||||||
|
|
||||||
u32 DTCMSetting, ITCMSetting;
|
u32 DTCMSetting, ITCMSetting;
|
||||||
|
|
||||||
|
|
|
@ -430,9 +430,9 @@ void A_LDM(ARM* cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 pc = 0;
|
||||||
if (cpu->CurInstr & (1<<15))
|
if (cpu->CurInstr & (1<<15))
|
||||||
{
|
{
|
||||||
u32 pc;
|
|
||||||
if (preinc) base += 4;
|
if (preinc) base += 4;
|
||||||
if (first) cpu->DataRead32 (base, &pc);
|
if (first) cpu->DataRead32 (base, &pc);
|
||||||
else cpu->DataRead32S(base, &pc);
|
else cpu->DataRead32S(base, &pc);
|
||||||
|
@ -440,13 +440,8 @@ void A_LDM(ARM* cpu)
|
||||||
|
|
||||||
if (cpu->Num == 1)
|
if (cpu->Num == 1)
|
||||||
pc &= ~0x1;
|
pc &= ~0x1;
|
||||||
|
|
||||||
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
|
||||||
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
|
|
||||||
|
|
||||||
if (cpu->CurInstr & (1<<21))
|
if (cpu->CurInstr & (1<<21))
|
||||||
{
|
{
|
||||||
// post writeback
|
// post writeback
|
||||||
|
@ -466,6 +461,12 @@ void A_LDM(ARM* cpu)
|
||||||
cpu->R[baseid] = wbbase;
|
cpu->R[baseid] = wbbase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
||||||
|
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
|
||||||
|
|
||||||
|
if (cpu->CurInstr & (1<<15))
|
||||||
|
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
|
||||||
|
|
||||||
cpu->AddCycles_CDI();
|
cpu->AddCycles_CDI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "ARMJIT_Internal.h"
|
#include "ARMJIT_Internal.h"
|
||||||
#include "ARMJIT_Memory.h"
|
#include "ARMJIT_Memory.h"
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "ARMJIT_Global.h"
|
||||||
|
|
||||||
#include "ARMInterpreter_ALU.h"
|
#include "ARMInterpreter_ALU.h"
|
||||||
#include "ARMInterpreter_LoadStore.h"
|
#include "ARMInterpreter_LoadStore.h"
|
||||||
|
@ -467,6 +468,16 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] =
|
||||||
};
|
};
|
||||||
#undef F
|
#undef F
|
||||||
|
|
||||||
|
ARMJIT::ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
|
||||||
|
NDS(nds),
|
||||||
|
Memory(nds),
|
||||||
|
JITCompiler(nds),
|
||||||
|
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
|
||||||
|
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
|
||||||
|
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
|
||||||
|
FastMemory((jit.has_value() ? jit->FastMemory : false) && ARMJIT_Memory::IsFastMemSupported())
|
||||||
|
{}
|
||||||
|
|
||||||
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
||||||
{
|
{
|
||||||
auto it = RestoreCandidates.find(block->InstrHash);
|
auto it = RestoreCandidates.find(block->InstrHash);
|
||||||
|
@ -483,6 +494,7 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept
|
||||||
|
|
||||||
void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
||||||
{
|
{
|
||||||
|
args.FastMemory = args.FastMemory && ARMJIT_Memory::IsFastMemSupported();
|
||||||
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
|
args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u);
|
||||||
|
|
||||||
if (MaxBlockSize != args.MaxBlockSize
|
if (MaxBlockSize != args.MaxBlockSize
|
||||||
|
@ -499,36 +511,22 @@ void ARMJIT::SetJITArgs(JITArgs args) noexcept
|
||||||
|
|
||||||
void ARMJIT::SetMaxBlockSize(int size) noexcept
|
void ARMJIT::SetMaxBlockSize(int size) noexcept
|
||||||
{
|
{
|
||||||
size = std::clamp(size, 1, 32);
|
SetJITArgs(JITArgs{static_cast<unsigned>(size), LiteralOptimizations, LiteralOptimizations, FastMemory});
|
||||||
|
|
||||||
if (size != MaxBlockSize)
|
|
||||||
ResetBlockCache();
|
|
||||||
|
|
||||||
MaxBlockSize = size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
|
void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept
|
||||||
{
|
{
|
||||||
if (LiteralOptimizations != enabled)
|
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), enabled, BranchOptimizations, FastMemory});
|
||||||
ResetBlockCache();
|
|
||||||
|
|
||||||
LiteralOptimizations = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
|
void ARMJIT::SetBranchOptimizations(bool enabled) noexcept
|
||||||
{
|
{
|
||||||
if (BranchOptimizations != enabled)
|
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, enabled, FastMemory});
|
||||||
ResetBlockCache();
|
|
||||||
|
|
||||||
BranchOptimizations = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMJIT::SetFastMemory(bool enabled) noexcept
|
void ARMJIT::SetFastMemory(bool enabled) noexcept
|
||||||
{
|
{
|
||||||
if (FastMemory != enabled)
|
SetJITArgs(JITArgs{static_cast<unsigned>(MaxBlockSize), LiteralOptimizations, BranchOptimizations, enabled});
|
||||||
ResetBlockCache();
|
|
||||||
|
|
||||||
FastMemory = enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
||||||
|
@ -918,7 +916,7 @@ void ARMJIT::CompileBlock(ARM* cpu) noexcept
|
||||||
|
|
||||||
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
|
AddressRange* region = CodeMemRegions[addressRanges[j] >> 27];
|
||||||
|
|
||||||
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512]))
|
if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||||
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true);
|
||||||
|
|
||||||
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
|
AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512];
|
||||||
|
@ -971,7 +969,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
|
||||||
range->Blocks.Remove(i);
|
range->Blocks.Remove(i);
|
||||||
|
|
||||||
if (range->Blocks.Length == 0
|
if (range->Blocks.Length == 0
|
||||||
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512]))
|
&& !PageContainsCode(®ion[(localAddr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||||
{
|
{
|
||||||
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false);
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1003,7 @@ void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept
|
||||||
|
|
||||||
if (otherRange->Blocks.Length == 0)
|
if (otherRange->Blocks.Length == 0)
|
||||||
{
|
{
|
||||||
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512]))
|
if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000 & ~(Memory.PageSize - 1)) / 512], Memory.PageSize))
|
||||||
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false);
|
||||||
|
|
||||||
otherRange->Code = 0;
|
otherRange->Code = 0;
|
||||||
|
|
11
src/ARMJIT.h
11
src/ARMJIT.h
|
@ -44,15 +44,7 @@ class JitBlock;
|
||||||
class ARMJIT
|
class ARMJIT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept :
|
ARMJIT(melonDS::NDS& nds, std::optional<JITArgs> jit) noexcept;
|
||||||
NDS(nds),
|
|
||||||
Memory(nds),
|
|
||||||
JITCompiler(nds),
|
|
||||||
MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32),
|
|
||||||
LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false),
|
|
||||||
BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false),
|
|
||||||
FastMemory(jit.has_value() ? jit->FastMemory : false)
|
|
||||||
{}
|
|
||||||
~ARMJIT() noexcept;
|
~ARMJIT() noexcept;
|
||||||
void InvalidateByAddr(u32) noexcept;
|
void InvalidateByAddr(u32) noexcept;
|
||||||
void CheckAndInvalidateWVRAM(int) noexcept;
|
void CheckAndInvalidateWVRAM(int) noexcept;
|
||||||
|
@ -80,6 +72,7 @@ private:
|
||||||
bool LiteralOptimizations = false;
|
bool LiteralOptimizations = false;
|
||||||
bool BranchOptimizations = false;
|
bool BranchOptimizations = false;
|
||||||
bool FastMemory = false;
|
bool FastMemory = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
melonDS::NDS& NDS;
|
melonDS::NDS& NDS;
|
||||||
TinyVector<u32> InvalidLiterals {};
|
TinyVector<u32> InvalidLiterals {};
|
||||||
|
|
|
@ -83,7 +83,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles)
|
||||||
// doesn't matter if we put garbage in the MSbs there
|
// doesn't matter if we put garbage in the MSbs there
|
||||||
if (addr & 0x2)
|
if (addr & 0x2)
|
||||||
{
|
{
|
||||||
cpu9->CodeRead32(addr-2, true) >> 16;
|
cpu9->CodeRead32(addr-2, true);
|
||||||
cycles += cpu9->CodeCycles;
|
cycles += cpu9->CodeCycles;
|
||||||
cpu9->CodeRead32(addr+2, false);
|
cpu9->CodeRead32(addr+2, false);
|
||||||
cycles += CurCPU->CodeCycles;
|
cycles += CurCPU->CodeCycles;
|
||||||
|
|
|
@ -22,17 +22,7 @@
|
||||||
#include "../ARMInterpreter.h"
|
#include "../ARMInterpreter.h"
|
||||||
#include "../ARMJIT.h"
|
#include "../ARMJIT.h"
|
||||||
#include "../NDS.h"
|
#include "../NDS.h"
|
||||||
|
#include "../ARMJIT_Global.h"
|
||||||
#if defined(__SWITCH__)
|
|
||||||
#include <switch.h>
|
|
||||||
|
|
||||||
extern char __start__;
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -66,11 +56,6 @@ const int RegisterCache<Compiler, ARM64Reg>::NativeRegsAvailable = 15;
|
||||||
|
|
||||||
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
|
const BitSet32 CallerSavedPushRegs({W8, W9, W10, W11, W12, W13, W14, W15});
|
||||||
|
|
||||||
const int JitMemSize = 16 * 1024 * 1024;
|
|
||||||
#ifndef __SWITCH__
|
|
||||||
u8 JitMem[JitMemSize];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Compiler::MovePC()
|
void Compiler::MovePC()
|
||||||
{
|
{
|
||||||
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
|
ADD(MapReg(15), MapReg(15), Thumb ? 2 : 4);
|
||||||
|
@ -260,29 +245,13 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds)
|
||||||
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
|
SetCodeBase((u8*)JitRWStart, (u8*)JitRXStart);
|
||||||
JitMemMainSize = JitMemSize;
|
JitMemMainSize = JitMemSize;
|
||||||
#else
|
#else
|
||||||
#ifdef _WIN32
|
ARMJIT_Global::Init();
|
||||||
SYSTEM_INFO sysInfo;
|
|
||||||
GetSystemInfo(&sysInfo);
|
|
||||||
|
|
||||||
u64 pageSize = (u64)sysInfo.dwPageSize;
|
CodeMemBase = ARMJIT_Global::AllocateCodeMem();
|
||||||
#else
|
|
||||||
u64 pageSize = sysconf(_SC_PAGE_SIZE);
|
|
||||||
#endif
|
|
||||||
u8* pageAligned = (u8*)(((u64)JitMem & ~(pageSize - 1)) + pageSize);
|
|
||||||
u64 alignedSize = (((u64)JitMem + sizeof(JitMem)) & ~(pageSize - 1)) - (u64)pageAligned;
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
|
||||||
DWORD dummy;
|
|
||||||
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
|
|
||||||
nds.JIT.JitEnableWrite();
|
nds.JIT.JitEnableWrite();
|
||||||
#else
|
|
||||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SetCodeBase(pageAligned, pageAligned);
|
SetCodeBase(reinterpret_cast<u8*>(CodeMemBase), reinterpret_cast<u8*>(CodeMemBase));
|
||||||
JitMemMainSize = alignedSize;
|
JitMemMainSize = ARMJIT_Global::CodeMemorySliceSize;
|
||||||
#endif
|
#endif
|
||||||
SetCodePtr(0);
|
SetCodePtr(0);
|
||||||
|
|
||||||
|
@ -493,6 +462,9 @@ Compiler::~Compiler()
|
||||||
free(JitRWBase);
|
free(JitRWBase);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ARMJIT_Global::FreeCodeMem(CodeMemBase);
|
||||||
|
ARMJIT_Global::DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::LoadCycles()
|
void Compiler::LoadCycles()
|
||||||
|
|
|
@ -275,6 +275,7 @@ public:
|
||||||
void* JitRWStart;
|
void* JitRWStart;
|
||||||
void* JitRXStart;
|
void* JitRXStart;
|
||||||
#endif
|
#endif
|
||||||
|
void* CodeMemBase;
|
||||||
|
|
||||||
void* ReadBanked, *WriteBanked;
|
void* ReadBanked, *WriteBanked;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
#include "ARMJIT_Global.h"
|
||||||
|
#include "ARMJIT_Memory.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ARMJIT_Global
|
||||||
|
{
|
||||||
|
|
||||||
|
std::mutex globalMutex;
|
||||||
|
|
||||||
|
#if defined(__APPLE__) && defined(__aarch64__)
|
||||||
|
#define APPLE_AARCH64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef APPLE_AARCH64
|
||||||
|
static constexpr size_t NumCodeMemSlices = 4;
|
||||||
|
static constexpr size_t CodeMemoryAlignedSize = NumCodeMemSlices * CodeMemorySliceSize;
|
||||||
|
|
||||||
|
// I haven't heard of pages larger than 16 KB
|
||||||
|
u8 CodeMemory[CodeMemoryAlignedSize + 16*1024];
|
||||||
|
|
||||||
|
u32 AvailableCodeMemSlices = (1 << NumCodeMemSlices) - 1;
|
||||||
|
|
||||||
|
u8* GetAlignedCodeMemoryStart()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<u8*>((reinterpret_cast<intptr_t>(CodeMemory) + (16*1024-1)) & ~static_cast<intptr_t>(16*1024-1));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int RefCounter = 0;
|
||||||
|
|
||||||
|
void* AllocateCodeMem()
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
#ifndef APPLE_AARCH64
|
||||||
|
if (AvailableCodeMemSlices)
|
||||||
|
{
|
||||||
|
int slice = __builtin_ctz(AvailableCodeMemSlices);
|
||||||
|
AvailableCodeMemSlices &= ~(1 << slice);
|
||||||
|
//printf("allocating slice %d\n", slice);
|
||||||
|
return &GetAlignedCodeMemoryStart()[slice * CodeMemorySliceSize];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// allocate
|
||||||
|
#ifdef _WIN32
|
||||||
|
return VirtualAlloc(nullptr, CodeMemorySliceSize, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||||
|
#elif defined(APPLE_AARCH64)
|
||||||
|
return mmap(NULL, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0);
|
||||||
|
#else
|
||||||
|
//printf("mmaping...\n");
|
||||||
|
return mmap(nullptr, CodeMemorySliceSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeCodeMem(void* codeMem)
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
#ifndef APPLE_AARCH64
|
||||||
|
for (int i = 0; i < NumCodeMemSlices; i++)
|
||||||
|
{
|
||||||
|
if (codeMem == &GetAlignedCodeMemoryStart()[CodeMemorySliceSize * i])
|
||||||
|
{
|
||||||
|
//printf("freeing slice\n");
|
||||||
|
AvailableCodeMemSlices |= 1 << i;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
VirtualFree(codeMem, CodeMemorySliceSize, MEM_RELEASE|MEM_DECOMMIT);
|
||||||
|
#else
|
||||||
|
munmap(codeMem, CodeMemorySliceSize);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
RefCounter++;
|
||||||
|
if (RefCounter == 1)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD dummy;
|
||||||
|
VirtualProtect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
||||||
|
#elif defined(APPLE_AARCH64)
|
||||||
|
// Apple aarch64 always uses dynamic allocation
|
||||||
|
#else
|
||||||
|
mprotect(GetAlignedCodeMemoryStart(), CodeMemoryAlignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ARMJIT_Memory::RegisterFaultHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeInit()
|
||||||
|
{
|
||||||
|
std::lock_guard guard(globalMutex);
|
||||||
|
|
||||||
|
RefCounter--;
|
||||||
|
if (RefCounter == 0)
|
||||||
|
{
|
||||||
|
ARMJIT_Memory::UnregisterFaultHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2024 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS 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 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. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ARMJIT_GLOBAL_H
|
||||||
|
#define ARMJIT_GLOBAL_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace melonDS
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ARMJIT_Global
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr size_t CodeMemorySliceSize = 1024*1024*32;
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void DeInit();
|
||||||
|
|
||||||
|
void* AllocateCodeMem();
|
||||||
|
void FreeCodeMem(void* codeMem);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -85,9 +85,9 @@ typedef void (*InterpreterFunc)(ARM* cpu);
|
||||||
extern InterpreterFunc InterpretARM[];
|
extern InterpreterFunc InterpretARM[];
|
||||||
extern InterpreterFunc InterpretTHUMB[];
|
extern InterpreterFunc InterpretTHUMB[];
|
||||||
|
|
||||||
inline bool PageContainsCode(const AddressRange* range)
|
inline bool PageContainsCode(const AddressRange* range, u32 pageSize)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 8; i++)
|
for (int i = 0; i < pageSize / 512; i++)
|
||||||
{
|
{
|
||||||
if (range[i].Blocks.Length > 0)
|
if (range[i].Blocks.Length > 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
|
|
||||||
#include "ARMJIT_Internal.h"
|
#include "ARMJIT_Internal.h"
|
||||||
#include "ARMJIT_Compiler.h"
|
#include "ARMJIT_Compiler.h"
|
||||||
|
#include "ARMJIT_Global.h"
|
||||||
|
|
||||||
#include "DSi.h"
|
#include "DSi.h"
|
||||||
#include "GPU.h"
|
#include "GPU.h"
|
||||||
|
@ -100,6 +101,9 @@
|
||||||
namespace melonDS
|
namespace melonDS
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static constexpr u64 AddrSpaceSize = 0x100000000;
|
||||||
|
static constexpr u64 VirtmemAreaSize = AddrSpaceSize * 2 + MemoryTotalSize;
|
||||||
|
|
||||||
using Platform::Log;
|
using Platform::Log;
|
||||||
using Platform::LogLevel;
|
using Platform::LogLevel;
|
||||||
|
|
||||||
|
@ -152,6 +156,15 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx)
|
||||||
|
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
static LPVOID ExceptionHandlerHandle = nullptr;
|
||||||
|
static HMODULE KernelBaseDll = nullptr;
|
||||||
|
|
||||||
|
using VirtualAlloc2Type = PVOID WINAPI (*)(HANDLE Process, PVOID BaseAddress, SIZE_T Size, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
|
||||||
|
using MapViewOfFile3Type = PVOID WINAPI (*)(HANDLE FileMapping, HANDLE Process, PVOID BaseAddress, ULONG64 Offset, SIZE_T ViewSize, ULONG AllocationType, ULONG PageProtection, MEM_EXTENDED_PARAMETER* ExtendedParameters, ULONG ParameterCount);
|
||||||
|
|
||||||
|
static VirtualAlloc2Type virtualAlloc2Ptr;
|
||||||
|
static MapViewOfFile3Type mapViewOfFile3Ptr;
|
||||||
|
|
||||||
LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
||||||
{
|
{
|
||||||
if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
|
||||||
|
@ -170,6 +183,7 @@ LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo)
|
||||||
return EXCEPTION_CONTINUE_EXECUTION;
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log(LogLevel::Debug, "it all returns to nothing\n");
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,18 +275,61 @@ enum
|
||||||
memstate_MappedProtected,
|
memstate_MappedProtected,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define CHECK_ALIGNED(value) assert(((value) & (PageSize-1)) == 0)
|
||||||
|
|
||||||
bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
|
bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
|
||||||
{
|
{
|
||||||
|
CHECK_ALIGNED(addr);
|
||||||
|
CHECK_ALIGNED(offset);
|
||||||
|
CHECK_ALIGNED(size);
|
||||||
|
|
||||||
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
|
Result r = (svcMapProcessMemory(dst, envGetOwnProcessHandle(),
|
||||||
(u64)(MemoryBaseCodeMem + offset), size));
|
(u64)(MemoryBaseCodeMem + offset), size));
|
||||||
return R_SUCCEEDED(r);
|
return R_SUCCEEDED(r);
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
bool r = MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, offset, size, dst) == dst;
|
uintptr_t uintptrDst = reinterpret_cast<uintptr_t>(dst);
|
||||||
return r;
|
for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end(); it++)
|
||||||
|
{
|
||||||
|
if (uintptrDst >= it->Start && uintptrDst+size <= it->Start+it->Size)
|
||||||
|
{
|
||||||
|
//Log(LogLevel::Debug, "found mapping %llx %llx %llx %llx\n", uintptrDst, size, it->Start, it->Size);
|
||||||
|
// we split this place holder so that we have a fitting place holder for the mapping
|
||||||
|
if (uintptrDst != it->Start || size != it->Size)
|
||||||
|
{
|
||||||
|
if (!VirtualFree(dst, size, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER))
|
||||||
|
{
|
||||||
|
Log(LogLevel::Debug, "VirtualFree failed with %x\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtmemPlaceholder splitPlaceholder = *it;
|
||||||
|
VirtmemPlaceholders.erase(it);
|
||||||
|
if (uintptrDst > splitPlaceholder.Start)
|
||||||
|
{
|
||||||
|
//Log(LogLevel::Debug, "splitting on the left %llx\n", uintptrDst - splitPlaceholder.Start);
|
||||||
|
VirtmemPlaceholders.push_back({splitPlaceholder.Start, uintptrDst - splitPlaceholder.Start});
|
||||||
|
}
|
||||||
|
if (uintptrDst+size < splitPlaceholder.Start+splitPlaceholder.Size)
|
||||||
|
{
|
||||||
|
//Log(LogLevel::Debug, "splitting on the right %llx\n", (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size));
|
||||||
|
VirtmemPlaceholders.push_back({uintptrDst+size, (splitPlaceholder.Start+splitPlaceholder.Size)-(uintptrDst+size)});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mapViewOfFile3Ptr(MemoryFile, nullptr, dst, offset, size, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0))
|
||||||
|
{
|
||||||
|
Log(LogLevel::Debug, "MapViewOfFile3 failed with %x\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(LogLevel::Debug, "no mapping at all found??? %p %x %p\n", dst, size, MemoryBase);
|
||||||
|
return false;
|
||||||
#else
|
#else
|
||||||
return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED;
|
return mmap(dst, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, offset) != MAP_FAILED;
|
||||||
#endif
|
#endif
|
||||||
|
@ -280,21 +337,68 @@ bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexce
|
||||||
|
|
||||||
bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
|
bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept
|
||||||
{
|
{
|
||||||
|
CHECK_ALIGNED(addr);
|
||||||
|
CHECK_ALIGNED(offset);
|
||||||
|
CHECK_ALIGNED(size);
|
||||||
|
|
||||||
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(),
|
Result r = svcUnmapProcessMemory(dst, envGetOwnProcessHandle(),
|
||||||
(u64)(MemoryBaseCodeMem + offset), size);
|
(u64)(MemoryBaseCodeMem + offset), size);
|
||||||
return R_SUCCEEDED(r);
|
return R_SUCCEEDED(r);
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
return UnmapViewOfFile(dst);
|
if (!UnmapViewOfFileEx(dst, MEM_PRESERVE_PLACEHOLDER))
|
||||||
|
{
|
||||||
|
Log(LogLevel::Debug, "UnmapViewOfFileEx failed %x\n", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t uintptrDst = reinterpret_cast<uintptr_t>(dst);
|
||||||
|
uintptr_t coalesceStart = uintptrDst;
|
||||||
|
size_t coalesceSize = size;
|
||||||
|
|
||||||
|
for (auto it = VirtmemPlaceholders.begin(); it != VirtmemPlaceholders.end();)
|
||||||
|
{
|
||||||
|
if (it->Start+it->Size == uintptrDst)
|
||||||
|
{
|
||||||
|
//Log(LogLevel::Debug, "Coalescing to the left\n");
|
||||||
|
coalesceStart = it->Start;
|
||||||
|
coalesceSize += it->Size;
|
||||||
|
it = VirtmemPlaceholders.erase(it);
|
||||||
|
}
|
||||||
|
else if (it->Start == uintptrDst+size)
|
||||||
|
{
|
||||||
|
//Log(LogLevel::Debug, "Coalescing to the right\n");
|
||||||
|
coalesceSize += it->Size;
|
||||||
|
it = VirtmemPlaceholders.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coalesceStart != uintptrDst || coalesceSize != size)
|
||||||
|
{
|
||||||
|
if (!VirtualFree(reinterpret_cast<void*>(coalesceStart), coalesceSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
VirtmemPlaceholders.push_back({coalesceStart, coalesceSize});
|
||||||
|
//Log(LogLevel::Debug, "Adding coalesced region %llx %llx", coalesceStart, coalesceSize);
|
||||||
|
|
||||||
|
return true;
|
||||||
#else
|
#else
|
||||||
return munmap(dst, size) == 0;
|
return mmap(dst, size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0) != MAP_FAILED;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __SWITCH__
|
#ifndef __SWITCH__
|
||||||
void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept
|
void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept
|
||||||
{
|
{
|
||||||
|
CHECK_ALIGNED(addr);
|
||||||
|
CHECK_ALIGNED(size);
|
||||||
|
|
||||||
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr;
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
DWORD winProtection, oldProtection;
|
DWORD winProtection, oldProtection;
|
||||||
|
@ -305,6 +409,10 @@ void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int prot
|
||||||
else
|
else
|
||||||
winProtection = PAGE_READWRITE;
|
winProtection = PAGE_READWRITE;
|
||||||
bool success = VirtualProtect(dst, size, winProtection, &oldProtection);
|
bool success = VirtualProtect(dst, size, winProtection, &oldProtection);
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
Log(LogLevel::Debug, "VirtualProtect failed with %x\n", GetLastError());
|
||||||
|
}
|
||||||
assert(success);
|
assert(success);
|
||||||
#else
|
#else
|
||||||
int posixProt;
|
int posixProt;
|
||||||
|
@ -335,14 +443,14 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u32 segmentOffset = offset;
|
u32 segmentOffset = offset;
|
||||||
u8 status = statuses[(Addr + offset) >> 12];
|
u8 status = statuses[(Addr + offset) >> PageShift];
|
||||||
while (statuses[(Addr + offset) >> 12] == status
|
while (statuses[(Addr + offset) >> PageShift] == status
|
||||||
&& offset < Size
|
&& offset < Size
|
||||||
&& (!skipDTCM || Addr + offset != dtcmStart))
|
&& (!skipDTCM || Addr + offset != dtcmStart))
|
||||||
{
|
{
|
||||||
assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped);
|
assert(statuses[(Addr + offset) >> PageShift] != memstate_Unmapped);
|
||||||
statuses[(Addr + offset) >> 12] = memstate_Unmapped;
|
statuses[(Addr + offset) >> PageShift] = memstate_Unmapped;
|
||||||
offset += 0x1000;
|
offset += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
|
@ -358,7 +466,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef __SWITCH__
|
#ifndef __SWITCH__
|
||||||
#ifndef _WIN32
|
|
||||||
u32 dtcmEnd = dtcmStart + dtcmSize;
|
u32 dtcmEnd = dtcmStart + dtcmSize;
|
||||||
if (Num == 0
|
if (Num == 0
|
||||||
&& dtcmEnd >= Addr
|
&& dtcmEnd >= Addr
|
||||||
|
@ -378,7 +485,6 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
|
bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size);
|
||||||
assert(succeded);
|
assert(succeded);
|
||||||
|
@ -388,7 +494,7 @@ void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept
|
||||||
|
|
||||||
void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept
|
void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept
|
||||||
{
|
{
|
||||||
offset &= ~0xFFF;
|
offset &= ~(PageSize - 1);
|
||||||
//printf("set code protection %d %x %d\n", region, offset, protect);
|
//printf("set code protection %d %x %d\n", region, offset, protect);
|
||||||
|
|
||||||
for (int i = 0; i < Mappings[region].Length; i++)
|
for (int i = 0; i < Mappings[region].Length; i++)
|
||||||
|
@ -406,9 +512,9 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
|
||||||
|
|
||||||
u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7);
|
u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7);
|
||||||
|
|
||||||
//printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> 12]);
|
//printf("%x %d %x %x %x %d\n", effectiveAddr, mapping.Num, mapping.Addr, mapping.LocalOffset, mapping.Size, states[effectiveAddr >> PageShift]);
|
||||||
assert(states[effectiveAddr >> 12] == (protect ? memstate_MappedRW : memstate_MappedProtected));
|
assert(states[effectiveAddr >> PageShift] == (protect ? memstate_MappedRW : memstate_MappedProtected));
|
||||||
states[effectiveAddr >> 12] = protect ? memstate_MappedProtected : memstate_MappedRW;
|
states[effectiveAddr >> PageShift] = protect ? memstate_MappedProtected : memstate_MappedRW;
|
||||||
|
|
||||||
#if defined(__SWITCH__)
|
#if defined(__SWITCH__)
|
||||||
bool success;
|
bool success;
|
||||||
|
@ -418,7 +524,7 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex
|
||||||
success = MapIntoRange(effectiveAddr, mapping.Num, OffsetsPerRegion[region] + offset, 0x1000);
|
success = MapIntoRange(effectiveAddr, mapping.Num, OffsetsPerRegion[region] + offset, 0x1000);
|
||||||
assert(success);
|
assert(success);
|
||||||
#else
|
#else
|
||||||
SetCodeProtectionRange(effectiveAddr, 0x1000, mapping.Num, protect ? 1 : 2);
|
SetCodeProtectionRange(effectiveAddr, PageSize, mapping.Num, protect ? 1 : 2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -543,11 +649,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
||||||
u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1;
|
u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1;
|
||||||
u32 dtcmEnd = dtcmStart + dtcmSize;
|
u32 dtcmEnd = dtcmStart + dtcmSize;
|
||||||
#ifndef __SWITCH__
|
#ifndef __SWITCH__
|
||||||
#ifndef _WIN32
|
|
||||||
if (num == 0
|
if (num == 0
|
||||||
&& dtcmEnd >= mirrorStart
|
&& dtcmEnd >= mirrorStart
|
||||||
&& dtcmStart < mirrorStart + mirrorSize)
|
&& dtcmStart < mirrorStart + mirrorSize)
|
||||||
{
|
{
|
||||||
|
if (dtcmSize < PageSize)
|
||||||
|
{
|
||||||
|
// we could technically mask out the DTCM by setting a hole to access permissions
|
||||||
|
// but realistically there isn't much of a point in mapping less than 16kb of DTCM
|
||||||
|
// so it isn't worth more complex support
|
||||||
|
Log(LogLevel::Info, "DTCM size smaller than 16kb skipping mapping entirely");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool success;
|
bool success;
|
||||||
if (dtcmStart > mirrorStart)
|
if (dtcmStart > mirrorStart)
|
||||||
{
|
{
|
||||||
|
@ -562,7 +676,6 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
|
bool succeded = MapIntoRange(mirrorStart, num, OffsetsPerRegion[region] + memoryOffset, mirrorSize);
|
||||||
assert(succeded);
|
assert(succeded);
|
||||||
|
@ -579,22 +692,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
||||||
{
|
{
|
||||||
if (skipDTCM && mirrorStart + offset == dtcmStart)
|
if (skipDTCM && mirrorStart + offset == dtcmStart)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
|
||||||
SetCodeProtectionRange(dtcmStart, dtcmSize, 0, 0);
|
|
||||||
#endif
|
|
||||||
offset += dtcmSize;
|
offset += dtcmSize;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u32 sectionOffset = offset;
|
u32 sectionOffset = offset;
|
||||||
bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]);
|
bool hasCode = isExecutable && PageContainsCode(&range[offset / 512], PageSize);
|
||||||
while (offset < mirrorSize
|
while (offset < mirrorSize
|
||||||
&& (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode)
|
&& (!isExecutable || PageContainsCode(&range[offset / 512], PageSize) == hasCode)
|
||||||
&& (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase))
|
&& (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase))
|
||||||
{
|
{
|
||||||
assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped);
|
assert(states[(mirrorStart + offset) >> PageShift] == memstate_Unmapped);
|
||||||
states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
|
states[(mirrorStart + offset) >> PageShift] = hasCode ? memstate_MappedProtected : memstate_MappedRW;
|
||||||
offset += 0x1000;
|
offset += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 sectionSize = offset - sectionOffset;
|
u32 sectionSize = offset - sectionOffset;
|
||||||
|
@ -624,6 +734,86 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 ARMJIT_Memory::PageSize = 0;
|
||||||
|
u32 ARMJIT_Memory::PageShift = 0;
|
||||||
|
|
||||||
|
bool ARMJIT_Memory::IsFastMemSupported()
|
||||||
|
{
|
||||||
|
#ifdef __APPLE__
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
static bool initialised = false;
|
||||||
|
static bool isSupported = false;
|
||||||
|
if (!initialised)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
ARMJIT_Global::Init();
|
||||||
|
isSupported = virtualAlloc2Ptr != nullptr;
|
||||||
|
ARMJIT_Global::DeInit();
|
||||||
|
|
||||||
|
PageSize = RegularPageSize;
|
||||||
|
#else
|
||||||
|
PageSize = __sysconf(_SC_PAGESIZE);
|
||||||
|
isSupported = PageSize == RegularPageSize || PageSize == LargePageSize;
|
||||||
|
#endif
|
||||||
|
PageShift = __builtin_ctz(PageSize);
|
||||||
|
initialised = true;
|
||||||
|
}
|
||||||
|
return isSupported;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT_Memory::RegisterFaultHandler()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
||||||
|
|
||||||
|
KernelBaseDll = LoadLibrary("KernelBase.dll");
|
||||||
|
if (KernelBaseDll)
|
||||||
|
{
|
||||||
|
virtualAlloc2Ptr = reinterpret_cast<VirtualAlloc2Type>(GetProcAddress(KernelBaseDll, "VirtualAlloc2"));
|
||||||
|
mapViewOfFile3Ptr = reinterpret_cast<MapViewOfFile3Type>(GetProcAddress(KernelBaseDll, "MapViewOfFile3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!virtualAlloc2Ptr)
|
||||||
|
{
|
||||||
|
Log(LogLevel::Error, "Could not load new Windows virtual memory functions, fast memory is disabled.\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = nullptr;
|
||||||
|
sa.sa_sigaction = &SigsegvHandler;
|
||||||
|
sa.sa_flags = SA_SIGINFO;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sigaction(SIGSEGV, &sa, &OldSaSegv);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
sigaction(SIGBUS, &sa, &OldSaBus);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ARMJIT_Memory::UnregisterFaultHandler()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (ExceptionHandlerHandle)
|
||||||
|
{
|
||||||
|
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
||||||
|
ExceptionHandlerHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (KernelBaseDll)
|
||||||
|
{
|
||||||
|
FreeLibrary(KernelBaseDll);
|
||||||
|
KernelBaseDll = nullptr;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
sigaction(SIGSEGV, &OldSaSegv, nullptr);
|
||||||
|
#ifdef __APPLE__
|
||||||
|
sigaction(SIGBUS, &OldSaBus, nullptr);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
||||||
{
|
{
|
||||||
if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC))
|
if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC))
|
||||||
|
@ -632,7 +822,7 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
||||||
|
|
||||||
u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7;
|
u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7;
|
||||||
|
|
||||||
if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped)
|
if (memStatus[faultDesc.EmulatedFaultAddr >> PageShift] == memstate_Unmapped)
|
||||||
rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr);
|
rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr);
|
||||||
|
|
||||||
if (rewriteToSlowPath)
|
if (rewriteToSlowPath)
|
||||||
|
@ -643,10 +833,9 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 AddrSpaceSize = 0x100000000;
|
|
||||||
|
|
||||||
ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
||||||
{
|
{
|
||||||
|
ARMJIT_Global::Init();
|
||||||
#if defined(__SWITCH__)
|
#if defined(__SWITCH__)
|
||||||
MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize);
|
MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize);
|
||||||
virtmemLock();
|
virtmemLock();
|
||||||
|
@ -671,33 +860,27 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
||||||
|
|
||||||
u8* basePtr = MemoryBaseCodeMem;
|
u8* basePtr = MemoryBaseCodeMem;
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
ExceptionHandlerHandle = AddVectoredExceptionHandler(1, ExceptionHandler);
|
if (virtualAlloc2Ptr)
|
||||||
|
{
|
||||||
|
MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, MemoryTotalSize, nullptr);
|
||||||
|
|
||||||
MemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MemoryTotalSize, NULL);
|
MemoryBase = reinterpret_cast<u8*>(virtualAlloc2Ptr(nullptr, nullptr, VirtmemAreaSize,
|
||||||
|
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||||
|
PAGE_NOACCESS,
|
||||||
|
nullptr, 0));
|
||||||
|
// split off placeholder and map base mapping
|
||||||
|
VirtualFree(MemoryBase, MemoryTotalSize, MEM_RELEASE|MEM_PRESERVE_PLACEHOLDER);
|
||||||
|
mapViewOfFile3Ptr(MemoryFile, nullptr, MemoryBase, 0, MemoryTotalSize, MEM_REPLACE_PLACEHOLDER, PAGE_READWRITE, nullptr, 0);
|
||||||
|
|
||||||
MemoryBase = (u8*)VirtualAlloc(NULL, AddrSpaceSize*4, MEM_RESERVE, PAGE_READWRITE);
|
VirtmemPlaceholders.push_back({reinterpret_cast<uintptr_t>(MemoryBase)+MemoryTotalSize, AddrSpaceSize*2});
|
||||||
VirtualFree(MemoryBase, 0, MEM_RELEASE);
|
}
|
||||||
// this is incredible hacky
|
else
|
||||||
// but someone else is trying to go into our address space!
|
{
|
||||||
// Windows will very likely give them virtual memory starting at the same address
|
// old Windows version
|
||||||
// as it is giving us now.
|
MemoryBase = new u8[MemoryTotalSize];
|
||||||
// That's why we don't use this address, but instead 4gb inwards
|
}
|
||||||
// I know this is terrible
|
|
||||||
FastMem9Start = MemoryBase + AddrSpaceSize;
|
|
||||||
FastMem7Start = MemoryBase + AddrSpaceSize*2;
|
|
||||||
MemoryBase = MemoryBase + AddrSpaceSize*3;
|
|
||||||
|
|
||||||
MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, MemoryTotalSize, MemoryBase);
|
|
||||||
#else
|
#else
|
||||||
// this used to be allocated with three different mmaps
|
MemoryBase = (u8*)mmap(nullptr, VirtmemAreaSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||||
// The idea was to give the OS more freedom where to position the buffers,
|
|
||||||
// but something was bad about this so instead we take this vmem eating monster
|
|
||||||
// which seems to work better.
|
|
||||||
MemoryBase = (u8*)mmap(NULL, AddrSpaceSize*4, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
|
||||||
munmap(MemoryBase, AddrSpaceSize*4);
|
|
||||||
FastMem9Start = MemoryBase;
|
|
||||||
FastMem7Start = MemoryBase + AddrSpaceSize;
|
|
||||||
MemoryBase = MemoryBase + AddrSpaceSize*2;
|
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
Libandroid = Platform::DynamicLibrary_Load("libandroid.so");
|
Libandroid = Platform::DynamicLibrary_Load("libandroid.so");
|
||||||
|
@ -717,7 +900,7 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
char fastmemPidName[snprintf(NULL, 0, "/melondsfastmem%d", getpid()) + 1];
|
char fastmemPidName[snprintf(NULL, 0, "/melondsfastmem%d", getpid()) + 1];
|
||||||
sprintf(fastmemPidName, "/melondsfastmem%d", getpid());
|
snprintf(fastmemPidName, sizeof(fastmemPidName), "/melondsfastmem%d", getpid());
|
||||||
MemoryFile = shm_open(fastmemPidName, O_RDWR | O_CREAT | O_EXCL, 0600);
|
MemoryFile = shm_open(fastmemPidName, O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||||
if (MemoryFile == -1)
|
if (MemoryFile == -1)
|
||||||
{
|
{
|
||||||
|
@ -730,20 +913,10 @@ ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds)
|
||||||
Log(LogLevel::Error, "Failed to allocate memory using ftruncate! (%s)", strerror(errno));
|
Log(LogLevel::Error, "Failed to allocate memory using ftruncate! (%s)", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sigaction sa;
|
|
||||||
sa.sa_handler = nullptr;
|
|
||||||
sa.sa_sigaction = &SigsegvHandler;
|
|
||||||
sa.sa_flags = SA_SIGINFO;
|
|
||||||
sigemptyset(&sa.sa_mask);
|
|
||||||
sigaction(SIGSEGV, &sa, &OldSaSegv);
|
|
||||||
#ifdef __APPLE__
|
|
||||||
sigaction(SIGBUS, &sa, &OldSaBus);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0);
|
mmap(MemoryBase, MemoryTotalSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, MemoryFile, 0);
|
||||||
|
|
||||||
u8* basePtr = MemoryBase;
|
|
||||||
#endif
|
#endif
|
||||||
|
FastMem9Start = MemoryBase+MemoryTotalSize;
|
||||||
|
FastMem7Start = static_cast<u8*>(FastMem9Start)+AddrSpaceSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
||||||
|
@ -764,10 +937,17 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
||||||
free(MemoryBase);
|
free(MemoryBase);
|
||||||
MemoryBase = nullptr;
|
MemoryBase = nullptr;
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
|
if (virtualAlloc2Ptr)
|
||||||
|
{
|
||||||
if (MemoryBase)
|
if (MemoryBase)
|
||||||
{
|
{
|
||||||
bool viewUnmapped = UnmapViewOfFile(MemoryBase);
|
bool viewUnmapped = UnmapViewOfFileEx(MemoryBase, MEM_PRESERVE_PLACEHOLDER);
|
||||||
assert(viewUnmapped);
|
assert(viewUnmapped);
|
||||||
|
bool viewCoalesced = VirtualFree(MemoryBase, VirtmemAreaSize, MEM_RELEASE|MEM_COALESCE_PLACEHOLDERS);
|
||||||
|
assert(viewCoalesced);
|
||||||
|
bool freeEverything = VirtualFree(MemoryBase, 0, MEM_RELEASE);
|
||||||
|
assert(freeEverything);
|
||||||
|
|
||||||
MemoryBase = nullptr;
|
MemoryBase = nullptr;
|
||||||
FastMem9Start = nullptr;
|
FastMem9Start = nullptr;
|
||||||
FastMem7Start = nullptr;
|
FastMem7Start = nullptr;
|
||||||
|
@ -778,20 +958,15 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
||||||
CloseHandle(MemoryFile);
|
CloseHandle(MemoryFile);
|
||||||
MemoryFile = INVALID_HANDLE_VALUE;
|
MemoryFile = INVALID_HANDLE_VALUE;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (ExceptionHandlerHandle)
|
else
|
||||||
{
|
{
|
||||||
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
|
delete[] MemoryBase;
|
||||||
ExceptionHandlerHandle = nullptr;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
sigaction(SIGSEGV, &OldSaSegv, nullptr);
|
|
||||||
#ifdef __APPLE__
|
|
||||||
sigaction(SIGBUS, &OldSaBus, nullptr);
|
|
||||||
#endif
|
|
||||||
if (MemoryBase)
|
if (MemoryBase)
|
||||||
{
|
{
|
||||||
munmap(MemoryBase, MemoryTotalSize);
|
munmap(MemoryBase, VirtmemAreaSize);
|
||||||
MemoryBase = nullptr;
|
MemoryBase = nullptr;
|
||||||
FastMem9Start = nullptr;
|
FastMem9Start = nullptr;
|
||||||
FastMem7Start = nullptr;
|
FastMem7Start = nullptr;
|
||||||
|
@ -803,6 +978,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
||||||
MemoryFile = -1;
|
MemoryFile = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log(LogLevel::Info, "unmappinged everything\n");
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
if (Libandroid)
|
if (Libandroid)
|
||||||
{
|
{
|
||||||
|
@ -812,6 +989,8 @@ ARMJIT_Memory::~ARMJIT_Memory() noexcept
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ARMJIT_Global::DeInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ARMJIT_Memory::Reset() noexcept
|
void ARMJIT_Memory::Reset() noexcept
|
||||||
|
@ -834,17 +1013,6 @@ void ARMJIT_Memory::Reset() noexcept
|
||||||
|
|
||||||
bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept
|
bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
|
||||||
/*
|
|
||||||
TODO: with some hacks, the smaller shared WRAM regions
|
|
||||||
could be mapped in some occaisons as well
|
|
||||||
*/
|
|
||||||
if (region == memregion_DTCM
|
|
||||||
|| region == memregion_SharedWRAM
|
|
||||||
|| region == memregion_NewSharedWRAM_B
|
|
||||||
|| region == memregion_NewSharedWRAM_C)
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
return OffsetsPerRegion[region] != UINT32_MAX;
|
return OffsetsPerRegion[region] != UINT32_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "MemConstants.h"
|
#include "MemConstants.h"
|
||||||
|
|
||||||
#ifdef JIT_ENABLED
|
#ifdef JIT_ENABLED
|
||||||
|
# include <mutex>
|
||||||
# include "TinyVector.h"
|
# include "TinyVector.h"
|
||||||
# include "ARM.h"
|
# include "ARM.h"
|
||||||
# if defined(__SWITCH__)
|
# if defined(__SWITCH__)
|
||||||
|
@ -48,23 +49,22 @@ class Compiler;
|
||||||
class ARMJIT;
|
class ARMJIT;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static constexpr u32 LargePageSize = 0x4000;
|
||||||
|
static constexpr u32 RegularPageSize = 0x1000;
|
||||||
|
|
||||||
constexpr u32 RoundUp(u32 size) noexcept
|
constexpr u32 RoundUp(u32 size) noexcept
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
return (size + LargePageSize - 1) & ~(LargePageSize - 1);
|
||||||
return (size + 0xFFFF) & ~0xFFFF;
|
|
||||||
#else
|
|
||||||
return size;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 MemBlockMainRAMOffset = 0;
|
static constexpr u32 MemBlockMainRAMOffset = 0;
|
||||||
const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
|
static constexpr u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize);
|
||||||
const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
|
static constexpr u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize);
|
||||||
const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
|
static constexpr u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize);
|
||||||
const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
|
static constexpr u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize);
|
||||||
const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
|
static constexpr u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize);
|
||||||
const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
|
static constexpr u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize);
|
||||||
const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
|
static constexpr u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize);
|
||||||
|
|
||||||
class ARMJIT_Memory
|
class ARMJIT_Memory
|
||||||
{
|
{
|
||||||
|
@ -137,6 +137,14 @@ public:
|
||||||
bool IsFastmemCompatible(int region) const noexcept;
|
bool IsFastmemCompatible(int region) const noexcept;
|
||||||
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
|
void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept;
|
||||||
bool MapAtAddress(u32 addr) noexcept;
|
bool MapAtAddress(u32 addr) noexcept;
|
||||||
|
|
||||||
|
static bool IsFastMemSupported();
|
||||||
|
|
||||||
|
static void RegisterFaultHandler();
|
||||||
|
static void UnregisterFaultHandler();
|
||||||
|
|
||||||
|
static u32 PageSize;
|
||||||
|
static u32 PageShift;
|
||||||
private:
|
private:
|
||||||
friend class Compiler;
|
friend class Compiler;
|
||||||
struct Mapping
|
struct Mapping
|
||||||
|
@ -162,14 +170,22 @@ private:
|
||||||
void* FastMem9Start;
|
void* FastMem9Start;
|
||||||
void* FastMem7Start;
|
void* FastMem7Start;
|
||||||
u8* MemoryBase = nullptr;
|
u8* MemoryBase = nullptr;
|
||||||
|
|
||||||
#if defined(__SWITCH__)
|
#if defined(__SWITCH__)
|
||||||
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
|
VirtmemReservation* FastMem9Reservation, *FastMem7Reservation;
|
||||||
u8* MemoryBaseCodeMem;
|
u8* MemoryBaseCodeMem;
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
|
struct VirtmemPlaceholder
|
||||||
|
{
|
||||||
|
uintptr_t Start;
|
||||||
|
size_t Size;
|
||||||
|
};
|
||||||
|
std::vector<VirtmemPlaceholder> VirtmemPlaceholders;
|
||||||
|
|
||||||
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
|
static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo);
|
||||||
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
|
HANDLE MemoryFile = INVALID_HANDLE_VALUE;
|
||||||
LPVOID ExceptionHandlerHandle = nullptr;
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
|
static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext);
|
||||||
int MemoryFile = -1;
|
int MemoryFile = -1;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -176,9 +176,9 @@ void Compiler::Comp_JumpTo(Gen::X64Reg addr, bool restoreCPSR)
|
||||||
else
|
else
|
||||||
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
|
MOV(32, R(ABI_PARAM3), Imm32(true)); // what a waste
|
||||||
if (Num == 0)
|
if (Num == 0)
|
||||||
CALL((void*)&ARMv5JumpToTrampoline);
|
ABI_CallFunction(ARMv5JumpToTrampoline);
|
||||||
else
|
else
|
||||||
CALL((void*)&ARMv4JumpToTrampoline);
|
ABI_CallFunction(ARMv4JumpToTrampoline);
|
||||||
|
|
||||||
PopRegs(restoreCPSR, true);
|
PopRegs(restoreCPSR, true);
|
||||||
|
|
||||||
|
|
|
@ -21,19 +21,13 @@
|
||||||
#include "../ARMJIT.h"
|
#include "../ARMJIT.h"
|
||||||
#include "../ARMInterpreter.h"
|
#include "../ARMInterpreter.h"
|
||||||
#include "../NDS.h"
|
#include "../NDS.h"
|
||||||
|
#include "../ARMJIT_Global.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include "../dolphin/CommonFuncs.h"
|
#include "../dolphin/CommonFuncs.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
using namespace Common;
|
using namespace Common;
|
||||||
|
|
||||||
|
@ -222,46 +216,21 @@ void Compiler::A_Comp_MSR()
|
||||||
MOV(32, R(ABI_PARAM3), R(RCPSR));
|
MOV(32, R(ABI_PARAM3), R(RCPSR));
|
||||||
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
|
MOV(32, R(ABI_PARAM2), R(RSCRATCH3));
|
||||||
MOV(64, R(ABI_PARAM1), R(RCPU));
|
MOV(64, R(ABI_PARAM1), R(RCPU));
|
||||||
CALL((void*)&UpdateModeTrampoline);
|
ABI_CallFunction(UpdateModeTrampoline);
|
||||||
|
|
||||||
PopRegs(true, true);
|
PopRegs(true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
We'll repurpose this .bss memory
|
|
||||||
|
|
||||||
*/
|
|
||||||
u8 CodeMemory[1024 * 1024 * 32];
|
|
||||||
|
|
||||||
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
||||||
{
|
{
|
||||||
{
|
ARMJIT_Global::Init();
|
||||||
#ifdef _WIN32
|
|
||||||
SYSTEM_INFO sysInfo;
|
|
||||||
GetSystemInfo(&sysInfo);
|
|
||||||
|
|
||||||
u64 pageSize = (u64)sysInfo.dwPageSize;
|
CodeMemBase = static_cast<u8*>(ARMJIT_Global::AllocateCodeMem());
|
||||||
#else
|
CodeMemSize = ARMJIT_Global::CodeMemorySliceSize;
|
||||||
u64 pageSize = sysconf(_SC_PAGE_SIZE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u8* pageAligned = (u8*)(((u64)CodeMemory & ~(pageSize - 1)) + pageSize);
|
ResetStart = CodeMemBase;
|
||||||
u64 alignedSize = (((u64)CodeMemory + sizeof(CodeMemory)) & ~(pageSize - 1)) - (u64)pageAligned;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD dummy;
|
|
||||||
VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy);
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
pageAligned = (u8*)mmap(NULL, 1024*1024*32, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS ,-1, 0);
|
|
||||||
#else
|
|
||||||
mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ResetStart = pageAligned;
|
|
||||||
CodeMemSize = alignedSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
|
@ -475,6 +444,13 @@ Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds)
|
||||||
FarSize = (ResetStart + CodeMemSize) - FarStart;
|
FarSize = (ResetStart + CodeMemSize) - FarStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Compiler::~Compiler()
|
||||||
|
{
|
||||||
|
ARMJIT_Global::FreeCodeMem(CodeMemBase);
|
||||||
|
|
||||||
|
ARMJIT_Global::DeInit();
|
||||||
|
}
|
||||||
|
|
||||||
void Compiler::LoadCPSR()
|
void Compiler::LoadCPSR()
|
||||||
{
|
{
|
||||||
assert(!CPSRDirty);
|
assert(!CPSRDirty);
|
||||||
|
@ -684,7 +660,7 @@ void Compiler::Comp_SpecialBranchBehaviour(bool taken)
|
||||||
|
|
||||||
if (ConstantCycles)
|
if (ConstantCycles)
|
||||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||||
JMP((u8*)&ARM_Ret, true);
|
ABI_TailCall(ARM_Ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,7 +822,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[]
|
||||||
|
|
||||||
if (ConstantCycles)
|
if (ConstantCycles)
|
||||||
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
ADD(32, MDisp(RCPU, offsetof(ARM, Cycles)), Imm32(ConstantCycles));
|
||||||
JMP((u8*)ARM_Ret, true);
|
ABI_TailCall(ARM_Ret);
|
||||||
|
|
||||||
#ifdef JIT_PROFILING_ENABLED
|
#ifdef JIT_PROFILING_ENABLED
|
||||||
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
|
CreateMethod("JIT_Block_%d_%d_%08X", (void*)res, Num, Thumb, instrs[0].Addr);
|
||||||
|
|
|
@ -84,6 +84,7 @@ class Compiler : public Gen::XEmitter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit Compiler(melonDS::NDS& nds);
|
explicit Compiler(melonDS::NDS& nds);
|
||||||
|
~Compiler();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
|
@ -256,6 +257,7 @@ public:
|
||||||
|
|
||||||
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
|
std::unordered_map<u8*, LoadStorePatch> LoadStorePatches {};
|
||||||
|
|
||||||
|
u8* CodeMemBase;
|
||||||
u8* ResetStart {};
|
u8* ResetStart {};
|
||||||
u32 CodeMemSize {};
|
u32 CodeMemSize {};
|
||||||
|
|
||||||
|
|
|
@ -316,24 +316,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
{
|
{
|
||||||
switch (size | NDS.ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowWrite9<u32, 0>); break;
|
case 32: ABI_CallFunction(SlowWrite9<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowWrite9<u16, 0>); break;
|
case 16: ABI_CallFunction(SlowWrite9<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowWrite9<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowWrite9<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowWrite9<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowWrite9<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowWrite9<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowWrite9<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowWrite9<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowWrite9<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (size | NDS.ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowRead9<u32, 0>); break;
|
case 32: ABI_CallFunction(&SlowRead9<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowRead9<u16, 0>); break;
|
case 16: ABI_CallFunction(&SlowRead9<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowRead9<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowRead9<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowRead9<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowRead9<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowRead9<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowRead9<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowRead9<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowRead9<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,24 +347,24 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag
|
||||||
|
|
||||||
switch (size | NDS.ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowWrite7<u32, 0>); break;
|
case 32: ABI_CallFunction(&SlowWrite7<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowWrite7<u16, 0>); break;
|
case 16: ABI_CallFunction(&SlowWrite7<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowWrite7<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowWrite7<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowWrite7<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowWrite7<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowWrite7<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowWrite7<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowWrite7<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowWrite7<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (size | NDS.ConsoleType)
|
switch (size | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 32: CALL((void*)&SlowRead7<u32, 0>); break;
|
case 32: ABI_CallFunction(&SlowRead7<u32, 0>); break;
|
||||||
case 16: CALL((void*)&SlowRead7<u16, 0>); break;
|
case 16: ABI_CallFunction(&SlowRead7<u16, 0>); break;
|
||||||
case 8: CALL((void*)&SlowRead7<u8, 0>); break;
|
case 8: ABI_CallFunction(&SlowRead7<u8, 0>); break;
|
||||||
case 33: CALL((void*)&SlowRead7<u32, 1>); break;
|
case 33: ABI_CallFunction(&SlowRead7<u32, 1>); break;
|
||||||
case 17: CALL((void*)&SlowRead7<u16, 1>); break;
|
case 17: ABI_CallFunction(&SlowRead7<u16, 1>); break;
|
||||||
case 9: CALL((void*)&SlowRead7<u8, 1>); break;
|
case 9: ABI_CallFunction(&SlowRead7<u8, 1>); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,10 +526,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
|
|
||||||
switch (Num * 2 | NDS.ConsoleType)
|
switch (Num * 2 | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 0: CALL((void*)&SlowBlockTransfer9<false, 0>); break;
|
case 0: ABI_CallFunction(&SlowBlockTransfer9<false, 0>); break;
|
||||||
case 1: CALL((void*)&SlowBlockTransfer9<false, 1>); break;
|
case 1: ABI_CallFunction(&SlowBlockTransfer9<false, 1>); break;
|
||||||
case 2: CALL((void*)&SlowBlockTransfer7<false, 0>); break;
|
case 2: ABI_CallFunction(&SlowBlockTransfer7<false, 0>); break;
|
||||||
case 3: CALL((void*)&SlowBlockTransfer7<false, 1>); break;
|
case 3: ABI_CallFunction(&SlowBlockTransfer7<false, 1>); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
PopRegs(false, false);
|
PopRegs(false, false);
|
||||||
|
@ -630,10 +630,10 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc
|
||||||
|
|
||||||
switch (Num * 2 | NDS.ConsoleType)
|
switch (Num * 2 | NDS.ConsoleType)
|
||||||
{
|
{
|
||||||
case 0: CALL((void*)&SlowBlockTransfer9<true, 0>); break;
|
case 0: ABI_CallFunction(&SlowBlockTransfer9<true, 0>); break;
|
||||||
case 1: CALL((void*)&SlowBlockTransfer9<true, 1>); break;
|
case 1: ABI_CallFunction(&SlowBlockTransfer9<true, 1>); break;
|
||||||
case 2: CALL((void*)&SlowBlockTransfer7<true, 0>); break;
|
case 2: ABI_CallFunction(&SlowBlockTransfer7<true, 0>); break;
|
||||||
case 3: CALL((void*)&SlowBlockTransfer7<true, 1>); break;
|
case 3: ABI_CallFunction(&SlowBlockTransfer7<true, 1>); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
|
ADD(64, R(RSP), stackAlloc <= INT8_MAX ? Imm8(stackAlloc) : Imm32(stackAlloc));
|
||||||
|
|
12
src/Args.h
12
src/Args.h
|
@ -85,18 +85,6 @@ struct GDBArgs
|
||||||
/// New fields here should have default values if possible.
|
/// New fields here should have default values if possible.
|
||||||
struct NDSArgs
|
struct NDSArgs
|
||||||
{
|
{
|
||||||
/// NDS ROM to install.
|
|
||||||
/// Defaults to nullptr, which means no cart.
|
|
||||||
/// Should be populated with the desired save data beforehand,
|
|
||||||
/// including an SD card if applicable.
|
|
||||||
std::unique_ptr<NDSCart::CartCommon> NDSROM = nullptr;
|
|
||||||
|
|
||||||
/// GBA ROM to install.
|
|
||||||
/// Defaults to nullptr, which means no cart.
|
|
||||||
/// Should be populated with the desired save data beforehand.
|
|
||||||
/// Ignored in DSi mode.
|
|
||||||
std::unique_ptr<GBACart::CartCommon> GBAROM = nullptr;
|
|
||||||
|
|
||||||
/// NDS ARM9 BIOS to install.
|
/// NDS ARM9 BIOS to install.
|
||||||
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
|
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
|
||||||
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
|
std::unique_ptr<ARM9BIOSImage> ARM9BIOS = std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
|
||||||
|
|
|
@ -97,9 +97,16 @@ if (ENABLE_JIT)
|
||||||
|
|
||||||
ARMJIT.cpp
|
ARMJIT.cpp
|
||||||
ARMJIT_Memory.cpp
|
ARMJIT_Memory.cpp
|
||||||
|
ARMJIT_Global.cpp
|
||||||
|
|
||||||
dolphin/CommonFuncs.cpp)
|
dolphin/CommonFuncs.cpp)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
# Required for memory mapping-related functions introduced in Windows 8
|
||||||
|
target_compile_definitions(core PRIVATE -D_WIN32_WINNT=_WIN32_WINNT_WIN8)
|
||||||
|
target_link_libraries(core PRIVATE onecore)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (ARCHITECTURE STREQUAL x86_64)
|
if (ARCHITECTURE STREQUAL x86_64)
|
||||||
target_sources(core PRIVATE
|
target_sources(core PRIVATE
|
||||||
dolphin/x64ABI.cpp
|
dolphin/x64ABI.cpp
|
||||||
|
@ -127,7 +134,20 @@ if (ENABLE_JIT)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(core INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
|
||||||
set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version")
|
set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version")
|
||||||
|
option(MELONDS_EMBED_BUILD_INFO "Embed detailed build info into the binary" OFF)
|
||||||
|
set(MELONDS_GIT_BRANCH "$ENV{MELONDS_GIT_BRANCH}" CACHE STRING "The Git branch used for this build")
|
||||||
|
set(MELONDS_GIT_HASH "$ENV{MELONDS_GIT_HASH}" CACHE STRING "The hash of the Git commit")
|
||||||
|
set(MELONDS_BUILD_PROVIDER "$ENV{MELONDS_BUILD_PROVIDER}" CACHE STRING "The name of the provider of this build")
|
||||||
|
|
||||||
|
if (MELONDS_EMBED_BUILD_INFO)
|
||||||
|
target_compile_definitions(core PUBLIC MELONDS_EMBED_BUILD_INFO)
|
||||||
|
if (NOT MELONDS_GIT_BRANCH OR NOT MELONDS_GIT_HASH OR NOT MELONDS_BUILD_PROVIDER)
|
||||||
|
message(FATAL_ERROR "When embedding build information, all fields must be filled out. See src/CMakeLists.txt.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h")
|
||||||
target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h")
|
target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h")
|
||||||
|
@ -167,6 +187,7 @@ endif()
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
|
target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32)
|
||||||
|
target_compile_definitions(core PUBLIC WIN32_LEAN_AND_MEAN NOMINMAX)
|
||||||
elseif(NOT APPLE AND NOT HAIKU)
|
elseif(NOT APPLE AND NOT HAIKU)
|
||||||
check_library_exists(rt shm_open "" NEED_LIBRT)
|
check_library_exists(rt shm_open "" NEED_LIBRT)
|
||||||
if (NEED_LIBRT)
|
if (NEED_LIBRT)
|
||||||
|
|
|
@ -44,6 +44,7 @@ void ARMv5::CP15Reset()
|
||||||
CP15Control = 0x2078; // dunno
|
CP15Control = 0x2078; // dunno
|
||||||
|
|
||||||
RNGSeed = 44203;
|
RNGSeed = 44203;
|
||||||
|
TraceProcessID = 0;
|
||||||
|
|
||||||
DTCMSetting = 0;
|
DTCMSetting = 0;
|
||||||
ITCMSetting = 0;
|
ITCMSetting = 0;
|
||||||
|
@ -643,6 +644,10 @@ void ARMv5::CP15Write(u32 id, u32 val)
|
||||||
UpdateITCMSetting();
|
UpdateITCMSetting();
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 0xD01:
|
||||||
|
TraceProcessID = val;
|
||||||
|
return;
|
||||||
|
|
||||||
case 0xF00:
|
case 0xF00:
|
||||||
//printf("cache debug index register %08X\n", val);
|
//printf("cache debug index register %08X\n", val);
|
||||||
return;
|
return;
|
||||||
|
@ -760,6 +765,9 @@ u32 ARMv5::CP15Read(u32 id) const
|
||||||
return DTCMSetting;
|
return DTCMSetting;
|
||||||
case 0x911:
|
case 0x911:
|
||||||
return ITCMSetting;
|
return ITCMSetting;
|
||||||
|
|
||||||
|
case 0xD01:
|
||||||
|
return TraceProcessID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((id & 0xF00) == 0xF00) // test/debug shit?
|
if ((id & 0xF00) == 0xF00) // test/debug shit?
|
||||||
|
|
|
@ -74,8 +74,6 @@ const u32 NDMAModes[] =
|
||||||
DSi(
|
DSi(
|
||||||
DSiArgs {
|
DSiArgs {
|
||||||
NDSArgs {
|
NDSArgs {
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
bios_arm9_bin,
|
bios_arm9_bin,
|
||||||
bios_arm7_bin,
|
bios_arm7_bin,
|
||||||
Firmware(0),
|
Firmware(0),
|
||||||
|
|
|
@ -40,8 +40,8 @@ const u32 DSi_CamModule::kTransferStart = 60000;
|
||||||
|
|
||||||
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
|
DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi)
|
||||||
{
|
{
|
||||||
DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ));
|
DSi.RegisterEventFuncs(Event_DSi_CamIRQ, this, {MakeEventThunk(DSi_CamModule, IRQ)});
|
||||||
DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline));
|
DSi.RegisterEventFuncs(Event_DSi_CamTransfer, this, {MakeEventThunk(DSi_CamModule, TransferScanline)});
|
||||||
|
|
||||||
Camera0 = DSi.I2C.GetOuterCamera();
|
Camera0 = DSi.I2C.GetOuterCamera();
|
||||||
Camera1 = DSi.I2C.GetInnerCamera();
|
Camera1 = DSi.I2C.GetInnerCamera();
|
||||||
|
@ -52,8 +52,8 @@ DSi_CamModule::~DSi_CamModule()
|
||||||
Camera0 = nullptr;
|
Camera0 = nullptr;
|
||||||
Camera1 = nullptr;
|
Camera1 = nullptr;
|
||||||
|
|
||||||
DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0);
|
DSi.UnregisterEventFuncs(Event_DSi_CamIRQ);
|
||||||
DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0);
|
DSi.UnregisterEventFuncs(Event_DSi_CamTransfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_CamModule::Reset()
|
void DSi_CamModule::Reset()
|
||||||
|
|
|
@ -109,7 +109,7 @@ void DSi_DSP::AudioCb(std::array<s16, 2> frame)
|
||||||
|
|
||||||
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi)
|
||||||
{
|
{
|
||||||
DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32));
|
DSi.RegisterEventFuncs(Event_DSi_DSP, this, {MakeEventThunk(DSi_DSP, DSPCatchUpU32)});
|
||||||
|
|
||||||
TeakraCore = new Teakra::Teakra();
|
TeakraCore = new Teakra::Teakra();
|
||||||
SCFG_RST = false;
|
SCFG_RST = false;
|
||||||
|
@ -156,7 +156,7 @@ DSi_DSP::~DSi_DSP()
|
||||||
//PDATAWriteFifo = NULL;
|
//PDATAWriteFifo = NULL;
|
||||||
TeakraCore = NULL;
|
TeakraCore = NULL;
|
||||||
|
|
||||||
DSi.UnregisterEventFunc(Event_DSi_DSP, 0);
|
DSi.UnregisterEventFuncs(Event_DSi_DSP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_DSP::Reset()
|
void DSi_DSP::Reset()
|
||||||
|
|
|
@ -189,20 +189,18 @@ void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr)
|
||||||
u8 iv[16];
|
u8 iv[16];
|
||||||
memcpy(iv, FATIV.data(), sizeof(iv));
|
memcpy(iv, FATIV.data(), sizeof(iv));
|
||||||
|
|
||||||
u32 res;
|
u8 ctr_value[16] = {0};
|
||||||
res = iv[15] + (ctr & 0xFF);
|
ctr_value[15] = ctr & 0xFF;
|
||||||
iv[15] = (res & 0xFF);
|
ctr_value[14] = (ctr >> 8) & 0xFF;
|
||||||
res = iv[14] + ((ctr >> 8) & 0xFF) + (res >> 8);
|
ctr_value[13] = (ctr >> 16) & 0xFF;
|
||||||
iv[14] = (res & 0xFF);
|
ctr_value[12] = (ctr >> 24) & 0xFF;
|
||||||
res = iv[13] + ((ctr >> 16) & 0xFF) + (res >> 8);
|
|
||||||
iv[13] = (res & 0xFF);
|
unsigned carry = 0;
|
||||||
res = iv[12] + (ctr >> 24) + (res >> 8);
|
for (unsigned i = 0; i < 16; i ++) {
|
||||||
iv[12] = (res & 0xFF);
|
unsigned j = 15-i;
|
||||||
iv[11] += (res >> 8);
|
unsigned x = iv[j] + ctr_value[j] + carry;
|
||||||
for (int i = 10; i >= 0; i--)
|
carry = x >= 0x100;
|
||||||
{
|
iv[j] = x;
|
||||||
if (iv[i+1] == 0) iv[i]++;
|
|
||||||
else break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AES_init_ctx_iv(ctx, FATKey.data(), iv);
|
AES_init_ctx_iv(ctx, FATKey.data(), iv);
|
||||||
|
|
|
@ -31,7 +31,7 @@ using Platform::Log;
|
||||||
using Platform::LogLevel;
|
using Platform::LogLevel;
|
||||||
|
|
||||||
|
|
||||||
const u8 CIS0[256] =
|
u8 CIS0[256] =
|
||||||
{
|
{
|
||||||
0x01, 0x03, 0xD9, 0x01, 0xFF,
|
0x01, 0x03, 0xD9, 0x01, 0xFF,
|
||||||
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
|
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
|
||||||
|
@ -70,7 +70,7 @@ const u8 CIS0[256] =
|
||||||
0x00, 0x00, 0x00
|
0x00, 0x00, 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
const u8 CIS1[256] =
|
u8 CIS1[256] =
|
||||||
{
|
{
|
||||||
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
|
0x20, 0x04, 0x71, 0x02, 0x00, 0x02,
|
||||||
0x21, 0x02, 0x0C, 0x00,
|
0x21, 0x02, 0x0C, 0x00,
|
||||||
|
@ -134,7 +134,7 @@ DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) :
|
||||||
},
|
},
|
||||||
DSi(dsi)
|
DSi(dsi)
|
||||||
{
|
{
|
||||||
DSi.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer));
|
DSi.RegisterEventFuncs(Event_DSi_NWifi, this, {MakeEventThunk(DSi_NWifi, MSTimer)});
|
||||||
|
|
||||||
// this seems to control whether the firmware upload is done
|
// this seems to control whether the firmware upload is done
|
||||||
EEPROMReady = 0;
|
EEPROMReady = 0;
|
||||||
|
@ -144,7 +144,7 @@ DSi_NWifi::~DSi_NWifi()
|
||||||
{
|
{
|
||||||
DSi.CancelEvent(Event_DSi_NWifi);
|
DSi.CancelEvent(Event_DSi_NWifi);
|
||||||
|
|
||||||
DSi.UnregisterEventFunc(Event_DSi_NWifi, 0);
|
DSi.UnregisterEventFuncs(Event_DSi_NWifi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSi_NWifi::Reset()
|
void DSi_NWifi::Reset()
|
||||||
|
@ -201,6 +201,9 @@ void DSi_NWifi::Reset()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CIS0[9] = ChipID >= 0x0D000000;
|
||||||
|
CIS1[4] = CIS0[9];
|
||||||
|
|
||||||
memset(EEPROM, 0, 0x400);
|
memset(EEPROM, 0, 0x400);
|
||||||
|
|
||||||
*(u32*)&EEPROM[0x000] = 0x300;
|
*(u32*)&EEPROM[0x000] = 0x300;
|
||||||
|
@ -227,6 +230,8 @@ void DSi_NWifi::Reset()
|
||||||
BeaconTimer = 0x10A2220ULL;
|
BeaconTimer = 0x10A2220ULL;
|
||||||
ConnectionStatus = 0;
|
ConnectionStatus = 0;
|
||||||
|
|
||||||
|
SendBSSInfo = true;
|
||||||
|
|
||||||
DSi.CancelEvent(Event_DSi_NWifi);
|
DSi.CancelEvent(Event_DSi_NWifi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1001,7 +1006,7 @@ void DSi_NWifi::WMI_Command()
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkme
|
// checkme
|
||||||
ScanTimer = scantime*5;
|
ScanTimer = scantime*8;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1036,6 +1041,7 @@ void DSi_NWifi::WMI_Command()
|
||||||
|
|
||||||
// TODO: store it somewhere
|
// TODO: store it somewhere
|
||||||
Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
|
Log(LogLevel::Debug, "WMI: set probed SSID: id=%d, flags=%02X, len=%d, SSID=%s\n", id, flags, len, ssid);
|
||||||
|
SendBSSInfo = flags == 0 || strcmp(ssid, WifiAP::APName) == 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1405,6 +1411,11 @@ void DSi_NWifi::SendWMIAck(u8 ep)
|
||||||
|
|
||||||
void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len)
|
void DSi_NWifi::SendWMIBSSInfo(u8 type, u8* data, u32 len)
|
||||||
{
|
{
|
||||||
|
if (!SendBSSInfo) {
|
||||||
|
Log(LogLevel::Info, "NWifi: melonAP filtered, not sending WMI BSSINFO event\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Mailbox[8].CanFit(6+len+2+16))
|
if (!Mailbox[8].CanFit(6+len+2+16))
|
||||||
{
|
{
|
||||||
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");
|
Log(LogLevel::Error, "NWifi: !! not enough space in RX buffer for WMI BSSINFO event\n");
|
||||||
|
@ -1445,7 +1456,6 @@ void DSi_NWifi::CheckRX()
|
||||||
int rxlen = Platform::Net_RecvPacket(LANBuffer, DSi.UserData);
|
int rxlen = Platform::Net_RecvPacket(LANBuffer, DSi.UserData);
|
||||||
while (rxlen > 0)
|
while (rxlen > 0)
|
||||||
{
|
{
|
||||||
//printf("WMI packet recv %04X %04X %04X\n", *(u16*)&LANBuffer[0], *(u16*)&LANBuffer[2], *(u16*)&LANBuffer[4]);
|
|
||||||
// check destination MAC
|
// check destination MAC
|
||||||
if (*(u32*)&LANBuffer[0] != 0xFFFFFFFF || *(u16*)&LANBuffer[4] != 0xFFFF)
|
if (*(u32*)&LANBuffer[0] != 0xFFFFFFFF || *(u16*)&LANBuffer[4] != 0xFFFF)
|
||||||
{
|
{
|
||||||
|
@ -1508,6 +1518,7 @@ void DSi_NWifi::CheckRX()
|
||||||
Mailbox[8].Write(LANBuffer[14+i]);
|
Mailbox[8].Write(LANBuffer[14+i]);
|
||||||
|
|
||||||
DrainRXBuffer();
|
DrainRXBuffer();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,8 @@ private:
|
||||||
u32 ConnectionStatus;
|
u32 ConnectionStatus;
|
||||||
|
|
||||||
u8 LANBuffer[2048];
|
u8 LANBuffer[2048];
|
||||||
|
|
||||||
|
bool SendBSSInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,9 @@ enum
|
||||||
|
|
||||||
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
|
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
|
||||||
{
|
{
|
||||||
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
|
DSi.RegisterEventFuncs(Event_DSi_SDMMCTransfer, this,
|
||||||
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
|
{MakeEventThunk(DSi_SDHost, FinishTX),
|
||||||
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
|
MakeEventThunk(DSi_SDHost, FinishRX)});
|
||||||
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
|
|
||||||
|
|
||||||
Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
|
Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
|
||||||
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
|
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
|
||||||
|
@ -77,10 +76,9 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optio
|
||||||
// Creates an SDIO host
|
// Creates an SDIO host
|
||||||
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
|
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
|
||||||
{
|
{
|
||||||
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer ,
|
DSi.RegisterEventFuncs(Event_DSi_SDIOTransfer, this,
|
||||||
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
|
{MakeEventThunk(DSi_SDHost, FinishTX),
|
||||||
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer,
|
MakeEventThunk(DSi_SDHost, FinishRX)});
|
||||||
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
|
|
||||||
|
|
||||||
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
|
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
|
||||||
Ports[1] = nullptr;
|
Ports[1] = nullptr;
|
||||||
|
@ -88,10 +86,7 @@ DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
|
||||||
|
|
||||||
DSi_SDHost::~DSi_SDHost()
|
DSi_SDHost::~DSi_SDHost()
|
||||||
{
|
{
|
||||||
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
DSi.UnregisterEventFuncs(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer);
|
||||||
Transfer_TX);
|
|
||||||
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
|
|
||||||
Transfer_RX);
|
|
||||||
|
|
||||||
// unique_ptr's destructor will clean up the ports
|
// unique_ptr's destructor will clean up the ports
|
||||||
}
|
}
|
||||||
|
|
105
src/GBACart.cpp
105
src/GBACart.cpp
|
@ -539,6 +539,57 @@ CartGameSolarSensor::CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, s
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata) noexcept
|
||||||
|
{
|
||||||
|
return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata) noexcept
|
||||||
|
{
|
||||||
|
return CreateFakeSolarSensorROM(gamecode, cart.GetHeader().NintendoLogo, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata) noexcept
|
||||||
|
{
|
||||||
|
if (!gamecode)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (strnlen(gamecode, sizeof(GBAHeader::GameCode)) > sizeof(GBAHeader::GameCode))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
bool solarsensor = false;
|
||||||
|
for (const char* i : SOLAR_SENSOR_GAMECODES)
|
||||||
|
{
|
||||||
|
if (strcmp(gamecode, i) == 0) {
|
||||||
|
solarsensor = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!solarsensor)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// just 256 bytes; we don't need a whole ROM!
|
||||||
|
constexpr size_t FAKE_BOKTAI_ROM_LENGTH = 0x100;
|
||||||
|
std::unique_ptr<u8[]> rom = std::make_unique<u8[]>(FAKE_BOKTAI_ROM_LENGTH);
|
||||||
|
|
||||||
|
// create a fake ROM
|
||||||
|
GBAHeader& header = *reinterpret_cast<GBAHeader*>(rom.get());
|
||||||
|
memcpy(header.Title, BOKTAI_STUB_TITLE, strnlen(BOKTAI_STUB_TITLE, sizeof(header.Title)));
|
||||||
|
memcpy(header.GameCode, gamecode, strnlen(gamecode, sizeof(header.GameCode)));
|
||||||
|
header.FixedValue = 0x96;
|
||||||
|
if (logo)
|
||||||
|
{
|
||||||
|
memcpy(header.NintendoLogo, logo, sizeof(header.NintendoLogo));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memset(header.NintendoLogo, 0xFF, sizeof(header.NintendoLogo));
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_unique<CartGameSolarSensor>(std::move(rom), FAKE_BOKTAI_ROM_LENGTH, nullptr, 0, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183};
|
const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183};
|
||||||
|
|
||||||
void CartGameSolarSensor::Reset()
|
void CartGameSolarSensor::Reset()
|
||||||
|
@ -582,6 +633,11 @@ int CartGameSolarSensor::SetInput(int num, bool pressed)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CartGameSolarSensor::SetLightLevel(u8 level) noexcept
|
||||||
|
{
|
||||||
|
LightLevel = std::clamp<u8>(level, 0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
void CartGameSolarSensor::ProcessGPIO()
|
void CartGameSolarSensor::ProcessGPIO()
|
||||||
{
|
{
|
||||||
if (GPIO.data & 4) return; // Boktai chip select
|
if (GPIO.data & 4) return; // Boktai chip select
|
||||||
|
@ -827,6 +883,38 @@ std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen
|
||||||
return cart;
|
return cart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CartCommon> LoadAddon(int type, void* userdata)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CartCommon> cart;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case GBAAddon_RAMExpansion:
|
||||||
|
cart = std::make_unique<CartRAMExpansion>();
|
||||||
|
break;
|
||||||
|
case GBAAddon_RumblePak:
|
||||||
|
cart = std::make_unique<CartRumblePak>(userdata);
|
||||||
|
break;
|
||||||
|
case GBAAddon_SolarSensorBoktai1:
|
||||||
|
// US Boktai 1
|
||||||
|
cart = CreateFakeSolarSensorROM("U3IE", nullptr, userdata);
|
||||||
|
break;
|
||||||
|
case GBAAddon_SolarSensorBoktai2:
|
||||||
|
// US Boktai 2
|
||||||
|
cart = CreateFakeSolarSensorROM("U32E", nullptr, userdata);
|
||||||
|
break;
|
||||||
|
case GBAAddon_SolarSensorBoktai3:
|
||||||
|
// JP Boktai 3
|
||||||
|
cart = CreateFakeSolarSensorROM("U33J", nullptr, userdata);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
cart->Reset();
|
||||||
|
return cart;
|
||||||
|
}
|
||||||
|
|
||||||
void GBACartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
|
void GBACartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
|
||||||
{
|
{
|
||||||
Cart = std::move(cart);
|
Cart = std::move(cart);
|
||||||
|
@ -859,23 +947,6 @@ void GBACartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBACartSlot::LoadAddon(void* userdata, int type) noexcept
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case GBAAddon_RAMExpansion:
|
|
||||||
Cart = std::make_unique<CartRAMExpansion>();
|
|
||||||
break;
|
|
||||||
case GBAAddon_RumblePak:
|
|
||||||
Cart = std::make_unique<CartRumblePak>(userdata);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<CartCommon> GBACartSlot::EjectCart() noexcept
|
std::unique_ptr<CartCommon> GBACartSlot::EjectCart() noexcept
|
||||||
{
|
{
|
||||||
return std::move(Cart);
|
return std::move(Cart);
|
||||||
|
|
|
@ -35,6 +35,25 @@ enum CartType
|
||||||
RumblePak = 0x202,
|
RumblePak = 0x202,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// See https://problemkaputt.de/gbatek.htm#gbacartridgeheader for details
|
||||||
|
struct GBAHeader
|
||||||
|
{
|
||||||
|
u32 EntryPoint;
|
||||||
|
u8 NintendoLogo[156]; // must be valid
|
||||||
|
char Title[12];
|
||||||
|
char GameCode[4];
|
||||||
|
char MakerCode[2];
|
||||||
|
u8 FixedValue; // must be 0x96
|
||||||
|
u8 MainUnitCode;
|
||||||
|
u8 DeviceType;
|
||||||
|
u8 Reserved0[7];
|
||||||
|
u8 SoftwareVersion;
|
||||||
|
u8 ComplementCheck;
|
||||||
|
u8 Reserved1[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(GBAHeader) == 192, "GBAHeader should be 192 bytes");
|
||||||
|
|
||||||
// CartCommon -- base code shared by all cart types
|
// CartCommon -- base code shared by all cart types
|
||||||
class CartCommon
|
class CartCommon
|
||||||
{
|
{
|
||||||
|
@ -91,6 +110,8 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
|
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
|
||||||
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
|
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
|
||||||
|
[[nodiscard]] const GBAHeader& GetHeader() const noexcept { return *reinterpret_cast<const GBAHeader*>(ROM.get()); }
|
||||||
|
[[nodiscard]] GBAHeader& GetHeader() noexcept { return *reinterpret_cast<GBAHeader*>(ROM.get()); }
|
||||||
|
|
||||||
u8* GetSaveMemory() const override;
|
u8* GetSaveMemory() const override;
|
||||||
u32 GetSaveMemoryLength() const override;
|
u32 GetSaveMemoryLength() const override;
|
||||||
|
@ -158,6 +179,8 @@ public:
|
||||||
void DoSavestate(Savestate* file) override;
|
void DoSavestate(Savestate* file) override;
|
||||||
|
|
||||||
int SetInput(int num, bool pressed) override;
|
int SetInput(int num, bool pressed) override;
|
||||||
|
void SetLightLevel(u8 level) noexcept;
|
||||||
|
[[nodiscard]] u8 GetLightLevel() const noexcept { return LightLevel; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ProcessGPIO() override;
|
void ProcessGPIO() override;
|
||||||
|
@ -239,8 +262,6 @@ public:
|
||||||
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
|
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
|
||||||
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
|
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
|
||||||
|
|
||||||
void LoadAddon(void* userdata, int type) noexcept;
|
|
||||||
|
|
||||||
/// @return The cart that was in the cart slot if any,
|
/// @return The cart that was in the cart slot if any,
|
||||||
/// or \c nullptr if the cart slot was empty.
|
/// or \c nullptr if the cart slot was empty.
|
||||||
std::unique_ptr<CartCommon> EjectCart() noexcept;
|
std::unique_ptr<CartCommon> EjectCart() noexcept;
|
||||||
|
@ -307,6 +328,25 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sr
|
||||||
/// or \c nullptr if there was an error.
|
/// or \c nullptr if there was an error.
|
||||||
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr);
|
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen, void* userdata = nullptr);
|
||||||
|
|
||||||
|
std::unique_ptr<CartCommon> LoadAddon(int type, void* userdata);
|
||||||
|
|
||||||
|
/// Creates a solar sensor-enabled GBA cart without needing a real Boktai ROM.
|
||||||
|
/// This enables the solar sensor to be used in supported games.
|
||||||
|
/// Will not contain any SRAM.
|
||||||
|
/// @param gamecode
|
||||||
|
/// @param logo The Nintendo logo data embedded in the headers for GBA ROMs and NDS ROMs.
|
||||||
|
/// Required for the cart to be recognized as a valid GBA cart.
|
||||||
|
/// Overloads that accept cart objects directly exist as well.
|
||||||
|
/// If not provided, then it will have to be patched with equivalent data
|
||||||
|
/// from a real ROM (NDS or GBA) before booting the emulator.
|
||||||
|
/// @param userdata Optional user data to associate with the cart.
|
||||||
|
/// @return A CartGameSolarSensor if the ROM was created successfully,
|
||||||
|
/// or nullptr if any argument is wrong (e.g. an incorrect game code).
|
||||||
|
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const u8* logo, void* userdata = nullptr) noexcept;
|
||||||
|
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const NDSCart::CartCommon& cart, void* userdata = nullptr) noexcept;
|
||||||
|
std::unique_ptr<CartGameSolarSensor> CreateFakeSolarSensorROM(const char* gamecode, const GBACart::CartGame& cart, void* userdata = nullptr) noexcept;
|
||||||
|
|
||||||
|
constexpr const char* BOKTAI_STUB_TITLE = "BOKTAI STUB";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // GBACART_H
|
#endif // GBACART_H
|
||||||
|
|
17
src/GPU.cpp
17
src/GPU.cpp
|
@ -70,10 +70,13 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr<Renderer3D>&& renderer3d, std::uniqu
|
||||||
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()),
|
GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique<SoftRenderer>()),
|
||||||
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
|
GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique<GPU2D::SoftRenderer>(*this))
|
||||||
{
|
{
|
||||||
NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank));
|
NDS.RegisterEventFuncs(Event_LCD, this,
|
||||||
NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline));
|
{
|
||||||
NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame));
|
MakeEventThunk(GPU, StartHBlank),
|
||||||
NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO));
|
MakeEventThunk(GPU, StartScanline),
|
||||||
|
MakeEventThunk(GPU, FinishFrame)
|
||||||
|
});
|
||||||
|
NDS.RegisterEventFuncs(Event_DisplayFIFO, this, {MakeEventThunk(GPU, DisplayFIFO)});
|
||||||
|
|
||||||
InitFramebuffers();
|
InitFramebuffers();
|
||||||
}
|
}
|
||||||
|
@ -82,10 +85,8 @@ GPU::~GPU() noexcept
|
||||||
{
|
{
|
||||||
// All unique_ptr fields are automatically cleaned up
|
// All unique_ptr fields are automatically cleaned up
|
||||||
|
|
||||||
NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank);
|
NDS.UnregisterEventFuncs(Event_LCD);
|
||||||
NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline);
|
NDS.UnregisterEventFuncs(Event_DisplayFIFO);
|
||||||
NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame);
|
|
||||||
NDS.UnregisterEventFunc(Event_DisplayFIFO, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::ResetVRAMCache() noexcept
|
void GPU::ResetVRAMCache() noexcept
|
||||||
|
|
11
src/GPU.h
11
src/GPU.h
|
@ -499,6 +499,17 @@ public:
|
||||||
OAMDirty |= 1 << (addr / 1024);
|
OAMDirty |= 1 << (addr / 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ReadVRAMFlat_Texture(u32 addr) const
|
||||||
|
{
|
||||||
|
return *(T*)&VRAMFlat_Texture[addr & 0x7FFFF];
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
inline T ReadVRAMFlat_TexPal(u32 addr) const
|
||||||
|
{
|
||||||
|
return *(T*)&VRAMFlat_TexPal[addr & 0x1FFFF];
|
||||||
|
}
|
||||||
|
|
||||||
void SetPowerCnt(u32 val) noexcept;
|
void SetPowerCnt(u32 val) noexcept;
|
||||||
|
|
||||||
void StartFrame() noexcept;
|
void StartFrame() noexcept;
|
||||||
|
|
|
@ -914,6 +914,9 @@ void SoftRenderer::DrawBG_3D()
|
||||||
template<bool mosaic, SoftRenderer::DrawPixel drawPixel>
|
template<bool mosaic, SoftRenderer::DrawPixel drawPixel>
|
||||||
void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum)
|
void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum)
|
||||||
{
|
{
|
||||||
|
// workaround for backgrounds missing on aarch64 with lto build
|
||||||
|
asm volatile ("" : : : "memory");
|
||||||
|
|
||||||
u16 bgcnt = CurUnit->BGCnt[bgnum];
|
u16 bgcnt = CurUnit->BGCnt[bgnum];
|
||||||
|
|
||||||
u32 tilesetaddr, tilemapaddr;
|
u32 tilesetaddr, tilemapaddr;
|
||||||
|
|
|
@ -681,6 +681,7 @@ void main()
|
||||||
|
|
||||||
vec4 fpos;
|
vec4 fpos;
|
||||||
fpos.xy = (((vec2(vPosition.xy) ) * 2.0) / uScreenSize) - 1.0;
|
fpos.xy = (((vec2(vPosition.xy) ) * 2.0) / uScreenSize) - 1.0;
|
||||||
|
fpos.z = 0.0;
|
||||||
fZ = float(vPosition.z << zshift) / 16777216.0;
|
fZ = float(vPosition.z << zshift) / 16777216.0;
|
||||||
fpos.w = float(vPosition.w) / 65536.0f;
|
fpos.w = float(vPosition.w) / 65536.0f;
|
||||||
fpos.xy *= fpos.w;
|
fpos.xy *= fpos.w;
|
||||||
|
|
|
@ -201,10 +201,10 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 1: // A3I5
|
case 1: // A3I5
|
||||||
{
|
{
|
||||||
vramaddr += ((t * width) + s);
|
vramaddr += ((t * width) + s);
|
||||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
|
u8 pixel = gpu.ReadVRAMFlat_Texture<u8>(vramaddr);
|
||||||
|
|
||||||
texpal <<= 4;
|
texpal <<= 4;
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1), gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
|
||||||
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
|
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
|
||||||
|
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
|
@ -214,12 +214,12 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 2: // 4-color
|
case 2: // 4-color
|
||||||
{
|
{
|
||||||
vramaddr += (((t * width) + s) >> 2);
|
vramaddr += (((t * width) + s) >> 2);
|
||||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
|
u8 pixel = gpu.ReadVRAMFlat_Texture<u8>(vramaddr);
|
||||||
pixel >>= ((s & 0x3) << 1);
|
pixel >>= ((s & 0x3) << 1);
|
||||||
pixel &= 0x3;
|
pixel &= 0x3;
|
||||||
|
|
||||||
texpal <<= 3;
|
texpal <<= 3;
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + (pixel<<1));
|
||||||
*alpha = (pixel==0) ? alpha0 : 31;
|
*alpha = (pixel==0) ? alpha0 : 31;
|
||||||
|
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
|
@ -229,12 +229,12 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 3: // 16-color
|
case 3: // 16-color
|
||||||
{
|
{
|
||||||
vramaddr += (((t * width) + s) >> 1);
|
vramaddr += (((t * width) + s) >> 1);
|
||||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
|
u8 pixel = gpu.ReadVRAMFlat_Texture<u8>(vramaddr);
|
||||||
if (s & 0x1) pixel >>= 4;
|
if (s & 0x1) pixel >>= 4;
|
||||||
else pixel &= 0xF;
|
else pixel &= 0xF;
|
||||||
|
|
||||||
texpal <<= 4;
|
texpal <<= 4;
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + (pixel<<1));
|
||||||
*alpha = (pixel==0) ? alpha0 : 31;
|
*alpha = (pixel==0) ? alpha0 : 31;
|
||||||
|
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
|
@ -244,10 +244,10 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 4: // 256-color
|
case 4: // 256-color
|
||||||
{
|
{
|
||||||
vramaddr += ((t * width) + s);
|
vramaddr += ((t * width) + s);
|
||||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
|
u8 pixel = gpu.ReadVRAMFlat_Texture<u8>(vramaddr);
|
||||||
|
|
||||||
texpal <<= 4;
|
texpal <<= 4;
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + (pixel<<1));
|
||||||
*alpha = (pixel==0) ? alpha0 : 31;
|
*alpha = (pixel==0) ? alpha0 : 31;
|
||||||
|
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
|
@ -273,11 +273,11 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
val = 0;
|
val = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val = ReadVRAM_Texture<u8>(vramaddr, gpu);
|
val = gpu.ReadVRAMFlat_Texture<u8>(vramaddr);
|
||||||
val >>= (2 * (s & 0x3));
|
val >>= (2 * (s & 0x3));
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 palinfo = ReadVRAM_Texture<u16>(slot1addr, gpu);
|
u16 palinfo = gpu.ReadVRAMFlat_Texture<u16>(slot1addr);
|
||||||
u32 paloffset = (palinfo & 0x3FFF) << 2;
|
u32 paloffset = (palinfo & 0x3FFF) << 2;
|
||||||
texpal <<= 4;
|
texpal <<= 4;
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset);
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
if (!((palinfo >> 14) & 0x1))
|
if (!((palinfo >> 14) & 0x1))
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
|
@ -296,7 +296,7 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 2);
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
if (!((palinfo >> 14) & 0x1))
|
if (!((palinfo >> 14) & 0x1))
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
|
@ -308,8 +308,8 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 2:
|
case 2:
|
||||||
if ((palinfo >> 14) == 1)
|
if ((palinfo >> 14) == 1)
|
||||||
{
|
{
|
||||||
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
|
u16 color0 = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset);
|
||||||
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
|
u16 color1 = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 2);
|
||||||
|
|
||||||
u32 r0 = color0 & 0x001F;
|
u32 r0 = color0 & 0x001F;
|
||||||
u32 g0 = color0 & 0x03E0;
|
u32 g0 = color0 & 0x03E0;
|
||||||
|
@ -324,8 +324,8 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
}
|
}
|
||||||
else if ((palinfo >> 14) == 3)
|
else if ((palinfo >> 14) == 3)
|
||||||
{
|
{
|
||||||
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
|
u16 color0 = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset);
|
||||||
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
|
u16 color1 = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 2);
|
||||||
|
|
||||||
u32 r0 = color0 & 0x001F;
|
u32 r0 = color0 & 0x001F;
|
||||||
u32 g0 = color0 & 0x03E0;
|
u32 g0 = color0 & 0x03E0;
|
||||||
|
@ -340,7 +340,7 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + paloffset + 4, gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 4);
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
}
|
}
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
|
@ -349,14 +349,14 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 3:
|
case 3:
|
||||||
if ((palinfo >> 14) == 2)
|
if ((palinfo >> 14) == 2)
|
||||||
{
|
{
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + paloffset + 6, gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 6);
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
}
|
}
|
||||||
else if ((palinfo >> 14) == 3)
|
else if ((palinfo >> 14) == 3)
|
||||||
{
|
{
|
||||||
u16 color0 = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
|
u16 color0 = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset);
|
||||||
u16 color1 = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
|
u16 color1 = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 2);
|
||||||
|
|
||||||
u32 r0 = color0 & 0x001F;
|
u32 r0 = color0 & 0x001F;
|
||||||
u32 g0 = color0 & 0x03E0;
|
u32 g0 = color0 & 0x03E0;
|
||||||
|
@ -386,10 +386,10 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 6: // A5I3
|
case 6: // A5I3
|
||||||
{
|
{
|
||||||
vramaddr += ((t * width) + s);
|
vramaddr += ((t * width) + s);
|
||||||
u8 pixel = ReadVRAM_Texture<u8>(vramaddr, gpu);
|
u8 pixel = gpu.ReadVRAMFlat_Texture<u8>(vramaddr);
|
||||||
|
|
||||||
texpal <<= 4;
|
texpal <<= 4;
|
||||||
u16 color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1), gpu);
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + ((pixel&0x7)<<1));
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
*alpha = (pixel >> 3);
|
*alpha = (pixel >> 3);
|
||||||
}
|
}
|
||||||
|
@ -398,7 +398,7 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
||||||
case 7: // direct color
|
case 7: // direct color
|
||||||
{
|
{
|
||||||
vramaddr += (((t * width) + s) << 1);
|
vramaddr += (((t * width) + s) << 1);
|
||||||
u16 color = ReadVRAM_Texture<u16>(vramaddr, gpu);
|
u16 color = gpu.ReadVRAMFlat_Texture<u16>(vramaddr);
|
||||||
ColorConv<true>(color, tr, tg, tb);
|
ColorConv<true>(color, tr, tg, tb);
|
||||||
*alpha = (color & 0x8000) ? 31 : 0;
|
*alpha = (color & 0x8000) ? 31 : 0;
|
||||||
}
|
}
|
||||||
|
@ -1690,8 +1690,8 @@ void SoftRenderer::ClearBuffers(const GPU& gpu)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < 256; x++)
|
for (int x = 0; x < 256; x++)
|
||||||
{
|
{
|
||||||
u16 val2 = ReadVRAM_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1), gpu);
|
u16 val2 = gpu.ReadVRAMFlat_Texture<u16>(0x40000 + (yoff << 9) + (xoff << 1));
|
||||||
u16 val3 = ReadVRAM_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1), gpu);
|
u16 val3 = gpu.ReadVRAMFlat_Texture<u16>(0x60000 + (yoff << 9) + (xoff << 1));
|
||||||
|
|
||||||
// TODO: confirm color conversion
|
// TODO: confirm color conversion
|
||||||
u32 r = (val2 << 1) & 0x3E; if (r) r++;
|
u32 r = (val2 << 1) & 0x3E; if (r) r++;
|
||||||
|
|
|
@ -430,16 +430,6 @@ private:
|
||||||
s32 ycoverage, ycov_incr;
|
s32 ycoverage, ycov_incr;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline T ReadVRAM_Texture(u32 addr, const GPU& gpu) const
|
|
||||||
{
|
|
||||||
return *(T*)&gpu.VRAMFlat_Texture[addr & 0x7FFFF];
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
inline T ReadVRAM_TexPal(u32 addr, const GPU& gpu) const
|
|
||||||
{
|
|
||||||
return *(T*)&gpu.VRAMFlat_TexPal[addr & 0x1FFFF];
|
|
||||||
}
|
|
||||||
u32 AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept;
|
u32 AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept;
|
||||||
|
|
||||||
struct RendererPolygon
|
struct RendererPolygon
|
||||||
|
|
|
@ -75,11 +75,11 @@ inline u32 ConvertRGB5ToRGB6(u16 val)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int outputFmt>
|
template <int outputFmt>
|
||||||
void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData)
|
void ConvertBitmapTexture(u32 width, u32 height, u32* output, u32 addr, GPU& gpu)
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < width*height; i++)
|
for (u32 i = 0; i < width*height; i++)
|
||||||
{
|
{
|
||||||
u16 value = *(u16*)&texData[i * 2];
|
u16 value = gpu.ReadVRAMFlat_Texture<u16>(addr + i * 2);
|
||||||
|
|
||||||
switch (outputFmt)
|
switch (outputFmt)
|
||||||
{
|
{
|
||||||
|
@ -96,28 +96,28 @@ void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template void ConvertBitmapTexture<outputFmt_RGB6A5>(u32 width, u32 height, u32* output, u8* texData);
|
template void ConvertBitmapTexture<outputFmt_RGB6A5>(u32 width, u32 height, u32* output, u32 addr, GPU& gpu);
|
||||||
|
|
||||||
template <int outputFmt>
|
template <int outputFmt>
|
||||||
void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u8* texAuxData, u16* palData)
|
void ConvertCompressedTexture(u32 width, u32 height, u32* output, u32 addr, u32 addrAux, u32 palAddr, GPU& gpu)
|
||||||
{
|
{
|
||||||
// we process a whole block at the time
|
// we process a whole block at the time
|
||||||
for (int y = 0; y < height / 4; y++)
|
for (int y = 0; y < height / 4; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < width / 4; x++)
|
for (int x = 0; x < width / 4; x++)
|
||||||
{
|
{
|
||||||
u32 data = ((u32*)texData)[x + y * (width / 4)];
|
u32 data = gpu.ReadVRAMFlat_Texture<u32>(addr + (x + y * (width / 4))*4);
|
||||||
u16 auxData = ((u16*)texAuxData)[x + y * (width / 4)];
|
u16 auxData = gpu.ReadVRAMFlat_Texture<u16>(addrAux + (x + y * (width / 4))*2);
|
||||||
|
|
||||||
u32 paletteOffset = auxData & 0x3FFF;
|
u32 paletteOffset = palAddr + (auxData & 0x3FFF) * 4;
|
||||||
u16 color0 = palData[paletteOffset*2] | 0x8000;
|
u16 color0 = gpu.ReadVRAMFlat_TexPal<u16>(paletteOffset) | 0x8000;
|
||||||
u16 color1 = palData[paletteOffset*2+1] | 0x8000;
|
u16 color1 = gpu.ReadVRAMFlat_TexPal<u16>(paletteOffset+2) | 0x8000;
|
||||||
u16 color2, color3;
|
u16 color2 = gpu.ReadVRAMFlat_TexPal<u16>(paletteOffset+4) | 0x8000;
|
||||||
|
u16 color3 = gpu.ReadVRAMFlat_TexPal<u16>(paletteOffset+6) | 0x8000;
|
||||||
|
|
||||||
switch ((auxData >> 14) & 0x3)
|
switch ((auxData >> 14) & 0x3)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
color2 = palData[paletteOffset*2+2] | 0x8000;
|
|
||||||
color3 = 0;
|
color3 = 0;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -137,8 +137,6 @@ void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u
|
||||||
color3 = 0;
|
color3 = 0;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
color2 = palData[paletteOffset*2+2] | 0x8000;
|
|
||||||
color3 = palData[paletteOffset*2+3] | 0x8000;
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
|
@ -179,7 +177,8 @@ void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
{
|
{
|
||||||
u16 color = (packed >> 16 * (data >> 2 * (i + j * 4))) & 0xFFFF;
|
u32 colorIdx = 16 * ((data >> 2 * (i + j * 4)) & 0x3);
|
||||||
|
u16 color = (packed >> colorIdx) & 0xFFFF;
|
||||||
u32 res;
|
u32 res;
|
||||||
switch (outputFmt)
|
switch (outputFmt)
|
||||||
{
|
{
|
||||||
|
@ -197,20 +196,20 @@ void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template void ConvertCompressedTexture<outputFmt_RGB6A5>(u32, u32, u32*, u8*, u8*, u16*);
|
template void ConvertCompressedTexture<outputFmt_RGB6A5>(u32, u32, u32*, u32, u32, u32, GPU&);
|
||||||
|
|
||||||
template <int outputFmt, int X, int Y>
|
template <int outputFmt, int X, int Y>
|
||||||
void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData)
|
void ConvertAXIYTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, GPU& gpu)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < width; x++)
|
for (int x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
u8 val = texData[x + y * width];
|
u8 val = gpu.ReadVRAMFlat_Texture<u8>(addr + x + y * width);
|
||||||
|
|
||||||
u32 idx = val & ((1 << Y) - 1);
|
u32 idx = val & ((1 << Y) - 1);
|
||||||
|
|
||||||
u16 color = palData[idx];
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(palAddr + idx * 2);
|
||||||
u32 alpha = (val >> Y) & ((1 << X) - 1);
|
u32 alpha = (val >> Y) & ((1 << X) - 1);
|
||||||
if (X != 5)
|
if (X != 5)
|
||||||
alpha = alpha * 4 + alpha / 2;
|
alpha = alpha * 4 + alpha / 2;
|
||||||
|
@ -228,22 +227,24 @@ void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* pa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template void ConvertAXIYTexture<outputFmt_RGB6A5, 5, 3>(u32, u32, u32*, u8*, u16*);
|
template void ConvertAXIYTexture<outputFmt_RGB6A5, 5, 3>(u32, u32, u32*, u32, u32, GPU&);
|
||||||
template void ConvertAXIYTexture<outputFmt_RGB6A5, 3, 5>(u32, u32, u32*, u8*, u16*);
|
template void ConvertAXIYTexture<outputFmt_RGB6A5, 3, 5>(u32, u32, u32*, u32, u32, GPU&);
|
||||||
|
|
||||||
template <int outputFmt, int colorBits>
|
template <int outputFmt, int colorBits>
|
||||||
void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData, bool color0Transparent)
|
void ConvertNColorsTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, bool color0Transparent, GPU& gpu)
|
||||||
{
|
{
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
for (int x = 0; x < width / (8 / colorBits); x++)
|
for (int x = 0; x < width / (16 / colorBits); x++)
|
||||||
{
|
{
|
||||||
u8 val = texData[x + y * (width / (8 / colorBits))];
|
// smallest possible row is 8 pixels with 2bpp => fits in u16
|
||||||
|
u16 val = gpu.ReadVRAMFlat_Texture<u16>(addr + 2 * (x + y * (width / (16 / colorBits))));
|
||||||
|
|
||||||
for (int i = 0; i < 8 / colorBits; i++)
|
for (int i = 0; i < 16 / colorBits; i++)
|
||||||
{
|
{
|
||||||
u32 index = (val >> (i * colorBits)) & ((1 << colorBits) - 1);
|
u32 index = val & ((1 << colorBits) - 1);
|
||||||
u16 color = palData[index];
|
val >>= colorBits;
|
||||||
|
u16 color = gpu.ReadVRAMFlat_TexPal<u16>(palAddr + index * 2);
|
||||||
|
|
||||||
bool transparent = color0Transparent && index == 0;
|
bool transparent = color0Transparent && index == 0;
|
||||||
u32 res;
|
u32 res;
|
||||||
|
@ -256,14 +257,14 @@ void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16*
|
||||||
case outputFmt_BGRA8: res = ConvertRGB5ToBGR8(color)
|
case outputFmt_BGRA8: res = ConvertRGB5ToBGR8(color)
|
||||||
| (transparent ? 0 : 0xFF000000); break;
|
| (transparent ? 0 : 0xFF000000); break;
|
||||||
}
|
}
|
||||||
output[x * (8 / colorBits) + y * width + i] = res;
|
output[x * (16 / colorBits) + y * width + i] = res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template void ConvertNColorsTexture<outputFmt_RGB6A5, 2>(u32, u32, u32*, u8*, u16*, bool);
|
template void ConvertNColorsTexture<outputFmt_RGB6A5, 2>(u32, u32, u32*, u32, u32, bool, GPU&);
|
||||||
template void ConvertNColorsTexture<outputFmt_RGB6A5, 4>(u32, u32, u32*, u8*, u16*, bool);
|
template void ConvertNColorsTexture<outputFmt_RGB6A5, 4>(u32, u32, u32*, u32, u32, bool, GPU&);
|
||||||
template void ConvertNColorsTexture<outputFmt_RGB6A5, 8>(u32, u32, u32*, u8*, u16*, bool);
|
template void ConvertNColorsTexture<outputFmt_RGB6A5, 8>(u32, u32, u32*, u32, u32, bool, GPU&);
|
||||||
|
|
||||||
}
|
}
|
|
@ -32,13 +32,13 @@ enum
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int outputFmt>
|
template <int outputFmt>
|
||||||
void ConvertBitmapTexture(u32 width, u32 height, u32* output, u8* texData);
|
void ConvertBitmapTexture(u32 width, u32 height, u32* output, u32 addr, GPU& gpu);
|
||||||
template <int outputFmt>
|
template <int outputFmt>
|
||||||
void ConvertCompressedTexture(u32 width, u32 height, u32* output, u8* texData, u8* texAuxData, u16* palData);
|
void ConvertCompressedTexture(u32 width, u32 height, u32* output, u32 addr, u32 addrAux, u32 palAddr, GPU& gpu);
|
||||||
template <int outputFmt, int X, int Y>
|
template <int outputFmt, int X, int Y>
|
||||||
void ConvertAXIYTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData);
|
void ConvertAXIYTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, GPU& gpu);
|
||||||
template <int outputFmt, int colorBits>
|
template <int outputFmt, int colorBits>
|
||||||
void ConvertNColorsTexture(u32 width, u32 height, u32* output, u8* texData, u16* palData, bool color0Transparent);
|
void ConvertNColorsTexture(u32 width, u32 height, u32* output, u32 addr, u32 palAddr, bool color0Transparent, GPU& gpu);
|
||||||
|
|
||||||
template <typename TexLoaderT, typename TexHandleT>
|
template <typename TexLoaderT, typename TexHandleT>
|
||||||
class Texcache
|
class Texcache
|
||||||
|
@ -48,6 +48,50 @@ public:
|
||||||
: TexLoader(texloader) // probably better if this would be a move constructor???
|
: TexLoader(texloader) // probably better if this would be a move constructor???
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
u64 MaskedHash(u8* vram, u32 vramSize, u32 addr, u32 size)
|
||||||
|
{
|
||||||
|
u64 hash = 0;
|
||||||
|
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
u32 pieceSize;
|
||||||
|
if (addr + size > vramSize)
|
||||||
|
// wraps around, only do the part inside
|
||||||
|
pieceSize = vramSize - addr;
|
||||||
|
else
|
||||||
|
// fits completely inside
|
||||||
|
pieceSize = size;
|
||||||
|
|
||||||
|
hash = XXH64(&vram[addr], pieceSize, hash);
|
||||||
|
|
||||||
|
addr += pieceSize;
|
||||||
|
addr &= (vramSize - 1);
|
||||||
|
assert(size >= pieceSize);
|
||||||
|
size -= pieceSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckInvalid(u32 start, u32 size, u64 oldHash, u64* dirty, u8* vram, u32 vramSize)
|
||||||
|
{
|
||||||
|
u32 startBit = start / VRAMDirtyGranularity;
|
||||||
|
u32 bitsCount = ((start + size + VRAMDirtyGranularity - 1) / VRAMDirtyGranularity) - startBit;
|
||||||
|
|
||||||
|
u32 startEntry = startBit >> 6;
|
||||||
|
u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry;
|
||||||
|
for (u32 j = startEntry; j < startEntry + entriesCount; j++)
|
||||||
|
{
|
||||||
|
if (GetRangedBitMask(j, startBit, bitsCount) & dirty[j & ((vramSize / VRAMDirtyGranularity)-1)])
|
||||||
|
{
|
||||||
|
if (MaskedHash(vram, vramSize, start, size) != oldHash)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Update(GPU& gpu)
|
bool Update(GPU& gpu)
|
||||||
{
|
{
|
||||||
auto textureDirty = gpu.VRAMDirty_Texture.DeriveState(gpu.VRAMMap_Texture, gpu);
|
auto textureDirty = gpu.VRAMDirty_Texture.DeriveState(gpu.VRAMMap_Texture, gpu);
|
||||||
|
@ -66,41 +110,22 @@ public:
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < 2; i++)
|
for (u32 i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
u32 startBit = entry.TextureRAMStart[i] / VRAMDirtyGranularity;
|
if (CheckInvalid(entry.TextureRAMStart[i], entry.TextureRAMSize[i],
|
||||||
u32 bitsCount = ((entry.TextureRAMStart[i] + entry.TextureRAMSize[i] + VRAMDirtyGranularity - 1) / VRAMDirtyGranularity) - startBit;
|
entry.TextureHash[i],
|
||||||
|
textureDirty.Data,
|
||||||
u32 startEntry = startBit >> 6;
|
gpu.VRAMFlat_Texture, sizeof(gpu.VRAMFlat_Texture)))
|
||||||
u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry;
|
|
||||||
for (u32 j = startEntry; j < startEntry + entriesCount; j++)
|
|
||||||
{
|
|
||||||
if (GetRangedBitMask(j, startBit, bitsCount) & textureDirty.Data[j])
|
|
||||||
{
|
|
||||||
u64 newTexHash = XXH3_64bits(&gpu.VRAMFlat_Texture[entry.TextureRAMStart[i]], entry.TextureRAMSize[i]);
|
|
||||||
|
|
||||||
if (newTexHash != entry.TextureHash[i])
|
|
||||||
goto invalidate;
|
goto invalidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texPalChanged && entry.TexPalSize > 0)
|
if (texPalChanged && entry.TexPalSize > 0)
|
||||||
{
|
{
|
||||||
u32 startBit = entry.TexPalStart / VRAMDirtyGranularity;
|
if (CheckInvalid(entry.TexPalStart, entry.TexPalSize,
|
||||||
u32 bitsCount = ((entry.TexPalStart + entry.TexPalSize + VRAMDirtyGranularity - 1) / VRAMDirtyGranularity) - startBit;
|
entry.TexPalHash,
|
||||||
|
texPalDirty.Data,
|
||||||
u32 startEntry = startBit >> 6;
|
gpu.VRAMFlat_TexPal, sizeof(gpu.VRAMFlat_TexPal)))
|
||||||
u64 entriesCount = ((startBit + bitsCount + 0x3F) >> 6) - startEntry;
|
|
||||||
for (u32 j = startEntry; j < startEntry + entriesCount; j++)
|
|
||||||
{
|
|
||||||
if (GetRangedBitMask(j, startBit, bitsCount) & texPalDirty.Data[j])
|
|
||||||
{
|
|
||||||
u64 newPalHash = XXH3_64bits(&gpu.VRAMFlat_TexPal[entry.TexPalStart], entry.TexPalSize);
|
|
||||||
if (newPalHash != entry.TexPalHash)
|
|
||||||
goto invalidate;
|
goto invalidate;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
it++;
|
it++;
|
||||||
continue;
|
continue;
|
||||||
|
@ -163,17 +188,13 @@ public:
|
||||||
{
|
{
|
||||||
entry.TextureRAMSize[0] = width*height*2;
|
entry.TextureRAMSize[0] = width*height*2;
|
||||||
|
|
||||||
ConvertBitmapTexture<outputFmt_RGB6A5>(width, height, DecodingBuffer, &gpu.VRAMFlat_Texture[addr]);
|
ConvertBitmapTexture<outputFmt_RGB6A5>(width, height, DecodingBuffer, addr, gpu);
|
||||||
}
|
}
|
||||||
else if (fmt == 5)
|
else if (fmt == 5)
|
||||||
{
|
{
|
||||||
u8* texData = &gpu.VRAMFlat_Texture[addr];
|
|
||||||
u32 slot1addr = 0x20000 + ((addr & 0x1FFFC) >> 1);
|
u32 slot1addr = 0x20000 + ((addr & 0x1FFFC) >> 1);
|
||||||
if (addr >= 0x40000)
|
if (addr >= 0x40000)
|
||||||
slot1addr += 0x10000;
|
slot1addr += 0x10000;
|
||||||
u8* texAuxData = &gpu.VRAMFlat_Texture[slot1addr];
|
|
||||||
|
|
||||||
u16* palData = (u16*)(gpu.VRAMFlat_TexPal + palBase*16);
|
|
||||||
|
|
||||||
entry.TextureRAMSize[0] = width*height/16*4;
|
entry.TextureRAMSize[0] = width*height/16*4;
|
||||||
entry.TextureRAMStart[1] = slot1addr;
|
entry.TextureRAMStart[1] = slot1addr;
|
||||||
|
@ -181,7 +202,7 @@ public:
|
||||||
entry.TexPalStart = palBase*16;
|
entry.TexPalStart = palBase*16;
|
||||||
entry.TexPalSize = 0x10000;
|
entry.TexPalSize = 0x10000;
|
||||||
|
|
||||||
ConvertCompressedTexture<outputFmt_RGB6A5>(width, height, DecodingBuffer, texData, texAuxData, palData);
|
ConvertCompressedTexture<outputFmt_RGB6A5>(width, height, DecodingBuffer, addr, slot1addr, entry.TexPalStart, gpu);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -204,30 +225,29 @@ public:
|
||||||
entry.TexPalStart = palAddr;
|
entry.TexPalStart = palAddr;
|
||||||
entry.TexPalSize = numPalEntries*2;
|
entry.TexPalSize = numPalEntries*2;
|
||||||
|
|
||||||
u8* texData = &gpu.VRAMFlat_Texture[addr];
|
|
||||||
u16* palData = (u16*)(gpu.VRAMFlat_TexPal + palAddr);
|
|
||||||
|
|
||||||
//assert(entry.TexPalStart+entry.TexPalSize <= 128*1024*1024);
|
//assert(entry.TexPalStart+entry.TexPalSize <= 128*1024*1024);
|
||||||
|
|
||||||
bool color0Transparent = texParam & (1 << 29);
|
bool color0Transparent = texParam & (1 << 29);
|
||||||
|
|
||||||
switch (fmt)
|
switch (fmt)
|
||||||
{
|
{
|
||||||
case 1: ConvertAXIYTexture<outputFmt_RGB6A5, 3, 5>(width, height, DecodingBuffer, texData, palData); break;
|
case 1: ConvertAXIYTexture<outputFmt_RGB6A5, 3, 5>(width, height, DecodingBuffer, addr, palAddr, gpu); break;
|
||||||
case 6: ConvertAXIYTexture<outputFmt_RGB6A5, 5, 3>(width, height, DecodingBuffer, texData, palData); break;
|
case 6: ConvertAXIYTexture<outputFmt_RGB6A5, 5, 3>(width, height, DecodingBuffer, addr, palAddr, gpu); break;
|
||||||
case 2: ConvertNColorsTexture<outputFmt_RGB6A5, 2>(width, height, DecodingBuffer, texData, palData, color0Transparent); break;
|
case 2: ConvertNColorsTexture<outputFmt_RGB6A5, 2>(width, height, DecodingBuffer, addr, palAddr, color0Transparent, gpu); break;
|
||||||
case 3: ConvertNColorsTexture<outputFmt_RGB6A5, 4>(width, height, DecodingBuffer, texData, palData, color0Transparent); break;
|
case 3: ConvertNColorsTexture<outputFmt_RGB6A5, 4>(width, height, DecodingBuffer, addr, palAddr, color0Transparent, gpu); break;
|
||||||
case 4: ConvertNColorsTexture<outputFmt_RGB6A5, 8>(width, height, DecodingBuffer, texData, palData, color0Transparent); break;
|
case 4: ConvertNColorsTexture<outputFmt_RGB6A5, 8>(width, height, DecodingBuffer, addr, palAddr, color0Transparent, gpu); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
if (entry.TextureRAMSize[i])
|
if (entry.TextureRAMSize[i])
|
||||||
entry.TextureHash[i] = XXH3_64bits(&gpu.VRAMFlat_Texture[entry.TextureRAMStart[i]], entry.TextureRAMSize[i]);
|
entry.TextureHash[i] = MaskedHash(gpu.VRAMFlat_Texture, sizeof(gpu.VRAMFlat_Texture),
|
||||||
|
entry.TextureRAMStart[i], entry.TextureRAMSize[i]);
|
||||||
}
|
}
|
||||||
if (entry.TexPalSize)
|
if (entry.TexPalSize)
|
||||||
entry.TexPalHash = XXH3_64bits(&gpu.VRAMFlat_TexPal[entry.TexPalStart], entry.TexPalSize);
|
entry.TexPalHash = MaskedHash(gpu.VRAMFlat_TexPal, sizeof(gpu.VRAMFlat_TexPal),
|
||||||
|
entry.TexPalStart, entry.TexPalSize);
|
||||||
|
|
||||||
auto& texArrays = TexArrays[widthLog2][heightLog2];
|
auto& texArrays = TexArrays[widthLog2][heightLog2];
|
||||||
auto& freeTextures = FreeTextures[widthLog2][heightLog2];
|
auto& freeTextures = FreeTextures[widthLog2][heightLog2];
|
||||||
|
|
100
src/NDS.cpp
100
src/NDS.cpp
|
@ -74,13 +74,11 @@ const s32 kIterationCycleMargin = 8;
|
||||||
//
|
//
|
||||||
// timings for GBA slot and wifi are set up at runtime
|
// timings for GBA slot and wifi are set up at runtime
|
||||||
|
|
||||||
NDS* NDS::Current = nullptr;
|
thread_local NDS* NDS::Current = nullptr;
|
||||||
|
|
||||||
NDS::NDS() noexcept :
|
NDS::NDS() noexcept :
|
||||||
NDS(
|
NDS(
|
||||||
NDSArgs {
|
NDSArgs {
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
std::make_unique<ARM9BIOSImage>(bios_arm9_bin),
|
std::make_unique<ARM9BIOSImage>(bios_arm9_bin),
|
||||||
std::make_unique<ARM7BIOSImage>(bios_arm7_bin),
|
std::make_unique<ARM7BIOSImage>(bios_arm7_bin),
|
||||||
Firmware(0),
|
Firmware(0),
|
||||||
|
@ -102,8 +100,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
|
||||||
SPI(*this, std::move(args.Firmware)),
|
SPI(*this, std::move(args.Firmware)),
|
||||||
RTC(*this),
|
RTC(*this),
|
||||||
Wifi(*this),
|
Wifi(*this),
|
||||||
NDSCartSlot(*this, std::move(args.NDSROM)),
|
NDSCartSlot(*this, nullptr),
|
||||||
GBACartSlot(*this, type == 1 ? nullptr : std::move(args.GBAROM)),
|
GBACartSlot(*this, nullptr),
|
||||||
AREngine(*this),
|
AREngine(*this),
|
||||||
ARM9(*this, args.GDB, args.JIT.has_value()),
|
ARM9(*this, args.GDB, args.JIT.has_value()),
|
||||||
ARM7(*this, args.GDB, args.JIT.has_value()),
|
ARM7(*this, args.GDB, args.JIT.has_value()),
|
||||||
|
@ -124,8 +122,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
|
||||||
DMA(1, 3, *this),
|
DMA(1, 3, *this),
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
RegisterEventFunc(Event_Div, 0, MemberEventFunc(NDS, DivDone));
|
RegisterEventFuncs(Event_Div, this, {MakeEventThunk(NDS, DivDone)});
|
||||||
RegisterEventFunc(Event_Sqrt, 0, MemberEventFunc(NDS, SqrtDone));
|
RegisterEventFuncs(Event_Sqrt, this, {MakeEventThunk(NDS, SqrtDone)});
|
||||||
|
|
||||||
MainRAM = JIT.Memory.GetMainRAM();
|
MainRAM = JIT.Memory.GetMainRAM();
|
||||||
SharedWRAM = JIT.Memory.GetSharedWRAM();
|
SharedWRAM = JIT.Memory.GetSharedWRAM();
|
||||||
|
@ -134,8 +132,8 @@ NDS::NDS(NDSArgs&& args, int type, void* userdata) noexcept :
|
||||||
|
|
||||||
NDS::~NDS() noexcept
|
NDS::~NDS() noexcept
|
||||||
{
|
{
|
||||||
UnregisterEventFunc(Event_Div, 0);
|
UnregisterEventFuncs(Event_Div);
|
||||||
UnregisterEventFunc(Event_Sqrt, 0);
|
UnregisterEventFuncs(Event_Sqrt);
|
||||||
// The destructor for each component is automatically called by the compiler
|
// The destructor for each component is automatically called by the compiler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +227,15 @@ void NDS::SetJITArgs(std::optional<JITArgs> args) noexcept
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
void NDS::SetGdbArgs(std::optional<GDBArgs> args) noexcept
|
||||||
|
{
|
||||||
|
ARM9.SetGdbArgs(args);
|
||||||
|
ARM7.SetGdbArgs(args);
|
||||||
|
EnableGDBStub = args.has_value();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void NDS::InitTimings()
|
void NDS::InitTimings()
|
||||||
{
|
{
|
||||||
// TODO, eventually:
|
// TODO, eventually:
|
||||||
|
@ -539,6 +546,26 @@ void NDS::Reset()
|
||||||
void NDS::Start()
|
void NDS::Start()
|
||||||
{
|
{
|
||||||
Running = true;
|
Running = true;
|
||||||
|
|
||||||
|
if (ConsoleType != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto* ndscart = NDSCartSlot.GetCart();
|
||||||
|
if (!ndscart)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (auto* cart = GBACartSlot.GetCart(); cart && cart->Type() == GBACart::CartType::GameSolarSensor)
|
||||||
|
{ // If we have a solar sensor cart inserted...
|
||||||
|
auto& solarcart = *static_cast<GBACart::CartGameSolarSensor*>(cart);
|
||||||
|
GBACart::GBAHeader& header = solarcart.GetHeader();
|
||||||
|
if (strncmp(header.Title, GBACart::BOKTAI_STUB_TITLE, sizeof(header.Title)) == 0) {
|
||||||
|
// If this is a stub Boktai cart (so we can use the sensor without a full ROM)...
|
||||||
|
|
||||||
|
// ...then copy the Nintendo logo data from the NDS ROM into the stub GBA ROM.
|
||||||
|
// Otherwise, the GBA cart won't be recognized.
|
||||||
|
memcpy(header.NintendoLogo, ndscart->GetHeader().NintendoLogo, sizeof(header.NintendoLogo));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* StopReasonName(Platform::StopReason reason)
|
static const char* StopReasonName(Platform::StopReason reason)
|
||||||
|
@ -752,11 +779,6 @@ void NDS::SetGBASave(const u8* savedata, u32 savelen)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDS::LoadGBAAddon(int type)
|
|
||||||
{
|
|
||||||
GBACartSlot.LoadAddon(UserData, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NDS::LoadBIOS()
|
void NDS::LoadBIOS()
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
|
@ -816,7 +838,7 @@ void NDS::RunSystem(u64 timestamp)
|
||||||
SchedListMask &= ~(1<<i);
|
SchedListMask &= ~(1<<i);
|
||||||
|
|
||||||
EventFunc func = evt.Funcs[evt.FuncID];
|
EventFunc func = evt.Funcs[evt.FuncID];
|
||||||
func(evt.Param);
|
func(evt.That, evt.Param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,7 +895,7 @@ void NDS::RunSystemSleep(u64 timestamp)
|
||||||
param = evt.Param;
|
param = evt.Param;
|
||||||
|
|
||||||
EventFunc func = evt.Funcs[evt.FuncID];
|
EventFunc func = evt.Funcs[evt.FuncID];
|
||||||
func(param);
|
func(evt.That, param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -892,6 +914,8 @@ void NDS::RunSystemSleep(u64 timestamp)
|
||||||
template <CPUExecuteMode cpuMode>
|
template <CPUExecuteMode cpuMode>
|
||||||
u32 NDS::RunFrame()
|
u32 NDS::RunFrame()
|
||||||
{
|
{
|
||||||
|
Current = this;
|
||||||
|
|
||||||
FrameStartTimestamp = SysTimestamp;
|
FrameStartTimestamp = SysTimestamp;
|
||||||
|
|
||||||
GPU.TotalScanlines = 0;
|
GPU.TotalScanlines = 0;
|
||||||
|
@ -1069,18 +1093,26 @@ void NDS::Reschedule(u64 target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDS::RegisterEventFunc(u32 id, u32 funcid, EventFunc func)
|
void NDS::RegisterEventFuncs(u32 id, void* that, const std::initializer_list<EventFunc>& funcs)
|
||||||
{
|
{
|
||||||
SchedEvent& evt = SchedList[id];
|
SchedEvent& evt = SchedList[id];
|
||||||
|
|
||||||
evt.Funcs[funcid] = func;
|
evt.That = that;
|
||||||
|
assert(funcs.size() <= MaxEventFunctions);
|
||||||
|
int i = 0;
|
||||||
|
for (EventFunc func : funcs)
|
||||||
|
{
|
||||||
|
evt.Funcs[i++] = func;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDS::UnregisterEventFunc(u32 id, u32 funcid)
|
void NDS::UnregisterEventFuncs(u32 id)
|
||||||
{
|
{
|
||||||
SchedEvent& evt = SchedList[id];
|
SchedEvent& evt = SchedList[id];
|
||||||
|
|
||||||
evt.Funcs.erase(funcid);
|
evt.That = nullptr;
|
||||||
|
for (int i = 0; i < MaxEventFunctions; i++)
|
||||||
|
evt.Funcs[i] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param)
|
void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param)
|
||||||
|
@ -2734,6 +2766,9 @@ u8 NDS::ARM9IORead8(u32 addr)
|
||||||
case 0x04000132: return KeyCnt[0] & 0xFF;
|
case 0x04000132: return KeyCnt[0] & 0xFF;
|
||||||
case 0x04000133: return KeyCnt[0] >> 8;
|
case 0x04000133: return KeyCnt[0] >> 8;
|
||||||
|
|
||||||
|
case 0x04000180: return IPCSync9 & 0xFF;
|
||||||
|
case 0x04000181: return IPCSync9 >> 8;
|
||||||
|
|
||||||
case 0x040001A0:
|
case 0x040001A0:
|
||||||
if (!(ExMemCnt[0] & (1<<11)))
|
if (!(ExMemCnt[0] & (1<<11)))
|
||||||
return NDSCartSlot.GetSPICnt() & 0xFF;
|
return NDSCartSlot.GetSPICnt() & 0xFF;
|
||||||
|
@ -3173,6 +3208,17 @@ void NDS::ARM9IOWrite8(u32 addr, u8 val)
|
||||||
KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8);
|
KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 0x04000181:
|
||||||
|
IPCSync7 &= 0xFFF0;
|
||||||
|
IPCSync7 |= (val & 0x0F);
|
||||||
|
IPCSync9 &= 0xB0FF;
|
||||||
|
IPCSync9 |= ((val & 0x4F) << 8);
|
||||||
|
if ((val & 0x20) && (IPCSync7 & 0x4000))
|
||||||
|
{
|
||||||
|
SetIRQ(1, IRQ_IPCSync);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
case 0x04000188:
|
case 0x04000188:
|
||||||
NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
|
NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
|
||||||
return;
|
return;
|
||||||
|
@ -3664,6 +3710,9 @@ u8 NDS::ARM7IORead8(u32 addr)
|
||||||
|
|
||||||
case 0x04000138: return RTC.Read() & 0xFF;
|
case 0x04000138: return RTC.Read() & 0xFF;
|
||||||
|
|
||||||
|
case 0x04000180: return IPCSync7 & 0xFF;
|
||||||
|
case 0x04000181: return IPCSync7 >> 8;
|
||||||
|
|
||||||
case 0x040001A0:
|
case 0x040001A0:
|
||||||
if (ExMemCnt[0] & (1<<11))
|
if (ExMemCnt[0] & (1<<11))
|
||||||
return NDSCartSlot.GetSPICnt() & 0xFF;
|
return NDSCartSlot.GetSPICnt() & 0xFF;
|
||||||
|
@ -3972,6 +4021,17 @@ void NDS::ARM7IOWrite8(u32 addr, u8 val)
|
||||||
|
|
||||||
case 0x04000138: RTC.Write(val, true); return;
|
case 0x04000138: RTC.Write(val, true); return;
|
||||||
|
|
||||||
|
case 0x04000181:
|
||||||
|
IPCSync9 &= 0xFFF0;
|
||||||
|
IPCSync9 |= (val & 0x0F);
|
||||||
|
IPCSync7 &= 0xB0FF;
|
||||||
|
IPCSync7 |= ((val & 0x4F) << 8);
|
||||||
|
if ((val & 0x20) && (IPCSync9 & 0x4000))
|
||||||
|
{
|
||||||
|
SetIRQ(0, IRQ_IPCSync);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
case 0x04000188:
|
case 0x04000188:
|
||||||
NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
|
NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24));
|
||||||
return;
|
return;
|
||||||
|
|
31
src/NDS.h
31
src/NDS.h
|
@ -76,11 +76,15 @@ enum
|
||||||
Event_MAX
|
Event_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::function<void(u32)> EventFunc;
|
static constexpr u32 MaxEventFunctions = 3;
|
||||||
#define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1)
|
|
||||||
|
typedef void (*EventFunc)(void* that, u32 param);
|
||||||
|
#define MakeEventThunk(class, func) [](void* that, u32 param) { static_cast<class*>(that)->func(param); }
|
||||||
|
|
||||||
struct SchedEvent
|
struct SchedEvent
|
||||||
{
|
{
|
||||||
std::map<u32, EventFunc> Funcs;
|
std::array<EventFunc, MaxEventFunctions> Funcs;
|
||||||
|
void* That;
|
||||||
u64 Timestamp;
|
u64 Timestamp;
|
||||||
u32 FuncID;
|
u32 FuncID;
|
||||||
u32 Param;
|
u32 Param;
|
||||||
|
@ -211,6 +215,12 @@ enum
|
||||||
{
|
{
|
||||||
GBAAddon_RAMExpansion = 1,
|
GBAAddon_RAMExpansion = 1,
|
||||||
GBAAddon_RumblePak = 2,
|
GBAAddon_RumblePak = 2,
|
||||||
|
// Each game in the GBA Boktai trilogy uses the same solar sensor,
|
||||||
|
// but Lunar Knights (the only NDS game to use the solar sensor)
|
||||||
|
// applies slightly different effects depending on the game.
|
||||||
|
GBAAddon_SolarSensorBoktai1 = 3,
|
||||||
|
GBAAddon_SolarSensorBoktai2 = 4,
|
||||||
|
GBAAddon_SolarSensorBoktai3 = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
class SPU;
|
class SPU;
|
||||||
|
@ -384,7 +394,6 @@ public: // TODO: Encapsulate the rest of these members
|
||||||
u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); }
|
u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); }
|
||||||
void SetGBASave(const u8* savedata, u32 savelen);
|
void SetGBASave(const u8* savedata, u32 savelen);
|
||||||
|
|
||||||
void LoadGBAAddon(int type);
|
|
||||||
std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); }
|
std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); }
|
||||||
|
|
||||||
u32 RunFrame();
|
u32 RunFrame();
|
||||||
|
@ -402,8 +411,8 @@ public: // TODO: Encapsulate the rest of these members
|
||||||
virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {}
|
virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {}
|
||||||
void MicInputFrame(s16* data, int samples);
|
void MicInputFrame(s16* data, int samples);
|
||||||
|
|
||||||
void RegisterEventFunc(u32 id, u32 funcid, EventFunc func);
|
void RegisterEventFuncs(u32 id, void* that, const std::initializer_list<EventFunc>& funcs);
|
||||||
void UnregisterEventFunc(u32 id, u32 funcid);
|
void UnregisterEventFuncs(u32 id);
|
||||||
void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param);
|
void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param);
|
||||||
void CancelEvent(u32 id);
|
void CancelEvent(u32 id);
|
||||||
|
|
||||||
|
@ -477,6 +486,12 @@ public: // TODO: Encapsulate the rest of these members
|
||||||
void SetJITArgs(std::optional<JITArgs> args) noexcept {}
|
void SetJITArgs(std::optional<JITArgs> args) noexcept {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef GDBSTUB_ENABLED
|
||||||
|
void SetGdbArgs(std::optional<GDBArgs> args) noexcept;
|
||||||
|
#else
|
||||||
|
void SetGdbArgs(std::optional<GDBArgs> args) noexcept {}
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitTimings();
|
void InitTimings();
|
||||||
u32 SchedListMask;
|
u32 SchedListMask;
|
||||||
|
@ -536,8 +551,8 @@ public:
|
||||||
NDS& operator=(const NDS&) = delete;
|
NDS& operator=(const NDS&) = delete;
|
||||||
NDS(NDS&&) = delete;
|
NDS(NDS&&) = delete;
|
||||||
NDS& operator=(NDS&&) = delete;
|
NDS& operator=(NDS&&) = delete;
|
||||||
// The frontend should set and unset this manually after creating and destroying the NDS object.
|
|
||||||
[[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current;
|
static thread_local NDS* Current;
|
||||||
protected:
|
protected:
|
||||||
explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept;
|
explicit NDS(NDSArgs&& args, int type, void* userdata) noexcept;
|
||||||
virtual void DoSavestateExtra(Savestate* file) {}
|
virtual void DoSavestateExtra(Savestate* file) {}
|
||||||
|
|
|
@ -109,36 +109,52 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept
|
void NDSCartSlot::Key1_LoadKeyBuf(bool dsimode) noexcept
|
||||||
{
|
{
|
||||||
if (NDS.IsLoadedARM7BIOSKnownNative())
|
if (NDS.ConsoleType == 1)
|
||||||
{
|
{
|
||||||
u32 expected_bios_length = dsi ? 0x10000 : 0x4000;
|
// DSi mode: grab the right key depending on the requested cart mode
|
||||||
if (biosLength != expected_bios_length)
|
|
||||||
|
auto& dsi = static_cast<DSi&>(NDS);
|
||||||
|
if (dsimode)
|
||||||
{
|
{
|
||||||
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got %u bytes\n", expected_bios_length, biosLength);
|
// load from ARM7 BIOS at 0xC6D0
|
||||||
}
|
|
||||||
else if (bios == nullptr)
|
const u8* bios = dsi.ARM7iBIOS.data();
|
||||||
{
|
memcpy(Key1_KeyBuf.data(), bios + 0xC6D0, sizeof(Key1_KeyBuf));
|
||||||
Platform::Log(LogLevel::Error, "NDSCart: Expected an ARM7 BIOS of %u bytes, got nullptr\n", expected_bios_length);
|
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7i BIOS\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memcpy(Key1_KeyBuf.data(), bios + (dsi ? 0xC6D0 : 0x0030), sizeof(Key1_KeyBuf));
|
// load from ARM9 BIOS at 0x99A0
|
||||||
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n");
|
|
||||||
|
const u8* bios = dsi.ARM9iBIOS.data();
|
||||||
|
memcpy(Key1_KeyBuf.data(), bios + 0x99A0, sizeof(Key1_KeyBuf));
|
||||||
|
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM9i BIOS\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// DS mode: load from ARM7 BIOS at 0x0030
|
||||||
|
|
||||||
|
if (NDS.IsLoadedARM7BIOSKnownNative())
|
||||||
|
{
|
||||||
|
const u8* bios = NDS.GetARM7BIOS().data();
|
||||||
|
memcpy(Key1_KeyBuf.data(), bios + 0x0030, sizeof(Key1_KeyBuf));
|
||||||
|
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from ARM7 BIOS\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// well
|
// well
|
||||||
memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf));
|
memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf));
|
||||||
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
|
Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept
|
void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept
|
||||||
{
|
{
|
||||||
Key1_LoadKeyBuf(dsi, bios, biosLength);
|
Key1_LoadKeyBuf(dsi);
|
||||||
|
|
||||||
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
|
u32 keycode[3] = {idcode, idcode>>1, idcode<<1};
|
||||||
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
|
if (level >= 1) Key1_ApplyKeycode(keycode, mod);
|
||||||
|
@ -262,16 +278,15 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd,
|
||||||
|
|
||||||
case 0x3C:
|
case 0x3C:
|
||||||
CmdEncMode = 1;
|
CmdEncMode = 1;
|
||||||
cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.GetARM7BIOS().data(), ARM7BIOSSize);
|
cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2);
|
||||||
DSiMode = false;
|
DSiMode = false;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case 0x3D:
|
case 0x3D:
|
||||||
if (IsDSi)
|
if (IsDSi)
|
||||||
{
|
{
|
||||||
auto& dsi = static_cast<DSi&>(nds);
|
|
||||||
CmdEncMode = 1;
|
CmdEncMode = 1;
|
||||||
cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS));
|
cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2);
|
||||||
DSiMode = true;
|
DSiMode = true;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1426,9 +1441,12 @@ void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len)
|
||||||
|
|
||||||
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) noexcept : NDS(nds)
|
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) noexcept : NDS(nds)
|
||||||
{
|
{
|
||||||
NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData));
|
NDS.RegisterEventFuncs(Event_ROMTransfer, this,
|
||||||
NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer));
|
{
|
||||||
NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone));
|
MakeEventThunk(NDSCartSlot, ROMPrepareData),
|
||||||
|
MakeEventThunk(NDSCartSlot, ROMEndTransfer)
|
||||||
|
});
|
||||||
|
NDS.RegisterEventFuncs(Event_ROMSPITransfer, this, {MakeEventThunk(NDSCartSlot, SPITransferDone)});
|
||||||
// All fields are default-constructed because they're listed as such in the class declaration
|
// All fields are default-constructed because they're listed as such in the class declaration
|
||||||
|
|
||||||
if (rom)
|
if (rom)
|
||||||
|
@ -1437,9 +1455,8 @@ NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) n
|
||||||
|
|
||||||
NDSCartSlot::~NDSCartSlot() noexcept
|
NDSCartSlot::~NDSCartSlot() noexcept
|
||||||
{
|
{
|
||||||
NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData);
|
NDS.UnregisterEventFuncs(Event_ROMTransfer);
|
||||||
NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_End);
|
NDS.UnregisterEventFuncs(Event_ROMSPITransfer);
|
||||||
NDS.UnregisterEventFunc(Event_ROMSPITransfer, 0);
|
|
||||||
|
|
||||||
// Cart is cleaned up automatically because it's a unique_ptr
|
// Cart is cleaned up automatically because it's a unique_ptr
|
||||||
}
|
}
|
||||||
|
@ -1552,10 +1569,10 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
|
||||||
|
|
||||||
memcpy(out, &cartrom[arm9base], 0x800);
|
memcpy(out, &cartrom[arm9base], 0x800);
|
||||||
|
|
||||||
Key1_InitKeycode(false, gamecode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
|
Key1_InitKeycode(false, gamecode, 2, 2);
|
||||||
Key1_Decrypt((u32*)&out[0]);
|
Key1_Decrypt((u32*)&out[0]);
|
||||||
|
|
||||||
Key1_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
|
Key1_InitKeycode(false, gamecode, 3, 2);
|
||||||
for (u32 i = 0; i < 0x800; i += 8)
|
for (u32 i = 0; i < 0x800; i += 8)
|
||||||
Key1_Decrypt((u32*)&out[i]);
|
Key1_Decrypt((u32*)&out[i]);
|
||||||
|
|
||||||
|
@ -1714,11 +1731,11 @@ void NDSCartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
|
||||||
|
|
||||||
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
|
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
|
||||||
|
|
||||||
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
|
Key1_InitKeycode(false, romparams.GameCode, 3, 2);
|
||||||
for (u32 i = 0; i < 0x800; i += 8)
|
for (u32 i = 0; i < 0x800; i += 8)
|
||||||
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
|
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
|
||||||
|
|
||||||
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize);
|
Key1_InitKeycode(false, romparams.GameCode, 2, 2);
|
||||||
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
|
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
|
||||||
|
|
||||||
Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
|
Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
|
||||||
|
|
|
@ -445,8 +445,8 @@ private:
|
||||||
void Key1_Encrypt(u32* data) const noexcept;
|
void Key1_Encrypt(u32* data) const noexcept;
|
||||||
void Key1_Decrypt(u32* data) const noexcept;
|
void Key1_Decrypt(u32* data) const noexcept;
|
||||||
void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept;
|
void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept;
|
||||||
void Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept;
|
void Key1_LoadKeyBuf(bool dsi) noexcept;
|
||||||
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept;
|
void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) noexcept;
|
||||||
void Key2_Encrypt(const u8* data, u32 len) noexcept;
|
void Key2_Encrypt(const u8* data, u32 len) noexcept;
|
||||||
void ROMEndTransfer(u32 param) noexcept;
|
void ROMEndTransfer(u32 param) noexcept;
|
||||||
void ROMPrepareData(u32 param) noexcept;
|
void ROMPrepareData(u32 param) noexcept;
|
||||||
|
|
|
@ -1830,7 +1830,7 @@ const ROMListEntry ROMList[] =
|
||||||
{0x45564E43, 0x10000000, 0x00000005},
|
{0x45564E43, 0x10000000, 0x00000005},
|
||||||
{0x45564F59, 0x00800000, 0x00000001},
|
{0x45564F59, 0x00800000, 0x00000001},
|
||||||
{0x45565041, 0x00800000, 0x00000002},
|
{0x45565041, 0x00800000, 0x00000002},
|
||||||
{0x45565042, 0x00800000, 0x00000004},
|
{0x45565042, 0x00800000, 0x00000002},
|
||||||
{0x45565043, 0x04000000, 0x00000002},
|
{0x45565043, 0x04000000, 0x00000002},
|
||||||
{0x45565056, 0x04000000, 0x00000002},
|
{0x45565056, 0x04000000, 0x00000002},
|
||||||
{0x45565059, 0x04000000, 0x00000001},
|
{0x45565059, 0x04000000, 0x00000001},
|
||||||
|
|
|
@ -34,18 +34,18 @@ void WriteDateTime(int num, u8 val);
|
||||||
|
|
||||||
RTC::RTC(melonDS::NDS& nds) : NDS(nds)
|
RTC::RTC(melonDS::NDS& nds) : NDS(nds)
|
||||||
{
|
{
|
||||||
NDS.RegisterEventFunc(Event_RTC, 0, MemberEventFunc(RTC, ClockTimer));
|
NDS.RegisterEventFuncs(Event_RTC, this, {MakeEventThunk(RTC, ClockTimer)});
|
||||||
|
|
||||||
ResetState();
|
ResetState();
|
||||||
|
|
||||||
// indicate the power was off
|
// indicate the power was off
|
||||||
// this will be changed if a previously saved RTC state is loaded
|
// this will be changed if a previously saved RTC state is loaded
|
||||||
State.StatusReg1 = 0x80;
|
State.StatusReg1 = 0x80 | (1<<1);
|
||||||
}
|
}
|
||||||
|
|
||||||
RTC::~RTC()
|
RTC::~RTC()
|
||||||
{
|
{
|
||||||
NDS.UnregisterEventFunc(Event_RTC, 0);
|
NDS.UnregisterEventFuncs(Event_RTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTC::Reset()
|
void RTC::Reset()
|
||||||
|
|
|
@ -474,7 +474,7 @@ void TSC::Write(u8 val)
|
||||||
|
|
||||||
SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds)
|
SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds)
|
||||||
{
|
{
|
||||||
NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone));
|
NDS.RegisterEventFuncs(Event_SPITransfer, this, {MakeEventThunk(SPIHost, TransferDone)});
|
||||||
|
|
||||||
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
|
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
|
||||||
Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
|
Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
|
||||||
|
@ -495,7 +495,7 @@ SPIHost::~SPIHost()
|
||||||
Devices[i] = nullptr;
|
Devices[i] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS.UnregisterEventFunc(Event_SPITransfer, 0);
|
NDS.UnregisterEventFuncs(Event_SPITransfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPIHost::Reset()
|
void SPIHost::Reset()
|
||||||
|
|
|
@ -202,7 +202,7 @@ SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpola
|
||||||
AudioLock(Platform::Mutex_Create()),
|
AudioLock(Platform::Mutex_Create()),
|
||||||
Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto))
|
Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto))
|
||||||
{
|
{
|
||||||
NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix));
|
NDS.RegisterEventFuncs(Event_SPU, this, {MakeEventThunk(SPU, Mix)});
|
||||||
|
|
||||||
ApplyBias = true;
|
ApplyBias = true;
|
||||||
Degrade10Bit = false;
|
Degrade10Bit = false;
|
||||||
|
@ -219,7 +219,7 @@ SPU::~SPU()
|
||||||
Platform::Mutex_Free(AudioLock);
|
Platform::Mutex_Free(AudioLock);
|
||||||
AudioLock = nullptr;
|
AudioLock = nullptr;
|
||||||
|
|
||||||
NDS.UnregisterEventFunc(Event_SPU, 0);
|
NDS.UnregisterEventFuncs(Event_SPU);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SPU::Reset()
|
void SPU::Reset()
|
||||||
|
|
|
@ -91,7 +91,7 @@ bool MACIsBroadcast(const u8* a)
|
||||||
|
|
||||||
Wifi::Wifi(melonDS::NDS& nds) : NDS(nds)
|
Wifi::Wifi(melonDS::NDS& nds) : NDS(nds)
|
||||||
{
|
{
|
||||||
NDS.RegisterEventFunc(Event_Wifi, 0, MemberEventFunc(Wifi, USTimer));
|
NDS.RegisterEventFuncs(Event_Wifi, this, {MakeEventThunk(Wifi, USTimer)});
|
||||||
|
|
||||||
WifiAP = new class WifiAP(this, NDS.UserData);
|
WifiAP = new class WifiAP(this, NDS.UserData);
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ Wifi::~Wifi()
|
||||||
{
|
{
|
||||||
delete WifiAP; WifiAP = nullptr;
|
delete WifiAP; WifiAP = nullptr;
|
||||||
|
|
||||||
NDS.UnregisterEventFunc(Event_Wifi, 0);
|
NDS.UnregisterEventFuncs(Event_Wifi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wifi::Reset()
|
void Wifi::Reset()
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "../CRC32.h"
|
#include "../CRC32.h"
|
||||||
#include "../Platform.h"
|
#include "../Platform.h"
|
||||||
#include "hexutil.h"
|
#include "hexutil.h"
|
||||||
|
|
||||||
#include "GdbProto.h"
|
#include "GdbStub.h"
|
||||||
|
|
||||||
using namespace melonDS;
|
using namespace melonDS;
|
||||||
using Platform::Log;
|
using Platform::Log;
|
||||||
|
@ -878,6 +879,7 @@ ExecResult GdbStub::Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||||
|
|
||||||
ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len)
|
ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||||
{
|
{
|
||||||
|
printf("must reply empty\n");
|
||||||
stub->Resp(NULL, 0);
|
stub->Resp(NULL, 0);
|
||||||
return ExecResult::Ok;
|
return ExecResult::Ok;
|
||||||
}
|
}
|
||||||
|
@ -886,6 +888,7 @@ ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||||
{
|
{
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
{
|
{
|
||||||
|
printf("insufficient length");
|
||||||
stub->RespStr("E01");
|
stub->RespStr("E01");
|
||||||
return ExecResult::Ok;
|
return ExecResult::Ok;
|
||||||
}
|
}
|
||||||
|
@ -902,6 +905,7 @@ ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||||
stub->RespStr("OK");
|
stub->RespStr("OK");
|
||||||
return ExecResult::MustBreak;
|
return ExecResult::MustBreak;
|
||||||
default:
|
default:
|
||||||
|
printf("invalid continue %c %s\n", cmd[0], cmd);
|
||||||
stub->RespStr("E01");
|
stub->RespStr("E01");
|
||||||
return ExecResult::Ok;
|
return ExecResult::Ok;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@
|
||||||
#include <winsock2.h>
|
#include <winsock2.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
@ -21,8 +23,7 @@
|
||||||
#include "../Platform.h"
|
#include "../Platform.h"
|
||||||
#include "hexutil.h"
|
#include "hexutil.h"
|
||||||
|
|
||||||
#include "GdbProto.h"
|
#include "GdbStub.h"
|
||||||
|
|
||||||
|
|
||||||
using namespace melonDS;
|
using namespace melonDS;
|
||||||
using Platform::Log;
|
using Platform::Log;
|
||||||
|
@ -42,87 +43,128 @@ namespace Gdb
|
||||||
* vKill;pid
|
* vKill;pid
|
||||||
* qRcmd? qSupported?
|
* qRcmd? qSupported?
|
||||||
*/
|
*/
|
||||||
u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY];
|
|
||||||
ssize_t Cmdlen;
|
|
||||||
|
|
||||||
namespace Proto
|
|
||||||
|
Gdb::ReadResult GdbStub::TryParsePacket(size_t start, size_t& packetStart, size_t& packetSize, size_t& packetContentSize)
|
||||||
{
|
{
|
||||||
|
RecvBuffer[RecvBufferFilled] = '\0';
|
||||||
u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY];
|
//Log(LogLevel::Debug, "[GDB] Trying to parse packet %s %d %d\n", &RecvBuffer[0], start, RecvBufferFilled);
|
||||||
u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5];
|
size_t i = start;
|
||||||
|
while (i < RecvBufferFilled)
|
||||||
ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/])
|
|
||||||
{
|
|
||||||
static ssize_t dataoff = 0;
|
|
||||||
|
|
||||||
ssize_t recv_total = dataoff;
|
|
||||||
ssize_t cksumoff = -1;
|
|
||||||
u8 sum = 0;
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
|
|
||||||
//printf("--- dataoff=%zd\n", dataoff);
|
|
||||||
if (dataoff != 0) {
|
|
||||||
printf("--- got preexisting: %s\n", PacketBuf);
|
|
||||||
|
|
||||||
ssize_t datastart = 0;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
if (PacketBuf[datastart] == '\x04') return ReadResult::Eof;
|
char curChar = RecvBuffer[i++];
|
||||||
else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-')
|
if (curChar == '\x04') return ReadResult::Eof;
|
||||||
|
else if (curChar == '\x03')
|
||||||
{
|
{
|
||||||
/*if (PacketBuf[datastart] == '+') SendAck(connfd);
|
packetStart = i - 1;
|
||||||
else SendNak(connfd);*/
|
packetSize = packetContentSize = 1;
|
||||||
++datastart;
|
return ReadResult::Break;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else if (PacketBuf[datastart] == '$')
|
else if (curChar == '+' || curChar == '-') continue;
|
||||||
|
else if (curChar == '$')
|
||||||
{
|
{
|
||||||
++datastart;
|
packetStart = i;
|
||||||
break;
|
uint8_t checksumGot = 0;
|
||||||
|
while (i < RecvBufferFilled)
|
||||||
|
{
|
||||||
|
curChar = RecvBuffer[i];
|
||||||
|
if (curChar == '#' && i + 2 < RecvBufferFilled)
|
||||||
|
{
|
||||||
|
u8 checksumShould = (hex2nyb(RecvBuffer[i+1]) << 4)
|
||||||
|
| hex2nyb(RecvBuffer[i+2]);
|
||||||
|
|
||||||
|
Log(LogLevel::Debug, "[GDB] found pkt, checksumGot: %02x vs %02x\n", checksumShould, checksumGot);
|
||||||
|
|
||||||
|
if (checksumShould != checksumGot)
|
||||||
|
{
|
||||||
|
return ReadResult::CksumErr;
|
||||||
|
}
|
||||||
|
|
||||||
|
packetContentSize = i - packetStart;
|
||||||
|
packetSize = packetContentSize + 3;
|
||||||
|
return ReadResult::CmdRecvd;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
__builtin_trap();
|
checksumGot += curChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log(LogLevel::Error, "[GDB] Received unknown character %c (%d)\n", curChar, curChar);
|
||||||
return ReadResult::Wut;
|
return ReadResult::Wut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("--- datastart=%zd\n", datastart);
|
|
||||||
|
|
||||||
for (ssize_t i = datastart; i < dataoff; ++i)
|
return ReadResult::NoPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gdb::ReadResult GdbStub::ParseAndSetupPacket()
|
||||||
|
{
|
||||||
|
// This complicated logic seems to be unfortunately necessary
|
||||||
|
// to handle the case of packet resends when we answered too slowly.
|
||||||
|
// GDB only expects a single response (as it assumes the previous packet was dropped)
|
||||||
|
size_t i = 0;
|
||||||
|
size_t prevPacketStart = SIZE_MAX, prevPacketSize, prevPacketContentSize;
|
||||||
|
size_t packetStart, packetSize, packetContentSize;
|
||||||
|
ReadResult result, prevResult;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
if (PacketBuf[i] == '#')
|
result = TryParsePacket(i, packetStart, packetSize, packetContentSize);
|
||||||
|
if (result == ReadResult::NoPacket)
|
||||||
|
break;
|
||||||
|
if (result != ReadResult::CmdRecvd && result != ReadResult::Break)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
// looks like there is a different packet coming up
|
||||||
|
// so we quit here
|
||||||
|
if (prevPacketStart != SIZE_MAX &&
|
||||||
|
(packetContentSize != prevPacketContentSize ||
|
||||||
|
memcmp(&RecvBuffer[packetStart], &RecvBuffer[prevPacketStart], prevPacketContentSize) != 0))
|
||||||
{
|
{
|
||||||
cksumoff = i + 1;
|
Log(LogLevel::Debug, "[GDB] found differing packet further back %zu %zu\n", packetContentSize, prevPacketContentSize);
|
||||||
printf("--- cksumoff=%zd\n", cksumoff);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sum += PacketBuf[i];
|
i = packetStart + packetSize;
|
||||||
|
prevPacketStart = packetStart;
|
||||||
|
prevPacketSize = packetSize;
|
||||||
|
prevPacketContentSize = packetContentSize;
|
||||||
|
prevResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cksumoff >= 0)
|
if (prevPacketStart != SIZE_MAX)
|
||||||
{
|
{
|
||||||
recv_total = dataoff - datastart + 1;
|
memcpy(&Cmdbuf[0], &RecvBuffer[prevPacketStart], prevPacketContentSize);
|
||||||
dataoff = cksumoff + 2 - datastart + 1;
|
Cmdbuf[prevPacketContentSize] = '\0';
|
||||||
cksumoff -= datastart - 1;
|
Cmdlen = static_cast<ssize_t>(prevPacketContentSize);
|
||||||
|
|
||||||
memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total);
|
RecvBufferFilled -= prevPacketStart + prevPacketSize;
|
||||||
PacketBuf[0] = '$';
|
if (RecvBufferFilled > 0)
|
||||||
PacketBuf[recv_total] = 0;
|
memmove(&RecvBuffer[0], &RecvBuffer[prevPacketStart + prevPacketSize], RecvBufferFilled);
|
||||||
|
|
||||||
printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n",
|
assert(prevResult == ReadResult::CmdRecvd || prevResult == ReadResult::Break);
|
||||||
cksumoff, recv_total, datastart, dataoff, PacketBuf);
|
return prevResult;
|
||||||
//break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (cksumoff < 0)
|
assert(result == ReadResult::NoPacket);
|
||||||
|
return ReadResult::NoPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadResult GdbStub::MsgRecv()
|
||||||
|
{
|
||||||
{
|
{
|
||||||
u8* pkt = &PacketBuf[dataoff];
|
ReadResult result = ParseAndSetupPacket();
|
||||||
ssize_t n, blehoff = 0;
|
if (result != ReadResult::NoPacket)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
memset(pkt, 0, sizeof(PacketBuf) - dataoff);
|
bool first = true;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
int flag = 0;
|
int flag = 0;
|
||||||
#if MOCKTEST
|
#if MOCKTEST
|
||||||
static bool FIRST = false;
|
static bool FIRST = false;
|
||||||
|
@ -142,113 +184,35 @@ ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/])
|
||||||
#else
|
#else
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if (first) flag |= MSG_DONTWAIT;
|
if (first) flag |= MSG_DONTWAIT;
|
||||||
n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag);
|
ssize_t receivedNum = recv(ConnFd, &RecvBuffer[RecvBufferFilled], sizeof(RecvBuffer) - RecvBufferFilled, flag);
|
||||||
|
Log(LogLevel::Debug, "[GDB] receiving from stream %d\n", receivedNum);
|
||||||
#else
|
#else
|
||||||
// fuck windows
|
// fuck windows
|
||||||
n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag);
|
ssize_t receivedNum = recv(ConnFd, (char*)&RecvBuffer[RecvBufferFilled], sizeof(RecvBuffer) - RecvBufferFilled, flag);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (n <= 0)
|
if (receivedNum <= 0)
|
||||||
{
|
{
|
||||||
if (first) return ReadResult::NoPacket;
|
if (first) return ReadResult::NoPacket;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno));
|
Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", receivedNum, errno, strerror(errno));
|
||||||
return ReadResult::Eof;
|
return ReadResult::Eof;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RecvBufferFilled += static_cast<u32>(receivedNum);
|
||||||
|
|
||||||
Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]);
|
ReadResult result = ParseAndSetupPacket();
|
||||||
first = false;
|
if (result != ReadResult::NoPacket)
|
||||||
|
return result;
|
||||||
do
|
|
||||||
{
|
|
||||||
if (dataoff == 0)
|
|
||||||
{
|
|
||||||
if (pkt[blehoff] == '\x04') return ReadResult::Eof;
|
|
||||||
else if (pkt[blehoff] == '\x03') return ReadResult::Break;
|
|
||||||
else if (pkt[blehoff] != '$')
|
|
||||||
{
|
|
||||||
++blehoff;
|
|
||||||
--n;
|
|
||||||
}
|
}
|
||||||
else break;
|
|
||||||
|
|
||||||
if (n == 0) goto next_outer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (true);
|
|
||||||
|
|
||||||
if (blehoff > 0)
|
|
||||||
{
|
|
||||||
memmove(pkt, &pkt[blehoff], n - blehoff + 1);
|
|
||||||
n -= blehoff - 1; // ???
|
|
||||||
}
|
|
||||||
|
|
||||||
recv_total += n;
|
|
||||||
|
|
||||||
Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total);
|
|
||||||
|
|
||||||
for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
u8 v = pkt[i];
|
|
||||||
if (v == '#')
|
|
||||||
{
|
|
||||||
cksumoff = dataoff + i + 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum += pkt[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cksumoff < 0)
|
|
||||||
{
|
|
||||||
// oops, need more data
|
|
||||||
dataoff += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
next_outer:;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4)
|
|
||||||
| hex2nyb(PacketBuf[cksumoff+1]);
|
|
||||||
|
|
||||||
Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum);
|
|
||||||
|
|
||||||
if (ck != sum)
|
|
||||||
{
|
|
||||||
//__builtin_trap();
|
|
||||||
return ReadResult::CksumErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cksumoff + 2 > recv_total)
|
|
||||||
{
|
|
||||||
Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total);
|
|
||||||
//__builtin_trap();
|
|
||||||
return ReadResult::Wut;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Cmdlen = cksumoff - 2;
|
|
||||||
memcpy(Cmdbuf, &PacketBuf[1], Cmdlen);
|
|
||||||
Cmdbuf[Cmdlen] = 0;
|
|
||||||
|
|
||||||
if (cksumoff + 2 < recv_total) {
|
|
||||||
// huh, we have the start of the next packet
|
|
||||||
dataoff = recv_total - (cksumoff + 2);
|
|
||||||
memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff);
|
|
||||||
PacketBuf[dataoff] = 0;
|
|
||||||
Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf);
|
|
||||||
}
|
|
||||||
else dataoff = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ReadResult::CmdRecvd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int SendAck(int connfd)
|
int GdbStub::SendAck()
|
||||||
{
|
{
|
||||||
|
if (NoAck) return 1;
|
||||||
|
|
||||||
Log(LogLevel::Debug, "[GDB] send ack\n");
|
Log(LogLevel::Debug, "[GDB] send ack\n");
|
||||||
u8 v = '+';
|
u8 v = '+';
|
||||||
#if MOCKTEST
|
#if MOCKTEST
|
||||||
|
@ -257,14 +221,16 @@ int SendAck(int connfd)
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// fuck windows
|
// fuck windows
|
||||||
return send(connfd, (const char*)&v, 1, 0);
|
return send(ConnFd, (const char*)&v, 1, 0);
|
||||||
#else
|
#else
|
||||||
return send(connfd, &v, 1, 0);
|
return send(ConnFd, &v, 1, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int SendNak(int connfd)
|
int GdbStub::SendNak()
|
||||||
{
|
{
|
||||||
|
if (NoAck) return 1;
|
||||||
|
|
||||||
Log(LogLevel::Debug, "[GDB] send nak\n");
|
Log(LogLevel::Debug, "[GDB] send nak\n");
|
||||||
u8 v = '-';
|
u8 v = '-';
|
||||||
#if MOCKTEST
|
#if MOCKTEST
|
||||||
|
@ -273,13 +239,13 @@ int SendNak(int connfd)
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// fuck windows
|
// fuck windows
|
||||||
return send(connfd, (const char*)&v, 1, 0);
|
return send(ConnFd, (const char*)&v, 1, 0);
|
||||||
#else
|
#else
|
||||||
return send(connfd, &v, 1, 0);
|
return send(ConnFd, &v, 1, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
int GdbStub::WaitAckBlocking(u8* ackp, int to_ms)
|
||||||
{
|
{
|
||||||
#if MOCKTEST
|
#if MOCKTEST
|
||||||
*ackp = '+';
|
*ackp = '+';
|
||||||
|
@ -289,18 +255,18 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
fd_set infd, outfd, errfd;
|
fd_set infd, outfd, errfd;
|
||||||
FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd);
|
FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd);
|
||||||
FD_SET(connfd, &infd);
|
FD_SET(ConnFd, &infd);
|
||||||
|
|
||||||
struct timeval to;
|
struct timeval to;
|
||||||
to.tv_sec = to_ms / 1000;
|
to.tv_sec = to_ms / 1000;
|
||||||
to.tv_usec = (to_ms % 1000) * 1000;
|
to.tv_usec = (to_ms % 1000) * 1000;
|
||||||
|
|
||||||
int r = select(connfd+1, &infd, &outfd, &errfd, &to);
|
int r = select(ConnFd+1, &infd, &outfd, &errfd, &to);
|
||||||
|
|
||||||
if (FD_ISSET(connfd, &errfd)) return -1;
|
if (FD_ISSET(ConnFd, &errfd)) return -1;
|
||||||
else if (FD_ISSET(connfd, &infd))
|
else if (FD_ISSET(ConnFd, &infd))
|
||||||
{
|
{
|
||||||
r = recv(connfd, (char*)ackp, 1, 0);
|
r = recv(ConnFd, (char*)ackp, 1, 0);
|
||||||
if (r < 0) return r;
|
if (r < 0) return r;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -309,7 +275,7 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||||
#else
|
#else
|
||||||
struct pollfd pfd;
|
struct pollfd pfd;
|
||||||
|
|
||||||
pfd.fd = connfd;
|
pfd.fd = ConnFd;
|
||||||
pfd.events = POLLIN;
|
pfd.events = POLLIN;
|
||||||
pfd.revents = 0;
|
pfd.revents = 0;
|
||||||
|
|
||||||
|
@ -319,14 +285,14 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||||
|
|
||||||
if (pfd.revents & (POLLHUP|POLLERR)) return -69;
|
if (pfd.revents & (POLLHUP|POLLERR)) return -69;
|
||||||
|
|
||||||
r = recv(connfd, ackp, 1, 0);
|
r = recv(ConnFd, ackp, 1, 0);
|
||||||
if (r < 0) return r;
|
if (r < 0) return r;
|
||||||
|
|
||||||
return (r == 1) ? 0 : -1;
|
return (r == 1) ? 0 : -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack)
|
int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack)
|
||||||
{
|
{
|
||||||
u8 cksum = 0;
|
u8 cksum = 0;
|
||||||
int tries = 0;
|
int tries = 0;
|
||||||
|
@ -359,22 +325,22 @@ int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2,
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
u8 ack;
|
u8 ack;
|
||||||
|
|
||||||
Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf);
|
Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", &RespBuf[0]);
|
||||||
#if MOCKTEST
|
#if MOCKTEST
|
||||||
r = totallen+4;
|
r = totallen+4;
|
||||||
#else
|
#else
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
r = send(connfd, (const char*)RespBuf, totallen+4, 0);
|
r = send(ConnFd, (const char*)&RespBuf[0], totallen+4, 0);
|
||||||
#else
|
#else
|
||||||
r = send(connfd, RespBuf, totallen+4, 0);
|
r = send(ConnFd, &RespBuf[0], totallen+4, 0);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
if (r < 0) return r;
|
if (r < 0) return r;
|
||||||
|
|
||||||
if (noack) break;
|
if (noack) break;
|
||||||
|
|
||||||
r = WaitAckBlocking(connfd, &ack, 2000);
|
r = WaitAckBlocking(&ack, 2000);
|
||||||
//Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack);
|
Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack);
|
||||||
if (r == 0 && ack == '+') break;
|
if (r == 0 && ack == '+') break;
|
||||||
|
|
||||||
++tries;
|
++tries;
|
||||||
|
@ -386,5 +352,4 @@ int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
|
|
||||||
#ifndef GDBPROTO_H_
|
|
||||||
#define GDBPROTO_H_
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include "GdbStub.h" /* class GdbStub */
|
|
||||||
|
|
||||||
|
|
||||||
#define MOCKTEST 0
|
|
||||||
|
|
||||||
|
|
||||||
namespace Gdb {
|
|
||||||
|
|
||||||
using namespace melonDS;
|
|
||||||
constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128;
|
|
||||||
|
|
||||||
extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY];
|
|
||||||
extern ssize_t Cmdlen;
|
|
||||||
|
|
||||||
namespace Proto {
|
|
||||||
|
|
||||||
extern u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY];
|
|
||||||
extern u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5];
|
|
||||||
|
|
||||||
Gdb::ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]);
|
|
||||||
|
|
||||||
int SendAck(int connfd);
|
|
||||||
int SendNak(int connfd);
|
|
||||||
|
|
||||||
int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack);
|
|
||||||
|
|
||||||
int WaitAckBlocking(int connfd, u8* ackp, int to_ms);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
|
|
||||||
#include "../Platform.h"
|
#include "../Platform.h"
|
||||||
#include "GdbProto.h"
|
#include "GdbStub.h"
|
||||||
|
|
||||||
using namespace melonDS;
|
using namespace melonDS;
|
||||||
using Platform::Log;
|
using Platform::Log;
|
||||||
|
@ -51,16 +51,17 @@ static int SocketSetBlocking(int fd, bool block)
|
||||||
namespace Gdb
|
namespace Gdb
|
||||||
{
|
{
|
||||||
|
|
||||||
GdbStub::GdbStub(StubCallbacks* cb, int port)
|
GdbStub::GdbStub(StubCallbacks* cb)
|
||||||
: Cb(cb), Port(port)
|
: Cb(cb), Port(0)
|
||||||
, SockFd(0), ConnFd(0)
|
, SockFd(0), ConnFd(0)
|
||||||
, Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false)
|
, Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false)
|
||||||
, ServerSA((void*)new struct sockaddr_in())
|
, ServerSA((void*)new struct sockaddr_in())
|
||||||
, ClientSA((void*)new struct sockaddr_in())
|
, ClientSA((void*)new struct sockaddr_in())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
bool GdbStub::Init()
|
bool GdbStub::Init(int port)
|
||||||
{
|
{
|
||||||
|
Port = port;
|
||||||
Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n",
|
Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n",
|
||||||
Cb->GetCPU(), Port);
|
Cb->GetCPU(), Port);
|
||||||
|
|
||||||
|
@ -101,6 +102,15 @@ bool GdbStub::Init()
|
||||||
Log(LogLevel::Error, "[GDB] err: can't create a socket fd\n");
|
Log(LogLevel::Error, "[GDB] err: can't create a socket fd\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// Make sure the port can be reused immediately after melonDS stops and/or restarts
|
||||||
|
int enable = 1;
|
||||||
|
#ifdef _WIN32
|
||||||
|
setsockopt(SockFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(enable));
|
||||||
|
#else
|
||||||
|
setsockopt(SockFd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#ifndef __linux__
|
#ifndef __linux__
|
||||||
SocketSetBlocking(SockFd, false);
|
SocketSetBlocking(SockFd, false);
|
||||||
#endif
|
#endif
|
||||||
|
@ -304,7 +314,7 @@ StubState GdbStub::Poll(bool wait)
|
||||||
if (ConnFd < 0) return StubState::NoConn;
|
if (ConnFd < 0) return StubState::NoConn;
|
||||||
|
|
||||||
u8 a;
|
u8 a;
|
||||||
if (Proto::WaitAckBlocking(ConnFd, &a, 1000) < 0)
|
if (WaitAckBlocking(&a, 1000) < 0)
|
||||||
{
|
{
|
||||||
Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n");
|
Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n");
|
||||||
close(ConnFd);
|
close(ConnFd);
|
||||||
|
@ -380,7 +390,7 @@ StubState GdbStub::Poll(bool wait)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ReadResult res = Proto::MsgRecv(ConnFd, Cmdbuf);
|
ReadResult res = MsgRecv();
|
||||||
|
|
||||||
switch (res)
|
switch (res)
|
||||||
{
|
{
|
||||||
|
@ -422,11 +432,12 @@ ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler*
|
||||||
// check if prefix matches
|
// check if prefix matches
|
||||||
if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr)))
|
if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr)))
|
||||||
{
|
{
|
||||||
if (SendAck() < 0)
|
// ack should have already been sent by CmdExec
|
||||||
|
/*if (SendAck() < 0)
|
||||||
{
|
{
|
||||||
Log(LogLevel::Error, "[GDB] send packet ack failed!\n");
|
Log(LogLevel::Error, "[GDB] send packet ack failed!\n");
|
||||||
return ExecResult::NetErr;
|
return ExecResult::NetErr;
|
||||||
}
|
}*/
|
||||||
return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr));
|
return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -444,7 +455,7 @@ ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler*
|
||||||
|
|
||||||
ExecResult GdbStub::CmdExec(const CmdHandler* handlers)
|
ExecResult GdbStub::CmdExec(const CmdHandler* handlers)
|
||||||
{
|
{
|
||||||
Log(LogLevel::Debug, "[GDB] command in: '%s'\n", Cmdbuf);
|
Log(LogLevel::Debug, "[GDB] command in: '%s'\n", &Cmdbuf[0]);
|
||||||
|
|
||||||
for (size_t i = 0; handlers[i].Handler != NULL; ++i)
|
for (size_t i = 0; handlers[i].Handler != NULL; ++i)
|
||||||
{
|
{
|
||||||
|
@ -644,24 +655,13 @@ StubState GdbStub::CheckWatchpt(u32 addr, int kind, bool enter, bool stay)
|
||||||
return StubState::CheckNoHit;
|
return StubState::CheckNoHit;
|
||||||
}
|
}
|
||||||
|
|
||||||
int GdbStub::SendAck()
|
|
||||||
{
|
|
||||||
if (NoAck) return 1;
|
|
||||||
return Proto::SendAck(ConnFd);
|
|
||||||
}
|
|
||||||
int GdbStub::SendNak()
|
|
||||||
{
|
|
||||||
if (NoAck) return 1;
|
|
||||||
return Proto::SendNak(ConnFd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2)
|
int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2)
|
||||||
{
|
{
|
||||||
return Proto::Resp(ConnFd, data1, len1, data2, len2, NoAck);
|
return Resp(data1, len1, data2, len2, NoAck);
|
||||||
}
|
}
|
||||||
int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2)
|
int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2)
|
||||||
{
|
{
|
||||||
return Proto::Resp(ConnFd, (const u8*)data1, len1, data2, len2, NoAck);
|
return Resp((const u8*)data1, len1, data2, len2, NoAck);
|
||||||
}
|
}
|
||||||
#if defined(__GCC__) || defined(__clang__)
|
#if defined(__GCC__) || defined(__clang__)
|
||||||
__attribute__((__format__(printf, 2/*includes implicit this*/, 3)))
|
__attribute__((__format__(printf, 2/*includes implicit this*/, 3)))
|
||||||
|
@ -670,19 +670,19 @@ int GdbStub::RespFmt(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
int r = vsnprintf((char*)&Proto::RespBuf[1], sizeof(Proto::RespBuf)-5, fmt, args);
|
int r = vsnprintf((char*)&RespBuf[1], sizeof(RespBuf)-5, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
if (r < 0) return r;
|
if (r < 0) return r;
|
||||||
|
|
||||||
if ((size_t)r >= sizeof(Proto::RespBuf)-5)
|
if ((size_t)r >= sizeof(RespBuf)-5)
|
||||||
{
|
{
|
||||||
Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n",
|
Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n",
|
||||||
(ssize_t)r - (ssize_t)(sizeof(Proto::RespBuf)-5));
|
(ssize_t)r - (ssize_t)(sizeof(RespBuf)-5));
|
||||||
r = sizeof(Proto::RespBuf)-5;
|
r = sizeof(RespBuf)-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Resp(&Proto::RespBuf[1], r);
|
return Resp(&RespBuf[1], r);
|
||||||
}
|
}
|
||||||
|
|
||||||
int GdbStub::RespStr(const char* str)
|
int GdbStub::RespStr(const char* str)
|
||||||
|
|
|
@ -86,6 +86,8 @@ enum class ExecResult
|
||||||
Continue
|
Continue
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128;
|
||||||
|
|
||||||
class GdbStub;
|
class GdbStub;
|
||||||
|
|
||||||
typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len);
|
typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||||
|
@ -113,10 +115,10 @@ public:
|
||||||
int kind;
|
int kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
GdbStub(StubCallbacks* cb, int port);
|
GdbStub(StubCallbacks* cb);
|
||||||
~GdbStub();
|
~GdbStub();
|
||||||
|
|
||||||
bool Init();
|
bool Init(int port);
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
StubState Poll(bool wait = false);
|
StubState Poll(bool wait = false);
|
||||||
|
@ -141,9 +143,6 @@ public:
|
||||||
Gdb::ExecResult CmdExec(const CmdHandler* handlers);
|
Gdb::ExecResult CmdExec(const CmdHandler* handlers);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int SendAck();
|
|
||||||
int SendNak();
|
|
||||||
|
|
||||||
int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
||||||
int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
||||||
#if defined(__GCC__) || defined(__clang__)
|
#if defined(__GCC__) || defined(__clang__)
|
||||||
|
@ -158,7 +157,20 @@ private:
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
StubState HandlePacket();
|
StubState HandlePacket();
|
||||||
|
|
||||||
private:
|
Gdb::ReadResult MsgRecv();
|
||||||
|
|
||||||
|
Gdb::ReadResult TryParsePacket(size_t start, size_t& packetStart, size_t& packetSize, size_t& packetContentSize);
|
||||||
|
Gdb::ReadResult ParseAndSetupPacket();
|
||||||
|
|
||||||
|
void SetupCommand(size_t packetStart, size_t packetSize);
|
||||||
|
|
||||||
|
int SendAck();
|
||||||
|
int SendNak();
|
||||||
|
|
||||||
|
int Resp(const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack);
|
||||||
|
|
||||||
|
int WaitAckBlocking(u8* ackp, int to_ms);
|
||||||
|
|
||||||
StubCallbacks* Cb;
|
StubCallbacks* Cb;
|
||||||
|
|
||||||
//struct sockaddr_in server, client;
|
//struct sockaddr_in server, client;
|
||||||
|
@ -172,6 +184,13 @@ private:
|
||||||
bool StatFlag;
|
bool StatFlag;
|
||||||
bool NoAck;
|
bool NoAck;
|
||||||
|
|
||||||
|
std::array<u8, GDBPROTO_BUFFER_CAPACITY> RecvBuffer;
|
||||||
|
u32 RecvBufferFilled = 0;
|
||||||
|
std::array<u8, GDBPROTO_BUFFER_CAPACITY> RespBuf;
|
||||||
|
|
||||||
|
std::array<u8, GDBPROTO_BUFFER_CAPACITY> Cmdbuf;
|
||||||
|
ssize_t Cmdlen;
|
||||||
|
|
||||||
std::map<u32, BpWp> BpList;
|
std::map<u32, BpWp> BpList;
|
||||||
std::vector<BpWp> WpList;
|
std::vector<BpWp> WpList;
|
||||||
|
|
||||||
|
|
|
@ -1019,6 +1019,28 @@ public:
|
||||||
CALL(ptr);
|
CALL(ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
template <typename FunctionPointer>
|
||||||
|
void ABI_TailCall(FunctionPointer func)
|
||||||
|
{
|
||||||
|
static_assert(std::is_pointer<FunctionPointer>() &&
|
||||||
|
std::is_function<std::remove_pointer_t<FunctionPointer>>(),
|
||||||
|
"Supplied type must be a function pointer.");
|
||||||
|
|
||||||
|
const u8* ptr = reinterpret_cast<const u8*>(func);
|
||||||
|
const u64 address = reinterpret_cast<u64>(ptr);
|
||||||
|
const u64 distance = address - (reinterpret_cast<u64>(code) + 5);
|
||||||
|
|
||||||
|
if (distance >= 0x0000000080000000ULL && distance < 0xFFFFFFFF80000000ULL)
|
||||||
|
{
|
||||||
|
// Far call
|
||||||
|
MOV(64, R(RAX), Imm64(address));
|
||||||
|
JMPptr(R(RAX));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JMP(ptr, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename FunctionPointer>
|
template <typename FunctionPointer>
|
||||||
void ABI_CallFunctionC16(FunctionPointer func, u16 param1)
|
void ABI_CallFunctionC16(FunctionPointer func, u16 param1)
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
enum ScreenLayoutType
|
enum ScreenLayoutType
|
||||||
{
|
{
|
||||||
screenLayout_Natural, // top screen above bottom screen always
|
screenLayout_Natural, // top screen above bottom screen always
|
||||||
screenLayout_Horizontal,
|
|
||||||
screenLayout_Vertical,
|
screenLayout_Vertical,
|
||||||
|
screenLayout_Horizontal,
|
||||||
screenLayout_Hybrid,
|
screenLayout_Hybrid,
|
||||||
screenLayout_MAX,
|
screenLayout_MAX,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2023 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS 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 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. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AboutDialog.h"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
#include "ui_AboutDialog.h"
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
AboutDialog::AboutDialog(QWidget *parent) :
|
||||||
|
QDialog(parent), ui(new Ui::AboutDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->lblVersionInfo->setText("Version " MELONDS_VERSION);
|
||||||
|
#ifdef MELONDS_EMBED_BUILD_INFO
|
||||||
|
ui->lblBuildInfo->setText(
|
||||||
|
"Branch: " MELONDS_GIT_BRANCH "\n"
|
||||||
|
"Commit: " MELONDS_GIT_HASH "\n"
|
||||||
|
"Built by: " MELONDS_BUILD_PROVIDER
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
ui->lblBuildInfo->hide();
|
||||||
|
#endif
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
AboutDialog::~AboutDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AboutDialog::openWebsite()
|
||||||
|
{
|
||||||
|
QDesktopServices::openUrl(QUrl(MELONDS_URL));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AboutDialog::openGitHub()
|
||||||
|
{
|
||||||
|
QDesktopServices::openUrl(QUrl("https://github.com/melonDS-emu/melonDS"));;
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016-2023 melonDS team
|
||||||
|
|
||||||
|
This file is part of melonDS.
|
||||||
|
|
||||||
|
melonDS 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 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. If not, see http://www.gnu.org/licenses/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MELONDS_ABOUTDIALOG_H
|
||||||
|
#define MELONDS_ABOUTDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class AboutDialog;
|
||||||
|
}
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class AboutDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AboutDialog(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
~AboutDialog() override;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
static void openWebsite();
|
||||||
|
static void openGitHub();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::AboutDialog *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //MELONDS_ABOUTDIALOG_H
|
|
@ -0,0 +1,303 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AboutDialog</class>
|
||||||
|
<widget class="QDialog" name="AboutDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>600</width>
|
||||||
|
<height>304</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>600</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>About melonDS</string>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,0">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
|
||||||
|
</property>
|
||||||
|
<item alignment="Qt::AlignmentFlag::AlignTop">
|
||||||
|
<widget class="QLabel" name="lblLogo">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>128</width>
|
||||||
|
<height>128</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="baseSize">
|
||||||
|
<size>
|
||||||
|
<width>128</width>
|
||||||
|
<height>128</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::TextFormat::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="pixmap">
|
||||||
|
<pixmap resource="../../../res/melon.qrc">:/melon-icon</pixmap>
|
||||||
|
</property>
|
||||||
|
<property name="scaledContents">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignmentFlag::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="margin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="indent">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,0,0,0,1">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<item alignment="Qt::AlignmentFlag::AlignTop">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>32</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>melonDS</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lblVersionInfo">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>[VERSION INFO PLACEHOLDER]</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::TextFormat::MarkdownText</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>By Arisotura, the melonDS team <a href="https://github.com/melonDS-emu/melonDS/graphs/contributors">and contributors</a>.</string>
|
||||||
|
</property>
|
||||||
|
<property name="openExternalLinks">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="lblBuildInfo">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>[EMBEDDED BUILD INFO PLACEHOLDER]</string>
|
||||||
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::TextInteractionFlag::LinksAccessibleByMouse|Qt::TextInteractionFlag::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item alignment="Qt::AlignmentFlag::AlignBottom">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>Licensed under the GNU General Public License v3 or any newer version.</p><p>melonDS is intended only for use with software that you own.</p><p>The Nintendo DS and Nintendo DSi systems are the property of Nintendo.<br />melonDS and the melonDS team are not affiliated with or endorsed by Nintendo.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::TextFormat::RichText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>12</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnOpenWebsite">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Visit the &website</string>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnOpenGitHub">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>View on &GitHub</string>
|
||||||
|
</property>
|
||||||
|
<property name="flat">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnClose">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Close</string>
|
||||||
|
</property>
|
||||||
|
<property name="default">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources>
|
||||||
|
<include location="../../../res/melon.qrc"/>
|
||||||
|
</resources>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>btnClose</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>AboutDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>586</x>
|
||||||
|
<y>261</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>294</x>
|
||||||
|
<y>154</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btnOpenGitHub</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>AboutDialog</receiver>
|
||||||
|
<slot>openGitHub()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>449</x>
|
||||||
|
<y>243</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>299</x>
|
||||||
|
<y>139</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>btnOpenWebsite</sender>
|
||||||
|
<signal>clicked()</signal>
|
||||||
|
<receiver>AboutDialog</receiver>
|
||||||
|
<slot>openWebsite()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>345</x>
|
||||||
|
<y>245</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>96</x>
|
||||||
|
<y>275</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
<slots>
|
||||||
|
<slot>openWebsite()</slot>
|
||||||
|
<slot>openGitHub()</slot>
|
||||||
|
</slots>
|
||||||
|
</ui>
|
|
@ -41,7 +41,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(
|
||||||
emuInstance = ((MainWindow*)parent)->getEmuInstance();
|
emuInstance = ((MainWindow*)parent)->getEmuInstance();
|
||||||
auto& cfg = emuInstance->getGlobalConfig();
|
auto& cfg = emuInstance->getGlobalConfig();
|
||||||
auto& instcfg = emuInstance->getLocalConfig();
|
auto& instcfg = emuInstance->getLocalConfig();
|
||||||
bool emuActive = emuInstance->getEmuThread()->emuIsActive();
|
bool emuActive = emuInstance->emuIsActive();
|
||||||
|
|
||||||
oldInterp = cfg.GetInt("Audio.Interpolation");
|
oldInterp = cfg.GetInt("Audio.Interpolation");
|
||||||
oldBitDepth = cfg.GetInt("Audio.BitDepth");
|
oldBitDepth = cfg.GetInt("Audio.BitDepth");
|
||||||
|
@ -170,6 +170,12 @@ void AudioSettingsDialog::on_AudioSettingsDialog_accepted()
|
||||||
|
|
||||||
void AudioSettingsDialog::on_AudioSettingsDialog_rejected()
|
void AudioSettingsDialog::on_AudioSettingsDialog_rejected()
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto& cfg = emuInstance->getGlobalConfig();
|
auto& cfg = emuInstance->getGlobalConfig();
|
||||||
auto& instcfg = emuInstance->getLocalConfig();
|
auto& instcfg = emuInstance->getLocalConfig();
|
||||||
cfg.SetInt("Audio.Interpolation", oldInterp);
|
cfg.SetInt("Audio.Interpolation", oldInterp);
|
||||||
|
|
|
@ -96,7 +96,7 @@ CommandLineOptions* ManageArgs(QApplication& melon)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
options->errorsToDisplay += "Option -a/--archive-file given, but no archive specified!";
|
Log(LogLevel::Error, "Option -a/--archive-file given, but no archive specified!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ CommandLineOptions* ManageArgs(QApplication& melon)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
options->errorsToDisplay += "Option -A/--archive-file-gba given, but no archive specified!";
|
Log(LogLevel::Error, "Option -A/--archive-file-gba given, but no archive specified!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,8 +28,6 @@ namespace CLI {
|
||||||
|
|
||||||
struct CommandLineOptions
|
struct CommandLineOptions
|
||||||
{
|
{
|
||||||
QStringList errorsToDisplay = {};
|
|
||||||
|
|
||||||
std::optional<QString> dsRomPath;
|
std::optional<QString> dsRomPath;
|
||||||
std::optional<QString> dsRomArchivePath;
|
std::optional<QString> dsRomArchivePath;
|
||||||
std::optional<QString> gbaRomPath;
|
std::optional<QString> gbaRomPath;
|
||||||
|
|
|
@ -37,6 +37,9 @@ set(SOURCES_QT_SDL
|
||||||
QPathInput.h
|
QPathInput.h
|
||||||
SaveManager.cpp
|
SaveManager.cpp
|
||||||
CameraManager.cpp
|
CameraManager.cpp
|
||||||
|
AboutDialog.cpp
|
||||||
|
AboutDialog.h
|
||||||
|
AboutDialog.ui
|
||||||
|
|
||||||
ArchiveUtil.h
|
ArchiveUtil.h
|
||||||
ArchiveUtil.cpp
|
ArchiveUtil.cpp
|
||||||
|
@ -56,11 +59,7 @@ set(SOURCES_QT_SDL
|
||||||
NetplayDialog.cpp
|
NetplayDialog.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (APPLE)
|
option(USE_QT6 "Use Qt 6 instead of Qt 5" ON)
|
||||||
option(USE_QT6 "Build using Qt 6 instead of 5" ON)
|
|
||||||
else()
|
|
||||||
option(USE_QT6 "Build using Qt 6 instead of 5" OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (USE_QT6)
|
if (USE_QT6)
|
||||||
find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets Svg REQUIRED)
|
find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets Svg REQUIRED)
|
||||||
|
@ -92,8 +91,7 @@ add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
|
||||||
add_executable(melonDS ${SOURCES_QT_SDL})
|
add_executable(melonDS ${SOURCES_QT_SDL})
|
||||||
|
|
||||||
add_subdirectory("../../net"
|
add_subdirectory("../../net"
|
||||||
"${CMAKE_BINARY_DIR}/net"
|
${CMAKE_BINARY_DIR}/net)
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(melonDS PRIVATE net-utils)
|
target_link_libraries(melonDS PRIVATE net-utils)
|
||||||
|
|
||||||
|
@ -119,6 +117,10 @@ elseif (APPLE)
|
||||||
target_sources(melonDS PRIVATE
|
target_sources(melonDS PRIVATE
|
||||||
../duckstation/gl/context_agl.mm
|
../duckstation/gl/context_agl.mm
|
||||||
)
|
)
|
||||||
|
set_source_files_properties(
|
||||||
|
../duckstation/gl/context_agl.mm
|
||||||
|
PROPERTIES COMPILE_OPTIONS "-Wno-deprecated-declarations"
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
find_package(EGL REQUIRED)
|
find_package(EGL REQUIRED)
|
||||||
|
@ -168,10 +170,10 @@ if (BUILD_STATIC)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
target_include_directories(melonDS PUBLIC
|
||||||
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
|
"${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||||
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../net")
|
|
||||||
if (USE_QT6)
|
if (USE_QT6)
|
||||||
target_include_directories(melonDS PUBLIC ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
target_include_directories(melonDS PUBLIC ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||||
else()
|
else()
|
||||||
|
@ -188,6 +190,7 @@ if (WIN32)
|
||||||
target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE)
|
target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
string(REPLACE . , MELON_RC_VERSION ${melonDS_VERSION})
|
||||||
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc")
|
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc")
|
||||||
target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc")
|
target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc")
|
||||||
target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res")
|
target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res")
|
||||||
|
@ -207,8 +210,6 @@ if (WIN32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
target_sources(melonDS PRIVATE sem_timedwait.cpp)
|
|
||||||
|
|
||||||
# Copy icon into the bundle
|
# Copy icon into the bundle
|
||||||
set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/res/melon.icns")
|
set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/res/melon.icns")
|
||||||
target_sources(melonDS PUBLIC "${RESOURCE_FILES}")
|
target_sources(melonDS PUBLIC "${RESOURCE_FILES}")
|
||||||
|
|
|
@ -59,6 +59,8 @@ void CameraFrameDumper::present(const QVideoFrame& _frame)
|
||||||
case QVideoFrameFormat::Format_NV12:
|
case QVideoFrameFormat::Format_NV12:
|
||||||
cam->feedFrame_NV12((u8*)frame.bits(0), (u8*)frame.bits(1), frame.width(), frame.height());
|
cam->feedFrame_NV12((u8*)frame.bits(0), (u8*)frame.bits(1), frame.width(), frame.height());
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.unmap();
|
frame.unmap();
|
||||||
|
|
|
@ -163,6 +163,12 @@ void CameraSettingsDialog::on_CameraSettingsDialog_accepted()
|
||||||
|
|
||||||
void CameraSettingsDialog::on_CameraSettingsDialog_rejected()
|
void CameraSettingsDialog::on_CameraSettingsDialog_rejected()
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
camManager[i]->stop();
|
camManager[i]->stop();
|
||||||
|
|
|
@ -55,7 +55,6 @@ DefaultList<int> DefaultInts =
|
||||||
{"Screen.VSyncInterval", 1},
|
{"Screen.VSyncInterval", 1},
|
||||||
{"3D.Renderer", renderer3D_Software},
|
{"3D.Renderer", renderer3D_Software},
|
||||||
{"3D.GL.ScaleFactor", 1},
|
{"3D.GL.ScaleFactor", 1},
|
||||||
{"MaxFPS", 1000},
|
|
||||||
#ifdef JIT_ENABLED
|
#ifdef JIT_ENABLED
|
||||||
{"JIT.MaxBlockSize", 32},
|
{"JIT.MaxBlockSize", 32},
|
||||||
#endif
|
#endif
|
||||||
|
@ -81,7 +80,7 @@ RangeList IntRanges =
|
||||||
{"3D.Renderer", {0, renderer3D_Max-1}},
|
{"3D.Renderer", {0, renderer3D_Max-1}},
|
||||||
{"Screen.VSyncInterval", {1, 20}},
|
{"Screen.VSyncInterval", {1, 20}},
|
||||||
{"3D.GL.ScaleFactor", {1, 16}},
|
{"3D.GL.ScaleFactor", {1, 16}},
|
||||||
{"Audio.Interpolation", {0, 3}},
|
{"Audio.Interpolation", {0, 4}},
|
||||||
{"Instance*.Audio.Volume", {0, 256}},
|
{"Instance*.Audio.Volume", {0, 256}},
|
||||||
{"Mic.InputType", {0, micInputType_MAX-1}},
|
{"Mic.InputType", {0, micInputType_MAX-1}},
|
||||||
{"Instance*.Window*.ScreenRotation", {0, screenRot_MAX-1}},
|
{"Instance*.Window*.ScreenRotation", {0, screenRot_MAX-1}},
|
||||||
|
@ -100,7 +99,7 @@ DefaultList<bool> DefaultBools =
|
||||||
{"3D.Soft.Threaded", true},
|
{"3D.Soft.Threaded", true},
|
||||||
{"3D.GL.HiresCoordinates", true},
|
{"3D.GL.HiresCoordinates", true},
|
||||||
{"LimitFPS", true},
|
{"LimitFPS", true},
|
||||||
{"Window*.ShowOSD", true},
|
{"Instance*.Window*.ShowOSD", true},
|
||||||
{"Emu.DirectBoot", true},
|
{"Emu.DirectBoot", true},
|
||||||
{"Instance*.DS.Battery.LevelOkay", true},
|
{"Instance*.DS.Battery.LevelOkay", true},
|
||||||
{"Instance*.DSi.Battery.Charging", true},
|
{"Instance*.DSi.Battery.Charging", true},
|
||||||
|
@ -120,6 +119,13 @@ DefaultList<std::string> DefaultStrings =
|
||||||
{"Instance*.Firmware.Username", "melonDS"}
|
{"Instance*.Firmware.Username", "melonDS"}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DefaultList<double> DefaultDoubles =
|
||||||
|
{
|
||||||
|
{"TargetFPS", 60.0},
|
||||||
|
{"FastForwardFPS", 1000.0},
|
||||||
|
{"SlowmoFPS", 30.0},
|
||||||
|
};
|
||||||
|
|
||||||
LegacyEntry LegacyFile[] =
|
LegacyEntry LegacyFile[] =
|
||||||
{
|
{
|
||||||
{"Key_A", 0, "Keyboard.A", true},
|
{"Key_A", 0, "Keyboard.A", true},
|
||||||
|
@ -153,7 +159,7 @@ LegacyEntry LegacyFile[] =
|
||||||
{"HKKey_Pause", 0, "Keyboard.HK_Pause", true},
|
{"HKKey_Pause", 0, "Keyboard.HK_Pause", true},
|
||||||
{"HKKey_Reset", 0, "Keyboard.HK_Reset", true},
|
{"HKKey_Reset", 0, "Keyboard.HK_Reset", true},
|
||||||
{"HKKey_FastForward", 0, "Keyboard.HK_FastForward", true},
|
{"HKKey_FastForward", 0, "Keyboard.HK_FastForward", true},
|
||||||
{"HKKey_FastForwardToggle", 0, "Keyboard.HK_FastForwardToggle", true},
|
{"HKKey_FastForwardToggle", 0, "Keyboard.HK_FrameLimitToggle", true},
|
||||||
{"HKKey_FullscreenToggle", 0, "Keyboard.HK_FullscreenToggle", true},
|
{"HKKey_FullscreenToggle", 0, "Keyboard.HK_FullscreenToggle", true},
|
||||||
{"HKKey_SwapScreens", 0, "Keyboard.HK_SwapScreens", true},
|
{"HKKey_SwapScreens", 0, "Keyboard.HK_SwapScreens", true},
|
||||||
{"HKKey_SwapScreenEmphasis", 0, "Keyboard.HK_SwapScreenEmphasis", true},
|
{"HKKey_SwapScreenEmphasis", 0, "Keyboard.HK_SwapScreenEmphasis", true},
|
||||||
|
@ -169,7 +175,7 @@ LegacyEntry LegacyFile[] =
|
||||||
{"HKJoy_Pause", 0, "Joystick.HK_Pause", true},
|
{"HKJoy_Pause", 0, "Joystick.HK_Pause", true},
|
||||||
{"HKJoy_Reset", 0, "Joystick.HK_Reset", true},
|
{"HKJoy_Reset", 0, "Joystick.HK_Reset", true},
|
||||||
{"HKJoy_FastForward", 0, "Joystick.HK_FastForward", true},
|
{"HKJoy_FastForward", 0, "Joystick.HK_FastForward", true},
|
||||||
{"HKJoy_FastForwardToggle", 0, "Joystick.HK_FastForwardToggle", true},
|
{"HKJoy_FastForwardToggle", 0, "Joystick.HK_FrameLimitToggle", true},
|
||||||
{"HKJoy_FullscreenToggle", 0, "Joystick.HK_FullscreenToggle", true},
|
{"HKJoy_FullscreenToggle", 0, "Joystick.HK_FullscreenToggle", true},
|
||||||
{"HKJoy_SwapScreens", 0, "Joystick.HK_SwapScreens", true},
|
{"HKJoy_SwapScreens", 0, "Joystick.HK_SwapScreens", true},
|
||||||
{"HKJoy_SwapScreenEmphasis", 0, "Joystick.HK_SwapScreenEmphasis", true},
|
{"HKJoy_SwapScreenEmphasis", 0, "Joystick.HK_SwapScreenEmphasis", true},
|
||||||
|
@ -434,6 +440,18 @@ std::string Array::GetString(const int id)
|
||||||
return tval.as_string();
|
return tval.as_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Array::GetDouble(const int id)
|
||||||
|
{
|
||||||
|
while (Data.size() < id+1)
|
||||||
|
Data.push_back(0.0);
|
||||||
|
|
||||||
|
toml::value& tval = Data[id];
|
||||||
|
if (!tval.is_floating())
|
||||||
|
tval = 0.0;
|
||||||
|
|
||||||
|
return tval.as_floating();
|
||||||
|
}
|
||||||
|
|
||||||
void Array::SetInt(const int id, int val)
|
void Array::SetInt(const int id, int val)
|
||||||
{
|
{
|
||||||
while (Data.size() < id+1)
|
while (Data.size() < id+1)
|
||||||
|
@ -470,6 +488,15 @@ void Array::SetString(const int id, const std::string& val)
|
||||||
tval = val;
|
tval = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Array::SetDouble(const int id, double val)
|
||||||
|
{
|
||||||
|
while (Data.size() < id+1)
|
||||||
|
Data.push_back(0.0);
|
||||||
|
|
||||||
|
toml::value& tval = Data[id];
|
||||||
|
tval = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*Table::Table()// : Data(toml::value())
|
/*Table::Table()// : Data(toml::value())
|
||||||
{
|
{
|
||||||
|
@ -562,6 +589,15 @@ std::string Table::GetString(const std::string& path)
|
||||||
return tval.as_string();
|
return tval.as_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double Table::GetDouble(const std::string& path)
|
||||||
|
{
|
||||||
|
toml::value& tval = ResolvePath(path);
|
||||||
|
if (!tval.is_floating())
|
||||||
|
tval = FindDefault(path, 0.0, DefaultDoubles);
|
||||||
|
|
||||||
|
return tval.as_floating();
|
||||||
|
}
|
||||||
|
|
||||||
void Table::SetInt(const std::string& path, int val)
|
void Table::SetInt(const std::string& path, int val)
|
||||||
{
|
{
|
||||||
std::string rngkey = GetDefaultKey(PathPrefix+path);
|
std::string rngkey = GetDefaultKey(PathPrefix+path);
|
||||||
|
@ -593,6 +629,13 @@ void Table::SetString(const std::string& path, const std::string& val)
|
||||||
tval = val;
|
tval = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Table::SetDouble(const std::string& path, double val)
|
||||||
|
{
|
||||||
|
toml::value& tval = ResolvePath(path);
|
||||||
|
toml::floating_format_info info = {.prec=10};
|
||||||
|
tval = toml::value(val, info);
|
||||||
|
}
|
||||||
|
|
||||||
toml::value& Table::ResolvePath(const std::string& path)
|
toml::value& Table::ResolvePath(const std::string& path)
|
||||||
{
|
{
|
||||||
toml::value* ret = &Data;
|
toml::value* ret = &Data;
|
||||||
|
@ -774,7 +817,7 @@ Table GetLocalTable(int instance)
|
||||||
|
|
||||||
std::string key = "Instance" + std::to_string(instance);
|
std::string key = "Instance" + std::to_string(instance);
|
||||||
toml::value& tbl = RootTable[key];
|
toml::value& tbl = RootTable[key];
|
||||||
if (tbl.is_uninitialized())
|
if (tbl.is_empty())
|
||||||
RootTable[key] = RootTable["Instance0"];
|
RootTable[key] = RootTable["Instance0"];
|
||||||
|
|
||||||
return Table(tbl, key);
|
return Table(tbl, key);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
#include "toml/toml/value.hpp"
|
#include "toml/toml11/types.hpp"
|
||||||
|
|
||||||
namespace Config
|
namespace Config
|
||||||
{
|
{
|
||||||
|
@ -61,11 +61,13 @@ public:
|
||||||
int64_t GetInt64(const int id);
|
int64_t GetInt64(const int id);
|
||||||
bool GetBool(const int id);
|
bool GetBool(const int id);
|
||||||
std::string GetString(const int id);
|
std::string GetString(const int id);
|
||||||
|
double GetDouble(const int id);
|
||||||
|
|
||||||
void SetInt(const int id, int val);
|
void SetInt(const int id, int val);
|
||||||
void SetInt64(const int id, int64_t val);
|
void SetInt64(const int id, int64_t val);
|
||||||
void SetBool(const int id, bool val);
|
void SetBool(const int id, bool val);
|
||||||
void SetString(const int id, const std::string& val);
|
void SetString(const int id, const std::string& val);
|
||||||
|
void SetDouble(const int id, double val);
|
||||||
|
|
||||||
// convenience
|
// convenience
|
||||||
|
|
||||||
|
@ -99,11 +101,13 @@ public:
|
||||||
int64_t GetInt64(const std::string& path);
|
int64_t GetInt64(const std::string& path);
|
||||||
bool GetBool(const std::string& path);
|
bool GetBool(const std::string& path);
|
||||||
std::string GetString(const std::string& path);
|
std::string GetString(const std::string& path);
|
||||||
|
double GetDouble(const std::string& path);
|
||||||
|
|
||||||
void SetInt(const std::string& path, int val);
|
void SetInt(const std::string& path, int val);
|
||||||
void SetInt64(const std::string& path, int64_t val);
|
void SetInt64(const std::string& path, int64_t val);
|
||||||
void SetBool(const std::string& path, bool val);
|
void SetBool(const std::string& path, bool val);
|
||||||
void SetString(const std::string& path, const std::string& val);
|
void SetString(const std::string& path, const std::string& val);
|
||||||
|
void SetDouble(const std::string& path, double val);
|
||||||
|
|
||||||
// convenience
|
// convenience
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ protected:
|
||||||
void timerEvent(QTimerEvent* event) override;
|
void timerEvent(QTimerEvent* event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void done(int r);
|
void done(int r) override;
|
||||||
|
|
||||||
void on_chkChangeTime_clicked(bool checked);
|
void on_chkChangeTime_clicked(bool checked);
|
||||||
void on_chkResetTime_clicked(bool checked);
|
void on_chkResetTime_clicked(bool checked);
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
#ifdef ARCHIVE_SUPPORT_ENABLED
|
#ifdef ARCHIVE_SUPPORT_ENABLED
|
||||||
|
@ -77,24 +76,52 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
|
||||||
baseROMDir = "";
|
baseROMDir = "";
|
||||||
baseROMName = "";
|
baseROMName = "";
|
||||||
baseAssetName = "";
|
baseAssetName = "";
|
||||||
|
nextCart = nullptr;
|
||||||
|
changeCart = false;
|
||||||
|
|
||||||
gbaSave = nullptr;
|
gbaSave = nullptr;
|
||||||
gbaCartType = -1;
|
gbaCartType = -1;
|
||||||
baseGBAROMDir = "";
|
baseGBAROMDir = "";
|
||||||
baseGBAROMName = "";
|
baseGBAROMName = "";
|
||||||
baseGBAAssetName = "";
|
baseGBAAssetName = "";
|
||||||
|
nextGBACart = nullptr;
|
||||||
|
changeGBACart = false;
|
||||||
|
|
||||||
cheatFile = nullptr;
|
cheatFile = nullptr;
|
||||||
cheatsOn = localCfg.GetBool("EnableCheats");
|
cheatsOn = localCfg.GetBool("EnableCheats");
|
||||||
|
|
||||||
doLimitFPS = globalCfg.GetBool("LimitFPS");
|
doLimitFPS = globalCfg.GetBool("LimitFPS");
|
||||||
maxFPS = globalCfg.GetInt("MaxFPS");
|
|
||||||
|
double val = globalCfg.GetDouble("TargetFPS");
|
||||||
|
if (val == 0.0)
|
||||||
|
{
|
||||||
|
Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n");
|
||||||
|
targetFPS = 60.0;
|
||||||
|
}
|
||||||
|
else targetFPS = val;
|
||||||
|
|
||||||
|
val = globalCfg.GetDouble("FastForwardFPS");
|
||||||
|
if (val == 0.0)
|
||||||
|
{
|
||||||
|
Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n");
|
||||||
|
fastForwardFPS = 60.0;
|
||||||
|
}
|
||||||
|
else fastForwardFPS = val;
|
||||||
|
|
||||||
|
val = globalCfg.GetDouble("SlowmoFPS");
|
||||||
|
if (val == 0.0)
|
||||||
|
{
|
||||||
|
Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n");
|
||||||
|
slowmoFPS = 60.0;
|
||||||
|
}
|
||||||
|
else slowmoFPS = val;
|
||||||
|
|
||||||
doAudioSync = globalCfg.GetBool("AudioSync");
|
doAudioSync = globalCfg.GetBool("AudioSync");
|
||||||
|
|
||||||
mpAudioMode = globalCfg.GetInt("MP.AudioMode");
|
mpAudioMode = globalCfg.GetInt("MP.AudioMode");
|
||||||
|
|
||||||
nds = nullptr;
|
nds = nullptr;
|
||||||
//updateConsole(nullptr, nullptr);
|
//updateConsole();
|
||||||
|
|
||||||
audioInit();
|
audioInit();
|
||||||
inputInit();
|
inputInit();
|
||||||
|
@ -112,7 +139,15 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
|
||||||
createWindow();
|
createWindow();
|
||||||
|
|
||||||
emuThread->start();
|
emuThread->start();
|
||||||
emuThread->emuPause();
|
|
||||||
|
// if any extra windows were saved as enabled, open them
|
||||||
|
for (int i = 1; i < kMaxWindows; i++)
|
||||||
|
{
|
||||||
|
std::string key = "Window" + std::to_string(i) + ".Enabled";
|
||||||
|
bool enable = localCfg.GetBool(key);
|
||||||
|
if (enable)
|
||||||
|
createWindow(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EmuInstance::~EmuInstance()
|
EmuInstance::~EmuInstance()
|
||||||
|
@ -120,8 +155,6 @@ EmuInstance::~EmuInstance()
|
||||||
deleting = true;
|
deleting = true;
|
||||||
deleteAllWindows();
|
deleteAllWindows();
|
||||||
|
|
||||||
MPInterface::Get().End(instanceID);
|
|
||||||
|
|
||||||
emuThread->emuExit();
|
emuThread->emuExit();
|
||||||
emuThread->wait();
|
emuThread->wait();
|
||||||
delete emuThread;
|
delete emuThread;
|
||||||
|
@ -130,6 +163,12 @@ EmuInstance::~EmuInstance()
|
||||||
|
|
||||||
audioDeInit();
|
audioDeInit();
|
||||||
inputDeInit();
|
inputDeInit();
|
||||||
|
|
||||||
|
if (nds)
|
||||||
|
{
|
||||||
|
saveRTCData();
|
||||||
|
delete nds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,7 +181,7 @@ std::string EmuInstance::instanceFileSuffix()
|
||||||
return suffix;
|
return suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::createWindow()
|
void EmuInstance::createWindow(int id)
|
||||||
{
|
{
|
||||||
if (numWindows >= kMaxWindows)
|
if (numWindows >= kMaxWindows)
|
||||||
{
|
{
|
||||||
|
@ -150,16 +189,20 @@ void EmuInstance::createWindow()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = -1;
|
if (id == -1)
|
||||||
|
{
|
||||||
for (int i = 0; i < kMaxWindows; i++)
|
for (int i = 0; i < kMaxWindows; i++)
|
||||||
{
|
{
|
||||||
if (windowList[i]) continue;
|
if (windowList[i]) continue;
|
||||||
id = i;
|
id = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (id == -1)
|
if (id == -1)
|
||||||
return;
|
return;
|
||||||
|
if (windowList[id])
|
||||||
|
return;
|
||||||
|
|
||||||
MainWindow* win = new MainWindow(id, this, topWindow);
|
MainWindow* win = new MainWindow(id, this, topWindow);
|
||||||
if (!topWindow) topWindow = win;
|
if (!topWindow) topWindow = win;
|
||||||
|
@ -168,6 +211,16 @@ void EmuInstance::createWindow()
|
||||||
numWindows++;
|
numWindows++;
|
||||||
|
|
||||||
emuThread->attachWindow(win);
|
emuThread->attachWindow(win);
|
||||||
|
|
||||||
|
// if creating a secondary window, we may need to initialize its OpenGL context here
|
||||||
|
if (win->hasOpenGL() && (id != 0))
|
||||||
|
emuThread->initContext(id);
|
||||||
|
|
||||||
|
bool enable = (numWindows < kMaxWindows);
|
||||||
|
doOnAllWindows([=](MainWindow* win)
|
||||||
|
{
|
||||||
|
win->actNewWindow->setEnabled(enable);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::deleteWindow(int id, bool close)
|
void EmuInstance::deleteWindow(int id, bool close)
|
||||||
|
@ -177,12 +230,8 @@ void EmuInstance::deleteWindow(int id, bool close)
|
||||||
MainWindow* win = windowList[id];
|
MainWindow* win = windowList[id];
|
||||||
if (!win) return;
|
if (!win) return;
|
||||||
|
|
||||||
if (win->hasOpenGL() && win == mainWindow)
|
if (win->hasOpenGL())
|
||||||
{
|
emuThread->deinitContext(id);
|
||||||
// we intentionally don't unpause here
|
|
||||||
emuThread->emuPause();
|
|
||||||
emuThread->deinitContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
emuThread->detachWindow(win);
|
emuThread->detachWindow(win);
|
||||||
|
|
||||||
|
@ -195,11 +244,22 @@ void EmuInstance::deleteWindow(int id, bool close)
|
||||||
if (close)
|
if (close)
|
||||||
win->close();
|
win->close();
|
||||||
|
|
||||||
if ((!mainWindow) && (!deleting))
|
if (deleting) return;
|
||||||
|
|
||||||
|
if (numWindows == 0)
|
||||||
{
|
{
|
||||||
// if we closed this instance's main window, delete the instance
|
// if we closed the last window, delete the instance
|
||||||
|
// if the main window is closed, Qt will take care of closing any secondary windows
|
||||||
deleteEmuInstance(instanceID);
|
deleteEmuInstance(instanceID);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool enable = (numWindows < kMaxWindows);
|
||||||
|
doOnAllWindows([=](MainWindow* win)
|
||||||
|
{
|
||||||
|
win->actNewWindow->setEnabled(enable);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::deleteAllWindows()
|
void EmuInstance::deleteAllWindows()
|
||||||
|
@ -208,6 +268,57 @@ void EmuInstance::deleteAllWindows()
|
||||||
deleteWindow(i, true);
|
deleteWindow(i, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmuInstance::doOnAllWindows(std::function<void(MainWindow*)> func, int exclude)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kMaxWindows; i++)
|
||||||
|
{
|
||||||
|
if (i == exclude) continue;
|
||||||
|
if (!windowList[i]) continue;
|
||||||
|
|
||||||
|
func(windowList[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuInstance::saveEnabledWindows()
|
||||||
|
{
|
||||||
|
doOnAllWindows([=](MainWindow* win)
|
||||||
|
{
|
||||||
|
win->saveEnabled(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void EmuInstance::broadcastCommand(int cmd, QVariant param)
|
||||||
|
{
|
||||||
|
broadcastInstanceCommand(cmd, param, instanceID);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuInstance::handleCommand(int cmd, QVariant& param)
|
||||||
|
{
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case InstCmd_Pause:
|
||||||
|
emuThread->emuPause(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstCmd_Unpause:
|
||||||
|
emuThread->emuUnpause(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InstCmd_UpdateRecentFiles:
|
||||||
|
for (int i = 0; i < kMaxWindows; i++)
|
||||||
|
{
|
||||||
|
if (windowList[i])
|
||||||
|
windowList[i]->loadRecentFilesMenu(true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*case InstCmd_UpdateVideoSettings:
|
||||||
|
mainWindow->updateVideoSettings(param.value<bool>());
|
||||||
|
break;*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void EmuInstance::osdAddMessage(unsigned int color, const char* fmt, ...)
|
void EmuInstance::osdAddMessage(unsigned int color, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
|
@ -261,24 +372,18 @@ bool EmuInstance::usesOpenGL()
|
||||||
(globalCfg.GetInt("3D.Renderer") != renderer3D_Software);
|
(globalCfg.GetInt("3D.Renderer") != renderer3D_Software);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::initOpenGL()
|
void EmuInstance::initOpenGL(int win)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < kMaxWindows; i++)
|
if (windowList[win])
|
||||||
{
|
windowList[win]->initOpenGL();
|
||||||
if (windowList[i])
|
|
||||||
windowList[i]->initOpenGL();
|
|
||||||
}
|
|
||||||
|
|
||||||
setVSyncGL(true);
|
setVSyncGL(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::deinitOpenGL()
|
void EmuInstance::deinitOpenGL(int win)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < kMaxWindows; i++)
|
if (windowList[win])
|
||||||
{
|
windowList[win]->deinitOpenGL();
|
||||||
if (windowList[i])
|
|
||||||
windowList[i]->deinitOpenGL();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::setVSyncGL(bool vsync)
|
void EmuInstance::setVSyncGL(bool vsync)
|
||||||
|
@ -582,7 +687,7 @@ std::string EmuInstance::getSavestateName(int slot)
|
||||||
{
|
{
|
||||||
std::string ext = ".ml";
|
std::string ext = ".ml";
|
||||||
ext += (char)('0'+slot);
|
ext += (char)('0'+slot);
|
||||||
return getAssetPath(false, globalCfg.GetString("SavestatePath"), ext);
|
return getAssetPath(false, localCfg.GetString("SavestatePath"), ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuInstance::savestateExists(int slot)
|
bool EmuInstance::savestateExists(int slot)
|
||||||
|
@ -648,7 +753,7 @@ bool EmuInstance::loadState(const std::string& filename)
|
||||||
previousSaveFile = ndsSave->GetPath();
|
previousSaveFile = ndsSave->GetPath();
|
||||||
|
|
||||||
std::string savefile = filename.substr(lastSep(filename)+1);
|
std::string savefile = filename.substr(lastSep(filename)+1);
|
||||||
savefile = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav", savefile);
|
savefile = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav", savefile);
|
||||||
savefile += instanceFileSuffix();
|
savefile += instanceFileSuffix();
|
||||||
ndsSave->SetPath(savefile, true);
|
ndsSave->SetPath(savefile, true);
|
||||||
}
|
}
|
||||||
|
@ -699,7 +804,7 @@ bool EmuInstance::saveState(const std::string& filename)
|
||||||
if (globalCfg.GetBool("Savestate.RelocSRAM") && ndsSave)
|
if (globalCfg.GetBool("Savestate.RelocSRAM") && ndsSave)
|
||||||
{
|
{
|
||||||
std::string savefile = filename.substr(lastSep(filename)+1);
|
std::string savefile = filename.substr(lastSep(filename)+1);
|
||||||
savefile = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav", savefile);
|
savefile = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav", savefile);
|
||||||
savefile += instanceFileSuffix();
|
savefile += instanceFileSuffix();
|
||||||
ndsSave->SetPath(savefile, false);
|
ndsSave->SetPath(savefile, false);
|
||||||
}
|
}
|
||||||
|
@ -735,7 +840,7 @@ void EmuInstance::loadCheats()
|
||||||
{
|
{
|
||||||
unloadCheats();
|
unloadCheats();
|
||||||
|
|
||||||
std::string filename = getAssetPath(false, globalCfg.GetString("CheatFilePath"), ".mch");
|
std::string filename = getAssetPath(false, localCfg.GetString("CheatFilePath"), ".mch");
|
||||||
|
|
||||||
// TODO: check for error (malformed cheat file, ...)
|
// TODO: check for error (malformed cheat file, ...)
|
||||||
cheatFile = std::make_unique<ARCodeFile>(filename);
|
cheatFile = std::make_unique<ARCodeFile>(filename);
|
||||||
|
@ -754,7 +859,7 @@ std::unique_ptr<ARM9BIOSImage> EmuInstance::loadARM9BIOS() noexcept
|
||||||
{
|
{
|
||||||
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
|
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
|
||||||
{
|
{
|
||||||
return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique<ARM9BIOSImage>(bios_arm9_bin) : nullptr;
|
return std::make_unique<ARM9BIOSImage>(bios_arm9_bin);
|
||||||
}
|
}
|
||||||
|
|
||||||
string path = globalCfg.GetString("DS.BIOS9Path");
|
string path = globalCfg.GetString("DS.BIOS9Path");
|
||||||
|
@ -777,7 +882,7 @@ std::unique_ptr<ARM7BIOSImage> EmuInstance::loadARM7BIOS() noexcept
|
||||||
{
|
{
|
||||||
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
|
if (!globalCfg.GetBool("Emu.ExternalBIOSEnable"))
|
||||||
{
|
{
|
||||||
return globalCfg.GetInt("Emu.ConsoleType") == 0 ? std::make_unique<ARM7BIOSImage>(bios_arm7_bin) : nullptr;
|
return std::make_unique<ARM7BIOSImage>(bios_arm7_bin);
|
||||||
}
|
}
|
||||||
|
|
||||||
string path = globalCfg.GetString("DS.BIOS7Path");
|
string path = globalCfg.GetString("DS.BIOS7Path");
|
||||||
|
@ -901,12 +1006,13 @@ std::optional<Firmware> EmuInstance::loadFirmware(int type) noexcept
|
||||||
{ // If we're using built-in firmware...
|
{ // If we're using built-in firmware...
|
||||||
if (type == 1)
|
if (type == 1)
|
||||||
{
|
{
|
||||||
Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n");
|
// TODO: support generating a firmware for DSi mode
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return generateFirmware(type);
|
return generateFirmware(type);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
|
//const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
|
||||||
string firmwarepath;
|
string firmwarepath;
|
||||||
if (type == 1)
|
if (type == 1)
|
||||||
|
@ -976,13 +1082,13 @@ std::optional<DSi_NAND::NANDImage> EmuInstance::loadNAND(const std::array<u8, DS
|
||||||
auto firmcfg = localCfg.GetTable("Firmware");
|
auto firmcfg = localCfg.GetTable("Firmware");
|
||||||
|
|
||||||
// we store relevant strings as UTF-8, so we need to convert them to UTF-16
|
// we store relevant strings as UTF-8, so we need to convert them to UTF-16
|
||||||
auto converter = wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{};
|
//auto converter = wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{};
|
||||||
|
|
||||||
// setting up username
|
// setting up username
|
||||||
std::u16string username = converter.from_bytes(firmcfg.GetString("Username"));
|
auto username = firmcfg.GetQString("Username");
|
||||||
size_t usernameLength = std::min(username.length(), (size_t) 10);
|
size_t usernameLength = std::min((int) username.length(), 10);
|
||||||
memset(&settings.Nickname, 0, sizeof(settings.Nickname));
|
memset(&settings.Nickname, 0, sizeof(settings.Nickname));
|
||||||
memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t));
|
memcpy(&settings.Nickname, username.utf16(), usernameLength * sizeof(char16_t));
|
||||||
|
|
||||||
// setting language
|
// setting language
|
||||||
settings.Language = static_cast<Firmware::Language>(firmcfg.GetInt("Language"));
|
settings.Language = static_cast<Firmware::Language>(firmcfg.GetInt("Language"));
|
||||||
|
@ -995,10 +1101,10 @@ std::optional<DSi_NAND::NANDImage> EmuInstance::loadNAND(const std::array<u8, DS
|
||||||
settings.BirthdayDay = firmcfg.GetInt("BirthdayDay");
|
settings.BirthdayDay = firmcfg.GetInt("BirthdayDay");
|
||||||
|
|
||||||
// setup message
|
// setup message
|
||||||
std::u16string message = converter.from_bytes(firmcfg.GetString("Message"));
|
auto message = firmcfg.GetQString("Message");
|
||||||
size_t messageLength = std::min(message.length(), (size_t) 26);
|
size_t messageLength = std::min((int) message.length(), 26);
|
||||||
memset(&settings.Message, 0, sizeof(settings.Message));
|
memset(&settings.Message, 0, sizeof(settings.Message));
|
||||||
memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t));
|
memcpy(&settings.Message, message.utf16(), messageLength * sizeof(char16_t));
|
||||||
|
|
||||||
// TODO: make other items configurable?
|
// TODO: make other items configurable?
|
||||||
}
|
}
|
||||||
|
@ -1056,7 +1162,7 @@ std::optional<FATStorage> EmuInstance::loadSDCard(const string& key) noexcept
|
||||||
void EmuInstance::enableCheats(bool enable)
|
void EmuInstance::enableCheats(bool enable)
|
||||||
{
|
{
|
||||||
cheatsOn = enable;
|
cheatsOn = enable;
|
||||||
if (cheatFile)
|
if (cheatsOn && cheatFile)
|
||||||
nds->AREngine.Cheats = cheatFile->GetCodes();
|
nds->AREngine.Cheats = cheatFile->GetCodes();
|
||||||
else
|
else
|
||||||
nds->AREngine.Cheats.clear();
|
nds->AREngine.Cheats.clear();
|
||||||
|
@ -1081,6 +1187,30 @@ void EmuInstance::setBatteryLevels()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmuInstance::loadRTCData()
|
||||||
|
{
|
||||||
|
auto file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read);
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
RTC::StateData state;
|
||||||
|
Platform::FileRead(&state, sizeof(state), 1, file);
|
||||||
|
Platform::CloseFile(file);
|
||||||
|
nds->RTC.SetState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuInstance::saveRTCData()
|
||||||
|
{
|
||||||
|
auto file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write);
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
RTC::StateData state;
|
||||||
|
nds->RTC.GetState(state);
|
||||||
|
Platform::FileWrite(&state, sizeof(state), 1, file);
|
||||||
|
Platform::CloseFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EmuInstance::setDateTime()
|
void EmuInstance::setDateTime()
|
||||||
{
|
{
|
||||||
QDateTime hosttime = QDateTime::currentDateTime();
|
QDateTime hosttime = QDateTime::currentDateTime();
|
||||||
|
@ -1090,20 +1220,22 @@ void EmuInstance::setDateTime()
|
||||||
time.time().hour(), time.time().minute(), time.time().second());
|
time.time().hour(), time.time().minute(), time.time().second());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) noexcept
|
bool EmuInstance::updateConsole() noexcept
|
||||||
{
|
{
|
||||||
|
// update the console type
|
||||||
|
consoleType = globalCfg.GetInt("Emu.ConsoleType");
|
||||||
|
|
||||||
// Let's get the cart we want to use;
|
// Let's get the cart we want to use;
|
||||||
// if we wnat to keep the cart, we'll eject it from the existing console first.
|
// if we want to keep the cart, we'll eject it from the existing console first.
|
||||||
std::unique_ptr<NDSCart::CartCommon> nextndscart;
|
std::unique_ptr<NDSCart::CartCommon> nextndscart;
|
||||||
if (std::holds_alternative<Keep>(_ndsargs))
|
if (!changeCart)
|
||||||
{ // If we want to keep the existing cart (if any)...
|
{ // If we want to keep the existing cart (if any)...
|
||||||
nextndscart = nds ? nds->EjectCart() : nullptr;
|
nextndscart = nds ? nds->EjectCart() : nullptr;
|
||||||
_ndsargs = {};
|
|
||||||
}
|
}
|
||||||
else if (const auto ptr = std::get_if<std::unique_ptr<NDSCart::CartCommon>>(&_ndsargs))
|
else
|
||||||
{
|
{
|
||||||
nextndscart = std::move(*ptr);
|
nextndscart = std::move(nextCart);
|
||||||
_ndsargs = {};
|
changeCart = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto* cartsd = dynamic_cast<NDSCart::CartSD*>(nextndscart.get()))
|
if (auto* cartsd = dynamic_cast<NDSCart::CartSD*>(nextndscart.get()))
|
||||||
|
@ -1114,19 +1246,17 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GBACart::CartCommon> nextgbacart;
|
std::unique_ptr<GBACart::CartCommon> nextgbacart;
|
||||||
if (std::holds_alternative<Keep>(_gbaargs))
|
if (!changeGBACart)
|
||||||
{
|
{
|
||||||
nextgbacart = nds ? nds->EjectGBACart() : nullptr;
|
nextgbacart = nds ? nds->EjectGBACart() : nullptr;
|
||||||
}
|
}
|
||||||
else if (const auto ptr = std::get_if<std::unique_ptr<GBACart::CartCommon>>(&_gbaargs))
|
else
|
||||||
{
|
{
|
||||||
nextgbacart = std::move(*ptr);
|
nextgbacart = std::move(nextGBACart);
|
||||||
_gbaargs = {};
|
changeGBACart = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int consoletype = globalCfg.GetInt("Emu.ConsoleType");
|
|
||||||
|
|
||||||
auto arm9bios = loadARM9BIOS();
|
auto arm9bios = loadARM9BIOS();
|
||||||
if (!arm9bios)
|
if (!arm9bios)
|
||||||
return false;
|
return false;
|
||||||
|
@ -1135,7 +1265,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||||
if (!arm7bios)
|
if (!arm7bios)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
auto firmware = loadFirmware(consoletype);
|
auto firmware = loadFirmware(consoleType);
|
||||||
if (!firmware)
|
if (!firmware)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1162,12 +1292,10 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||||
};
|
};
|
||||||
auto gdbargs = gdbopt.GetBool("Enabled") ? std::make_optional(_gdbargs) : std::nullopt;
|
auto gdbargs = gdbopt.GetBool("Enabled") ? std::make_optional(_gdbargs) : std::nullopt;
|
||||||
#else
|
#else
|
||||||
optional<GDBArgs> gdbargs = std::nullopt;
|
std::optional<GDBArgs> gdbargs = std::nullopt;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NDSArgs ndsargs {
|
NDSArgs ndsargs {
|
||||||
std::move(nextndscart),
|
|
||||||
std::move(nextgbacart),
|
|
||||||
std::move(arm9bios),
|
std::move(arm9bios),
|
||||||
std::move(arm7bios),
|
std::move(arm7bios),
|
||||||
std::move(*firmware),
|
std::move(*firmware),
|
||||||
|
@ -1179,10 +1307,8 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||||
NDSArgs* args = &ndsargs;
|
NDSArgs* args = &ndsargs;
|
||||||
|
|
||||||
std::optional<DSiArgs> dsiargs = std::nullopt;
|
std::optional<DSiArgs> dsiargs = std::nullopt;
|
||||||
if (consoletype == 1)
|
if (consoleType == 1)
|
||||||
{
|
{
|
||||||
ndsargs.GBAROM = nullptr;
|
|
||||||
|
|
||||||
auto arm7ibios = loadDSiARM7BIOS();
|
auto arm7ibios = loadDSiARM7BIOS();
|
||||||
if (!arm7ibios)
|
if (!arm7ibios)
|
||||||
return false;
|
return false;
|
||||||
|
@ -1210,33 +1336,35 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||||
args = &(*dsiargs);
|
args = &(*dsiargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLock.lock();
|
||||||
if ((!nds) || (consoletype != nds->ConsoleType))
|
if ((!nds) || (consoleType != nds->ConsoleType))
|
||||||
{
|
{
|
||||||
NDS::Current = nullptr;
|
if (nds)
|
||||||
if (nds) delete nds;
|
{
|
||||||
|
saveRTCData();
|
||||||
|
delete nds;
|
||||||
|
}
|
||||||
|
|
||||||
if (consoletype == 1)
|
if (consoleType == 1)
|
||||||
nds = new DSi(std::move(dsiargs.value()), this);
|
nds = new DSi(std::move(dsiargs.value()), this);
|
||||||
else
|
else
|
||||||
nds = new NDS(std::move(ndsargs), this);
|
nds = new NDS(std::move(ndsargs), this);
|
||||||
|
|
||||||
NDS::Current = nds;
|
|
||||||
nds->Reset();
|
nds->Reset();
|
||||||
|
loadRTCData();
|
||||||
|
//emuThread->updateVideoRenderer(); // not actually needed?
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nds->SetARM7BIOS(*args->ARM7BIOS);
|
nds->SetARM7BIOS(*args->ARM7BIOS);
|
||||||
nds->SetARM9BIOS(*args->ARM9BIOS);
|
nds->SetARM9BIOS(*args->ARM9BIOS);
|
||||||
nds->SetFirmware(std::move(args->Firmware));
|
nds->SetFirmware(std::move(args->Firmware));
|
||||||
nds->SetNDSCart(std::move(args->NDSROM));
|
|
||||||
nds->SetGBACart(std::move(args->GBAROM));
|
|
||||||
nds->SetJITArgs(args->JIT);
|
nds->SetJITArgs(args->JIT);
|
||||||
// TODO GDB stub shit
|
nds->SetGdbArgs(args->GDB);
|
||||||
nds->SPU.SetInterpolation(args->Interpolation);
|
nds->SPU.SetInterpolation(args->Interpolation);
|
||||||
nds->SPU.SetDegrade10Bit(args->BitDepth);
|
nds->SPU.SetDegrade10Bit(args->BitDepth);
|
||||||
|
|
||||||
if (consoletype == 1)
|
if (consoleType == 1)
|
||||||
{
|
{
|
||||||
DSi* dsi = (DSi*)nds;
|
DSi* dsi = (DSi*)nds;
|
||||||
DSiArgs& _dsiargs = *dsiargs;
|
DSiArgs& _dsiargs = *dsiargs;
|
||||||
|
@ -1248,19 +1376,26 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||||
dsi->SetSDCard(std::move(_dsiargs.DSiSDCard));
|
dsi->SetSDCard(std::move(_dsiargs.DSiSDCard));
|
||||||
// We're moving the optional, not the card
|
// We're moving the optional, not the card
|
||||||
// (inserting std::nullopt here is okay, it means no card)
|
// (inserting std::nullopt here is okay, it means no card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dsi->EjectGBACart();
|
// loads the carts later -- to be sure that everything else is initialized
|
||||||
}
|
nds->SetNDSCart(std::move(nextndscart));
|
||||||
}
|
if (consoleType == 1)
|
||||||
|
nds->EjectGBACart();
|
||||||
|
else
|
||||||
|
nds->SetGBACart(std::move(nextgbacart));
|
||||||
|
|
||||||
|
renderLock.unlock();
|
||||||
|
|
||||||
|
loadCheats();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::reset()
|
void EmuInstance::reset()
|
||||||
{
|
{
|
||||||
consoleType = globalCfg.GetInt("Emu.ConsoleType");
|
updateConsole();
|
||||||
|
|
||||||
updateConsole(Keep {}, Keep {});
|
|
||||||
|
|
||||||
if (consoleType == 1) ejectGBACart();
|
if (consoleType == 1) ejectGBACart();
|
||||||
|
|
||||||
|
@ -1271,7 +1406,7 @@ void EmuInstance::reset()
|
||||||
if ((cartType != -1) && ndsSave)
|
if ((cartType != -1) && ndsSave)
|
||||||
{
|
{
|
||||||
std::string oldsave = ndsSave->GetPath();
|
std::string oldsave = ndsSave->GetPath();
|
||||||
std::string newsave = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav");
|
std::string newsave = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav");
|
||||||
newsave += instanceFileSuffix();
|
newsave += instanceFileSuffix();
|
||||||
if (oldsave != newsave)
|
if (oldsave != newsave)
|
||||||
ndsSave->SetPath(newsave, false);
|
ndsSave->SetPath(newsave, false);
|
||||||
|
@ -1280,7 +1415,7 @@ void EmuInstance::reset()
|
||||||
if ((gbaCartType != -1) && gbaSave)
|
if ((gbaCartType != -1) && gbaSave)
|
||||||
{
|
{
|
||||||
std::string oldsave = gbaSave->GetPath();
|
std::string oldsave = gbaSave->GetPath();
|
||||||
std::string newsave = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav");
|
std::string newsave = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav");
|
||||||
newsave += instanceFileSuffix();
|
newsave += instanceFileSuffix();
|
||||||
if (oldsave != newsave)
|
if (oldsave != newsave)
|
||||||
gbaSave->SetPath(newsave, false);
|
gbaSave->SetPath(newsave, false);
|
||||||
|
@ -1321,16 +1456,22 @@ void EmuInstance::reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EmuInstance::bootToMenu()
|
bool EmuInstance::bootToMenu(QString& errorstr)
|
||||||
{
|
{
|
||||||
// Keep whatever cart is in the console, if any.
|
// Keep whatever cart is in the console, if any.
|
||||||
if (!updateConsole(Keep {}, Keep {}))
|
if (!updateConsole())
|
||||||
|
{
|
||||||
// Try to update the console, but keep the existing cart. If that fails...
|
// Try to update the console, but keep the existing cart. If that fails...
|
||||||
|
errorstr = "Failed to boot the firmware.";
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// BIOS and firmware files are loaded, patched, and installed in UpdateConsole
|
// BIOS and firmware files are loaded, patched, and installed in UpdateConsole
|
||||||
if (nds->NeedsDirectBoot())
|
if (nds->NeedsDirectBoot())
|
||||||
|
{
|
||||||
|
errorstr = "This firmware is not bootable.";
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
initFirmwareSaveManager();
|
initFirmwareSaveManager();
|
||||||
nds->Reset();
|
nds->Reset();
|
||||||
|
@ -1529,14 +1670,12 @@ void EmuInstance::customizeFirmware(Firmware& firmware, bool overridesettings) n
|
||||||
auto firmcfg = localCfg.GetTable("Firmware");
|
auto firmcfg = localCfg.GetTable("Firmware");
|
||||||
|
|
||||||
// setting up username
|
// setting up username
|
||||||
std::string orig_username = firmcfg.GetString("Username");
|
auto username = firmcfg.GetQString("Username");
|
||||||
if (!orig_username.empty())
|
if (!username.isEmpty())
|
||||||
{ // If the frontend defines a username, take it. If not, leave the existing one.
|
{ // If the frontend defines a username, take it. If not, leave the existing one.
|
||||||
std::u16string username = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
|
size_t usernameLength = std::min((int) username.length(), 10);
|
||||||
orig_username);
|
|
||||||
size_t usernameLength = std::min(username.length(), (size_t) 10);
|
|
||||||
currentData.NameLength = usernameLength;
|
currentData.NameLength = usernameLength;
|
||||||
memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t));
|
memcpy(currentData.Nickname, username.utf16(), usernameLength * sizeof(char16_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto language = static_cast<Firmware::Language>(firmcfg.GetInt("Language"));
|
auto language = static_cast<Firmware::Language>(firmcfg.GetInt("Language"));
|
||||||
|
@ -1566,12 +1705,10 @@ void EmuInstance::customizeFirmware(Firmware& firmware, bool overridesettings) n
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup message
|
// setup message
|
||||||
std::string orig_message = firmcfg.GetString("Message");
|
auto message = firmcfg.GetQString("Message");
|
||||||
if (!orig_message.empty())
|
if (!message.isEmpty())
|
||||||
{
|
{
|
||||||
std::u16string message = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
|
size_t messageLength = std::min((int) message.length(), 26);
|
||||||
orig_message);
|
|
||||||
size_t messageLength = std::min(message.length(), (size_t) 26);
|
|
||||||
currentData.MessageLength = messageLength;
|
currentData.MessageLength = messageLength;
|
||||||
memcpy(currentData.Message, message.data(), messageLength * sizeof(char16_t));
|
memcpy(currentData.Message, message.data(), messageLength * sizeof(char16_t));
|
||||||
}
|
}
|
||||||
|
@ -1707,7 +1844,7 @@ QString EmuInstance::getSavErrorString(std::string& filepath, bool gba)
|
||||||
return QString::fromStdString(err1);
|
return QString::fromStdString(err1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
bool EmuInstance::loadROM(QStringList filepath, bool reset, QString& errorstr)
|
||||||
{
|
{
|
||||||
unique_ptr<u8[]> filedata = nullptr;
|
unique_ptr<u8[]> filedata = nullptr;
|
||||||
u32 filelen;
|
u32 filelen;
|
||||||
|
@ -1716,7 +1853,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
||||||
|
|
||||||
if (!loadROMData(filepath, filedata, filelen, basepath, romname))
|
if (!loadROMData(filepath, filedata, filelen, basepath, romname))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
|
errorstr = "Failed to load the DS ROM.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1729,7 +1866,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
||||||
u32 savelen = 0;
|
u32 savelen = 0;
|
||||||
std::unique_ptr<u8[]> savedata = nullptr;
|
std::unique_ptr<u8[]> savedata = nullptr;
|
||||||
|
|
||||||
std::string savname = getAssetPath(false, globalCfg.GetString("SaveFilePath"), ".sav");
|
std::string savname = getAssetPath(false, localCfg.GetString("SaveFilePath"), ".sav");
|
||||||
std::string origsav = savname;
|
std::string origsav = savname;
|
||||||
savname += instanceFileSuffix();
|
savname += instanceFileSuffix();
|
||||||
|
|
||||||
|
@ -1738,7 +1875,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
||||||
{
|
{
|
||||||
if (!Platform::CheckFileWritable(origsav))
|
if (!Platform::CheckFileWritable(origsav))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, false));
|
errorstr = getSavErrorString(origsav, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1746,7 +1883,7 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
||||||
}
|
}
|
||||||
else if (!Platform::CheckFileWritable(savname))
|
else if (!Platform::CheckFileWritable(savname))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, false));
|
errorstr = getSavErrorString(savname, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1773,15 +1910,18 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
||||||
if (!cart)
|
if (!cart)
|
||||||
{
|
{
|
||||||
// If we couldn't parse the ROM...
|
// If we couldn't parse the ROM...
|
||||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
|
errorstr = "Failed to load the DS ROM.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reset)
|
if (reset)
|
||||||
{
|
{
|
||||||
if (!updateConsole(std::move(cart), Keep {}))
|
nextCart = std::move(cart);
|
||||||
|
changeCart = true;
|
||||||
|
|
||||||
|
if (!updateConsole())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM.");
|
errorstr = "Failed to load the DS ROM.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1798,13 +1938,20 @@ bool EmuInstance::loadROM(QStringList filepath, bool reset)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(nds != nullptr);
|
if (emuIsActive())
|
||||||
|
{
|
||||||
nds->SetNDSCart(std::move(cart));
|
nds->SetNDSCart(std::move(cart));
|
||||||
|
loadCheats();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextCart = std::move(cart);
|
||||||
|
changeCart = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cartType = 0;
|
cartType = 0;
|
||||||
ndsSave = std::make_unique<SaveManager>(savname);
|
ndsSave = std::make_unique<SaveManager>(savname);
|
||||||
loadCheats();
|
|
||||||
|
|
||||||
return true; // success
|
return true; // success
|
||||||
}
|
}
|
||||||
|
@ -1813,9 +1960,16 @@ void EmuInstance::ejectCart()
|
||||||
{
|
{
|
||||||
ndsSave = nullptr;
|
ndsSave = nullptr;
|
||||||
|
|
||||||
unloadCheats();
|
if (emuIsActive())
|
||||||
|
{
|
||||||
nds->EjectCart();
|
nds->EjectCart();
|
||||||
|
unloadCheats();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextCart = nullptr;
|
||||||
|
changeCart = true;
|
||||||
|
}
|
||||||
|
|
||||||
cartType = -1;
|
cartType = -1;
|
||||||
baseROMDir = "";
|
baseROMDir = "";
|
||||||
|
@ -1843,11 +1997,11 @@ QString EmuInstance::cartLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool EmuInstance::loadGBAROM(QStringList filepath)
|
bool EmuInstance::loadGBAROM(QStringList filepath, QString& errorstr)
|
||||||
{
|
{
|
||||||
if (consoleType == 1)
|
if (consoleType == 1)
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", "The DSi doesn't have a GBA slot.");
|
errorstr = "The DSi doesn't have a GBA slot.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1858,7 +2012,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
|
||||||
|
|
||||||
if (!loadROMData(filepath, filedata, filelen, basepath, romname))
|
if (!loadROMData(filepath, filedata, filelen, basepath, romname))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM.");
|
errorstr = "Failed to load the GBA ROM.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1871,7 +2025,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
|
||||||
u32 savelen = 0;
|
u32 savelen = 0;
|
||||||
std::unique_ptr<u8[]> savedata = nullptr;
|
std::unique_ptr<u8[]> savedata = nullptr;
|
||||||
|
|
||||||
std::string savname = getAssetPath(true, globalCfg.GetString("SaveFilePath"), ".sav");
|
std::string savname = getAssetPath(true, localCfg.GetString("SaveFilePath"), ".sav");
|
||||||
std::string origsav = savname;
|
std::string origsav = savname;
|
||||||
savname += instanceFileSuffix();
|
savname += instanceFileSuffix();
|
||||||
|
|
||||||
|
@ -1880,7 +2034,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
|
||||||
{
|
{
|
||||||
if (!Platform::CheckFileWritable(origsav))
|
if (!Platform::CheckFileWritable(origsav))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(origsav, true));
|
errorstr = getSavErrorString(origsav, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1888,7 +2042,7 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
|
||||||
}
|
}
|
||||||
else if (!Platform::CheckFileWritable(savname))
|
else if (!Platform::CheckFileWritable(savname))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", getSavErrorString(savname, true));
|
errorstr = getSavErrorString(savname, true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1908,24 +2062,47 @@ bool EmuInstance::loadGBAROM(QStringList filepath)
|
||||||
auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen, this);
|
auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen, this);
|
||||||
if (!cart)
|
if (!cart)
|
||||||
{
|
{
|
||||||
QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM.");
|
errorstr = "Failed to load the GBA ROM.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
nds->SetGBACart(std::move(cart));
|
|
||||||
gbaCartType = 0;
|
gbaCartType = 0;
|
||||||
|
if (emuIsActive())
|
||||||
|
{
|
||||||
|
nds->SetGBACart(std::move(cart));
|
||||||
gbaSave = std::make_unique<SaveManager>(savname);
|
gbaSave = std::make_unique<SaveManager>(savname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextGBACart = std::move(cart);
|
||||||
|
changeGBACart = true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::loadGBAAddon(int type)
|
void EmuInstance::loadGBAAddon(int type, QString& errorstr)
|
||||||
{
|
{
|
||||||
if (consoleType == 1) return;
|
if (consoleType == 1) return;
|
||||||
|
|
||||||
|
auto cart = GBACart::LoadAddon(type, this);
|
||||||
|
if (!cart)
|
||||||
|
{
|
||||||
|
errorstr = "Failed to load the GBA addon.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (emuIsActive())
|
||||||
|
{
|
||||||
|
nds->SetGBACart(std::move(cart));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextGBACart = std::move(cart);
|
||||||
|
changeGBACart = true;
|
||||||
|
}
|
||||||
|
|
||||||
gbaSave = nullptr;
|
gbaSave = nullptr;
|
||||||
|
|
||||||
nds->LoadGBAAddon(type);
|
|
||||||
|
|
||||||
gbaCartType = type;
|
gbaCartType = type;
|
||||||
baseGBAROMDir = "";
|
baseGBAROMDir = "";
|
||||||
baseGBAROMName = "";
|
baseGBAROMName = "";
|
||||||
|
@ -1936,7 +2113,15 @@ void EmuInstance::ejectGBACart()
|
||||||
{
|
{
|
||||||
gbaSave = nullptr;
|
gbaSave = nullptr;
|
||||||
|
|
||||||
|
if (emuIsActive())
|
||||||
|
{
|
||||||
nds->EjectGBACart();
|
nds->EjectGBACart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextGBACart = nullptr;
|
||||||
|
changeGBACart = true;
|
||||||
|
}
|
||||||
|
|
||||||
gbaCartType = -1;
|
gbaCartType = -1;
|
||||||
baseGBAROMDir = "";
|
baseGBAROMDir = "";
|
||||||
|
@ -1949,13 +2134,30 @@ bool EmuInstance::gbaCartInserted()
|
||||||
return gbaCartType != -1;
|
return gbaCartType != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString EmuInstance::gbaAddonName(int addon)
|
||||||
|
{
|
||||||
|
switch (addon)
|
||||||
|
{
|
||||||
|
case GBAAddon_RumblePak:
|
||||||
|
return "Rumble Pak";
|
||||||
|
case GBAAddon_RAMExpansion:
|
||||||
|
return "Memory expansion";
|
||||||
|
case GBAAddon_SolarSensorBoktai1:
|
||||||
|
return "Solar Sensor (Boktai 1)";
|
||||||
|
case GBAAddon_SolarSensorBoktai2:
|
||||||
|
return "Solar Sensor (Boktai 2)";
|
||||||
|
case GBAAddon_SolarSensorBoktai3:
|
||||||
|
return "Solar Sensor (Boktai 3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
QString EmuInstance::gbaCartLabel()
|
QString EmuInstance::gbaCartLabel()
|
||||||
{
|
{
|
||||||
if (consoleType == 1) return "none (DSi)";
|
if (consoleType == 1) return "none (DSi)";
|
||||||
|
|
||||||
switch (gbaCartType)
|
if (gbaCartType == 0)
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
{
|
||||||
QString ret = QString::fromStdString(baseGBAROMName);
|
QString ret = QString::fromStdString(baseGBAROMName);
|
||||||
|
|
||||||
|
@ -1965,9 +2167,9 @@ QString EmuInstance::gbaCartLabel()
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
else if (gbaCartType != -1)
|
||||||
case GBAAddon_RAMExpansion:
|
{
|
||||||
return "Memory expansion";
|
return gbaAddonName(gbaCartType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "(none)";
|
return "(none)";
|
||||||
|
|
|
@ -21,13 +21,14 @@
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
#include "NDS.h"
|
#include "NDS.h"
|
||||||
#include "EmuThread.h"
|
#include "EmuThread.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "SaveManager.h"
|
#include "SaveManager.h"
|
||||||
|
|
||||||
const int kMaxWindows = 16;
|
const int kMaxWindows = 4;
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -36,7 +37,7 @@ enum
|
||||||
HK_Pause,
|
HK_Pause,
|
||||||
HK_Reset,
|
HK_Reset,
|
||||||
HK_FastForward,
|
HK_FastForward,
|
||||||
HK_FastForwardToggle,
|
HK_FrameLimitToggle,
|
||||||
HK_FullscreenToggle,
|
HK_FullscreenToggle,
|
||||||
HK_SwapScreens,
|
HK_SwapScreens,
|
||||||
HK_SwapScreenEmphasis,
|
HK_SwapScreenEmphasis,
|
||||||
|
@ -46,6 +47,9 @@ enum
|
||||||
HK_PowerButton,
|
HK_PowerButton,
|
||||||
HK_VolumeUp,
|
HK_VolumeUp,
|
||||||
HK_VolumeDown,
|
HK_VolumeDown,
|
||||||
|
HK_SlowMo,
|
||||||
|
HK_FastForwardToggle,
|
||||||
|
HK_SlowMoToggle,
|
||||||
HK_MAX
|
HK_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,14 +87,21 @@ public:
|
||||||
melonDS::NDS* getNDS() { return nds; }
|
melonDS::NDS* getNDS() { return nds; }
|
||||||
|
|
||||||
MainWindow* getMainWindow() { return mainWindow; }
|
MainWindow* getMainWindow() { return mainWindow; }
|
||||||
|
int getNumWindows() { return numWindows; }
|
||||||
MainWindow* getWindow(int id) { return windowList[id]; }
|
MainWindow* getWindow(int id) { return windowList[id]; }
|
||||||
|
|
||||||
|
void doOnAllWindows(std::function<void(MainWindow*)> func, int exclude = -1);
|
||||||
|
void saveEnabledWindows();
|
||||||
|
|
||||||
Config::Table& getGlobalConfig() { return globalCfg; }
|
Config::Table& getGlobalConfig() { return globalCfg; }
|
||||||
Config::Table& getLocalConfig() { return localCfg; }
|
Config::Table& getLocalConfig() { return localCfg; }
|
||||||
|
|
||||||
|
void broadcastCommand(int cmd, QVariant param = QVariant());
|
||||||
|
void handleCommand(int cmd, QVariant& param);
|
||||||
|
|
||||||
std::string instanceFileSuffix();
|
std::string instanceFileSuffix();
|
||||||
|
|
||||||
void createWindow();
|
void createWindow(int id = -1);
|
||||||
void deleteWindow(int id, bool close);
|
void deleteWindow(int id, bool close);
|
||||||
void deleteAllWindows();
|
void deleteAllWindows();
|
||||||
|
|
||||||
|
@ -100,8 +111,8 @@ public:
|
||||||
void emuStop(melonDS::Platform::StopReason reason);
|
void emuStop(melonDS::Platform::StopReason reason);
|
||||||
|
|
||||||
bool usesOpenGL();
|
bool usesOpenGL();
|
||||||
void initOpenGL();
|
void initOpenGL(int win);
|
||||||
void deinitOpenGL();
|
void deinitOpenGL(int win);
|
||||||
void setVSyncGL(bool vsync);
|
void setVSyncGL(bool vsync);
|
||||||
void makeCurrentGL();
|
void makeCurrentGL();
|
||||||
void drawScreenGL();
|
void drawScreenGL();
|
||||||
|
@ -109,7 +120,7 @@ public:
|
||||||
// return: empty string = setup OK, non-empty = error message
|
// return: empty string = setup OK, non-empty = error message
|
||||||
QString verifySetup();
|
QString verifySetup();
|
||||||
|
|
||||||
bool updateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept;
|
bool updateConsole() noexcept;
|
||||||
|
|
||||||
void enableCheats(bool enable);
|
void enableCheats(bool enable);
|
||||||
melonDS::ARCodeFile* getCheatFile();
|
melonDS::ARCodeFile* getCheatFile();
|
||||||
|
@ -136,6 +147,11 @@ public:
|
||||||
int getJoystickID() { return joystickID; }
|
int getJoystickID() { return joystickID; }
|
||||||
SDL_Joystick* getJoystick() { return joystick; }
|
SDL_Joystick* getJoystick() { return joystick; }
|
||||||
|
|
||||||
|
void touchScreen(int x, int y);
|
||||||
|
void releaseScreen();
|
||||||
|
|
||||||
|
QMutex renderLock;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int lastSep(const std::string& path);
|
static int lastSep(const std::string& path);
|
||||||
std::string getAssetPath(bool gba, const std::string& configpath, const std::string& ext, const std::string& file);
|
std::string getAssetPath(bool gba, const std::string& configpath, const std::string& ext, const std::string& file);
|
||||||
|
@ -165,24 +181,26 @@ private:
|
||||||
std::optional<melonDS::FATStorageArgs> getSDCardArgs(const std::string& key) noexcept;
|
std::optional<melonDS::FATStorageArgs> getSDCardArgs(const std::string& key) noexcept;
|
||||||
std::optional<melonDS::FATStorage> loadSDCard(const std::string& key) noexcept;
|
std::optional<melonDS::FATStorage> loadSDCard(const std::string& key) noexcept;
|
||||||
void setBatteryLevels();
|
void setBatteryLevels();
|
||||||
void setDateTime();
|
|
||||||
void reset();
|
void reset();
|
||||||
bool bootToMenu();
|
bool bootToMenu(QString& errorstr);
|
||||||
melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr<melonDS::u8[]>& outContent);
|
melonDS::u32 decompressROM(const melonDS::u8* inContent, const melonDS::u32 inSize, std::unique_ptr<melonDS::u8[]>& outContent);
|
||||||
void clearBackupState();
|
void clearBackupState();
|
||||||
std::pair<std::unique_ptr<melonDS::Firmware>, std::string> generateDefaultFirmware();
|
std::pair<std::unique_ptr<melonDS::Firmware>, std::string> generateDefaultFirmware();
|
||||||
bool parseMacAddress(void* data);
|
bool parseMacAddress(void* data);
|
||||||
void customizeFirmware(melonDS::Firmware& firmware, bool overridesettings) noexcept;
|
void customizeFirmware(melonDS::Firmware& firmware, bool overridesettings) noexcept;
|
||||||
|
|
||||||
bool loadROMData(const QStringList& filepath, std::unique_ptr<melonDS::u8[]>& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept;
|
bool loadROMData(const QStringList& filepath, std::unique_ptr<melonDS::u8[]>& filedata, melonDS::u32& filelen, std::string& basepath, std::string& romname) noexcept;
|
||||||
QString getSavErrorString(std::string& filepath, bool gba);
|
QString getSavErrorString(std::string& filepath, bool gba);
|
||||||
bool loadROM(QStringList filepath, bool reset);
|
bool loadROM(QStringList filepath, bool reset, QString& errorstr);
|
||||||
void ejectCart();
|
void ejectCart();
|
||||||
bool cartInserted();
|
bool cartInserted();
|
||||||
QString cartLabel();
|
QString cartLabel();
|
||||||
bool loadGBAROM(QStringList filepath);
|
|
||||||
void loadGBAAddon(int type);
|
bool loadGBAROM(QStringList filepath, QString& errorstr);
|
||||||
|
void loadGBAAddon(int type, QString& errorstr);
|
||||||
void ejectGBACart();
|
void ejectGBACart();
|
||||||
bool gbaCartInserted();
|
bool gbaCartInserted();
|
||||||
|
QString gbaAddonName(int addon);
|
||||||
QString gbaCartLabel();
|
QString gbaCartLabel();
|
||||||
|
|
||||||
void audioInit();
|
void audioInit();
|
||||||
|
@ -219,6 +237,10 @@ private:
|
||||||
bool hotkeyPressed(int id) { return hotkeyPress & (1<<id); }
|
bool hotkeyPressed(int id) { return hotkeyPress & (1<<id); }
|
||||||
bool hotkeyReleased(int id) { return hotkeyRelease & (1<<id); }
|
bool hotkeyReleased(int id) { return hotkeyRelease & (1<<id); }
|
||||||
|
|
||||||
|
void loadRTCData();
|
||||||
|
void saveRTCData();
|
||||||
|
void setDateTime();
|
||||||
|
|
||||||
bool deleting;
|
bool deleting;
|
||||||
|
|
||||||
int instanceID;
|
int instanceID;
|
||||||
|
@ -239,11 +261,15 @@ private:
|
||||||
std::string baseROMDir;
|
std::string baseROMDir;
|
||||||
std::string baseROMName;
|
std::string baseROMName;
|
||||||
std::string baseAssetName;
|
std::string baseAssetName;
|
||||||
|
bool changeCart;
|
||||||
|
std::unique_ptr<melonDS::NDSCart::CartCommon> nextCart;
|
||||||
|
|
||||||
int gbaCartType;
|
int gbaCartType;
|
||||||
std::string baseGBAROMDir;
|
std::string baseGBAROMDir;
|
||||||
std::string baseGBAROMName;
|
std::string baseGBAROMName;
|
||||||
std::string baseGBAAssetName;
|
std::string baseGBAAssetName;
|
||||||
|
bool changeGBACart;
|
||||||
|
std::unique_ptr<melonDS::GBACart::CartCommon> nextGBACart;
|
||||||
|
|
||||||
// HACK
|
// HACK
|
||||||
public:
|
public:
|
||||||
|
@ -252,7 +278,12 @@ public:
|
||||||
std::unique_ptr<SaveManager> firmwareSave;
|
std::unique_ptr<SaveManager> firmwareSave;
|
||||||
|
|
||||||
bool doLimitFPS;
|
bool doLimitFPS;
|
||||||
int maxFPS;
|
double curFPS;
|
||||||
|
double targetFPS;
|
||||||
|
double fastForwardFPS;
|
||||||
|
double slowmoFPS;
|
||||||
|
bool fastForwardToggled;
|
||||||
|
bool slowmoToggled;
|
||||||
bool doAudioSync;
|
bool doAudioSync;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -273,8 +304,9 @@ private:
|
||||||
int mpAudioMode;
|
int mpAudioMode;
|
||||||
|
|
||||||
SDL_AudioDeviceID micDevice;
|
SDL_AudioDeviceID micDevice;
|
||||||
melonDS::s16 micExtBuffer[2048];
|
melonDS::s16 micExtBuffer[4096];
|
||||||
melonDS::u32 micExtBufferWritePos;
|
melonDS::u32 micExtBufferWritePos;
|
||||||
|
melonDS::u32 micExtBufferCount;
|
||||||
|
|
||||||
melonDS::u32 micWavLength;
|
melonDS::u32 micWavLength;
|
||||||
melonDS::s16* micWavBuffer;
|
melonDS::s16* micWavBuffer;
|
||||||
|
@ -283,6 +315,8 @@ private:
|
||||||
melonDS::u32 micBufferLength;
|
melonDS::u32 micBufferLength;
|
||||||
melonDS::u32 micBufferReadPos;
|
melonDS::u32 micBufferReadPos;
|
||||||
|
|
||||||
|
SDL_mutex* micLock;
|
||||||
|
|
||||||
//int audioInterp;
|
//int audioInterp;
|
||||||
int audioVolume;
|
int audioVolume;
|
||||||
bool audioDSiVolumeSync;
|
bool audioDSiVolumeSync;
|
||||||
|
@ -308,6 +342,9 @@ private:
|
||||||
|
|
||||||
melonDS::u32 inputMask;
|
melonDS::u32 inputMask;
|
||||||
|
|
||||||
|
bool isTouching;
|
||||||
|
melonDS::u16 touchX, touchY;
|
||||||
|
|
||||||
friend class EmuThread;
|
friend class EmuThread;
|
||||||
friend class MainWindow;
|
friend class MainWindow;
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,7 +29,7 @@ using namespace melonDS;
|
||||||
|
|
||||||
int EmuInstance::audioGetNumSamplesOut(int outlen)
|
int EmuInstance::audioGetNumSamplesOut(int outlen)
|
||||||
{
|
{
|
||||||
float f_len_in = (outlen * 32823.6328125) / (float)audioFreq;
|
float f_len_in = (outlen * 32823.6328125 * (curFPS/60.0)) / (float)audioFreq;
|
||||||
f_len_in += audioSampleFrac;
|
f_len_in += audioSampleFrac;
|
||||||
int len_in = (int)floor(f_len_in);
|
int len_in = (int)floor(f_len_in);
|
||||||
audioSampleFrac = f_len_in - len_in;
|
audioSampleFrac = f_len_in - len_in;
|
||||||
|
@ -73,6 +73,7 @@ void EmuInstance::audioCallback(void* data, Uint8* stream, int len)
|
||||||
// resample incoming audio to match the output sample rate
|
// resample incoming audio to match the output sample rate
|
||||||
|
|
||||||
int len_in = inst->audioGetNumSamplesOut(len);
|
int len_in = inst->audioGetNumSamplesOut(len);
|
||||||
|
if (len_in > 1024) len_in = 1024;
|
||||||
s16 buf_in[1024*2];
|
s16 buf_in[1024*2];
|
||||||
int num_in;
|
int num_in;
|
||||||
|
|
||||||
|
@ -107,8 +108,12 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len)
|
||||||
s16* input = (s16*)stream;
|
s16* input = (s16*)stream;
|
||||||
len /= sizeof(s16);
|
len /= sizeof(s16);
|
||||||
|
|
||||||
|
SDL_LockMutex(inst->micLock);
|
||||||
int maxlen = sizeof(micExtBuffer) / sizeof(s16);
|
int maxlen = sizeof(micExtBuffer) / sizeof(s16);
|
||||||
|
|
||||||
|
if ((inst->micExtBufferCount + len) > maxlen)
|
||||||
|
len = maxlen - inst->micExtBufferCount;
|
||||||
|
|
||||||
if ((inst->micExtBufferWritePos + len) > maxlen)
|
if ((inst->micExtBufferWritePos + len) > maxlen)
|
||||||
{
|
{
|
||||||
u32 len1 = maxlen - inst->micExtBufferWritePos;
|
u32 len1 = maxlen - inst->micExtBufferWritePos;
|
||||||
|
@ -121,11 +126,15 @@ void EmuInstance::micCallback(void* data, Uint8* stream, int len)
|
||||||
memcpy(&inst->micExtBuffer[inst->micExtBufferWritePos], input, len*sizeof(s16));
|
memcpy(&inst->micExtBuffer[inst->micExtBufferWritePos], input, len*sizeof(s16));
|
||||||
inst->micExtBufferWritePos += len;
|
inst->micExtBufferWritePos += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inst->micExtBufferCount += len;
|
||||||
|
SDL_UnlockMutex(inst->micLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::audioMute()
|
void EmuInstance::audioMute()
|
||||||
{
|
{
|
||||||
audioMuted = false;
|
audioMuted = false;
|
||||||
|
if (numEmuInstances() < 2) return;
|
||||||
|
|
||||||
switch (mpAudioMode)
|
switch (mpAudioMode)
|
||||||
{
|
{
|
||||||
|
@ -134,10 +143,16 @@ void EmuInstance::audioMute()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // only currently focused instance
|
case 2: // only currently focused instance
|
||||||
//if (mainWindow != nullptr)
|
audioMuted = true;
|
||||||
// audioMuted = !mainWindow->isActiveWindow();
|
for (int i = 0; i < kMaxWindows; i++)
|
||||||
// TODO!!
|
{
|
||||||
printf("TODO!! audioMute mode 2\n");
|
if (!windowList[i]) continue;
|
||||||
|
if (windowList[i]->isFocused())
|
||||||
|
{
|
||||||
|
audioMuted = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,6 +160,8 @@ void EmuInstance::audioMute()
|
||||||
|
|
||||||
void EmuInstance::micOpen()
|
void EmuInstance::micOpen()
|
||||||
{
|
{
|
||||||
|
if (micDevice) return;
|
||||||
|
|
||||||
if (micInputType != micInputType_External)
|
if (micInputType != micInputType_External)
|
||||||
{
|
{
|
||||||
micDevice = 0;
|
micDevice = 0;
|
||||||
|
@ -270,6 +287,8 @@ void EmuInstance::micLoadWav(const std::string& name)
|
||||||
|
|
||||||
void EmuInstance::micProcess()
|
void EmuInstance::micProcess()
|
||||||
{
|
{
|
||||||
|
SDL_LockMutex(micLock);
|
||||||
|
|
||||||
int type = micInputType;
|
int type = micInputType;
|
||||||
bool cmd = hotkeyDown(HK_Mic);
|
bool cmd = hotkeyDown(HK_Mic);
|
||||||
|
|
||||||
|
@ -278,6 +297,8 @@ void EmuInstance::micProcess()
|
||||||
type = micInputType_Silence;
|
type = micInputType_Silence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int kFrameLen = 735;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case micInputType_Silence: // no mic
|
case micInputType_Silence: // no mic
|
||||||
|
@ -289,21 +310,39 @@ void EmuInstance::micProcess()
|
||||||
case micInputType_Wav: // WAV
|
case micInputType_Wav: // WAV
|
||||||
if (micBuffer)
|
if (micBuffer)
|
||||||
{
|
{
|
||||||
if ((micBufferReadPos + 735) > micBufferLength)
|
int len = kFrameLen;
|
||||||
{
|
if (micExtBufferCount < len)
|
||||||
s16 tmp[735];
|
len = micExtBufferCount;
|
||||||
u32 len1 = micBufferLength - micBufferReadPos;
|
|
||||||
memcpy(&tmp[0], &micBuffer[micBufferReadPos], len1*sizeof(s16));
|
|
||||||
memcpy(&tmp[len1], &micBuffer[0], (735 - len1)*sizeof(s16));
|
|
||||||
|
|
||||||
nds->MicInputFrame(tmp, 735);
|
s16 tmp[kFrameLen];
|
||||||
micBufferReadPos = 735 - len1;
|
|
||||||
|
if ((micBufferReadPos + len) > micBufferLength)
|
||||||
|
{
|
||||||
|
u32 part1 = micBufferLength - micBufferReadPos;
|
||||||
|
memcpy(&tmp[0], &micBuffer[micBufferReadPos], part1*sizeof(s16));
|
||||||
|
memcpy(&tmp[part1], &micBuffer[0], (len - part1)*sizeof(s16));
|
||||||
|
|
||||||
|
micBufferReadPos = len - part1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nds->MicInputFrame(&micBuffer[micBufferReadPos], 735);
|
memcpy(&tmp[0], &micBuffer[micBufferReadPos], len*sizeof(s16));
|
||||||
micBufferReadPos += 735;
|
|
||||||
|
micBufferReadPos += len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
{
|
||||||
|
memset(tmp, 0, sizeof(tmp));
|
||||||
|
}
|
||||||
|
else if (len < kFrameLen)
|
||||||
|
{
|
||||||
|
for (int i = len; i < kFrameLen; i++)
|
||||||
|
tmp[i] = tmp[len-1];
|
||||||
|
}
|
||||||
|
nds->MicInputFrame(tmp, 735);
|
||||||
|
|
||||||
|
micExtBufferCount -= len;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -317,19 +356,21 @@ void EmuInstance::micProcess()
|
||||||
int sample_len = sizeof(mic_blow) / sizeof(u16);
|
int sample_len = sizeof(mic_blow) / sizeof(u16);
|
||||||
static int sample_pos = 0;
|
static int sample_pos = 0;
|
||||||
|
|
||||||
s16 tmp[735];
|
s16 tmp[kFrameLen];
|
||||||
|
|
||||||
for (int i = 0; i < 735; i++)
|
for (int i = 0; i < kFrameLen; i++)
|
||||||
{
|
{
|
||||||
tmp[i] = mic_blow[sample_pos];
|
tmp[i] = mic_blow[sample_pos] ^ 0x8000;
|
||||||
sample_pos++;
|
sample_pos++;
|
||||||
if (sample_pos >= sample_len) sample_pos = 0;
|
if (sample_pos >= sample_len) sample_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nds->MicInputFrame(tmp, 735);
|
nds->MicInputFrame(tmp, kFrameLen);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_UnlockMutex(micLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::setupMicInputData()
|
void EmuInstance::setupMicInputData()
|
||||||
|
@ -402,12 +443,15 @@ void EmuInstance::audioInit()
|
||||||
|
|
||||||
memset(micExtBuffer, 0, sizeof(micExtBuffer));
|
memset(micExtBuffer, 0, sizeof(micExtBuffer));
|
||||||
micExtBufferWritePos = 0;
|
micExtBufferWritePos = 0;
|
||||||
|
micExtBufferCount = 0;
|
||||||
micWavBuffer = nullptr;
|
micWavBuffer = nullptr;
|
||||||
|
|
||||||
micBuffer = nullptr;
|
micBuffer = nullptr;
|
||||||
micBufferLength = 0;
|
micBufferLength = 0;
|
||||||
micBufferReadPos = 0;
|
micBufferReadPos = 0;
|
||||||
|
|
||||||
|
micLock = SDL_CreateMutex();
|
||||||
|
|
||||||
setupMicInputData();
|
setupMicInputData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,6 +469,9 @@ void EmuInstance::audioDeInit()
|
||||||
|
|
||||||
if (micWavBuffer) delete[] micWavBuffer;
|
if (micWavBuffer) delete[] micWavBuffer;
|
||||||
micWavBuffer = nullptr;
|
micWavBuffer = nullptr;
|
||||||
|
|
||||||
|
if (micLock) SDL_DestroyMutex(micLock);
|
||||||
|
micLock = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::audioSync()
|
void EmuInstance::audioSync()
|
||||||
|
|
|
@ -47,7 +47,7 @@ const char* EmuInstance::hotkeyNames[HK_MAX] =
|
||||||
"HK_Pause",
|
"HK_Pause",
|
||||||
"HK_Reset",
|
"HK_Reset",
|
||||||
"HK_FastForward",
|
"HK_FastForward",
|
||||||
"HK_FastForwardToggle",
|
"HK_FrameLimitToggle",
|
||||||
"HK_FullscreenToggle",
|
"HK_FullscreenToggle",
|
||||||
"HK_SwapScreens",
|
"HK_SwapScreens",
|
||||||
"HK_SwapScreenEmphasis",
|
"HK_SwapScreenEmphasis",
|
||||||
|
@ -56,7 +56,10 @@ const char* EmuInstance::hotkeyNames[HK_MAX] =
|
||||||
"HK_FrameStep",
|
"HK_FrameStep",
|
||||||
"HK_PowerButton",
|
"HK_PowerButton",
|
||||||
"HK_VolumeUp",
|
"HK_VolumeUp",
|
||||||
"HK_VolumeDown"
|
"HK_VolumeDown",
|
||||||
|
"HK_SlowMo",
|
||||||
|
"HK_FastForwardToggle",
|
||||||
|
"HK_SlowMoToggle"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +74,10 @@ void EmuInstance::inputInit()
|
||||||
hotkeyMask = 0;
|
hotkeyMask = 0;
|
||||||
lastHotkeyMask = 0;
|
lastHotkeyMask = 0;
|
||||||
|
|
||||||
|
isTouching = false;
|
||||||
|
touchX = 0;
|
||||||
|
touchY = 0;
|
||||||
|
|
||||||
joystick = nullptr;
|
joystick = nullptr;
|
||||||
controller = nullptr;
|
controller = nullptr;
|
||||||
hasRumble = false;
|
hasRumble = false;
|
||||||
|
@ -350,3 +357,15 @@ void EmuInstance::inputProcess()
|
||||||
hotkeyRelease = lastHotkeyMask & ~hotkeyMask;
|
hotkeyRelease = lastHotkeyMask & ~hotkeyMask;
|
||||||
lastHotkeyMask = hotkeyMask;
|
lastHotkeyMask = hotkeyMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmuInstance::touchScreen(int x, int y)
|
||||||
|
{
|
||||||
|
touchX = x;
|
||||||
|
touchY = y;
|
||||||
|
isTouching = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuInstance::releaseScreen()
|
||||||
|
{
|
||||||
|
isTouching = false;
|
||||||
|
}
|
||||||
|
|
|
@ -82,9 +82,6 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
|
||||||
ui->chkJITBranchOptimisations->setChecked(cfg.GetBool("JIT.BranchOptimisations"));
|
ui->chkJITBranchOptimisations->setChecked(cfg.GetBool("JIT.BranchOptimisations"));
|
||||||
ui->chkJITLiteralOptimisations->setChecked(cfg.GetBool("JIT.LiteralOptimisations"));
|
ui->chkJITLiteralOptimisations->setChecked(cfg.GetBool("JIT.LiteralOptimisations"));
|
||||||
ui->chkJITFastMemory->setChecked(cfg.GetBool("JIT.FastMemory"));
|
ui->chkJITFastMemory->setChecked(cfg.GetBool("JIT.FastMemory"));
|
||||||
#ifdef __APPLE__
|
|
||||||
ui->chkJITFastMemory->setDisabled(true);
|
|
||||||
#endif
|
|
||||||
ui->spnJITMaximumBlockSize->setValue(cfg.GetInt("JIT.MaxBlockSize"));
|
ui->spnJITMaximumBlockSize->setValue(cfg.GetInt("JIT.MaxBlockSize"));
|
||||||
#else
|
#else
|
||||||
ui->chkEnableJIT->setDisabled(true);
|
ui->chkEnableJIT->setDisabled(true);
|
||||||
|
@ -215,6 +212,13 @@ void EmuSettingsDialog::verifyFirmware()
|
||||||
|
|
||||||
void EmuSettingsDialog::done(int r)
|
void EmuSettingsDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
needsReset = false;
|
needsReset = false;
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
|
@ -532,11 +536,14 @@ void EmuSettingsDialog::on_btnDSiSDFolderBrowse_clicked()
|
||||||
void EmuSettingsDialog::on_chkEnableJIT_toggled()
|
void EmuSettingsDialog::on_chkEnableJIT_toggled()
|
||||||
{
|
{
|
||||||
bool disabled = !ui->chkEnableJIT->isChecked();
|
bool disabled = !ui->chkEnableJIT->isChecked();
|
||||||
|
#ifdef JIT_ENABLED
|
||||||
|
bool fastmemSupported = ARMJIT_Memory::IsFastMemSupported();
|
||||||
|
#else
|
||||||
|
bool fastmemSupported = false;
|
||||||
|
#endif
|
||||||
ui->chkJITBranchOptimisations->setDisabled(disabled);
|
ui->chkJITBranchOptimisations->setDisabled(disabled);
|
||||||
ui->chkJITLiteralOptimisations->setDisabled(disabled);
|
ui->chkJITLiteralOptimisations->setDisabled(disabled);
|
||||||
#ifndef __APPLE__
|
ui->chkJITFastMemory->setDisabled(disabled || !fastmemSupported);
|
||||||
ui->chkJITFastMemory->setDisabled(disabled);
|
|
||||||
#endif
|
|
||||||
ui->spnJITMaximumBlockSize->setDisabled(disabled);
|
ui->spnJITMaximumBlockSize->setDisabled(disabled);
|
||||||
|
|
||||||
on_cbGdbEnabled_toggled();
|
on_cbGdbEnabled_toggled();
|
||||||
|
|
|
@ -70,41 +70,46 @@ EmuThread::EmuThread(EmuInstance* inst, QObject* parent) : QThread(parent)
|
||||||
|
|
||||||
void EmuThread::attachWindow(MainWindow* window)
|
void EmuThread::attachWindow(MainWindow* window)
|
||||||
{
|
{
|
||||||
connect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint()));
|
|
||||||
connect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString)));
|
connect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString)));
|
||||||
connect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart()));
|
connect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart()));
|
||||||
connect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop()));
|
connect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop()));
|
||||||
connect(this, SIGNAL(windowEmuPause(bool)), window, SLOT(onEmuPause(bool)));
|
connect(this, SIGNAL(windowEmuPause(bool)), window, SLOT(onEmuPause(bool)));
|
||||||
connect(this, SIGNAL(windowEmuReset()), window, SLOT(onEmuReset()));
|
connect(this, SIGNAL(windowEmuReset()), window, SLOT(onEmuReset()));
|
||||||
connect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger()));
|
|
||||||
connect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int)));
|
connect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int)));
|
||||||
connect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled()));
|
connect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled()));
|
||||||
connect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger()));
|
|
||||||
connect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled()));
|
connect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled()));
|
||||||
|
|
||||||
|
if (window->winHasMenu())
|
||||||
|
{
|
||||||
|
connect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger()));
|
||||||
|
connect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::detachWindow(MainWindow* window)
|
void EmuThread::detachWindow(MainWindow* window)
|
||||||
{
|
{
|
||||||
disconnect(this, SIGNAL(windowUpdate()), window->panel, SLOT(repaint()));
|
|
||||||
disconnect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString)));
|
disconnect(this, SIGNAL(windowTitleChange(QString)), window, SLOT(onTitleUpdate(QString)));
|
||||||
disconnect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart()));
|
disconnect(this, SIGNAL(windowEmuStart()), window, SLOT(onEmuStart()));
|
||||||
disconnect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop()));
|
disconnect(this, SIGNAL(windowEmuStop()), window, SLOT(onEmuStop()));
|
||||||
disconnect(this, SIGNAL(windowEmuPause(bool)), window, SLOT(onEmuPause(bool)));
|
disconnect(this, SIGNAL(windowEmuPause(bool)), window, SLOT(onEmuPause(bool)));
|
||||||
disconnect(this, SIGNAL(windowEmuReset()), window, SLOT(onEmuReset()));
|
disconnect(this, SIGNAL(windowEmuReset()), window, SLOT(onEmuReset()));
|
||||||
disconnect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger()));
|
|
||||||
disconnect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int)));
|
disconnect(this, SIGNAL(autoScreenSizingChange(int)), window->panel, SLOT(onAutoScreenSizingChanged(int)));
|
||||||
disconnect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled()));
|
disconnect(this, SIGNAL(windowFullscreenToggle()), window, SLOT(onFullscreenToggled()));
|
||||||
disconnect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger()));
|
|
||||||
disconnect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled()));
|
disconnect(this, SIGNAL(screenEmphasisToggle()), window, SLOT(onScreenEmphasisToggled()));
|
||||||
|
|
||||||
|
if (window->winHasMenu())
|
||||||
|
{
|
||||||
|
disconnect(this, SIGNAL(windowLimitFPSChange()), window->actLimitFramerate, SLOT(trigger()));
|
||||||
|
disconnect(this, SIGNAL(swapScreensToggle()), window->actScreenSwap, SLOT(trigger()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::run()
|
void EmuThread::run()
|
||||||
{
|
{
|
||||||
Config::Table& globalCfg = emuInstance->getGlobalConfig();
|
Config::Table& globalCfg = emuInstance->getGlobalConfig();
|
||||||
u32 mainScreenPos[3];
|
u32 mainScreenPos[3];
|
||||||
Platform::FileHandle* file;
|
|
||||||
|
|
||||||
emuInstance->updateConsole(nullptr, nullptr);
|
//emuInstance->updateConsole();
|
||||||
// No carts are inserted when melonDS first boots
|
// No carts are inserted when melonDS first boots
|
||||||
|
|
||||||
mainScreenPos[0] = 0;
|
mainScreenPos[0] = 0;
|
||||||
|
@ -112,11 +117,11 @@ void EmuThread::run()
|
||||||
mainScreenPos[2] = 0;
|
mainScreenPos[2] = 0;
|
||||||
autoScreenSizing = 0;
|
autoScreenSizing = 0;
|
||||||
|
|
||||||
videoSettingsDirty = false;
|
//videoSettingsDirty = false;
|
||||||
|
|
||||||
if (emuInstance->usesOpenGL())
|
if (emuInstance->usesOpenGL())
|
||||||
{
|
{
|
||||||
emuInstance->initOpenGL();
|
emuInstance->initOpenGL(0);
|
||||||
|
|
||||||
useOpenGL = true;
|
useOpenGL = true;
|
||||||
videoRenderer = globalCfg.GetInt("3D.Renderer");
|
videoRenderer = globalCfg.GetInt("3D.Renderer");
|
||||||
|
@ -127,7 +132,8 @@ void EmuThread::run()
|
||||||
videoRenderer = 0;
|
videoRenderer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRenderer();
|
//updateRenderer();
|
||||||
|
videoSettingsDirty = true;
|
||||||
|
|
||||||
u32 nframes = 0;
|
u32 nframes = 0;
|
||||||
double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency();
|
double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency();
|
||||||
|
@ -138,23 +144,19 @@ void EmuThread::run()
|
||||||
u32 winUpdateCount = 0, winUpdateFreq = 1;
|
u32 winUpdateCount = 0, winUpdateFreq = 1;
|
||||||
u8 dsiVolumeLevel = 0x1F;
|
u8 dsiVolumeLevel = 0x1F;
|
||||||
|
|
||||||
file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read);
|
|
||||||
if (file)
|
|
||||||
{
|
|
||||||
RTC::StateData state;
|
|
||||||
Platform::FileRead(&state, sizeof(state), 1, file);
|
|
||||||
Platform::CloseFile(file);
|
|
||||||
emuInstance->nds->RTC.SetState(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
char melontitle[100];
|
char melontitle[100];
|
||||||
|
|
||||||
|
bool fastforward = false;
|
||||||
|
bool slowmo = false;
|
||||||
|
emuInstance->fastForwardToggled = false;
|
||||||
|
emuInstance->slowmoToggled = false;
|
||||||
|
|
||||||
while (emuStatus != emuStatus_Exit)
|
while (emuStatus != emuStatus_Exit)
|
||||||
{
|
{
|
||||||
MPInterface::Get().Process();
|
MPInterface::Get().Process();
|
||||||
emuInstance->inputProcess();
|
emuInstance->inputProcess();
|
||||||
|
|
||||||
if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange();
|
if (emuInstance->hotkeyPressed(HK_FrameLimitToggle)) emit windowLimitFPSChange();
|
||||||
|
|
||||||
if (emuInstance->hotkeyPressed(HK_Pause)) emuTogglePause();
|
if (emuInstance->hotkeyPressed(HK_Pause)) emuTogglePause();
|
||||||
if (emuInstance->hotkeyPressed(HK_Reset)) emuReset();
|
if (emuInstance->hotkeyPressed(HK_Reset)) emuReset();
|
||||||
|
@ -229,6 +231,7 @@ void EmuThread::run()
|
||||||
// update render settings if needed
|
// update render settings if needed
|
||||||
if (videoSettingsDirty)
|
if (videoSettingsDirty)
|
||||||
{
|
{
|
||||||
|
emuInstance->renderLock.lock();
|
||||||
if (useOpenGL)
|
if (useOpenGL)
|
||||||
{
|
{
|
||||||
emuInstance->setVSyncGL(true);
|
emuInstance->setVSyncGL(true);
|
||||||
|
@ -244,11 +247,17 @@ void EmuThread::run()
|
||||||
updateRenderer();
|
updateRenderer();
|
||||||
|
|
||||||
videoSettingsDirty = false;
|
videoSettingsDirty = false;
|
||||||
|
emuInstance->renderLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// process input and hotkeys
|
// process input and hotkeys
|
||||||
emuInstance->nds->SetKeyMask(emuInstance->inputMask);
|
emuInstance->nds->SetKeyMask(emuInstance->inputMask);
|
||||||
|
|
||||||
|
if (emuInstance->isTouching)
|
||||||
|
emuInstance->nds->TouchScreen(emuInstance->touchX, emuInstance->touchY);
|
||||||
|
else
|
||||||
|
emuInstance->nds->ReleaseScreen();
|
||||||
|
|
||||||
if (emuInstance->hotkeyPressed(HK_Lid))
|
if (emuInstance->hotkeyPressed(HK_Lid))
|
||||||
{
|
{
|
||||||
bool lid = !emuInstance->nds->IsLidClosed();
|
bool lid = !emuInstance->nds->IsLidClosed();
|
||||||
|
@ -312,13 +321,13 @@ void EmuThread::run()
|
||||||
|
|
||||||
if (!useOpenGL)
|
if (!useOpenGL)
|
||||||
{
|
{
|
||||||
FrontBufferLock.lock();
|
frontBufferLock.lock();
|
||||||
FrontBuffer = emuInstance->nds->GPU.FrontBuffer;
|
frontBuffer = emuInstance->nds->GPU.FrontBuffer;
|
||||||
FrontBufferLock.unlock();
|
frontBufferLock.unlock();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FrontBuffer = emuInstance->nds->GPU.FrontBuffer;
|
frontBuffer = emuInstance->nds->GPU.FrontBuffer;
|
||||||
emuInstance->drawScreenGL();
|
emuInstance->drawScreenGL();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,21 +342,33 @@ void EmuThread::run()
|
||||||
winUpdateCount = 0;
|
winUpdateCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fastforward = emuInstance->hotkeyDown(HK_FastForward);
|
if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emuInstance->fastForwardToggled = !emuInstance->fastForwardToggled;
|
||||||
|
if (emuInstance->hotkeyPressed(HK_SlowMoToggle)) emuInstance->slowmoToggled = !emuInstance->slowmoToggled;
|
||||||
|
|
||||||
|
bool enablefastforward = emuInstance->hotkeyDown(HK_FastForward) | emuInstance->fastForwardToggled;
|
||||||
|
bool enableslowmo = emuInstance->hotkeyDown(HK_SlowMo) | emuInstance->slowmoToggled;
|
||||||
|
|
||||||
if (useOpenGL)
|
if (useOpenGL)
|
||||||
{
|
{
|
||||||
// when using OpenGL: when toggling fast-forward, change the vsync interval
|
// when using OpenGL: when toggling fast-forward or slowmo, change the vsync interval
|
||||||
if (emuInstance->hotkeyPressed(HK_FastForward))
|
if ((enablefastforward || enableslowmo) && !(fastforward || slowmo))
|
||||||
{
|
{
|
||||||
emuInstance->setVSyncGL(false);
|
emuInstance->setVSyncGL(false);
|
||||||
}
|
}
|
||||||
else if (emuInstance->hotkeyReleased(HK_FastForward))
|
else if (!(enablefastforward || enableslowmo) && (fastforward || slowmo))
|
||||||
{
|
{
|
||||||
emuInstance->setVSyncGL(true);
|
emuInstance->setVSyncGL(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fastforward = enablefastforward;
|
||||||
|
slowmo = enableslowmo;
|
||||||
|
|
||||||
|
if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS;
|
||||||
|
else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS;
|
||||||
|
else if (!emuInstance->doLimitFPS && !emuInstance->doAudioSync) emuInstance->curFPS = 1000.0;
|
||||||
|
else emuInstance->curFPS = emuInstance->targetFPS;
|
||||||
|
|
||||||
if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1)
|
if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1)
|
||||||
{
|
{
|
||||||
DSi* dsi = static_cast<DSi*>(emuInstance->nds);
|
DSi* dsi = static_cast<DSi*>(emuInstance->nds);
|
||||||
|
@ -361,23 +382,22 @@ void EmuThread::run()
|
||||||
emuInstance->audioVolume = volumeLevel * (256.0 / 31.0);
|
emuInstance->audioVolume = volumeLevel * (256.0 / 31.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emuInstance->doAudioSync && !fastforward)
|
if (emuInstance->doAudioSync && !(fastforward || slowmo))
|
||||||
emuInstance->audioSync();
|
emuInstance->audioSync();
|
||||||
|
|
||||||
double frametimeStep = nlines / (60.0 * 263.0);
|
double frametimeStep = nlines / (emuInstance->curFPS * 263.0);
|
||||||
|
|
||||||
|
if (frametimeStep < 0.001) frametimeStep = 0.001;
|
||||||
|
|
||||||
|
if (emuInstance->doLimitFPS)
|
||||||
{
|
{
|
||||||
bool limitfps = emuInstance->doLimitFPS && !fastforward;
|
|
||||||
|
|
||||||
double practicalFramelimit = limitfps ? frametimeStep : 1.0 / emuInstance->maxFPS;
|
|
||||||
|
|
||||||
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||||
|
|
||||||
frameLimitError += practicalFramelimit - (curtime - lastTime);
|
frameLimitError += frametimeStep - (curtime - lastTime);
|
||||||
if (frameLimitError < -practicalFramelimit)
|
if (frameLimitError < -frametimeStep)
|
||||||
frameLimitError = -practicalFramelimit;
|
frameLimitError = -frametimeStep;
|
||||||
if (frameLimitError > practicalFramelimit)
|
if (frameLimitError > frametimeStep)
|
||||||
frameLimitError = practicalFramelimit;
|
frameLimitError = frametimeStep;
|
||||||
|
|
||||||
if (round(frameLimitError * 1000.0) > 0.0)
|
if (round(frameLimitError * 1000.0) > 0.0)
|
||||||
{
|
{
|
||||||
|
@ -406,11 +426,12 @@ void EmuThread::run()
|
||||||
if (winUpdateFreq < 1)
|
if (winUpdateFreq < 1)
|
||||||
winUpdateFreq = 1;
|
winUpdateFreq = 1;
|
||||||
|
|
||||||
|
double actualfps = (59.8261 * 263.0) / nlines;
|
||||||
int inst = emuInstance->instanceID;
|
int inst = emuInstance->instanceID;
|
||||||
if (inst == 0)
|
if (inst == 0)
|
||||||
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
|
snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps);
|
||||||
else
|
else
|
||||||
sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
|
snprintf(melontitle, sizeof(melontitle), "[%d/%.0f] melonDS (%d)", fps, actualfps, inst+1);
|
||||||
changeWindowTitle(melontitle);
|
changeWindowTitle(melontitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,9 +446,9 @@ void EmuThread::run()
|
||||||
|
|
||||||
int inst = emuInstance->instanceID;
|
int inst = emuInstance->instanceID;
|
||||||
if (inst == 0)
|
if (inst == 0)
|
||||||
sprintf(melontitle, "melonDS " MELONDS_VERSION);
|
snprintf(melontitle, sizeof(melontitle), "melonDS " MELONDS_VERSION);
|
||||||
else
|
else
|
||||||
sprintf(melontitle, "melonDS (%d)", inst+1);
|
snprintf(melontitle, sizeof(melontitle), "melonDS (%d)", inst+1);
|
||||||
changeWindowTitle(melontitle);
|
changeWindowTitle(melontitle);
|
||||||
|
|
||||||
SDL_Delay(75);
|
SDL_Delay(75);
|
||||||
|
@ -440,17 +461,6 @@ void EmuThread::run()
|
||||||
|
|
||||||
handleMessages();
|
handleMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write);
|
|
||||||
if (file)
|
|
||||||
{
|
|
||||||
RTC::StateData state;
|
|
||||||
emuInstance->nds->RTC.GetState(state);
|
|
||||||
Platform::FileWrite(&state, sizeof(state), 1, file);
|
|
||||||
Platform::CloseFile(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
NDS::Current = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::sendMessage(Message msg)
|
void EmuThread::sendMessage(Message msg)
|
||||||
|
@ -469,7 +479,8 @@ void EmuThread::waitMessage(int num)
|
||||||
void EmuThread::waitAllMessages()
|
void EmuThread::waitAllMessages()
|
||||||
{
|
{
|
||||||
if (QThread::currentThread() == this) return;
|
if (QThread::currentThread() == this) return;
|
||||||
msgSemaphore.acquire(msgSemaphore.available());
|
while (!msgQueue.empty())
|
||||||
|
msgSemaphore.acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::handleMessages()
|
void EmuThread::handleMessages()
|
||||||
|
@ -485,6 +496,7 @@ void EmuThread::handleMessages()
|
||||||
emuPauseStack = emuPauseStackRunning;
|
emuPauseStack = emuPauseStackRunning;
|
||||||
|
|
||||||
emuInstance->audioDisable();
|
emuInstance->audioDisable();
|
||||||
|
MPInterface::Get().End(emuInstance->instanceID);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case msg_EmuRun:
|
case msg_EmuRun:
|
||||||
|
@ -528,7 +540,8 @@ void EmuThread::handleMessages()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case msg_EmuStop:
|
case msg_EmuStop:
|
||||||
if (msg.stopExternal) emuInstance->nds->Stop();
|
if (msg.param.value<bool>())
|
||||||
|
emuInstance->nds->Stop();
|
||||||
emuStatus = emuStatus_Paused;
|
emuStatus = emuStatus_Paused;
|
||||||
emuActive = false;
|
emuActive = false;
|
||||||
|
|
||||||
|
@ -553,14 +566,102 @@ void EmuThread::handleMessages()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case msg_InitGL:
|
case msg_InitGL:
|
||||||
emuInstance->initOpenGL();
|
emuInstance->initOpenGL(msg.param.value<int>());
|
||||||
useOpenGL = true;
|
useOpenGL = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case msg_DeInitGL:
|
case msg_DeInitGL:
|
||||||
emuInstance->deinitOpenGL();
|
emuInstance->deinitOpenGL(msg.param.value<int>());
|
||||||
|
if (msg.param.value<int>() == 0)
|
||||||
useOpenGL = false;
|
useOpenGL = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case msg_BootROM:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->loadROM(msg.param.value<QStringList>(), true, msgError))
|
||||||
|
break;
|
||||||
|
|
||||||
|
assert(emuInstance->nds != nullptr);
|
||||||
|
emuInstance->nds->Start();
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_BootFirmware:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->bootToMenu(msgError))
|
||||||
|
break;
|
||||||
|
|
||||||
|
assert(emuInstance->nds != nullptr);
|
||||||
|
emuInstance->nds->Start();
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_InsertCart:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->loadROM(msg.param.value<QStringList>(), false, msgError))
|
||||||
|
break;
|
||||||
|
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_EjectCart:
|
||||||
|
emuInstance->ejectCart();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_InsertGBACart:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->loadGBAROM(msg.param.value<QStringList>(), msgError))
|
||||||
|
break;
|
||||||
|
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_InsertGBAAddon:
|
||||||
|
msgResult = 0;
|
||||||
|
emuInstance->loadGBAAddon(msg.param.value<int>(), msgError);
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_EjectGBACart:
|
||||||
|
emuInstance->ejectGBACart();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_SaveState:
|
||||||
|
msgResult = emuInstance->saveState(msg.param.value<QString>().toStdString());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_LoadState:
|
||||||
|
msgResult = emuInstance->loadState(msg.param.value<QString>().toStdString());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_UndoStateLoad:
|
||||||
|
emuInstance->undoStateLoad();
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_ImportSavefile:
|
||||||
|
{
|
||||||
|
msgResult = 0;
|
||||||
|
auto f = Platform::OpenFile(msg.param.value<QString>().toStdString(), Platform::FileMode::Read);
|
||||||
|
if (!f) break;
|
||||||
|
|
||||||
|
u32 len = FileLength(f);
|
||||||
|
|
||||||
|
std::unique_ptr<u8[]> data = std::make_unique<u8[]>(len);
|
||||||
|
Platform::FileRewind(f);
|
||||||
|
Platform::FileRead(data.get(), len, 1, f);
|
||||||
|
|
||||||
|
assert(emuInstance->nds != nullptr);
|
||||||
|
emuInstance->nds->SetNDSSave(data.get(), len);
|
||||||
|
|
||||||
|
CloseFile(f);
|
||||||
|
msgResult = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_EnableCheats:
|
||||||
|
emuInstance->enableCheats(msg.param.value<bool>());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
msgSemaphore.release();
|
msgSemaphore.release();
|
||||||
|
@ -573,15 +674,15 @@ void EmuThread::changeWindowTitle(char* title)
|
||||||
emit windowTitleChange(QString(title));
|
emit windowTitleChange(QString(title));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::initContext()
|
void EmuThread::initContext(int win)
|
||||||
{
|
{
|
||||||
sendMessage(msg_InitGL);
|
sendMessage({.type = msg_InitGL, .param = win});
|
||||||
waitMessage();
|
waitMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::deinitContext()
|
void EmuThread::deinitContext(int win)
|
||||||
{
|
{
|
||||||
sendMessage(msg_DeInitGL);
|
sendMessage({.type = msg_DeInitGL, .param = win});
|
||||||
waitMessage();
|
waitMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -591,29 +692,35 @@ void EmuThread::emuRun()
|
||||||
waitMessage();
|
waitMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuPause()
|
void EmuThread::emuPause(bool broadcast)
|
||||||
{
|
{
|
||||||
sendMessage(msg_EmuPause);
|
sendMessage(msg_EmuPause);
|
||||||
waitMessage();
|
waitMessage();
|
||||||
|
|
||||||
|
if (broadcast)
|
||||||
|
emuInstance->broadcastCommand(InstCmd_Pause);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuUnpause()
|
void EmuThread::emuUnpause(bool broadcast)
|
||||||
{
|
{
|
||||||
sendMessage(msg_EmuUnpause);
|
sendMessage(msg_EmuUnpause);
|
||||||
waitMessage();
|
waitMessage();
|
||||||
|
|
||||||
|
if (broadcast)
|
||||||
|
emuInstance->broadcastCommand(InstCmd_Unpause);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuTogglePause()
|
void EmuThread::emuTogglePause(bool broadcast)
|
||||||
{
|
{
|
||||||
if (emuStatus == emuStatus_Paused)
|
if (emuStatus == emuStatus_Paused)
|
||||||
emuUnpause();
|
emuUnpause(broadcast);
|
||||||
else
|
else
|
||||||
emuPause();
|
emuPause(broadcast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuThread::emuStop(bool external)
|
void EmuThread::emuStop(bool external)
|
||||||
{
|
{
|
||||||
sendMessage({.type = msg_EmuStop, .stopExternal = external});
|
sendMessage({.type = msg_EmuStop, .param = external});
|
||||||
waitMessage();
|
waitMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,11 +754,101 @@ bool EmuThread::emuIsActive()
|
||||||
return emuActive;
|
return emuActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int EmuThread::bootROM(const QStringList& filename, QString& errorstr)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_BootROM, .param = filename});
|
||||||
|
waitMessage();
|
||||||
|
if (!msgResult)
|
||||||
|
{
|
||||||
|
errorstr = msgError;
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(msg_EmuRun);
|
||||||
|
waitMessage();
|
||||||
|
errorstr = "";
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::bootFirmware(QString& errorstr)
|
||||||
|
{
|
||||||
|
sendMessage(msg_BootFirmware);
|
||||||
|
waitMessage();
|
||||||
|
if (!msgResult)
|
||||||
|
{
|
||||||
|
errorstr = msgError;
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(msg_EmuRun);
|
||||||
|
waitMessage();
|
||||||
|
errorstr = "";
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::insertCart(const QStringList& filename, bool gba, QString& errorstr)
|
||||||
|
{
|
||||||
|
MessageType msgtype = gba ? msg_InsertGBACart : msg_InsertCart;
|
||||||
|
|
||||||
|
sendMessage({.type = msgtype, .param = filename});
|
||||||
|
waitMessage();
|
||||||
|
errorstr = msgResult ? "" : msgError;
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuThread::ejectCart(bool gba)
|
||||||
|
{
|
||||||
|
sendMessage(gba ? msg_EjectGBACart : msg_EjectCart);
|
||||||
|
waitMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::insertGBAAddon(int type, QString& errorstr)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_InsertGBAAddon, .param = type});
|
||||||
|
waitMessage();
|
||||||
|
errorstr = msgResult ? "" : msgError;
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::saveState(const QString& filename)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_SaveState, .param = filename});
|
||||||
|
waitMessage();
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::loadState(const QString& filename)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_LoadState, .param = filename});
|
||||||
|
waitMessage();
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::undoStateLoad()
|
||||||
|
{
|
||||||
|
sendMessage(msg_UndoStateLoad);
|
||||||
|
waitMessage();
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::importSavefile(const QString& filename)
|
||||||
|
{
|
||||||
|
sendMessage(msg_EmuReset);
|
||||||
|
sendMessage({.type = msg_ImportSavefile, .param = filename});
|
||||||
|
waitMessage(2);
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuThread::enableCheats(bool enable)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_EnableCheats, .param = enable});
|
||||||
|
waitMessage();
|
||||||
|
}
|
||||||
|
|
||||||
void EmuThread::updateRenderer()
|
void EmuThread::updateRenderer()
|
||||||
{
|
{
|
||||||
if (videoRenderer != lastVideoRenderer)
|
if (videoRenderer != lastVideoRenderer)
|
||||||
{
|
{
|
||||||
printf("creating renderer %d\n", videoRenderer);
|
|
||||||
switch (videoRenderer)
|
switch (videoRenderer)
|
||||||
{
|
{
|
||||||
case renderer3D_Software:
|
case renderer3D_Software:
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QSemaphore>
|
#include <QSemaphore>
|
||||||
#include <QQueue>
|
#include <QQueue>
|
||||||
|
#include <QVariant>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
@ -32,9 +33,6 @@
|
||||||
#include "NDSCart.h"
|
#include "NDSCart.h"
|
||||||
#include "GBACart.h"
|
#include "GBACart.h"
|
||||||
|
|
||||||
using Keep = std::monostate;
|
|
||||||
using UpdateConsoleNDSArgs = std::variant<Keep, std::unique_ptr<melonDS::NDSCart::CartCommon>>;
|
|
||||||
using UpdateConsoleGBAArgs = std::variant<Keep, std::unique_ptr<melonDS::GBACart::CartCommon>>;
|
|
||||||
namespace melonDS
|
namespace melonDS
|
||||||
{
|
{
|
||||||
class NDS;
|
class NDS;
|
||||||
|
@ -68,15 +66,28 @@ public:
|
||||||
|
|
||||||
msg_InitGL,
|
msg_InitGL,
|
||||||
msg_DeInitGL,
|
msg_DeInitGL,
|
||||||
|
|
||||||
|
msg_BootROM,
|
||||||
|
msg_BootFirmware,
|
||||||
|
msg_InsertCart,
|
||||||
|
msg_EjectCart,
|
||||||
|
msg_InsertGBACart,
|
||||||
|
msg_InsertGBAAddon,
|
||||||
|
msg_EjectGBACart,
|
||||||
|
|
||||||
|
msg_LoadState,
|
||||||
|
msg_SaveState,
|
||||||
|
msg_UndoStateLoad,
|
||||||
|
|
||||||
|
msg_ImportSavefile,
|
||||||
|
|
||||||
|
msg_EnableCheats,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Message
|
struct Message
|
||||||
{
|
{
|
||||||
MessageType type;
|
MessageType type;
|
||||||
union
|
QVariant param;
|
||||||
{
|
|
||||||
bool stopExternal;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void sendMessage(Message msg);
|
void sendMessage(Message msg);
|
||||||
|
@ -92,23 +103,38 @@ public:
|
||||||
|
|
||||||
// to be called from the UI thread
|
// to be called from the UI thread
|
||||||
void emuRun();
|
void emuRun();
|
||||||
void emuPause();
|
void emuPause(bool broadcast = true);
|
||||||
void emuUnpause();
|
void emuUnpause(bool broadcast = true);
|
||||||
void emuTogglePause();
|
void emuTogglePause(bool broadcast = true);
|
||||||
void emuStop(bool external);
|
void emuStop(bool external);
|
||||||
void emuExit();
|
void emuExit();
|
||||||
void emuFrameStep();
|
void emuFrameStep();
|
||||||
void emuReset();
|
void emuReset();
|
||||||
|
|
||||||
|
int bootROM(const QStringList& filename, QString& errorstr);
|
||||||
|
int bootFirmware(QString& errorstr);
|
||||||
|
int insertCart(const QStringList& filename, bool gba, QString& errorstr);
|
||||||
|
void ejectCart(bool gba);
|
||||||
|
int insertGBAAddon(int type, QString& errorstr);
|
||||||
|
|
||||||
|
int saveState(const QString& filename);
|
||||||
|
int loadState(const QString& filename);
|
||||||
|
int undoStateLoad();
|
||||||
|
|
||||||
|
int importSavefile(const QString& filename);
|
||||||
|
|
||||||
|
void enableCheats(bool enable);
|
||||||
|
|
||||||
bool emuIsRunning();
|
bool emuIsRunning();
|
||||||
bool emuIsActive();
|
bool emuIsActive();
|
||||||
|
|
||||||
void initContext();
|
void initContext(int win);
|
||||||
void deinitContext();
|
void deinitContext(int win);
|
||||||
void updateVideoSettings() { videoSettingsDirty = true; }
|
void updateVideoSettings() { videoSettingsDirty = true; }
|
||||||
|
void updateVideoRenderer() { videoSettingsDirty = true; lastVideoRenderer = -1; }
|
||||||
|
|
||||||
int FrontBuffer = 0;
|
int frontBuffer = 0;
|
||||||
QMutex FrontBufferLock;
|
QMutex frontBufferLock;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void windowUpdate();
|
void windowUpdate();
|
||||||
|
@ -152,6 +178,9 @@ private:
|
||||||
constexpr static int emuPauseStackPauseThreshold = 1;
|
constexpr static int emuPauseStackPauseThreshold = 1;
|
||||||
int emuPauseStack;
|
int emuPauseStack;
|
||||||
|
|
||||||
|
int msgResult = 0;
|
||||||
|
QString msgError;
|
||||||
|
|
||||||
QMutex msgMutex;
|
QMutex msgMutex;
|
||||||
QSemaphore msgSemaphore;
|
QSemaphore msgSemaphore;
|
||||||
QQueue<Message> msgQueue;
|
QQueue<Message> msgQueue;
|
||||||
|
|
|
@ -132,6 +132,13 @@ bool FirmwareSettingsDialog::verifyMAC()
|
||||||
|
|
||||||
void FirmwareSettingsDialog::done(int r)
|
void FirmwareSettingsDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
needsReset = false;
|
needsReset = false;
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
|
|
|
@ -49,6 +49,9 @@ static constexpr std::initializer_list<int> hk_general =
|
||||||
HK_FrameStep,
|
HK_FrameStep,
|
||||||
HK_FastForward,
|
HK_FastForward,
|
||||||
HK_FastForwardToggle,
|
HK_FastForwardToggle,
|
||||||
|
HK_SlowMo,
|
||||||
|
HK_SlowMoToggle,
|
||||||
|
HK_FrameLimitToggle,
|
||||||
HK_FullscreenToggle,
|
HK_FullscreenToggle,
|
||||||
HK_Lid,
|
HK_Lid,
|
||||||
HK_Mic,
|
HK_Mic,
|
||||||
|
@ -65,6 +68,9 @@ static constexpr std::initializer_list<const char*> hk_general_labels =
|
||||||
"Reset",
|
"Reset",
|
||||||
"Frame step",
|
"Frame step",
|
||||||
"Fast forward",
|
"Fast forward",
|
||||||
|
"Toggle fast forward",
|
||||||
|
"Slow mo",
|
||||||
|
"Toggle slow mo",
|
||||||
"Toggle FPS limit",
|
"Toggle FPS limit",
|
||||||
"Toggle fullscreen",
|
"Toggle fullscreen",
|
||||||
"Close/open lid",
|
"Close/open lid",
|
||||||
|
|
|
@ -35,11 +35,13 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare
|
||||||
|
|
||||||
auto& cfg = emuInstance->getGlobalConfig();
|
auto& cfg = emuInstance->getGlobalConfig();
|
||||||
|
|
||||||
ui->cbMouseHide->setChecked(cfg.GetBool("MouseHide"));
|
ui->cbMouseHide->setChecked(cfg.GetBool("Mouse.Hide"));
|
||||||
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
|
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
|
||||||
ui->spinMouseHideSeconds->setValue(cfg.GetInt("MouseHideSeconds"));
|
ui->spinMouseHideSeconds->setValue(cfg.GetInt("Mouse.HideSeconds"));
|
||||||
ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus"));
|
ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus"));
|
||||||
ui->spinMaxFPS->setValue(cfg.GetInt("MaxFPS"));
|
ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS"));
|
||||||
|
ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS"));
|
||||||
|
ui->spinSlow->setValue(cfg.GetDouble("SlowmoFPS"));
|
||||||
|
|
||||||
const QList<QString> themeKeys = QStyleFactory::keys();
|
const QList<QString> themeKeys = QStyleFactory::keys();
|
||||||
const QString currentTheme = qApp->style()->objectName();
|
const QString currentTheme = qApp->style()->objectName();
|
||||||
|
@ -65,16 +67,69 @@ void InterfaceSettingsDialog::on_cbMouseHide_clicked()
|
||||||
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
|
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pbClean_clicked()
|
||||||
|
{
|
||||||
|
ui->spinTargetFPS->setValue(60.0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pbAccurate_clicked()
|
||||||
|
{
|
||||||
|
ui->spinTargetFPS->setValue(59.8261);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pb2x_clicked()
|
||||||
|
{
|
||||||
|
ui->spinFFW->setValue(ui->spinTargetFPS->value() * 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pb3x_clicked()
|
||||||
|
{
|
||||||
|
ui->spinFFW->setValue(ui->spinTargetFPS->value() * 3.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pbMAX_clicked()
|
||||||
|
{
|
||||||
|
ui->spinFFW->setValue(1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pbHalf_clicked()
|
||||||
|
{
|
||||||
|
ui->spinSlow->setValue(ui->spinTargetFPS->value() / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceSettingsDialog::on_pbQuarter_clicked()
|
||||||
|
{
|
||||||
|
ui->spinSlow->setValue(ui->spinTargetFPS->value() / 4.0);
|
||||||
|
}
|
||||||
|
|
||||||
void InterfaceSettingsDialog::done(int r)
|
void InterfaceSettingsDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
auto& cfg = emuInstance->getGlobalConfig();
|
auto& cfg = emuInstance->getGlobalConfig();
|
||||||
|
|
||||||
cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked());
|
cfg.SetBool("Mouse.Hide", ui->cbMouseHide->isChecked());
|
||||||
cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value());
|
cfg.SetInt("Mouse.HideSeconds", ui->spinMouseHideSeconds->value());
|
||||||
cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked());
|
cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked());
|
||||||
cfg.SetInt("MaxFPS", ui->spinMaxFPS->value());
|
|
||||||
|
double val = ui->spinTargetFPS->value();
|
||||||
|
if (val == 0.0) cfg.SetDouble("TargetFPS", 0.0001);
|
||||||
|
else cfg.SetDouble("TargetFPS", val);
|
||||||
|
|
||||||
|
val = ui->spinFFW->value();
|
||||||
|
if (val == 0.0) cfg.SetDouble("FastForwardFPS", 0.0001);
|
||||||
|
else cfg.SetDouble("FastForwardFPS", val);
|
||||||
|
|
||||||
|
val = ui->spinSlow->value();
|
||||||
|
if (val == 0.0) cfg.SetDouble("SlowmoFPS", 0.0001);
|
||||||
|
else cfg.SetDouble("SlowmoFPS", val);
|
||||||
|
|
||||||
QString themeName = ui->cbxUITheme->currentData().toString();
|
QString themeName = ui->cbxUITheme->currentData().toString();
|
||||||
cfg.SetQString("UITheme", themeName);
|
cfg.SetQString("UITheme", themeName);
|
||||||
|
|
|
@ -60,6 +60,16 @@ private slots:
|
||||||
|
|
||||||
void on_cbMouseHide_clicked();
|
void on_cbMouseHide_clicked();
|
||||||
|
|
||||||
|
void on_pbClean_clicked();
|
||||||
|
void on_pbAccurate_clicked();
|
||||||
|
|
||||||
|
void on_pb2x_clicked();
|
||||||
|
void on_pb3x_clicked();
|
||||||
|
void on_pbMAX_clicked();
|
||||||
|
|
||||||
|
void on_pbHalf_clicked();
|
||||||
|
void on_pbQuarter_clicked();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::InterfaceSettingsDialog* ui;
|
Ui::InterfaceSettingsDialog* ui;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>337</width>
|
<width>389</width>
|
||||||
<height>275</height>
|
<height>356</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
|
@ -20,6 +20,9 @@
|
||||||
<string>Interface settings - melonDS</string>
|
<string>Interface settings - melonDS</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
|
||||||
|
<property name="sizeConstraint">
|
||||||
|
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="groupBox">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
@ -95,43 +98,220 @@
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Framerate </string>
|
<string>Framerate </string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<property name="horizontalSpacing">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<property name="verticalSpacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Target FPS</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_3">
|
<widget class="QLabel" name="label_3">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Fast-forward limit</string>
|
<string>Fast-Forward</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="buddy">
|
<property name="buddy">
|
||||||
<cstring>spinMaxFPS</cstring>
|
<cstring>spinFFW</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="spinTargetFPS">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> FPS</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.000100000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>1000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>59.826099999999997</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pbQuarter">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>63</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1/4</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QSpinBox" name="spinMaxFPS">
|
<widget class="QPushButton" name="pbHalf">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>62</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>1/2</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="spinSlow">
|
||||||
<property name="suffix">
|
<property name="suffix">
|
||||||
<string> FPS</string>
|
<string> FPS</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
<property name="minimum">
|
<property name="minimum">
|
||||||
<number>60</number>
|
<double>0.000100000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
<property name="maximum">
|
||||||
<number>1000</number>
|
<double>1000.000000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
<property name="value">
|
<property name="value">
|
||||||
<number>1000</number>
|
<double>29.913000000000000</double>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QDoubleSpinBox" name="spinFFW">
|
||||||
|
<property name="suffix">
|
||||||
|
<string> FPS</string>
|
||||||
|
</property>
|
||||||
|
<property name="decimals">
|
||||||
|
<number>4</number>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<double>0.000100000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<double>1000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<double>1000.000000000000000</double>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="text">
|
||||||
|
<string>Slow-Mo</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pbAccurate">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>63</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Accurate</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pbClean">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>62</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Clean</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pb2x">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>41</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>2x</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pb3x">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>41</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>3x</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="pbMAX">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>41</width>
|
||||||
|
<height>16777215</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>MAX</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -65,6 +65,12 @@ LANStartHostDialog::~LANStartHostDialog()
|
||||||
|
|
||||||
void LANStartHostDialog::done(int r)
|
void LANStartHostDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
if (ui->txtPlayerName->text().trimmed().isEmpty())
|
if (ui->txtPlayerName->text().trimmed().isEmpty())
|
||||||
|
@ -186,6 +192,12 @@ void LANStartClientDialog::onDirectConnect()
|
||||||
|
|
||||||
void LANStartClientDialog::done(int r)
|
void LANStartClientDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
if (ui->txtPlayerName->text().trimmed().isEmpty())
|
if (ui->txtPlayerName->text().trimmed().isEmpty())
|
||||||
|
@ -313,6 +325,12 @@ void LANDialog::on_btnLeaveGame_clicked()
|
||||||
|
|
||||||
void LANDialog::done(int r)
|
void LANDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool showwarning = true;
|
bool showwarning = true;
|
||||||
if (lan().GetNumPlayers() < 2)
|
if (lan().GetNumPlayers() < 2)
|
||||||
showwarning = false;
|
showwarning = false;
|
||||||
|
@ -385,6 +403,8 @@ void LANDialog::doUpdatePlayerList()
|
||||||
case LAN::Player_Disconnected:
|
case LAN::Player_Disconnected:
|
||||||
status = "Connection lost";
|
status = "Connection lost";
|
||||||
break;
|
break;
|
||||||
|
case LAN::Player_None:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
model->item(i, 2)->setText(status);
|
model->item(i, 2)->setText(status);
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void done(int r);
|
void done(int r) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::LANStartHostDialog* ui;
|
Ui::LANStartHostDialog* ui;
|
||||||
|
@ -76,7 +76,7 @@ private slots:
|
||||||
void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev);
|
void onGameSelectionChanged(const QItemSelection& cur, const QItemSelection& prev);
|
||||||
void on_tvAvailableGames_doubleClicked(QModelIndex index);
|
void on_tvAvailableGames_doubleClicked(QModelIndex index);
|
||||||
void onDirectConnect();
|
void onDirectConnect();
|
||||||
void done(int r);
|
void done(int r) override;
|
||||||
|
|
||||||
void doUpdateDiscoveryList();
|
void doUpdateDiscoveryList();
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ protected:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void on_btnLeaveGame_clicked();
|
void on_btnLeaveGame_clicked();
|
||||||
void done(int r);
|
void done(int r) override;
|
||||||
|
|
||||||
void doUpdatePlayerList();
|
void doUpdatePlayerList();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>547</width>
|
<width>547</width>
|
||||||
<height>409</height>
|
<height>407</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
|
@ -63,17 +63,24 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTreeView" name="tvAvailableGames">
|
<widget class="QTreeView" name="tvAvailableGames">
|
||||||
<property name="editTriggers">
|
<property name="editTriggers">
|
||||||
<set>QAbstractItemView::NoEditTriggers</set>
|
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>Warning: LAN requires low network latency to work.</p><p>Do not expect it to work through a VPN or any sort of tunnel.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>389</width>
|
<width>389</width>
|
||||||
<height>228</height>
|
<height>202</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetFixedSize</enum>
|
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QFormLayout" name="formLayout">
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
@ -45,15 +45,29 @@
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QSpinBox" name="sbNumPlayers"/>
|
<widget class="QSpinBox" name="sbNumPlayers"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="3" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string><html><head/><body><p>Warning: LAN requires low network latency to work.</p><p>Do not expect it to work through a VPN or any sort of tunnel.</p></body></html></string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="2">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Orientation::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="standardButtons">
|
<property name="standardButtons">
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -59,6 +59,13 @@ MPSettingsDialog::~MPSettingsDialog()
|
||||||
|
|
||||||
void MPSettingsDialog::done(int r)
|
void MPSettingsDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
auto& cfg = emuInstance->getGlobalConfig();
|
auto& cfg = emuInstance->getGlobalConfig();
|
||||||
|
|
|
@ -54,7 +54,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void done(int r);
|
void done(int r) override;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,12 @@ NetplayStartHostDialog::~NetplayStartHostDialog()
|
||||||
|
|
||||||
void NetplayStartHostDialog::done(int r)
|
void NetplayStartHostDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
std::string player = ui->txtPlayerName->text().toStdString();
|
std::string player = ui->txtPlayerName->text().toStdString();
|
||||||
|
@ -94,6 +100,12 @@ NetplayStartClientDialog::~NetplayStartClientDialog()
|
||||||
|
|
||||||
void NetplayStartClientDialog::done(int r)
|
void NetplayStartClientDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
{
|
{
|
||||||
std::string player = ui->txtPlayerName->text().toStdString();
|
std::string player = ui->txtPlayerName->text().toStdString();
|
||||||
|
@ -175,7 +187,7 @@ void NetplayDialog::doUpdatePlayerList(Netplay::Player* players, int num)
|
||||||
|
|
||||||
char ip[32];
|
char ip[32];
|
||||||
u32 addr = player->Address;
|
u32 addr = player->Address;
|
||||||
sprintf(ip, "%d.%d.%d.%d", addr&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF, addr>>24);
|
snprintf(ip, sizeof(ip), "%d.%d.%d.%d", addr&0xFF, (addr>>8)&0xFF, (addr>>16)&0xFF, addr>>24);
|
||||||
model->setItem(i, 4, new QStandardItem(ip));
|
model->setItem(i, 4, new QStandardItem(ip));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue