mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2024-11-14 13:27:41 -07:00
Merge remote-tracking branch 'upstream/master' into interpreter-fixes
This commit is contained in:
commit
3bd6274477
8
.github/workflows/build-macos.yml
vendored
8
.github/workflows/build-macos.yml
vendored
@ -9,6 +9,11 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-macos:
|
build-macos:
|
||||||
strategy:
|
strategy:
|
||||||
@ -28,12 +33,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: 10b7a178346f3f0abef60cecd5130e295afd8da4
|
||||||
- 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: |
|
||||||
|
9
.github/workflows/build-ubuntu.yml
vendored
9
.github/workflows/build-ubuntu.yml
vendored
@ -8,6 +8,11 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-x86_64:
|
build-x86_64:
|
||||||
name: x86_64
|
name: x86_64
|
||||||
@ -23,7 +28,7 @@ jobs:
|
|||||||
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
|
||||||
@ -74,7 +79,7 @@ jobs:
|
|||||||
-DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \
|
-DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \
|
||||||
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
|
-DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \
|
||||||
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
|
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \
|
||||||
-DUSE_QT6=ON
|
-DMELONDS_EMBED_BUILD_INFO=ON
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
9
.github/workflows/build-windows.yml
vendored
9
.github/workflows/build-windows.yml
vendored
@ -9,6 +9,11 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
|
env:
|
||||||
|
MELONDS_GIT_BRANCH: ${{ github.ref }}
|
||||||
|
MELONDS_GIT_HASH: ${{ github.sha }}
|
||||||
|
MELONDS_BUILD_PROVIDER: GitHub Actions
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
@ -27,9 +32,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: 10b7a178346f3f0abef60cecd5130e295afd8da4
|
||||||
- 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
|
||||||
|
81
BUILD.md
Normal file
81
BUILD.md
Normal file
@ -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,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 <prefi>-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.
|
@ -27,6 +27,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_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||||
|
|
||||||
|
@ -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 2024.10.21
|
||||||
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)
|
||||||
|
8
cmake/Sanitizers.cmake
Normal file
8
cmake/Sanitizers.cmake
Normal file
@ -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": 1726560853,
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1725432240,
|
"lastModified": 1730531603,
|
||||||
"narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
|
"narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "ad416d066ca1222956472ab7d0555a6946746a80",
|
"rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
15
flake.nix
15
flake.nix
@ -12,13 +12,16 @@
|
|||||||
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 = "0.9.5-${shortRevision}";
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
@ -46,8 +49,13 @@
|
|||||||
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
|
||||||
|
@ -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">melon.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -110,6 +110,7 @@ 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, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0),
|
||||||
|
BreakOnStartup(gdb ? (num ? gdb->ARM7BreakOnStartup : gdb->ARM9BreakOnStartup) : false),
|
||||||
#endif
|
#endif
|
||||||
Num(num), // well uh
|
Num(num), // well uh
|
||||||
NDS(nds)
|
NDS(nds)
|
||||||
|
@ -543,8 +543,8 @@ void A_LDM(ARM* cpu)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 pc;
|
u32 pc = 0;
|
||||||
if ((cpu->CurInstr & (1<<15)))
|
if (cpu->CurInstr & (1<<15))
|
||||||
{
|
{
|
||||||
if (preinc) base += 4;
|
if (preinc) base += 4;
|
||||||
dabort |= !(first ? cpu->DataRead32 (base, &pc)
|
dabort |= !(first ? cpu->DataRead32 (base, &pc)
|
||||||
@ -556,13 +556,12 @@ void A_LDM(ARM* cpu)
|
|||||||
pc &= ~0x1;
|
pc &= ~0x1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch back to previous regs
|
|
||||||
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
|
||||||
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
|
|
||||||
|
|
||||||
// handle data aborts
|
// handle data aborts
|
||||||
if (dabort) [[unlikely]]
|
if (dabort) [[unlikely]]
|
||||||
{
|
{
|
||||||
|
if ((cpu->CurInstr & (1<<22)) && !(cpu->CurInstr & (1<<15)))
|
||||||
|
cpu->UpdateMode((cpu->CPSR&~0x1F)|0x10, cpu->CPSR, true);
|
||||||
|
|
||||||
cpu->AddCycles_CDI();
|
cpu->AddCycles_CDI();
|
||||||
((ARMv5*)cpu)->DataAbort();
|
((ARMv5*)cpu)->DataAbort();
|
||||||
return;
|
return;
|
||||||
@ -587,7 +586,10 @@ void A_LDM(ARM* cpu)
|
|||||||
else
|
else
|
||||||
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);
|
||||||
|
|
||||||
// jump if pc got written
|
// jump if pc got written
|
||||||
if (cpu->CurInstr & (1<<15))
|
if (cpu->CurInstr & (1<<15))
|
||||||
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
|
cpu->JumpTo(pc, cpu->CurInstr & (1<<22));
|
||||||
|
@ -128,6 +128,17 @@ if (ENABLE_JIT)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
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")
|
||||||
|
@ -1445,7 +1445,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 +1507,7 @@ void DSi_NWifi::CheckRX()
|
|||||||
Mailbox[8].Write(LANBuffer[14+i]);
|
Mailbox[8].Write(LANBuffer[14+i]);
|
||||||
|
|
||||||
DrainRXBuffer();
|
DrainRXBuffer();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,6 +582,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
|
||||||
|
@ -158,6 +158,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;
|
||||||
|
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;
|
||||||
|
@ -193,10 +193,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;
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x1F)<<1), gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + ((pixel&0x1F)<<1));
|
||||||
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
|
*alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -204,12 +204,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;
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + (pixel<<1));
|
||||||
*alpha = (pixel==0) ? alpha0 : 31;
|
*alpha = (pixel==0) ? alpha0 : 31;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -217,12 +217,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;
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + (pixel<<1));
|
||||||
*alpha = (pixel==0) ? alpha0 : 31;
|
*alpha = (pixel==0) ? alpha0 : 31;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -230,10 +230,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;
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + (pixel<<1), gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + (pixel<<1));
|
||||||
*alpha = (pixel==0) ? alpha0 : 31;
|
*alpha = (pixel==0) ? alpha0 : 31;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -253,31 +253,31 @@ 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;
|
||||||
|
|
||||||
switch (val & 0x3)
|
switch (val & 0x3)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset, gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset);
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 2, gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 2);
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
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;
|
||||||
@ -294,8 +294,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;
|
||||||
@ -311,20 +311,20 @@ void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s
|
|||||||
*color = r | g | b;
|
*color = r | g | b;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 4, gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 4);
|
||||||
*alpha = 31;
|
*alpha = 31;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
if ((palinfo >> 14) == 2)
|
if ((palinfo >> 14) == 2)
|
||||||
{
|
{
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + paloffset + 6, gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + paloffset + 6);
|
||||||
*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;
|
||||||
@ -353,10 +353,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;
|
||||||
*color = ReadVRAM_TexPal<u16>(texpal + ((pixel&0x7)<<1), gpu);
|
*color = gpu.ReadVRAMFlat_TexPal<u16>(texpal + ((pixel&0x7)<<1));
|
||||||
*alpha = (pixel >> 3);
|
*alpha = (pixel >> 3);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -364,7 +364,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);
|
||||||
*color = ReadVRAM_Texture<u16>(vramaddr, gpu);
|
*color = gpu.ReadVRAMFlat_Texture<u16>(vramaddr);
|
||||||
*alpha = (*color & 0x8000) ? 31 : 0;
|
*alpha = (*color & 0x8000) ? 31 : 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1659,8 +1659,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,40 +110,21 @@ 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;
|
goto invalidate;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
goto invalidate;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it++;
|
it++;
|
||||||
@ -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];
|
||||||
|
@ -40,7 +40,7 @@ RTC::RTC(melonDS::NDS& nds) : NDS(nds)
|
|||||||
|
|
||||||
// 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()
|
||||||
@ -943,4 +943,4 @@ void RTC::Write(u16 val, bool byte)
|
|||||||
IO = (IO & 0x0001) | (val & 0xFFFE);
|
IO = (IO & 0x0001) | (val & 0xFFFE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,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
|
||||||
|
59
src/frontend/qt_sdl/AboutDialog.cpp
Normal file
59
src/frontend/qt_sdl/AboutDialog.cpp
Normal file
@ -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"));;
|
||||||
|
}
|
50
src/frontend/qt_sdl/AboutDialog.h
Normal file
50
src/frontend/qt_sdl/AboutDialog.h
Normal file
@ -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
|
300
src/frontend/qt_sdl/AboutDialog.ui
Normal file
300
src/frontend/qt_sdl/AboutDialog.ui
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
<?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>
|
||||||
|
</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)
|
||||||
@ -119,6 +118,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)
|
||||||
|
@ -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();
|
||||||
|
@ -80,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}},
|
||||||
@ -99,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},
|
||||||
|
@ -93,25 +93,25 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
|
|||||||
if (val == 0.0)
|
if (val == 0.0)
|
||||||
{
|
{
|
||||||
Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n");
|
Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n");
|
||||||
targetFPS = 1.0 / 60.0;
|
targetFPS = 60.0;
|
||||||
}
|
}
|
||||||
else targetFPS = 1.0 / val;
|
else targetFPS = val;
|
||||||
|
|
||||||
val = globalCfg.GetDouble("FastForwardFPS");
|
val = globalCfg.GetDouble("FastForwardFPS");
|
||||||
if (val == 0.0)
|
if (val == 0.0)
|
||||||
{
|
{
|
||||||
Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n");
|
Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n");
|
||||||
fastForwardFPS = 1.0 / 60.0;
|
fastForwardFPS = 60.0;
|
||||||
}
|
}
|
||||||
else fastForwardFPS = 1.0 / val;
|
else fastForwardFPS = val;
|
||||||
|
|
||||||
val = globalCfg.GetDouble("SlowmoFPS");
|
val = globalCfg.GetDouble("SlowmoFPS");
|
||||||
if (val == 0.0)
|
if (val == 0.0)
|
||||||
{
|
{
|
||||||
Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n");
|
Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n");
|
||||||
slowmoFPS = 1.0 / 60.0;
|
slowmoFPS = 60.0;
|
||||||
}
|
}
|
||||||
else slowmoFPS = 1.0 / val;
|
else slowmoFPS = val;
|
||||||
|
|
||||||
doAudioSync = globalCfg.GetBool("AudioSync");
|
doAudioSync = globalCfg.GetBool("AudioSync");
|
||||||
|
|
||||||
@ -136,7 +136,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()
|
||||||
@ -144,8 +152,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;
|
||||||
@ -154,6 +160,13 @@ EmuInstance::~EmuInstance()
|
|||||||
|
|
||||||
audioDeInit();
|
audioDeInit();
|
||||||
inputDeInit();
|
inputDeInit();
|
||||||
|
|
||||||
|
NDS::Current = nullptr;
|
||||||
|
if (nds)
|
||||||
|
{
|
||||||
|
saveRTCData();
|
||||||
|
delete nds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -166,7 +179,7 @@ std::string EmuInstance::instanceFileSuffix()
|
|||||||
return suffix;
|
return suffix;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::createWindow()
|
void EmuInstance::createWindow(int id)
|
||||||
{
|
{
|
||||||
if (numWindows >= kMaxWindows)
|
if (numWindows >= kMaxWindows)
|
||||||
{
|
{
|
||||||
@ -174,16 +187,20 @@ void EmuInstance::createWindow()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int id = -1;
|
if (id == -1)
|
||||||
for (int i = 0; i < kMaxWindows; i++)
|
|
||||||
{
|
{
|
||||||
if (windowList[i]) continue;
|
for (int i = 0; i < kMaxWindows; i++)
|
||||||
id = i;
|
{
|
||||||
break;
|
if (windowList[i]) continue;
|
||||||
|
id = i;
|
||||||
|
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;
|
||||||
@ -192,6 +209,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)
|
||||||
@ -201,12 +228,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);
|
||||||
|
|
||||||
@ -219,11 +242,20 @@ void EmuInstance::deleteWindow(int id, bool close)
|
|||||||
if (close)
|
if (close)
|
||||||
win->close();
|
win->close();
|
||||||
|
|
||||||
if ((!mainWindow) && (!deleting))
|
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()
|
||||||
@ -232,6 +264,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, ...)
|
||||||
{
|
{
|
||||||
@ -285,24 +368,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)
|
||||||
@ -1080,7 +1157,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();
|
||||||
@ -1105,6 +1182,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();
|
||||||
@ -1116,6 +1217,9 @@ void EmuInstance::setDateTime()
|
|||||||
|
|
||||||
bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) noexcept
|
bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGBAArgs&& _gbaargs) 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 wnat 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;
|
||||||
@ -1149,8 +1253,6 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int consoletype = globalCfg.GetInt("Emu.ConsoleType");
|
|
||||||
|
|
||||||
auto arm9bios = loadARM9BIOS();
|
auto arm9bios = loadARM9BIOS();
|
||||||
if (!arm9bios)
|
if (!arm9bios)
|
||||||
return false;
|
return false;
|
||||||
@ -1159,7 +1261,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;
|
||||||
|
|
||||||
@ -1203,7 +1305,7 @@ 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;
|
ndsargs.GBAROM = nullptr;
|
||||||
|
|
||||||
@ -1234,19 +1336,25 @@ 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;
|
NDS::Current = nullptr;
|
||||||
if (nds) delete nds;
|
if (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::Current = nds;
|
||||||
nds->Reset();
|
nds->Reset();
|
||||||
|
loadRTCData();
|
||||||
|
//emuThread->updateVideoRenderer(); // not actually needed?
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1260,7 +1368,7 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
|||||||
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;
|
||||||
@ -1276,14 +1384,13 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
|||||||
dsi->EjectGBACart();
|
dsi->EjectGBACart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
renderLock.unlock();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmuInstance::reset()
|
void EmuInstance::reset()
|
||||||
{
|
{
|
||||||
consoleType = globalCfg.GetInt("Emu.ConsoleType");
|
|
||||||
|
|
||||||
updateConsole(Keep {}, Keep {});
|
updateConsole(Keep {}, Keep {});
|
||||||
|
|
||||||
if (consoleType == 1) ejectGBACart();
|
if (consoleType == 1) ejectGBACart();
|
||||||
@ -1973,25 +2080,36 @@ 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
|
||||||
|
|
||||||
int maxlen = 32;
|
int maxlen = 32;
|
||||||
if (ret.length() > maxlen)
|
if (ret.length() > maxlen)
|
||||||
ret = ret.left(maxlen-6) + "..." + ret.right(3);
|
ret = ret.left(maxlen-6) + "..." + ret.right(3);
|
||||||
|
|
||||||
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
|
||||||
{
|
{
|
||||||
@ -86,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();
|
||||||
|
|
||||||
@ -103,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();
|
||||||
@ -139,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);
|
||||||
@ -168,7 +181,6 @@ 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();
|
||||||
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);
|
||||||
@ -186,6 +198,7 @@ private:
|
|||||||
void loadGBAAddon(int type);
|
void loadGBAAddon(int type);
|
||||||
void ejectGBACart();
|
void ejectGBACart();
|
||||||
bool gbaCartInserted();
|
bool gbaCartInserted();
|
||||||
|
QString gbaAddonName(int addon);
|
||||||
QString gbaCartLabel();
|
QString gbaCartLabel();
|
||||||
|
|
||||||
void audioInit();
|
void audioInit();
|
||||||
@ -222,6 +235,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;
|
||||||
@ -281,8 +298,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;
|
||||||
@ -291,6 +309,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;
|
||||||
@ -316,6 +336,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;
|
||||||
};
|
};
|
||||||
|
@ -107,8 +107,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 +125,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 +142,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,6 +284,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 +294,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 +307,35 @@ 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 < kFrameLen)
|
||||||
|
{
|
||||||
|
for (int i = len; i < kFrameLen; i++)
|
||||||
|
tmp[i] = tmp[len-1];
|
||||||
|
}
|
||||||
|
nds->MicInputFrame(tmp, 735);
|
||||||
|
|
||||||
|
micExtBufferCount -= len;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -317,19 +349,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 +436,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 +462,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()
|
||||||
|
@ -74,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;
|
||||||
@ -353,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;
|
||||||
|
}
|
||||||
|
@ -215,6 +215,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)
|
||||||
|
@ -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(nullptr, nullptr);
|
||||||
// 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,15 +144,6 @@ 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 fastforward = false;
|
||||||
@ -234,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);
|
||||||
@ -249,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();
|
||||||
@ -317,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +366,7 @@ void EmuThread::run()
|
|||||||
|
|
||||||
if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS;
|
if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS;
|
||||||
else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS;
|
else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS;
|
||||||
else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1.0 / 1000.0;
|
else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1000.0;
|
||||||
else emuInstance->curFPS = emuInstance->targetFPS;
|
else emuInstance->curFPS = emuInstance->targetFPS;
|
||||||
|
|
||||||
if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1)
|
if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1)
|
||||||
@ -381,16 +385,18 @@ void EmuThread::run()
|
|||||||
if (emuInstance->doAudioSync && !(fastforward || slowmo))
|
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;
|
||||||
|
|
||||||
{
|
{
|
||||||
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||||
|
|
||||||
frameLimitError += emuInstance->curFPS - (curtime - lastTime);
|
frameLimitError += frametimeStep - (curtime - lastTime);
|
||||||
if (frameLimitError < -emuInstance->curFPS)
|
if (frameLimitError < -frametimeStep)
|
||||||
frameLimitError = -emuInstance->curFPS;
|
frameLimitError = -frametimeStep;
|
||||||
if (frameLimitError > emuInstance->curFPS)
|
if (frameLimitError > frametimeStep)
|
||||||
frameLimitError = emuInstance->curFPS;
|
frameLimitError = frametimeStep;
|
||||||
|
|
||||||
if (round(frameLimitError * 1000.0) > 0.0)
|
if (round(frameLimitError * 1000.0) > 0.0)
|
||||||
{
|
{
|
||||||
@ -418,10 +424,11 @@ void EmuThread::run()
|
|||||||
winUpdateFreq = fps / (u32)round(fpstarget);
|
winUpdateFreq = fps / (u32)round(fpstarget);
|
||||||
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);
|
sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, actualfps);
|
||||||
else
|
else
|
||||||
sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
|
sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
|
||||||
changeWindowTitle(melontitle);
|
changeWindowTitle(melontitle);
|
||||||
@ -453,17 +460,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)
|
||||||
@ -482,7 +478,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()
|
||||||
@ -498,6 +495,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:
|
||||||
@ -541,7 +539,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;
|
||||||
|
|
||||||
@ -566,13 +565,101 @@ 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>());
|
||||||
useOpenGL = false;
|
if (msg.param.value<int>() == 0)
|
||||||
|
useOpenGL = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_BootROM:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->loadROM(msg.param.value<QStringList>(), true))
|
||||||
|
break;
|
||||||
|
|
||||||
|
assert(emuInstance->nds != nullptr);
|
||||||
|
emuInstance->nds->Start();
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_BootFirmware:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->bootToMenu())
|
||||||
|
break;
|
||||||
|
|
||||||
|
assert(emuInstance->nds != nullptr);
|
||||||
|
emuInstance->nds->Start();
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_InsertCart:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->loadROM(msg.param.value<QStringList>(), false))
|
||||||
|
break;
|
||||||
|
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_EjectCart:
|
||||||
|
emuInstance->ejectCart();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_InsertGBACart:
|
||||||
|
msgResult = 0;
|
||||||
|
if (!emuInstance->loadGBAROM(msg.param.value<QStringList>()))
|
||||||
|
break;
|
||||||
|
|
||||||
|
msgResult = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case msg_InsertGBAAddon:
|
||||||
|
msgResult = 0;
|
||||||
|
emuInstance->loadGBAAddon(msg.param.value<int>());
|
||||||
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -586,15 +673,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,29 +691,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,11 +753,91 @@ bool EmuThread::emuIsActive()
|
|||||||
return emuActive;
|
return emuActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int EmuThread::bootROM(const QStringList& filename)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_BootROM, .param = filename});
|
||||||
|
waitMessage();
|
||||||
|
if (!msgResult)
|
||||||
|
return msgResult;
|
||||||
|
|
||||||
|
sendMessage(msg_EmuRun);
|
||||||
|
waitMessage();
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::bootFirmware()
|
||||||
|
{
|
||||||
|
sendMessage(msg_BootFirmware);
|
||||||
|
waitMessage();
|
||||||
|
if (!msgResult)
|
||||||
|
return msgResult;
|
||||||
|
|
||||||
|
sendMessage(msg_EmuRun);
|
||||||
|
waitMessage();
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::insertCart(const QStringList& filename, bool gba)
|
||||||
|
{
|
||||||
|
MessageType msgtype = gba ? msg_InsertGBACart : msg_InsertCart;
|
||||||
|
|
||||||
|
sendMessage({.type = msgtype, .param = filename});
|
||||||
|
waitMessage();
|
||||||
|
return msgResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmuThread::ejectCart(bool gba)
|
||||||
|
{
|
||||||
|
sendMessage(gba ? msg_EjectGBACart : msg_EjectCart);
|
||||||
|
waitMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
int EmuThread::insertGBAAddon(int type)
|
||||||
|
{
|
||||||
|
sendMessage({.type = msg_InsertGBAAddon, .param = type});
|
||||||
|
waitMessage();
|
||||||
|
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>
|
||||||
@ -68,15 +69,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 +106,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);
|
||||||
|
int bootFirmware();
|
||||||
|
int insertCart(const QStringList& filename, bool gba);
|
||||||
|
void ejectCart(bool gba);
|
||||||
|
int insertGBAAddon(int type);
|
||||||
|
|
||||||
|
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 +181,8 @@ private:
|
|||||||
constexpr static int emuPauseStackPauseThreshold = 1;
|
constexpr static int emuPauseStackPauseThreshold = 1;
|
||||||
int emuPauseStack;
|
int emuPauseStack;
|
||||||
|
|
||||||
|
int msgResult = 0;
|
||||||
|
|
||||||
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)
|
||||||
|
@ -104,6 +104,13 @@ void InterfaceSettingsDialog::on_pbQuarter_clicked()
|
|||||||
|
|
||||||
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();
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -26,6 +26,7 @@ uniform vec2 uScreenSize;
|
|||||||
uniform ivec2 uOSDPos;
|
uniform ivec2 uOSDPos;
|
||||||
uniform ivec2 uOSDSize;
|
uniform ivec2 uOSDSize;
|
||||||
uniform float uScaleFactor;
|
uniform float uScaleFactor;
|
||||||
|
uniform float uTexScale;
|
||||||
|
|
||||||
in vec2 vPosition;
|
in vec2 vPosition;
|
||||||
|
|
||||||
@ -35,8 +36,8 @@ void main()
|
|||||||
{
|
{
|
||||||
vec4 fpos;
|
vec4 fpos;
|
||||||
|
|
||||||
vec2 osdpos = (vPosition * vec2(uOSDSize * uScaleFactor));
|
vec2 osdpos = (vPosition * vec2(uOSDSize));
|
||||||
fTexcoord = osdpos;
|
fTexcoord = osdpos * uTexScale;
|
||||||
osdpos += uOSDPos;
|
osdpos += uOSDPos;
|
||||||
|
|
||||||
fpos.xy = ((osdpos * 2.0) / uScreenSize * uScaleFactor) - 1.0;
|
fpos.xy = ((osdpos * 2.0) / uScreenSize * uScaleFactor) - 1.0;
|
||||||
|
@ -72,6 +72,13 @@ PathSettingsDialog::~PathSettingsDialog()
|
|||||||
|
|
||||||
void PathSettingsDialog::done(int r)
|
void PathSettingsDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
needsReset = false;
|
needsReset = false;
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
|
@ -46,11 +46,13 @@
|
|||||||
#include "main_shaders.h"
|
#include "main_shaders.h"
|
||||||
#include "OSD_shaders.h"
|
#include "OSD_shaders.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
using namespace melonDS;
|
using namespace melonDS;
|
||||||
|
|
||||||
|
|
||||||
const u32 kOSDMargin = 6;
|
const u32 kOSDMargin = 6;
|
||||||
|
const int kLogoWidth = 192;
|
||||||
|
|
||||||
|
|
||||||
ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent)
|
ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent)
|
||||||
@ -80,6 +82,29 @@ ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent)
|
|||||||
|
|
||||||
loadConfig();
|
loadConfig();
|
||||||
setFilter(mainWindow->getWindowConfig().GetBool("ScreenFilter"));
|
setFilter(mainWindow->getWindowConfig().GetBool("ScreenFilter"));
|
||||||
|
|
||||||
|
splashLogo = QPixmap(":/melon-logo");
|
||||||
|
|
||||||
|
strncpy(splashText[0].text, "File->Open ROM...", 256);
|
||||||
|
splashText[0].id = 0x80000000;
|
||||||
|
splashText[0].color = 0;
|
||||||
|
splashText[0].rendered = false;
|
||||||
|
splashText[0].rainbowstart = -1;
|
||||||
|
|
||||||
|
strncpy(splashText[1].text, "to get started", 256);
|
||||||
|
splashText[1].id = 0x80000001;
|
||||||
|
splashText[1].color = 0;
|
||||||
|
splashText[1].rendered = false;
|
||||||
|
splashText[1].rainbowstart = -1;
|
||||||
|
|
||||||
|
std::string url = MELONDS_URL;
|
||||||
|
int urlpos = url.find("://");
|
||||||
|
urlpos = (urlpos == std::string::npos) ? 0 : urlpos+3;
|
||||||
|
strncpy(splashText[2].text, url.c_str() + urlpos, 256);
|
||||||
|
splashText[2].id = 0x80000002;
|
||||||
|
splashText[2].color = 0;
|
||||||
|
splashText[2].rendered = false;
|
||||||
|
splashText[2].rainbowstart = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenPanel::~ScreenPanel()
|
ScreenPanel::~ScreenPanel()
|
||||||
@ -150,6 +175,8 @@ void ScreenPanel::setupScreenLayout()
|
|||||||
aspectBot);
|
aspectBot);
|
||||||
|
|
||||||
numScreens = layout.GetScreenTransforms(screenMatrix[0], screenKind);
|
numScreens = layout.GetScreenTransforms(screenMatrix[0], screenKind);
|
||||||
|
|
||||||
|
calcSplashLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize ScreenPanel::screenGetMinSize(int factor = 1)
|
QSize ScreenPanel::screenGetMinSize(int factor = 1)
|
||||||
@ -222,6 +249,7 @@ void ScreenPanel::resizeEvent(QResizeEvent* event)
|
|||||||
void ScreenPanel::mousePressEvent(QMouseEvent* event)
|
void ScreenPanel::mousePressEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
event->accept();
|
event->accept();
|
||||||
|
if (!emuInstance->emuIsActive()) { touching = false; return; }
|
||||||
if (event->button() != Qt::LeftButton) return;
|
if (event->button() != Qt::LeftButton) return;
|
||||||
|
|
||||||
int x = event->pos().x();
|
int x = event->pos().x();
|
||||||
@ -230,21 +258,20 @@ void ScreenPanel::mousePressEvent(QMouseEvent* event)
|
|||||||
if (layout.GetTouchCoords(x, y, false))
|
if (layout.GetTouchCoords(x, y, false))
|
||||||
{
|
{
|
||||||
touching = true;
|
touching = true;
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->touchScreen(x, y);
|
||||||
emuInstance->getNDS()->TouchScreen(x, y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenPanel::mouseReleaseEvent(QMouseEvent* event)
|
void ScreenPanel::mouseReleaseEvent(QMouseEvent* event)
|
||||||
{
|
{
|
||||||
event->accept();
|
event->accept();
|
||||||
|
if (!emuInstance->emuIsActive()) { touching = false; return; }
|
||||||
if (event->button() != Qt::LeftButton) return;
|
if (event->button() != Qt::LeftButton) return;
|
||||||
|
|
||||||
if (touching)
|
if (touching)
|
||||||
{
|
{
|
||||||
touching = false;
|
touching = false;
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->releaseScreen();
|
||||||
emuInstance->getNDS()->ReleaseScreen();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,6 +281,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event)
|
|||||||
|
|
||||||
showCursor();
|
showCursor();
|
||||||
|
|
||||||
|
if (!emuInstance->emuIsActive()) return;
|
||||||
//if (!(event->buttons() & Qt::LeftButton)) return;
|
//if (!(event->buttons() & Qt::LeftButton)) return;
|
||||||
if (!touching) return;
|
if (!touching) return;
|
||||||
|
|
||||||
@ -262,14 +290,14 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event)
|
|||||||
|
|
||||||
if (layout.GetTouchCoords(x, y, true))
|
if (layout.GetTouchCoords(x, y, true))
|
||||||
{
|
{
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->touchScreen(x, y);
|
||||||
emuInstance->getNDS()->TouchScreen(x, y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenPanel::tabletEvent(QTabletEvent* event)
|
void ScreenPanel::tabletEvent(QTabletEvent* event)
|
||||||
{
|
{
|
||||||
event->accept();
|
event->accept();
|
||||||
|
if (!emuInstance->emuIsActive()) { touching = false; return; }
|
||||||
|
|
||||||
switch(event->type())
|
switch(event->type())
|
||||||
{
|
{
|
||||||
@ -287,16 +315,14 @@ void ScreenPanel::tabletEvent(QTabletEvent* event)
|
|||||||
if (layout.GetTouchCoords(x, y, event->type()==QEvent::TabletMove))
|
if (layout.GetTouchCoords(x, y, event->type()==QEvent::TabletMove))
|
||||||
{
|
{
|
||||||
touching = true;
|
touching = true;
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->touchScreen(x, y);
|
||||||
emuInstance->getNDS()->TouchScreen(x, y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEvent::TabletRelease:
|
case QEvent::TabletRelease:
|
||||||
if (touching)
|
if (touching)
|
||||||
{
|
{
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->releaseScreen();
|
||||||
emuInstance->getNDS()->ReleaseScreen();
|
|
||||||
touching = false;
|
touching = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -313,6 +339,7 @@ void ScreenPanel::touchEvent(QTouchEvent* event)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
event->accept();
|
event->accept();
|
||||||
|
if (!emuInstance->emuIsActive()) { touching = false; return; }
|
||||||
|
|
||||||
switch(event->type())
|
switch(event->type())
|
||||||
{
|
{
|
||||||
@ -333,16 +360,14 @@ void ScreenPanel::touchEvent(QTouchEvent* event)
|
|||||||
if (layout.GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate))
|
if (layout.GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate))
|
||||||
{
|
{
|
||||||
touching = true;
|
touching = true;
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->touchScreen(x, y);
|
||||||
emuInstance->getNDS()->TouchScreen(x, y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QEvent::TouchEnd:
|
case QEvent::TouchEnd:
|
||||||
if (touching)
|
if (touching)
|
||||||
{
|
{
|
||||||
assert(emuInstance->getNDS() != nullptr);
|
emuInstance->releaseScreen();
|
||||||
emuInstance->getNDS()->ReleaseScreen();
|
|
||||||
touching = false;
|
touching = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -360,6 +385,10 @@ bool ScreenPanel::event(QEvent* event)
|
|||||||
touchEvent((QTouchEvent*)event);
|
touchEvent((QTouchEvent*)event);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (event->type() == QEvent::FocusIn)
|
||||||
|
mainWindow->onFocusIn();
|
||||||
|
else if (event->type() == QEvent::FocusOut)
|
||||||
|
mainWindow->onFocusOut();
|
||||||
|
|
||||||
return QWidget::event(event);
|
return QWidget::event(event);
|
||||||
}
|
}
|
||||||
@ -478,8 +507,14 @@ void ScreenPanel::osdRenderItem(OSDItem* item)
|
|||||||
u32 color = item->color;
|
u32 color = item->color;
|
||||||
|
|
||||||
bool rainbow = (color == 0);
|
bool rainbow = (color == 0);
|
||||||
u32 ticks = (u32)QDateTime::currentMSecsSinceEpoch();
|
u32 rainbowinc;
|
||||||
u32 rainbowinc = ((text[0] * 17) + (ticks * 13)) % 600;
|
if (item->rainbowstart == -1)
|
||||||
|
{
|
||||||
|
u32 ticks = (u32) QDateTime::currentMSecsSinceEpoch();
|
||||||
|
rainbowinc = ((text[0] * 17) + (ticks * 13)) % 600;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rainbowinc = (u32)item->rainbowstart;
|
||||||
|
|
||||||
color |= 0xFF000000;
|
color |= 0xFF000000;
|
||||||
const u32 shadow = 0xE0000000;
|
const u32 shadow = 0xE0000000;
|
||||||
@ -577,6 +612,8 @@ void ScreenPanel::osdRenderItem(OSDItem* item)
|
|||||||
bitmap[(y * w) + x] = shadow;
|
bitmap[(y * w) + x] = shadow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item->rainbowend = (int)rainbowinc;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenPanel::osdDeleteItem(OSDItem* item)
|
void ScreenPanel::osdDeleteItem(OSDItem* item)
|
||||||
@ -598,11 +635,12 @@ void ScreenPanel::osdAddMessage(unsigned int color, const char* text)
|
|||||||
|
|
||||||
OSDItem item;
|
OSDItem item;
|
||||||
|
|
||||||
item.id = osdID++;
|
item.id = (osdID++) & 0x7FFFFFFF;
|
||||||
item.timestamp = QDateTime::currentMSecsSinceEpoch();
|
item.timestamp = QDateTime::currentMSecsSinceEpoch();
|
||||||
strncpy(item.text, text, 255); item.text[255] = '\0';
|
strncpy(item.text, text, 255); item.text[255] = '\0';
|
||||||
item.color = color;
|
item.color = color;
|
||||||
item.rendered = false;
|
item.rendered = false;
|
||||||
|
item.rainbowstart = -1;
|
||||||
|
|
||||||
osdItems.push_back(item);
|
osdItems.push_back(item);
|
||||||
|
|
||||||
@ -636,6 +674,73 @@ void ScreenPanel::osdUpdate()
|
|||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render splashscreen text items if needed
|
||||||
|
|
||||||
|
int rainbowinc = -1;
|
||||||
|
bool needrecalc = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (!splashText[i].rendered)
|
||||||
|
{
|
||||||
|
splashText[i].rainbowstart = rainbowinc;
|
||||||
|
osdRenderItem(&splashText[i]);
|
||||||
|
splashText[i].rendered = true;
|
||||||
|
rainbowinc = splashText[i].rainbowend;
|
||||||
|
needrecalc = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osdMutex.unlock();
|
||||||
|
|
||||||
|
if (needrecalc)
|
||||||
|
calcSplashLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScreenPanel::calcSplashLayout()
|
||||||
|
{
|
||||||
|
if (!splashText[0].rendered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
osdMutex.lock();
|
||||||
|
|
||||||
|
int w = width();
|
||||||
|
int h = height();
|
||||||
|
|
||||||
|
int xlogo = (w - kLogoWidth) / 2;
|
||||||
|
int ylogo = (h - kLogoWidth) / 2;
|
||||||
|
|
||||||
|
// top text
|
||||||
|
int totalwidth = splashText[0].bitmap.width() + 6 + splashText[1].bitmap.width();
|
||||||
|
if (totalwidth >= w)
|
||||||
|
{
|
||||||
|
// stacked vertically
|
||||||
|
splashPos[0].setX((width() - splashText[0].bitmap.width()) / 2);
|
||||||
|
splashPos[1].setX((width() - splashText[1].bitmap.width()) / 2);
|
||||||
|
|
||||||
|
int basey = ylogo / 2;
|
||||||
|
splashPos[0].setY(basey - splashText[0].bitmap.height() - 1);
|
||||||
|
splashPos[1].setY(basey + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// horizontal
|
||||||
|
splashPos[0].setX((w - totalwidth) / 2);
|
||||||
|
splashPos[1].setX(splashPos[0].x() + splashText[0].bitmap.width() + 6);
|
||||||
|
|
||||||
|
int basey = (ylogo - splashText[0].bitmap.height()) / 2;
|
||||||
|
splashPos[0].setY(basey);
|
||||||
|
splashPos[1].setY(basey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom text
|
||||||
|
splashPos[2].setX((w - splashText[2].bitmap.width()) / 2);
|
||||||
|
splashPos[2].setY(ylogo + kLogoWidth + ((ylogo - splashText[2].bitmap.height()) / 2));
|
||||||
|
|
||||||
|
// logo
|
||||||
|
splashPos[3].setX(xlogo);
|
||||||
|
splashPos[3].setY(ylogo);
|
||||||
|
|
||||||
osdMutex.unlock();
|
osdMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,20 +783,21 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event)
|
|||||||
|
|
||||||
if (emuThread->emuIsActive())
|
if (emuThread->emuIsActive())
|
||||||
{
|
{
|
||||||
|
emuInstance->renderLock.lock();
|
||||||
auto nds = emuInstance->getNDS();
|
auto nds = emuInstance->getNDS();
|
||||||
|
|
||||||
assert(nds != nullptr);
|
assert(nds != nullptr);
|
||||||
emuThread->FrontBufferLock.lock();
|
emuThread->frontBufferLock.lock();
|
||||||
int frontbuf = emuThread->FrontBuffer;
|
int frontbuf = emuThread->frontBuffer;
|
||||||
if (!nds->GPU.Framebuffer[frontbuf][0] || !nds->GPU.Framebuffer[frontbuf][1])
|
if (!nds->GPU.Framebuffer[frontbuf][0] || !nds->GPU.Framebuffer[frontbuf][1])
|
||||||
{
|
{
|
||||||
emuThread->FrontBufferLock.unlock();
|
emuThread->frontBufferLock.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(screen[0].scanLine(0), nds->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4);
|
memcpy(screen[0].scanLine(0), nds->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4);
|
||||||
memcpy(screen[1].scanLine(0), nds->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4);
|
memcpy(screen[1].scanLine(0), nds->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4);
|
||||||
emuThread->FrontBufferLock.unlock();
|
emuThread->frontBufferLock.unlock();
|
||||||
|
|
||||||
QRect screenrc(0, 0, 256, 192);
|
QRect screenrc(0, 0, 256, 192);
|
||||||
|
|
||||||
@ -700,9 +806,24 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event)
|
|||||||
painter.setTransform(screenTrans[i]);
|
painter.setTransform(screenTrans[i]);
|
||||||
painter.drawImage(screenrc, screen[screenKind[i]]);
|
painter.drawImage(screenrc, screen[screenKind[i]]);
|
||||||
}
|
}
|
||||||
|
emuInstance->renderLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
osdUpdate();
|
osdUpdate();
|
||||||
|
|
||||||
|
if (!emuThread->emuIsActive())
|
||||||
|
{
|
||||||
|
// splashscreen
|
||||||
|
osdMutex.lock();
|
||||||
|
|
||||||
|
painter.drawPixmap(QRect(splashPos[3], QSize(kLogoWidth, kLogoWidth)), splashLogo);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
painter.drawImage(splashPos[i], splashText[i].bitmap);
|
||||||
|
|
||||||
|
osdMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
if (osdEnabled)
|
if (osdEnabled)
|
||||||
{
|
{
|
||||||
osdMutex.lock();
|
osdMutex.lock();
|
||||||
@ -736,6 +857,8 @@ ScreenPanelGL::ScreenPanelGL(QWidget* parent) : ScreenPanel(parent)
|
|||||||
setAttribute(Qt::WA_KeyCompression, false);
|
setAttribute(Qt::WA_KeyCompression, false);
|
||||||
setFocusPolicy(Qt::StrongFocus);
|
setFocusPolicy(Qt::StrongFocus);
|
||||||
setMinimumSize(screenGetMinSize());
|
setMinimumSize(screenGetMinSize());
|
||||||
|
|
||||||
|
glInited = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenPanelGL::~ScreenPanelGL()
|
ScreenPanelGL::~ScreenPanelGL()
|
||||||
@ -747,14 +870,14 @@ bool ScreenPanelGL::createContext()
|
|||||||
|
|
||||||
// if our parent window is parented to another window, we will
|
// if our parent window is parented to another window, we will
|
||||||
// share our OpenGL context with that window
|
// share our OpenGL context with that window
|
||||||
|
MainWindow* ourwin = (MainWindow*)parentWidget();
|
||||||
MainWindow* parentwin = (MainWindow*)parentWidget()->parentWidget();
|
MainWindow* parentwin = (MainWindow*)parentWidget()->parentWidget();
|
||||||
if (parentwin)
|
//if (parentwin)
|
||||||
|
if (ourwin->getWindowID() != 0)
|
||||||
{
|
{
|
||||||
if (windowinfo.has_value())
|
if (windowinfo.has_value())
|
||||||
{
|
if (glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo))
|
||||||
glContext = parentwin->getOGLContext()->CreateSharedContext(*windowinfo);
|
glContext->DoneCurrent();
|
||||||
glContext->DoneCurrent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -762,10 +885,8 @@ bool ScreenPanelGL::createContext()
|
|||||||
GL::Context::Version{GL::Context::Profile::Core, 4, 3},
|
GL::Context::Version{GL::Context::Profile::Core, 4, 3},
|
||||||
GL::Context::Version{GL::Context::Profile::Core, 3, 2}};
|
GL::Context::Version{GL::Context::Profile::Core, 3, 2}};
|
||||||
if (windowinfo.has_value())
|
if (windowinfo.has_value())
|
||||||
{
|
if (glContext = GL::Context::Create(*windowinfo, versionsToTry))
|
||||||
glContext = GL::Context::Create(*windowinfo, versionsToTry);
|
glContext->DoneCurrent();
|
||||||
glContext->DoneCurrent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return glContext != nullptr;
|
return glContext != nullptr;
|
||||||
@ -781,6 +902,7 @@ void ScreenPanelGL::setSwapInterval(int intv)
|
|||||||
void ScreenPanelGL::initOpenGL()
|
void ScreenPanelGL::initOpenGL()
|
||||||
{
|
{
|
||||||
if (!glContext) return;
|
if (!glContext) return;
|
||||||
|
if (glInited) return;
|
||||||
|
|
||||||
glContext->MakeCurrent();
|
glContext->MakeCurrent();
|
||||||
|
|
||||||
@ -856,6 +978,7 @@ void ScreenPanelGL::initOpenGL()
|
|||||||
osdPosULoc = glGetUniformLocation(osdShader, "uOSDPos");
|
osdPosULoc = glGetUniformLocation(osdShader, "uOSDPos");
|
||||||
osdSizeULoc = glGetUniformLocation(osdShader, "uOSDSize");
|
osdSizeULoc = glGetUniformLocation(osdShader, "uOSDSize");
|
||||||
osdScaleFactorULoc = glGetUniformLocation(osdShader, "uScaleFactor");
|
osdScaleFactorULoc = glGetUniformLocation(osdShader, "uScaleFactor");
|
||||||
|
osdTexScaleULoc = glGetUniformLocation(osdShader, "uTexScale");
|
||||||
|
|
||||||
const float osdvertices[6*2] =
|
const float osdvertices[6*2] =
|
||||||
{
|
{
|
||||||
@ -876,12 +999,26 @@ void ScreenPanelGL::initOpenGL()
|
|||||||
glEnableVertexAttribArray(0); // position
|
glEnableVertexAttribArray(0); // position
|
||||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
|
||||||
|
|
||||||
|
// splash logo texture
|
||||||
|
QImage logo = splashLogo.scaled(kLogoWidth*2, kLogoWidth*2).toImage();
|
||||||
|
GLuint tex;
|
||||||
|
glGenTextures(1, &tex);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, logo.width(), logo.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, logo.bits());
|
||||||
|
logoTexture = tex;
|
||||||
|
|
||||||
transferLayout();
|
transferLayout();
|
||||||
|
glInited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenPanelGL::deinitOpenGL()
|
void ScreenPanelGL::deinitOpenGL()
|
||||||
{
|
{
|
||||||
if (!glContext) return;
|
if (!glContext) return;
|
||||||
|
if (!glInited) return;
|
||||||
|
|
||||||
glDeleteTextures(1, &screenTexture);
|
glDeleteTextures(1, &screenTexture);
|
||||||
|
|
||||||
@ -900,12 +1037,15 @@ void ScreenPanelGL::deinitOpenGL()
|
|||||||
glDeleteVertexArrays(1, &osdVertexArray);
|
glDeleteVertexArrays(1, &osdVertexArray);
|
||||||
glDeleteBuffers(1, &osdVertexBuffer);
|
glDeleteBuffers(1, &osdVertexBuffer);
|
||||||
|
|
||||||
|
glDeleteTextures(1, &logoTexture);
|
||||||
|
|
||||||
glDeleteProgram(osdShader);
|
glDeleteProgram(osdShader);
|
||||||
|
|
||||||
|
|
||||||
glContext->DoneCurrent();
|
glContext->DoneCurrent();
|
||||||
|
|
||||||
lastScreenWidth = lastScreenHeight = -1;
|
lastScreenWidth = lastScreenHeight = -1;
|
||||||
|
glInited = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScreenPanelGL::makeCurrentGL()
|
void ScreenPanelGL::makeCurrentGL()
|
||||||
@ -947,9 +1087,6 @@ void ScreenPanelGL::drawScreenGL()
|
|||||||
{
|
{
|
||||||
if (!glContext) return;
|
if (!glContext) return;
|
||||||
|
|
||||||
auto nds = emuInstance->getNDS();
|
|
||||||
if (!nds) return;
|
|
||||||
|
|
||||||
auto emuThread = emuInstance->getEmuThread();
|
auto emuThread = emuInstance->getEmuThread();
|
||||||
|
|
||||||
glContext->MakeCurrent();
|
glContext->MakeCurrent();
|
||||||
@ -968,51 +1105,101 @@ void ScreenPanelGL::drawScreenGL()
|
|||||||
|
|
||||||
glViewport(0, 0, w, h);
|
glViewport(0, 0, w, h);
|
||||||
|
|
||||||
glUseProgram(screenShaderProgram);
|
if (emuThread->emuIsActive())
|
||||||
glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor);
|
{
|
||||||
|
auto nds = emuInstance->getNDS();
|
||||||
|
|
||||||
int frontbuf = emuThread->FrontBuffer;
|
glUseProgram(screenShaderProgram);
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor);
|
||||||
|
|
||||||
|
int frontbuf = emuThread->frontBuffer;
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
#ifdef OGLRENDERER_ENABLED
|
#ifdef OGLRENDERER_ENABLED
|
||||||
if (nds->GPU.GetRenderer3D().Accelerated)
|
if (nds->GPU.GetRenderer3D().Accelerated)
|
||||||
{
|
|
||||||
// hardware-accelerated render
|
|
||||||
nds->GPU.GetRenderer3D().BindOutputTexture(frontbuf);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
// regular render
|
|
||||||
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
|
||||||
|
|
||||||
if (nds->GPU.Framebuffer[frontbuf][0] && nds->GPU.Framebuffer[frontbuf][1])
|
|
||||||
{
|
{
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA,
|
// hardware-accelerated render
|
||||||
GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][0].get());
|
nds->GPU.GetRenderer3D().BindOutputTexture(frontbuf);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA,
|
} else
|
||||||
GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][1].get());
|
#endif
|
||||||
|
{
|
||||||
|
// regular render
|
||||||
|
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
||||||
|
|
||||||
|
if (nds->GPU.Framebuffer[frontbuf][0] && nds->GPU.Framebuffer[frontbuf][1])
|
||||||
|
{
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][0].get());
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192 + 2, 256, 192, GL_RGBA,
|
||||||
|
GL_UNSIGNED_BYTE, nds->GPU.Framebuffer[frontbuf][1].get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screenSettingsLock.lock();
|
||||||
|
|
||||||
|
GLint filter = this->filter ? GL_LINEAR : GL_NEAREST;
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer);
|
||||||
|
glBindVertexArray(screenVertexArray);
|
||||||
|
|
||||||
|
for (int i = 0; i < numScreens; i++)
|
||||||
|
{
|
||||||
|
glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]);
|
||||||
|
glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2 * 3, 2 * 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
screenSettingsLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
screenSettingsLock.lock();
|
|
||||||
|
|
||||||
GLint filter = this->filter ? GL_LINEAR : GL_NEAREST;
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
|
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer);
|
|
||||||
glBindVertexArray(screenVertexArray);
|
|
||||||
|
|
||||||
for (int i = 0; i < numScreens; i++)
|
|
||||||
{
|
|
||||||
glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]);
|
|
||||||
glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3);
|
|
||||||
}
|
|
||||||
|
|
||||||
screenSettingsLock.unlock();
|
|
||||||
|
|
||||||
osdUpdate();
|
osdUpdate();
|
||||||
|
|
||||||
|
if (!emuThread->emuIsActive())
|
||||||
|
{
|
||||||
|
// splashscreen
|
||||||
|
osdMutex.lock();
|
||||||
|
|
||||||
|
glUseProgram(osdShader);
|
||||||
|
|
||||||
|
glUniform2f(osdScreenSizeULoc, w, h);
|
||||||
|
glUniform1f(osdScaleFactorULoc, factor);
|
||||||
|
glUniform1f(osdTexScaleULoc, 2.0);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer);
|
||||||
|
glBindVertexArray(osdVertexArray);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, logoTexture);
|
||||||
|
glUniform2i(osdPosULoc, splashPos[3].x(), splashPos[3].y());
|
||||||
|
glUniform2i(osdSizeULoc, kLogoWidth, kLogoWidth);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 2*3);
|
||||||
|
|
||||||
|
glUniform1f(osdTexScaleULoc, 1.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
OSDItem& item = splashText[i];
|
||||||
|
|
||||||
|
if (!osdTextures.count(item.id))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, osdTextures[item.id]);
|
||||||
|
glUniform2i(osdPosULoc, splashPos[i].x(), splashPos[i].y());
|
||||||
|
glUniform2i(osdSizeULoc, item.bitmap.width(), item.bitmap.height());
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 2*3);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glUseProgram(0);
|
||||||
|
|
||||||
|
osdMutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
if (osdEnabled)
|
if (osdEnabled)
|
||||||
{
|
{
|
||||||
osdMutex.lock();
|
osdMutex.lock();
|
||||||
@ -1023,6 +1210,7 @@ void ScreenPanelGL::drawScreenGL()
|
|||||||
|
|
||||||
glUniform2f(osdScreenSizeULoc, w, h);
|
glUniform2f(osdScreenSizeULoc, w, h);
|
||||||
glUniform1f(osdScaleFactorULoc, factor);
|
glUniform1f(osdScaleFactorULoc, factor);
|
||||||
|
glUniform1f(osdTexScaleULoc, 1.0);
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer);
|
glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer);
|
||||||
glBindVertexArray(osdVertexArray);
|
glBindVertexArray(osdVertexArray);
|
||||||
|
@ -110,6 +110,9 @@ protected:
|
|||||||
|
|
||||||
bool rendered;
|
bool rendered;
|
||||||
QImage bitmap;
|
QImage bitmap;
|
||||||
|
|
||||||
|
int rainbowstart;
|
||||||
|
int rainbowend;
|
||||||
};
|
};
|
||||||
|
|
||||||
QMutex osdMutex;
|
QMutex osdMutex;
|
||||||
@ -117,6 +120,10 @@ protected:
|
|||||||
unsigned int osdID;
|
unsigned int osdID;
|
||||||
std::deque<OSDItem> osdItems;
|
std::deque<OSDItem> osdItems;
|
||||||
|
|
||||||
|
QPixmap splashLogo;
|
||||||
|
OSDItem splashText[3];
|
||||||
|
QPoint splashPos[4];
|
||||||
|
|
||||||
void loadConfig();
|
void loadConfig();
|
||||||
|
|
||||||
virtual void setupScreenLayout();
|
virtual void setupScreenLayout();
|
||||||
@ -141,6 +148,8 @@ protected:
|
|||||||
virtual void osdDeleteItem(OSDItem* item);
|
virtual void osdDeleteItem(OSDItem* item);
|
||||||
|
|
||||||
void osdUpdate();
|
void osdUpdate();
|
||||||
|
|
||||||
|
void calcSplashLayout();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -197,11 +206,12 @@ private:
|
|||||||
void setupScreenLayout() override;
|
void setupScreenLayout() override;
|
||||||
|
|
||||||
std::unique_ptr<GL::Context> glContext;
|
std::unique_ptr<GL::Context> glContext;
|
||||||
|
bool glInited;
|
||||||
|
|
||||||
GLuint screenVertexBuffer, screenVertexArray;
|
GLuint screenVertexBuffer, screenVertexArray;
|
||||||
GLuint screenTexture;
|
GLuint screenTexture;
|
||||||
GLuint screenShaderProgram;
|
GLuint screenShaderProgram;
|
||||||
GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc;
|
GLint screenShaderTransformULoc, screenShaderScreenSizeULoc;
|
||||||
|
|
||||||
QMutex screenSettingsLock;
|
QMutex screenSettingsLock;
|
||||||
WindowInfo windowInfo;
|
WindowInfo windowInfo;
|
||||||
@ -210,11 +220,14 @@ private:
|
|||||||
|
|
||||||
GLuint osdShader;
|
GLuint osdShader;
|
||||||
GLint osdScreenSizeULoc, osdPosULoc, osdSizeULoc;
|
GLint osdScreenSizeULoc, osdPosULoc, osdSizeULoc;
|
||||||
GLfloat osdScaleFactorULoc;
|
GLint osdScaleFactorULoc;
|
||||||
|
GLint osdTexScaleULoc;
|
||||||
GLuint osdVertexArray;
|
GLuint osdVertexArray;
|
||||||
GLuint osdVertexBuffer;
|
GLuint osdVertexBuffer;
|
||||||
std::map<unsigned int, GLuint> osdTextures;
|
std::map<unsigned int, GLuint> osdTextures;
|
||||||
|
|
||||||
|
GLuint logoTexture;
|
||||||
|
|
||||||
void osdRenderItem(OSDItem* item) override;
|
void osdRenderItem(OSDItem* item) override;
|
||||||
void osdDeleteItem(OSDItem* item) override;
|
void osdDeleteItem(OSDItem* item) override;
|
||||||
};
|
};
|
||||||
|
@ -121,6 +121,12 @@ void VideoSettingsDialog::on_VideoSettingsDialog_accepted()
|
|||||||
|
|
||||||
void VideoSettingsDialog::on_VideoSettingsDialog_rejected()
|
void VideoSettingsDialog::on_VideoSettingsDialog_rejected()
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool old_gl = UsesGL();
|
bool old_gl = UsesGL();
|
||||||
|
|
||||||
auto& cfg = emuInstance->getGlobalConfig();
|
auto& cfg = emuInstance->getGlobalConfig();
|
||||||
|
@ -94,6 +94,13 @@ WifiSettingsDialog::~WifiSettingsDialog()
|
|||||||
|
|
||||||
void WifiSettingsDialog::done(int r)
|
void WifiSettingsDialog::done(int r)
|
||||||
{
|
{
|
||||||
|
if (!((MainWindow*)parent())->getEmuInstance())
|
||||||
|
{
|
||||||
|
QDialog::done(r);
|
||||||
|
closeDlg();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
needsReset = false;
|
needsReset = false;
|
||||||
|
|
||||||
if (r == QDialog::Accepted)
|
if (r == QDialog::Accepted)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -110,6 +110,13 @@ public:
|
|||||||
|
|
||||||
EmuInstance* getEmuInstance() { return emuInstance; }
|
EmuInstance* getEmuInstance() { return emuInstance; }
|
||||||
Config::Table& getWindowConfig() { return windowCfg; }
|
Config::Table& getWindowConfig() { return windowCfg; }
|
||||||
|
int getWindowID() { return windowID; }
|
||||||
|
|
||||||
|
bool winHasMenu() { return hasMenu; }
|
||||||
|
|
||||||
|
void saveEnabled(bool enabled);
|
||||||
|
|
||||||
|
void toggleFullscreen();
|
||||||
|
|
||||||
bool hasOpenGL() { return hasOGL; }
|
bool hasOpenGL() { return hasOGL; }
|
||||||
GL::Context* getOGLContext();
|
GL::Context* getOGLContext();
|
||||||
@ -124,11 +131,18 @@ public:
|
|||||||
|
|
||||||
void onAppStateChanged(Qt::ApplicationState state);
|
void onAppStateChanged(Qt::ApplicationState state);
|
||||||
|
|
||||||
|
void onFocusIn();
|
||||||
|
void onFocusOut();
|
||||||
|
bool isFocused() { return focused; }
|
||||||
|
|
||||||
void osdAddMessage(unsigned int color, const char* msg);
|
void osdAddMessage(unsigned int color, const char* msg);
|
||||||
|
|
||||||
// called when the MP interface is changed
|
// called when the MP interface is changed
|
||||||
void updateMPInterface(melonDS::MPInterfaceType type);
|
void updateMPInterface(melonDS::MPInterfaceType type);
|
||||||
|
|
||||||
|
void loadRecentFilesMenu(bool loadcfg);
|
||||||
|
//void updateVideoSettings(bool glchange);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent* event) override;
|
void keyPressEvent(QKeyEvent* event) override;
|
||||||
void keyReleaseEvent(QKeyEvent* event) override;
|
void keyReleaseEvent(QKeyEvent* event) override;
|
||||||
@ -208,6 +222,7 @@ private slots:
|
|||||||
void onChangeScreenSizing(QAction* act);
|
void onChangeScreenSizing(QAction* act);
|
||||||
void onChangeScreenAspect(QAction* act);
|
void onChangeScreenAspect(QAction* act);
|
||||||
void onChangeIntegerScaling(bool checked);
|
void onChangeIntegerScaling(bool checked);
|
||||||
|
void onOpenNewWindow();
|
||||||
void onChangeScreenFiltering(bool checked);
|
void onChangeScreenFiltering(bool checked);
|
||||||
void onChangeShowOSD(bool checked);
|
void onChangeShowOSD(bool checked);
|
||||||
void onChangeLimitFramerate(bool checked);
|
void onChangeLimitFramerate(bool checked);
|
||||||
@ -251,6 +266,9 @@ private:
|
|||||||
bool pausedManually;
|
bool pausedManually;
|
||||||
|
|
||||||
int windowID;
|
int windowID;
|
||||||
|
bool enabledSaved;
|
||||||
|
|
||||||
|
bool focused;
|
||||||
|
|
||||||
EmuInstance* emuInstance;
|
EmuInstance* emuInstance;
|
||||||
EmuThread* emuThread;
|
EmuThread* emuThread;
|
||||||
@ -262,6 +280,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
ScreenPanel* panel;
|
ScreenPanel* panel;
|
||||||
|
|
||||||
|
bool hasMenu;
|
||||||
|
|
||||||
QAction* actOpenROM;
|
QAction* actOpenROM;
|
||||||
QAction* actBootFirmware;
|
QAction* actBootFirmware;
|
||||||
QAction* actCurrentCart;
|
QAction* actCurrentCart;
|
||||||
@ -269,7 +289,7 @@ public:
|
|||||||
QAction* actEjectCart;
|
QAction* actEjectCart;
|
||||||
QAction* actCurrentGBACart;
|
QAction* actCurrentGBACart;
|
||||||
QAction* actInsertGBACart;
|
QAction* actInsertGBACart;
|
||||||
QAction* actInsertGBAAddon[2];
|
QList<QAction*> actInsertGBAAddon;
|
||||||
QAction* actEjectGBACart;
|
QAction* actEjectGBACart;
|
||||||
QAction* actImportSavefile;
|
QAction* actImportSavefile;
|
||||||
QAction* actSaveState[9];
|
QAction* actSaveState[9];
|
||||||
@ -325,12 +345,13 @@ public:
|
|||||||
QAction** actScreenAspectTop;
|
QAction** actScreenAspectTop;
|
||||||
QActionGroup* grpScreenAspectBot;
|
QActionGroup* grpScreenAspectBot;
|
||||||
QAction** actScreenAspectBot;
|
QAction** actScreenAspectBot;
|
||||||
|
QAction* actNewWindow;
|
||||||
QAction* actScreenFiltering;
|
QAction* actScreenFiltering;
|
||||||
QAction* actShowOSD;
|
QAction* actShowOSD;
|
||||||
QAction* actLimitFramerate;
|
QAction* actLimitFramerate;
|
||||||
QAction* actAudioSync;
|
QAction* actAudioSync;
|
||||||
|
|
||||||
|
QAction* actAbout;
|
||||||
};
|
};
|
||||||
|
|
||||||
void ToggleFullscreen(MainWindow* mainWindow);
|
|
||||||
|
|
||||||
#endif // WINDOW_H
|
#endif // WINDOW_H
|
||||||
|
@ -168,6 +168,18 @@ int numEmuInstances()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void broadcastInstanceCommand(int cmd, QVariant& param, int sourceinst)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < kMaxEmuInstances; i++)
|
||||||
|
{
|
||||||
|
if (i == sourceinst) continue;
|
||||||
|
if (!emuInstances[i]) continue;
|
||||||
|
|
||||||
|
emuInstances[i]->handleCommand(cmd, param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void pathInit()
|
void pathInit()
|
||||||
{
|
{
|
||||||
// First, check for the portable directory next to the executable.
|
// First, check for the portable directory next to the executable.
|
||||||
@ -250,10 +262,8 @@ bool MelonApplication::event(QEvent *event)
|
|||||||
MainWindow* win = inst->getMainWindow();
|
MainWindow* win = inst->getMainWindow();
|
||||||
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent*>(event);
|
QFileOpenEvent *openEvent = static_cast<QFileOpenEvent*>(event);
|
||||||
|
|
||||||
inst->getEmuThread()->emuPause();
|
|
||||||
const QStringList file = win->splitArchivePath(openEvent->file(), true);
|
const QStringList file = win->splitArchivePath(openEvent->file(), true);
|
||||||
if (!win->preloadROMs(file, {}, true))
|
win->preloadROMs(file, {}, true);
|
||||||
inst->getEmuThread()->emuUnpause();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return QApplication::event(event);
|
return QApplication::event(event);
|
||||||
@ -364,6 +374,9 @@ int main(int argc, char** argv)
|
|||||||
if (memberSyntaxUsed) printf("Warning: use the a.zip|b.nds format at your own risk!\n");
|
if (memberSyntaxUsed) printf("Warning: use the a.zip|b.nds format at your own risk!\n");
|
||||||
|
|
||||||
win->preloadROMs(dsfile, gbafile, options->boot);
|
win->preloadROMs(dsfile, gbafile, options->boot);
|
||||||
|
|
||||||
|
if (options->fullscreen)
|
||||||
|
win->toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = melon.exec();
|
int ret = melon.exec();
|
||||||
|
@ -31,6 +31,15 @@
|
|||||||
#include "ScreenLayout.h"
|
#include "ScreenLayout.h"
|
||||||
#include "MPInterface.h"
|
#include "MPInterface.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
InstCmd_Pause,
|
||||||
|
InstCmd_Unpause,
|
||||||
|
|
||||||
|
InstCmd_UpdateRecentFiles,
|
||||||
|
//InstCmd_UpdateVideoSettings,
|
||||||
|
};
|
||||||
|
|
||||||
class MelonApplication : public QApplication
|
class MelonApplication : public QApplication
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -50,6 +59,8 @@ void deleteEmuInstance(int id);
|
|||||||
void deleteAllEmuInstances(int first = 0);
|
void deleteAllEmuInstances(int first = 0);
|
||||||
int numEmuInstances();
|
int numEmuInstances();
|
||||||
|
|
||||||
|
void broadcastInstanceCommand(int cmd, QVariant& param, int sourceinst);
|
||||||
|
|
||||||
void setMPInterface(melonDS::MPInterfaceType type);
|
void setMPInterface(melonDS::MPInterfaceType type);
|
||||||
|
|
||||||
#endif // MAIN_H
|
#endif // MAIN_H
|
||||||
|
@ -25,5 +25,11 @@
|
|||||||
#define MELONDS_VERSION_SUFFIX "${MELONDS_VERSION_SUFFIX}"
|
#define MELONDS_VERSION_SUFFIX "${MELONDS_VERSION_SUFFIX}"
|
||||||
#define MELONDS_VERSION MELONDS_VERSION_BASE MELONDS_VERSION_SUFFIX
|
#define MELONDS_VERSION MELONDS_VERSION_BASE MELONDS_VERSION_SUFFIX
|
||||||
|
|
||||||
|
#ifdef MELONDS_EMBED_BUILD_INFO
|
||||||
|
#define MELONDS_GIT_BRANCH "${MELONDS_GIT_BRANCH}"
|
||||||
|
#define MELONDS_GIT_HASH "${MELONDS_GIT_HASH}"
|
||||||
|
#define MELONDS_BUILD_PROVIDER "${MELONDS_BUILD_PROVIDER}"
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // VERSION_H
|
#endif // VERSION_H
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user